Files
paseo/packages/app/src/components/status-ring/clock.ts
T
Mohamed BoudraandChristoph Leiter c0daf8e066 feat(app): add running status ring and lighten status dots
Keep the animated ring phase-locked across hosts and derive its knockout surface from each host's actual interaction state. Align filled status circles at 6px while preserving the larger needs-input alert.

Co-authored-by: Christoph Leiter <christoph@leiter.io>
2026-08-09 11:33:58 +02:00

59 lines
1.5 KiB
TypeScript

import { useLayoutEffect } from "react";
import { makeMutable, type SharedValue, useSharedValue } from "react-native-reanimated";
import { scheduleOnUI } from "react-native-worklets";
import { getStatusRingRotation } from "@/components/status-ring/geometry";
const sharedRotation = makeMutable(getStatusRingRotation(Date.now()));
const activeRingCount = makeMutable(0);
const clockRunning = makeMutable(false);
function advanceSharedRotation(): void {
"worklet";
if (activeRingCount.value === 0) {
clockRunning.value = false;
return;
}
sharedRotation.value = getStatusRingRotation(Date.now());
requestAnimationFrame(advanceSharedRotation);
}
function registerStatusRing(registered: SharedValue<boolean>): void {
"worklet";
if (registered.value) {
return;
}
registered.value = true;
activeRingCount.value += 1;
if (!clockRunning.value) {
clockRunning.value = true;
sharedRotation.value = getStatusRingRotation(Date.now());
requestAnimationFrame(advanceSharedRotation);
}
}
function unregisterStatusRing(registered: SharedValue<boolean>): void {
"worklet";
if (!registered.value) {
return;
}
registered.value = false;
activeRingCount.value -= 1;
}
export function useStatusRingRotation(): SharedValue<number> {
const registered = useSharedValue(false);
useLayoutEffect(() => {
scheduleOnUI(registerStatusRing, registered);
return () => {
scheduleOnUI(unregisterStatusRing, registered);
};
}, [registered]);
return sharedRotation;
}