signal-desktop/ts/components/StoryProgressSegment.tsx
2024-09-04 09:33:58 -07:00

61 lines
1.7 KiB
TypeScript

// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { memo, useEffect, useRef } from 'react';
import { animated, useSpring } from '@react-spring/web';
export type StoryProgressSegmentProps = Readonly<{
currentIndex: number;
duration: number | null;
index: number;
playing: boolean;
onFinish: () => void;
}>;
function isValidDuration(duration: number | null): boolean {
return duration != null && Number.isFinite(duration) && duration > 0;
}
export const StoryProgressSegment = memo(function StoryProgressSegment({
currentIndex,
duration,
index,
playing,
onFinish,
}: StoryProgressSegmentProps): JSX.Element {
const onFinishRef = useRef(onFinish);
useEffect(() => {
onFinishRef.current = onFinish;
}, [onFinish]);
const [progressBarStyle] = useSpring(() => {
return {
immediate: index !== currentIndex,
// Pause while we are waiting for a valid duration
pause: !playing || !isValidDuration(duration),
from: { x: index < currentIndex ? 1 : 0 },
to: { x: index <= currentIndex ? 1 : 0 },
config: { duration: duration ?? Infinity },
onRest: result => {
if (index === currentIndex && result.finished) {
onFinishRef.current();
}
},
};
}, [index, playing, currentIndex, duration]);
return (
<div
className="StoryProgressSegment"
aria-current={index === currentIndex ? 'step' : false}
>
<animated.div
className="StoryProgressSegment__bar"
style={{
transform: progressBarStyle.x.to(
value => `translateX(${value * 100 - 100}%)`
),
}}
/>
</div>
);
});