The server now sends each fresh position on every poll that has one, and sends positions advanced to send time; clients count only the time since they received it. Bars realign every poll and no longer drift by however far the viewer's clock is off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
This commit is contained in:
@@ -7,26 +7,28 @@ import { colors } from '@/lib/theme';
|
||||
|
||||
/** Where they are in the song. Renders nothing when the source doesn't report a position. */
|
||||
export function ProgressBar({ np }: { np: NowPlayingDTO }) {
|
||||
const [now, setNow] = useState(() => Date.now());
|
||||
const pos = playbackPosition(np, now);
|
||||
const visible = np.isPlaying && pos !== null;
|
||||
const fraction = pos ? pos.positionMs / pos.durationMs : 0;
|
||||
if (!np.isPlaying || playbackPosition(np, 0) === null) return null;
|
||||
// Each fresh position from the server restarts the clock, realigning the bar.
|
||||
return <Bar key={`${np.track.id}|${np.progressMs}|${np.updatedAt}`} np={np} />;
|
||||
}
|
||||
|
||||
function Bar({ np }: { np: NowPlayingDTO }) {
|
||||
const [receivedAt] = useState(() => Date.now());
|
||||
const [now, setNow] = useState(receivedAt);
|
||||
useEffect(() => {
|
||||
if (!visible) return;
|
||||
const id = setInterval(() => setNow(Date.now()), 1000);
|
||||
return () => clearInterval(id);
|
||||
}, [visible]);
|
||||
}, []);
|
||||
|
||||
const pos = playbackPosition(np, now - receivedAt);
|
||||
const fraction = pos ? pos.positionMs / pos.durationMs : 0;
|
||||
const width = useSharedValue(fraction);
|
||||
useEffect(() => {
|
||||
// Glide between ticks; jump straight there after a seek or a new song.
|
||||
const jump = Math.abs(fraction - width.value) > 0.05;
|
||||
width.set(jump ? fraction : withTiming(fraction, { duration: 1000, easing: Easing.linear }));
|
||||
width.set(withTiming(fraction, { duration: 1000, easing: Easing.linear })); // glide between ticks
|
||||
}, [fraction, width]);
|
||||
const fill = useAnimatedStyle(() => ({ width: `${width.value * 100}%` }));
|
||||
|
||||
if (!visible || !pos) return null;
|
||||
if (!pos) return null;
|
||||
return (
|
||||
<View style={styles.wrap} accessibilityLabel={`${formatDuration(pos.positionMs)} of ${formatDuration(pos.durationMs)}`}>
|
||||
<View style={styles.track}>
|
||||
|
||||
Reference in New Issue
Block a user