Skip to content

Commit

Permalink
Merge pull request #282 from jaredh159/multi-stage-expandable-image
Browse files Browse the repository at this point in the history
  • Loading branch information
jaredh159 authored Jan 10, 2024
2 parents ee0d53e + a6764d0 commit da95c3c
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 54 deletions.
147 changes: 102 additions & 45 deletions appviews/src/Onboarding/ExpandableContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { CdnAsset } from './cdn-assets';
import useWindowWidth from '../lib/hooks';

interface Props {
asset: CdnAsset;
asset: CdnAsset | Array<{ duration: number; asset: CdnAsset<'image'> }>;
width: number;
height: number;
lessRounded?: boolean;
Expand All @@ -23,14 +23,21 @@ const ExpandableContent: React.FC<Props> = ({
const [expanded, setExpanded] = useState(false);
const [frameCoords, setFrameCoords] = useState({ x: 0, y: 0 });
const [hasBeenExpanded, setHasBeenExpanded] = useState(false);
const [currentStep, setCurrentStep] = useState(-1);
const [autoPlay, setAutoPlay] = useState(true);
const contentRef = useRef<HTMLDivElement>(null);
const videoRef = useRef<HTMLVideoElement>(null);
const windowWidth = useWindowWidth();

const aspectRatio = width / height;
const isImage = asset.type === `image`;
const isMultiPart = Array.isArray(asset);
const isImage = isMultiPart ? true : asset.type === `image`;
const maxWidth = 800;

useEffect(() => {
setCurrentStep(0);
}, []);

useEffect(() => {
if (contentRef.current) {
const x = contentRef.current.offsetLeft;
Expand All @@ -39,6 +46,18 @@ const ExpandableContent: React.FC<Props> = ({
}
}, [contentRef, windowWidth]);

useEffect(() => {
if (!isMultiPart || !autoPlay) return;
const currentAsset = asset[currentStep];
if (!currentAsset) return;

const timeoutId = setTimeout(() => {
setCurrentStep((currentStep + 1) % asset.length);
}, currentAsset.duration * 1000);

return () => clearTimeout(timeoutId);
}, [autoPlay, currentStep, asset, isMultiPart]);

const style = {
width: expanded
? isImage
Expand Down Expand Up @@ -113,51 +132,89 @@ const ExpandableContent: React.FC<Props> = ({
<span>Click to enlarge</span>
<i className="fa-solid fa-chevron-down animate-bounce" />
</div>
{contentRef.current && asset.type === `video` && (
<div
className={cx(classes, `bg-black`)}
style={style}
onClick={() => {
if (!expanded) {
setExpanded(true);
<div style={style} className="absolute">
{isImage && (
<img
className={classes}
src={
isMultiPart
? asset[currentStep === -1 ? 0 : currentStep]?.asset.url
: asset.url
}
alt=""
onClick={() => {
setExpanded(!expanded);
setHasBeenExpanded(true);
const video = videoRef.current;
if (video) {
video.play();
video.onended = () => setExpanded(false);
}}
/>
)}
{!isImage && !isMultiPart && (
<div
style={style}
onClick={() => {
if (!expanded) {
setExpanded(true);
setHasBeenExpanded(true);
const video = videoRef.current;
if (video) {
video.play();
video.onended = () => setExpanded(false);
}
}
}
}}
>
{asset.render && (
<video
ref={videoRef}
preload="auto"
className={cx(
`transition-[width,height] duration-[250ms] cursor-pointer`,
expanded ? `rounded-md` : `rounded-2xl pointer-events-none`,
)}
width={expanded ? style.width : width}
height={expanded ? style.height : height}
controls
>
<source src={asset.url} type="video/mp4" />
</video>
)}
</div>
)}
{contentRef.current && asset.type === `image` && (
<img
className={classes}
style={style}
src={asset.url}
alt=""
onClick={() => {
setExpanded(!expanded);
setHasBeenExpanded(true);
}}
/>
)}
}}
>
{asset.render && (
<video
ref={videoRef}
preload="auto"
className={cx(
classes,
`transition-[width,height] duration-[250ms] !cursor-pointer`,
expanded ? `rounded-md` : `rounded-2xl pointer-events-none`,
)}
width={expanded ? style.width : width}
height={expanded ? style.height : height}
controls
>
<source src={asset.url} type="video/mp4" />
</video>
)}
</div>
)}
{isMultiPart && (
<div className="rounded-full absolute w-full h-4 -bottom-8 flex justify-center items-center gap-2">
{asset.map((a, i) => (
<div
key={a.asset.url}
onClick={() => {
setAutoPlay(false);
setCurrentStep(i);
}}
className={cx(
`h-3 rounded-full bg-slate-300 transition-[background-color,width,transform] duration-300 hover:scale-110 cursor-pointer relative overflow-hidden`,
currentStep === i ? `w-12` : `w-3`,
expanded && `!bg-slate-400/60`,
)}
>
<div
style={{
// animate-progress-right <- need this since it's never being used as a tailwind utility
animation:
currentStep === i
? `progress-right ${a.duration}s 0s linear infinite`
: `none`,
}}
className={cx(
`absolute left-0 top-0 w-2 h-full bg-violet-400`,
currentStep !== i && `opacity-0`,
expanded && `!bg-violet-500`,
)}
/>
</div>
))}
</div>
)}
</div>
</div>
</>
);
Expand Down
28 changes: 27 additions & 1 deletion appviews/src/Onboarding/Steps/AllowNotifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,33 @@ const AllowNotifications: React.FC<Props> = ({ step }) => {
/>
</div>
<ExpandableContent
asset={assets.os(os).img(`allow-notifications.gif`)}
// asset={assets.os(os).img(`allow-notifications.gif`)}
asset={[
{
duration: 5.73,
asset: {
url: `http://localhost:3000/allow-notifs-1.gif`,
type: `image`,
render: true,
},
},
{
duration: 4.73,
asset: {
url: `http://localhost:3000/allow-notifs-2.gif`,
type: `image`,
render: true,
},
},
{
duration: 3.73,
asset: {
url: `http://localhost:3000/allow-notifs-3.gif`,
type: `image`,
render: true,
},
},
]}
width={800 / 2}
height={600 / 2}
/>
Expand Down
19 changes: 11 additions & 8 deletions appviews/src/Onboarding/cdn-assets.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import type { OSGroup } from './onboarding-store';

export interface CdnAsset {
export type AssetType = 'video' | 'image';

export interface CdnAsset<T extends AssetType = AssetType> {
url: string;
type: 'video' | 'image';
type: T;
render: boolean;
}

Expand All @@ -11,19 +13,19 @@ class CdnAssets implements ExhaustiveAssets {
return new OsCdnAssets(os);
}

video(id: VideoId, render = false): CdnAsset {
video(id: VideoId, render = false): CdnAsset<'video'> {
return { type: `video`, url: `${ENDPOINT}/common/${id}.mp4`, render };
}

osVideo(os: OSGroup, id: OsVideoId, render = false): CdnAsset {
osVideo(os: OSGroup, id: OsVideoId, render = false): CdnAsset<'video'> {
return new OsCdnAssets(os).video(id, render);
}

img(filename: ImgFilename): CdnAsset {
img(filename: ImgFilename): CdnAsset<'image'> {
return { type: `image`, url: `${ENDPOINT}/common/${filename}`, render: true };
}

osImg(os: OSGroup, filename: OsImgFilename): CdnAsset {
osImg(os: OSGroup, filename: OsImgFilename): CdnAsset<'image'> {
return new OsCdnAssets(os).img(filename);
}

Expand All @@ -41,11 +43,11 @@ class CdnAssets implements ExhaustiveAssets {
class OsCdnAssets implements ExhaustiveAssets {
constructor(public readonly os: OSGroup) {}

video(id: OsVideoId, render = false): CdnAsset {
video(id: OsVideoId, render = false): CdnAsset<'video'> {
return { type: `video`, url: `${ENDPOINT}/${this.os}/${id}.mp4`, render };
}

img(filename: OsImgFilename): CdnAsset {
img(filename: OsImgFilename): CdnAsset<'image'> {
return { type: `image`, url: `${ENDPOINT}/${this.os}/${filename}`, render: true };
}

Expand Down Expand Up @@ -87,6 +89,7 @@ const OS_IMAGE_FILENAMES = [
interface ExhaustiveAssets {
all(): CdnAsset[];
}

type ImgFilename = (typeof IMAGE_FILENAMES)[number];
type OsImgFilename = (typeof OS_IMAGE_FILENAMES)[number];
type VideoId = (typeof VIDEO_IDS)[number];
Expand Down
9 changes: 9 additions & 0 deletions shared/tailwind/src/preset.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module.exports = {
extend: {
animation: {
'bounce-right': `bounce-right 0.5s linear infinite`,
'progress-right': `progress-right 1.5s ease-in-out infinite`,
},
keyframes: {
'bounce-right': {
Expand All @@ -29,6 +30,14 @@ module.exports = {
animationTimingFunction: `cubic-bezier(0, 0, 0.2, 1)`,
},
},
'progress-right': {
'0%': {
width: `0%`,
},
'100%': {
width: `100%`,
},
},
'loader-bounce': {
'0%': {
height: `18px`,
Expand Down
Binary file added site/app/public/allow-notifs-1.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added site/app/public/allow-notifs-2.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added site/app/public/allow-notifs-3.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit da95c3c

Please sign in to comment.