Files
cinny/src/playground/usePlaygroundMatrix.ts
litruv 0da4f0d6cb Add emoji confetti bursts and component playground.
Support app.relay.emoji_confetti on jumbo emoji clicks with canvas physics,
per-emoji particle profiles, and a fixed overlay that does not affect scroll.
Also add a Vite playground for live component previews and theme hover preview.
2026-07-29 20:40:43 +10:00

124 lines
3.1 KiB
TypeScript

import { useCallback, useEffect, useRef, useState } from 'react';
import type { MatrixClient, Room } from 'matrix-js-sdk';
import {
connectLiveMatrixClient,
listJoinedRooms,
loadManualSession,
peekPaarrotSession,
pickDefaultRoom,
saveManualSession,
stopLiveMatrixClient,
type LiveConnectMode,
type ManualSessionInput,
} from './liveClient';
type LiveState = {
mode: LiveConnectMode;
status: 'idle' | 'connecting' | 'ready' | 'error';
error: string | null;
client: MatrixClient | null;
room: Room | null;
rooms: Room[];
sessionLabel: string | null;
};
const initial: LiveState = {
mode: 'mock',
status: 'idle',
error: null,
client: null,
room: null,
rooms: [],
sessionLabel: null,
};
export function usePlaygroundMatrix() {
const [state, setState] = useState<LiveState>(initial);
const clientRef = useRef<MatrixClient | null>(null);
const roomIdRef = useRef<string | undefined>(undefined);
const paarrotSession = peekPaarrotSession();
const hasPaarrotSession = Boolean(paarrotSession);
const hasManualSession = Boolean(loadManualSession());
const teardown = useCallback(() => {
stopLiveMatrixClient(clientRef.current);
clientRef.current = null;
}, []);
useEffect(() => () => teardown(), [teardown]);
const selectRoom = useCallback((roomId: string) => {
roomIdRef.current = roomId;
setState((prev) => {
if (!prev.client) return prev;
const room = pickDefaultRoom(prev.client, roomId) ?? null;
return { ...prev, room };
});
}, []);
const connectLive = useCallback(
async (manual?: ManualSessionInput) => {
teardown();
setState((prev) => ({
...prev,
mode: 'live',
status: 'connecting',
error: null,
client: null,
room: null,
rooms: [],
sessionLabel: null,
}));
try {
if (manual) saveManualSession(manual);
const mx = await connectLiveMatrixClient(manual);
clientRef.current = mx;
const rooms = listJoinedRooms(mx);
const room = pickDefaultRoom(mx, roomIdRef.current) ?? null;
if (room) roomIdRef.current = room.roomId;
const hs =
peekPaarrotSession()?.baseUrl ??
loadManualSession()?.baseUrl ??
'';
setState({
mode: 'live',
status: 'ready',
error: null,
client: mx,
room,
rooms,
sessionLabel: `${mx.getUserId() ?? '?'} · ${hs}`,
});
} catch (err) {
clientRef.current = null;
setState({
...initial,
mode: 'live',
status: 'error',
error: err instanceof Error ? err.message : String(err),
});
}
},
[teardown]
);
const useMocks = useCallback(() => {
teardown();
roomIdRef.current = undefined;
setState(initial);
}, [teardown]);
return {
...state,
hasPaarrotSession,
hasManualSession,
paarrotUserId: paarrotSession?.userId ?? null,
paarrotBaseUrl: paarrotSession?.baseUrl ?? null,
connectLive,
useMocks,
selectRoom,
};
}