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.
This commit is contained in:
123
src/playground/usePlaygroundMatrix.ts
Normal file
123
src/playground/usePlaygroundMatrix.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user