Function bodies 62 total
clearPlayerId function · typescript · L32-L34 (3 LOC)src/hooks/useOnlineStore.ts
function clearPlayerId(roomCode: string) {
localStorage.removeItem(PLAYER_ID_PREFIX + roomCode);
}saveLastRoom function · typescript · L38-L40 (3 LOC)src/hooks/useOnlineStore.ts
function saveLastRoom(roomCode: string) {
localStorage.setItem(LAST_ROOM_KEY, JSON.stringify({ roomCode, ts: Date.now() }));
}clearLastRoom function · typescript · L42-L44 (3 LOC)src/hooks/useOnlineStore.ts
function clearLastRoom() {
localStorage.removeItem(LAST_ROOM_KEY);
}getLastRoom function · typescript · L46-L52 (7 LOC)src/hooks/useOnlineStore.ts
export function getLastRoom(): string | null {
try {
const data = JSON.parse(localStorage.getItem(LAST_ROOM_KEY) || '');
if (Date.now() - data.ts < 60 * 60 * 1000) return data.roomCode;
} catch { /* ignore */ }
return null;
}cleanupStalePlayerIds function · typescript · L55-L64 (10 LOC)src/hooks/useOnlineStore.ts
function cleanupStalePlayerIds() {
for (let i = localStorage.length - 1; i >= 0; i--) {
const key = localStorage.key(i);
if (!key?.startsWith(PLAYER_ID_PREFIX)) continue;
try {
const { ts } = JSON.parse(localStorage.getItem(key)!);
if (Date.now() - ts > 60 * 60 * 1000) localStorage.removeItem(key);
} catch { localStorage.removeItem(key!); }
}
}applySnapshot function · typescript · L140-L182 (43 LOC)src/hooks/useOnlineStore.ts
function applySnapshot(state: RoomStateSnapshot) {
const myId = get().myPlayerId;
const isJoined = state.players.some(p => p.id === myId);
// If we were in a game screen but got removed from the room
if (!isJoined && get().screen !== 'setup') {
const socket = get().socket;
if (socket) socket.close();
set({ ...initialState, error: 'removedFromRoom' });
return;
}
const scores: Record<string, number> = {};
for (const p of state.players) {
scores[p.id] = p.score;
}
let screen: OnlineScreen = get().screen;
if (isJoined) {
if (state.phase === 'lobby') screen = 'lobby';
else if (state.phase === 'playing') screen = 'playing';
else if (state.phase === 'finished') screen = 'finished';
}
set({
roomCode: state.roomCode,
hostId: state.hostId,
phase: state.phase,
players: state.players,
maxPlayers: state.maxPlayers,
questionIds: state.questionIds,
currentQuehandleMessage function · typescript · L184-L303 (120 LOC)src/hooks/useOnlineStore.ts
function handleMessage(event: MessageEvent) {
let msg: ServerMessage;
try {
msg = JSON.parse(event.data);
} catch {
return;
}
switch (msg.type) {
case 'room-state':
applySnapshot(msg.state);
break;
case 'player-joined': {
const player = msg.player;
set(state => ({
players: [...state.players, player],
}));
break;
}
case 'player-left': {
const playerId = msg.playerId;
set(state => ({
players: state.players.filter(p => p.id !== playerId),
}));
break;
}
case 'game-started':
applySnapshot(msg.state);
set({
screen: 'playing',
lastAnswerCorrect: null,
lastAnswerPlayerId: null,
timeoutPlayerId: null,
rankings: null,
});
break;
case 'answer-revealed': {
const { letter, correct, scores, playerId } = msg;
set({
sProvenance: Repobility (https://repobility.com) — every score reproducible from /scan/
connectToRoom function · typescript · L305-L350 (46 LOC)src/hooks/useOnlineStore.ts
function connectToRoom(roomCode: string, playerName: string) {
const existing = get().socket;
if (existing) {
existing.close();
}
const playerId = getOrCreatePlayerId(roomCode);
const socket = new PartySocket({
host: PARTYKIT_HOST,
room: roomCode,
id: playerId,
});
set({
socket,
myPlayerId: playerId,
roomCode,
connecting: true,
error: null,
});
const timeout = setTimeout(() => {
if (get().connecting) {
socket.close();
set({ ...initialState, error: 'connectionTimeout' });
}
}, 10_000);
socket.addEventListener('open', () => {
clearTimeout(timeout);
// Use socket.id to guarantee myPlayerId matches server's connection.id
const actualId = socket.id;
set({ connected: true, myPlayerId: actualId });
// Sync localStorage with the actual ID the server sees
const key = PLAYER_ID_PREFIX + roomCode;
localStorage.setItem(key, JSusePresence function · typescript · L6-L28 (23 LOC)src/hooks/usePresence.ts
export function usePresence() {
const [count, setCount] = useState<number | null>(null);
useEffect(() => {
const socket = new PartySocket({
host: PARTYKIT_HOST,
room: '__presence__',
});
socket.addEventListener('message', (event) => {
try {
const msg = JSON.parse(event.data);
if (msg.type === 'presence-count') {
setCount(msg.count);
}
} catch { /* ignore */ }
});
return () => { socket.close(); };
}, []);
return count;
}useWakeLock function · typescript · L3-L31 (29 LOC)src/hooks/useWakeLock.ts
export function useWakeLock(active: boolean) {
const wakeLock = useRef<WakeLockSentinel | null>(null);
useEffect(() => {
if (!active) return;
const request = async () => {
try {
if ('wakeLock' in navigator) {
wakeLock.current = await navigator.wakeLock.request('screen');
}
} catch (err) {
console.warn('Wake Lock error:', err);
}
};
request();
const handleVisibilityChange = () => {
if (document.visibilityState === 'visible') request();
};
document.addEventListener('visibilitychange', handleVisibilityChange);
return () => {
document.removeEventListener('visibilitychange', handleVisibilityChange);
if (wakeLock.current) wakeLock.current.release();
};
}, [active]);
}t function · typescript · L85-L87 (3 LOC)src/i18n/translations.ts
export function t(key: TranslationKey, lang: Lang): string {
return translations[key][lang];
}Library function · typescript · L7-L126 (120 LOC)src/Library.tsx
export default function Library({ lang, onBack }: { lang: Lang; onBack: () => void }) {
const isRTL = lang === 'ar';
const [activeCategory, setActiveCategory] = useState<string>(CATEGORIES[0].id);
const [expandedQuestionId, setExpandedQuestionId] = useState<number | null>(null);
const currentQuestions = useMemo(
() =>
questionsDB.filter((q) => {
const cat = CATEGORIES.find((c) => c.id === activeCategory);
return cat && q.id >= cat.startId && q.id <= cat.endId;
}),
[activeCategory]
);
return (
<div className="w-full max-w-4xl mx-auto animate-fade-in flex flex-col h-[85vh]">
<div className="bg-white/80 backdrop-blur-md p-6 rounded-3xl shadow-xl flex-1 flex flex-col mb-4 overflow-hidden">
{/* Header */}
<div className="flex items-center justify-between mb-6 pb-4 border-b border-teal-100/50">
<div className="flex items-center gap-3">
<BookOpen className="w-8 h-8 text-teal-600" />
<‹ prevpage 2 / 2