mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-24 13:32:22 +02:00
75 lines
2.4 KiB
TypeScript
75 lines
2.4 KiB
TypeScript
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
import {
|
|
subscribeActivity,
|
|
getActivity,
|
|
type BrowserActivity,
|
|
type BrowserAction,
|
|
} from './browserCommandHandler';
|
|
|
|
export interface BrowserActivityState {
|
|
active: boolean;
|
|
action: BrowserAction | null;
|
|
detail: string | null;
|
|
/** Action that just completed; stays briefly for exit animations. */
|
|
lastAction: BrowserAction | null;
|
|
/** Increments per new action; use as React key to restart CSS animations. */
|
|
actionSeq: number;
|
|
/** Viewport-relative click coords (0-1) for positioning the click ripple. */
|
|
coords: { xPercent: number; yPercent: number } | null;
|
|
}
|
|
|
|
const EMPTY: BrowserActivityState = { active: false, action: null, detail: null, lastAction: null, actionSeq: 0, coords: null };
|
|
|
|
export function useBrowserActivity(browserId: string): BrowserActivityState {
|
|
const [state, setState] = useState<BrowserActivityState>(() => {
|
|
const current = getActivity(browserId);
|
|
return current
|
|
? { active: true, action: current.action, detail: current.detail ?? null, lastAction: null, actionSeq: 0, coords: current.coords ?? null }
|
|
: EMPTY;
|
|
});
|
|
|
|
const lastActionTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
|
|
const handleChange = useCallback(
|
|
(changedId: string, activity: BrowserActivity | null) => {
|
|
if (changedId !== browserId) return;
|
|
if (activity) {
|
|
if (lastActionTimer.current) clearTimeout(lastActionTimer.current);
|
|
setState((prev) => ({
|
|
active: true,
|
|
action: activity.action,
|
|
detail: activity.detail ?? null,
|
|
lastAction: null,
|
|
actionSeq: prev.actionSeq + 1,
|
|
coords: activity.coords ?? prev.coords,
|
|
}));
|
|
} else {
|
|
setState((prev) => ({
|
|
active: false,
|
|
action: null,
|
|
detail: null,
|
|
lastAction: prev.action,
|
|
actionSeq: prev.actionSeq,
|
|
coords: prev.coords,
|
|
}));
|
|
lastActionTimer.current = setTimeout(() => {
|
|
setState((prev) => (prev.active ? prev : { ...prev, lastAction: null, coords: null }));
|
|
}, 600);
|
|
}
|
|
},
|
|
[browserId],
|
|
);
|
|
|
|
useEffect(() => {
|
|
return subscribeActivity(handleChange);
|
|
}, [handleChange]);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (lastActionTimer.current) clearTimeout(lastActionTimer.current);
|
|
};
|
|
}, []);
|
|
|
|
return state;
|
|
}
|