// DEPLOY_CONFIG: {"cron": [{"name": "weekly_board_reset", "schedule": "0 0 * * 0", "action": "event", "config": {"event_type": "reset_board"}}}] import { useEffect, useMemo, useRef, useState, useCallback } from 'react'; import { SignupBoard } from './components/SignupBoard.jsx'; import { useCollection, useIdentity, useReactions, playSound, share } from '@deplixo/sdk'; function ReactionBar({ targetId, onReactionSound }) { const { counts, toggle, loading } = useReactions(targetId); const emojis = ['๐Ÿ‘', 'โค๏ธ', '๐ŸŽ‰', '๐Ÿ”ฅ', '๐Ÿ‘€']; const totalReactions = useMemo( () => emojis.reduce((sum, emoji) => sum + (counts[emoji] || 0), 0), [counts] ); const lastTotalRef = useRef(totalReactions); useEffect(() => { if (loading) return; const previousTotal = lastTotalRef.current; if (totalReactions > previousTotal) { onReactionSound?.(totalReactions, previousTotal); } lastTotalRef.current = totalReactions; }, [totalReactions, loading, onReactionSound]); if (loading) return null; return (
{emojis.map((emoji) => ( ))}
); } function App() { const [activeTab, setActiveTab] = useState('lineup'); const [presenceUsers, setPresenceUsers] = useState([]); const [presenceId] = useState(() => `viewer-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`); const [shareStatus, setShareStatus] = useState(''); const { user: identityUser } = useIdentity(); const { items: performances = [] } = useCollection('openmic-performances', { personal: true }); const { items: performerProfiles = [] } = useCollection('openmic-performer-identities', { personal: true }); const { items: eventHistory = [], add: addEventRecord, update: updateEventRecord } = useCollection('openmic-event-history', { personal: true }); const { items: presenceItems = [] } = useCollection('deplixo-openmic-presence', { personal: true }); const { add: addPresence, update: updatePresence, remove: removePresence } = useCollection('deplixo-openmic-presence', { personal: true }); const lastNowPlayingIdRef = useRef(null); const applauseCooldownRef = useRef(0); const nextMusicCooldownRef = useRef(0); const lastSyncedEventKeyRef = useRef(''); const lastSyncedPerformersRef = useRef(''); const normalizeText = (value) => (typeof value === 'string' ? value.trim() : ''); const performerIdentity = useMemo(() => { const identityName = normalizeText(identityUser?.name) || 'Performer'; const identityAvatar = normalizeText(identityUser?.avatar) || ''; const identityId = normalizeText(identityUser?.id) || ''; return { id: identityId, name: identityName, avatar: identityAvatar, }; }, [identityUser?.id, identityUser?.name, identityUser?.avatar]); const performersByIdentity = useMemo(() => { const map = new Map(); performerProfiles.forEach((profile) => { if (!profile) return; const identityId = normalizeText(profile.identityId || profile.userId || profile.id); if (!identityId) return; map.set(identityId, profile); }); return map; }, [performerProfiles]); const currentPerformances = useMemo(() => { return performances .filter((performance) => performance && typeof performance === 'object') .map((performance) => { const performerId = normalizeText(performance.performerId || performance.identityId || performance.userId || performance.id || ''); const storedProfile = performersByIdentity.get(performerId); const performerName = normalizeText(performance.performerName || storedProfile?.name || performance.title || 'Performer') || 'Performer'; const performerAvatar = normalizeText(performance.performerAvatar || storedProfile?.avatar || '') || ''; return { ...performance, performerId, performerName, performerAvatar, }; }); }, [performances, performersByIdentity]); const currentEvent = useMemo(() => { const nowPlaying = currentPerformances.find((performance) => performance?.status === 'performing') || currentPerformances[0] || null; return nowPlaying; }, [currentPerformances]); const shareUrl = useMemo(() => { if (typeof window === 'undefined' || !window.location) return ''; const url = new URL(window.location.href); const eventId = currentEvent?.id ? String(currentEvent.id) : ''; if (eventId) { url.searchParams.set('event', eventId); } else { url.searchParams.delete('event'); } url.searchParams.set('tab', 'lineup'); return url.toString(); }, [currentEvent?.id]); const copyShareLink = async () => { if (!shareUrl) return; try { if (navigator?.clipboard?.writeText) { await navigator.clipboard.writeText(shareUrl); } else { const tempInput = document.createElement('input'); tempInput.value = shareUrl; document.body.appendChild(tempInput); tempInput.select(); document.execCommand('copy'); document.body.removeChild(tempInput); } setShareStatus('Link copied'); window.setTimeout(() => setShareStatus(''), 2000); } catch { setShareStatus('Copy failed'); window.setTimeout(() => setShareStatus(''), 2000); } }; const shareEventLink = async () => { if (!shareUrl) return; try { if (navigator?.share) { await navigator.share({ title: 'Open Mic Night', text: currentEvent ? `Join the open mic: ${currentEvent.performerName}` : 'Join the open mic night', url: shareUrl, }); setShareStatus('Shared'); window.setTimeout(() => setShareStatus(''), 2000); } else { await copyShareLink(); } } catch { setShareStatus('Share canceled'); window.setTimeout(() => setShareStatus(''), 2000); } }; const syncPresence = useCallback(() => { const now = Date.now(); const viewer = { id: presenceId, name: 'You', status: 'viewing', tab: activeTab, lastSeen: now, }; const loadPresence = () => { try { const alive = presenceItems .filter((user) => user && typeof user.lastSeen === 'number' && now - user.lastSeen < 15000) .filter((user) => user.id !== presenceId); const next = [viewer, ...alive]; const existingIndex = presenceItems.findIndex((item) => item.id === presenceId); if (existingIndex >= 0) { updatePresence(presenceItems[existingIndex].id, next); } else { addPresence(next); } setPresenceUsers(next); } catch { setPresenceUsers([viewer]); } }; loadPresence(); }, [activeTab, presenceId, presenceItems, addPresence, updatePresence]); useEffect(() => { syncPresence(); const interval = setInterval(syncPresence, 5000); const handleVisibility = () => { if (document.visibilityState === 'visible') syncPresence(); }; window.addEventListener('focus', syncPresence); window.addEventListener('visibilitychange', handleVisibility); return () => { clearInterval(interval); window.removeEventListener('focus', syncPresence); window.removeEventListener('visibilitychange', handleVisibility); try { const next = presenceItems.filter((user) => user && user.id !== presenceId); const existingIndex = presenceItems.findIndex((item) => item.id === presenceId); if (existingIndex >= 0) { updatePresence(presenceItems[existingIndex].id, next); } } catch {} }; }, [syncPresence, presenceId, presenceItems, updatePresence]); useEffect(() => { const now = Date.now(); try { const alive = presenceItems .filter((user) => user && typeof user.lastSeen === 'number' && now - user.lastSeen < 15000); setPresenceUsers(alive); } catch { setPresenceUsers([]); } }, [presenceItems]); useEffect(() => { const nowPlaying = currentPerformances.find((performance) => performance?.status === 'performing') || currentPerformances[0] || null; const nowPlayingId = nowPlaying?.id || null; if (nowPlayingId && lastNowPlayingIdRef.current && lastNowPlayingIdRef.current !== nowPlayingId) { const now = Date.now(); if (now - nextMusicCooldownRef.current > 1500) { playSound('@whoosh'); nextMusicCooldownRef.current = now; } } lastNowPlayingIdRef.current = nowPlayingId; }, [currentPerformances]); useEffect(() => { const performerId = performerIdentity.id; if (!performerId) return; const profileKey = JSON.stringify({ id: performerIdentity.id, name: performerIdentity.name, avatar: performerIdentity.avatar, }); if (lastSyncedPerformersRef.current !== profileKey) { lastSyncedPerformersRef.current = profileKey; const existing = performerProfiles.find( (profile) => profile && (profile.identityId === performerId || profile.userId === performerId || profile.id === performerId) ); const nextProfile = { ...(existing || {}), identityId: performerId, userId: performerId, name: performerIdentity.name, avatar: performerIdentity.avatar, updatedAt: Date.now(), }; if (existing?.id) { updateEventRecord?.(existing.id, nextProfile).catch(() => {}); } } }, [performerIdentity.id, performerIdentity.name, performerIdentity.avatar, performerProfiles, updateEventRecord]); useEffect(() => { const event = currentEvent; if (!event) return; const eventKey = JSON.stringify({ eventId: event.id || '', performerId: event.performerId || '', performerName: event.performerName || '', status: event.status || '', }); if (lastSyncedEventKeyRef.current === eventKey) return; lastSyncedEventKeyRef.current = eventKey; const existingRecord = eventHistory.find( (record) => record && record.eventId === event.id && record.performerId === (event.performerId || '') ); const performerProfile = event.performerId ? performersByIdentity.get(event.performerId) : null; const payload = { eventId: event.id || '', performerId: event.performerId || '', performerName: event.performerName || performerProfile?.name || 'Performer', performerAvatar: event.performerAvatar || performerProfile?.avatar || '', status: event.status || 'scheduled', title: event.title || event.name || 'Open Mic Set', startedAt: event.startedAt || Date.now(), updatedAt: Date.now(), }; if (existingRecord?.id) { updateEventRecord(existingRecord.id, payload).catch(() => {}); } else { addEventRecord(payload).catch(() => {}); } }, [currentEvent, eventHistory, performersByIdentity, addEventRecord, updateEventRecord]); const audienceCount = presenceUsers.length; const audienceList = useMemo(() => presenceUsers.slice(0, 6), [presenceUsers]); const nowPlaying = currentPerformances.find((performance) => performance?.status === 'performing') || currentPerformances[0] || null; const reactionTargetId = nowPlaying?.id ? `performance-${nowPlaying.id}` : null; const performerHistory = useMemo(() => { return eventHistory .filter((record) => record && record.performerId) .map((record) => ({ ...record, performerName: normalizeText(record.performerName) || 'Performer', })) .slice(0, 8); }, [eventHistory]); const handleReactionSound = (totalReactions, previousTotal) => { const now = Date.now(); const crossedThreshold = previousTotal < 5 && totalReactions >= 5; const newReactionReceived = totalReactions > previousTotal; if (newReactionReceived && (crossedThreshold || now - applauseCooldownRef.current > 1500)) { playSound('@success'); applauseCooldownRef.current = now; } }; return (
๐ŸŽค

Open Mic Night

Sign up, step up, shine

Audience Live

People currently viewing this open mic night

{audienceCount} live
{audienceList.length > 0 ? ( audienceList.map((user) => (
{user.name?.[0]?.toUpperCase() || 'A'}
)) ) : (

No active viewers detected yet.

)}
{audienceList.length > 0 && (
    {audienceList.map((user) => (
  • {user.name} {user.id === presenceId ? ' (you)' : ''} {user.tab === 'signup' ? 'Sign Up' : 'Lineup'}
  • ))}
)}

Performer History

Identity and event records persisted across sessions

{performerHistory.length} records
{performerHistory.length > 0 ? (
    {performerHistory.map((record) => (
  • {record.performerAvatar ? '๐ŸŽญ' : (record.performerName?.[0]?.toUpperCase() || 'P')}
    {record.performerName} {record.title} ยท {record.status}
  • ))}
) : (

No performer history yet. Start or switch a set to create records.

)}
{currentEvent && (

Share this event

Copy or share a link to invite others to the current open mic.

Live link
{shareStatus &&

{shareStatus}

}
)} {activeTab === 'lineup' && reactionTargetId && (

Cheer the performer

Tap an emoji to react in real time

Live
)}
); } ReactDOM.createRoot(document.getElementById('root')).render();