From 731e99d1b7c08b82c5cd6a9f9bd6a8e1e30d1307 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Singh Date: Thu, 2 Apr 2026 14:20:23 +0530 Subject: [PATCH] Polish Hangout lobby state feedback --- app/src/screens/HangoutScreen.tsx | 114 ++++++++++++++++++++++++++---- 1 file changed, 99 insertions(+), 15 deletions(-) diff --git a/app/src/screens/HangoutScreen.tsx b/app/src/screens/HangoutScreen.tsx index 9bf6bfd..c53a201 100644 --- a/app/src/screens/HangoutScreen.tsx +++ b/app/src/screens/HangoutScreen.tsx @@ -58,6 +58,9 @@ type PersistedHangoutState = { activeRoom: HangoutRoom | null; }; +type StatusTone = 'neutral' | 'info' | 'success' | 'error'; +type LoadingAction = 'idle' | 'refreshing' | 'creating' | 'joining' | 'opening' | 'sharing'; + const friends: Friend[] = [ { name: 'Ananya', status: 'online', note: 'Ready for revision' }, { name: 'Isha', status: 'online', note: 'Can join in 2 min' }, @@ -90,7 +93,8 @@ export default function HangoutScreen({ const [roomType, setRoomType] = useState('Study'); const [joinInput, setJoinInput] = useState(''); const [statusMessage, setStatusMessage] = useState('Create a room or paste a room link to join one.'); - const [loading, setLoading] = useState(false); + const [statusTone, setStatusTone] = useState('neutral'); + const [loadingAction, setLoadingAction] = useState('idle'); const [meetingOpen, setMeetingOpen] = useState(false); const [meetingPanel, setMeetingPanel] = useState('none'); @@ -206,11 +210,20 @@ export default function HangoutScreen({ }, [activeRoom, hydrated, joinInput, roomName, roomType, setPersistedState]); async function refreshRooms() { + setLoadingAction('refreshing'); const result = await listRooms(); + setLoadingAction('idle'); if (result.ok) { setRooms(result.rooms); + setStatusTone('info'); + setStatusMessage( + result.rooms.length + ? `${result.rooms.length} live room${result.rooms.length === 1 ? '' : 's'} ready to browse.` + : 'No live rooms yet. Create the first one or join from a Sentri link.' + ); return; } + setStatusTone('error'); setStatusMessage(result.message); } @@ -233,18 +246,20 @@ export default function HangoutScreen({ async function handleCreateRoom() { if (!sessionToken) { + setStatusTone('error'); setStatusMessage('Login first so Sentri can create a room under your account.'); return; } - setLoading(true); + setLoadingAction('creating'); const result = await createRoom(sessionToken, { roomName: roomName.trim() || 'Sentri Room', roomType, }); - setLoading(false); + setLoadingAction('idle'); if (!result.ok) { + setStatusTone('error'); setStatusMessage(result.message); return; } @@ -252,6 +267,7 @@ export default function HangoutScreen({ setActiveRoom(result.room); setJoinInput(result.room.joinLink); seedMeetingRoom(result.room); + setStatusTone('success'); setStatusMessage(`Room ${result.room.roomCode} is live and ready to share.`); await refreshRooms(); } @@ -259,6 +275,7 @@ export default function HangoutScreen({ async function handleJoinByCode(rawValue?: string, fromIncomingLink = false) { const code = extractRoomCode(rawValue ?? joinInput); if (!code) { + setStatusTone('error'); setStatusMessage('Paste a Sentri room link or room code first.'); if (fromIncomingLink) { onConsumeIncomingRoomCode(); @@ -266,11 +283,12 @@ export default function HangoutScreen({ return; } - setLoading(true); + setLoadingAction('joining'); const result = await joinRoom(code, userName); - setLoading(false); + setLoadingAction('idle'); if (!result.ok) { + setStatusTone('error'); setStatusMessage(result.message); if (fromIncomingLink) { onConsumeIncomingRoomCode(); @@ -281,6 +299,7 @@ export default function HangoutScreen({ setActiveRoom(result.room); setJoinInput(result.room.roomCode); seedMeetingRoom(result.room); + setStatusTone('success'); setStatusMessage(`Joined ${result.room.roomName}.`); await refreshRooms(); if (fromIncomingLink) { @@ -289,11 +308,12 @@ export default function HangoutScreen({ } async function handleOpenRoom(roomCode: string) { - setLoading(true); + setLoadingAction('opening'); const result = await getRoom(roomCode); - setLoading(false); + setLoadingAction('idle'); if (!result.ok) { + setStatusTone('error'); setStatusMessage(result.message); return; } @@ -301,11 +321,13 @@ export default function HangoutScreen({ setActiveRoom(result.room); setJoinInput(result.room.roomCode); seedMeetingRoom(result.room); + setStatusTone('info'); setStatusMessage(`${result.room.roomName} is ready.`); } async function handleShareRoom(friendName?: string) { if (!activeRoom) { + setStatusTone('error'); setStatusMessage('Create or join a room before sharing it.'); return; } @@ -314,15 +336,19 @@ export default function HangoutScreen({ ? `${activeRoomShareText}\nInviting ${friendName} from Sentri.` : activeRoomShareText; + setLoadingAction('sharing'); await Share.share({ title: activeRoom.roomName, message, }); + setLoadingAction('idle'); + setStatusTone('success'); setStatusMessage(friendName ? `Share sheet opened for ${friendName}.` : 'Share sheet opened.'); } function handleEnterMeeting() { if (!activeRoom) { + setStatusTone('error'); setStatusMessage('Open a room first.'); return; } @@ -330,6 +356,7 @@ export default function HangoutScreen({ seedMeetingRoom(activeRoom); } setMeetingOpen(true); + setStatusTone('info'); setStatusMessage(`Inside ${activeRoom.roomName}.`); } @@ -339,6 +366,7 @@ export default function HangoutScreen({ setShareScreenOn(false); setRecordingOn(false); setFocusedParticipantId(meetingParticipants[1]?.id ?? meetingParticipants[0]?.id ?? null); + setStatusTone('info'); setStatusMessage(activeRoom ? `Left ${activeRoom.roomName}. Room is still active.` : 'Left the meeting.'); } @@ -497,11 +525,13 @@ export default function HangoutScreen({ void handleCreateRoom()} - disabled={loading} + disabled={loadingAction !== 'idle'} > - {loading ? 'Please wait' : 'Create room'} + + {loadingAction === 'creating' ? 'Creating room' : 'Create room'} + @@ -518,15 +548,43 @@ export default function HangoutScreen({ autoCorrect={false} /> void handleJoinByCode()} - disabled={loading} + disabled={loadingAction !== 'idle'} > - {loading ? 'Please wait' : 'Join room'} + + {loadingAction === 'joining' ? 'Joining room' : 'Join room'} + - + + {statusMessage} @@ -585,9 +643,19 @@ export default function HangoutScreen({ Live rooms void refreshRooms()}> - Refresh + + {loadingAction === 'refreshing' ? 'Refreshing…' : 'Refresh'} + + {loadingAction === 'refreshing' && rooms.length === 0 ? ( + + Refreshing room list + + Sentri is checking for live study rooms and shared hangouts right now. + + + ) : null} {rooms.map((room) => ( void handleOpenRoom(room.roomCode)}> @@ -1344,13 +1412,29 @@ const styles = StyleSheet.create({ marginTop: 14, borderRadius: 18, backgroundColor: theme.colors.surfaceAlt, + borderWidth: 1, + borderColor: theme.colors.line, paddingHorizontal: 14, paddingVertical: 12, + flexDirection: 'row', + alignItems: 'center', + gap: 10, + }, + statusBannerSuccess: { + backgroundColor: theme.colors.accentSoft, + }, + statusBannerError: { + backgroundColor: '#FDEDED', + borderColor: '#F5C2C0', + }, + statusBannerInfo: { + backgroundColor: '#EEF3FD', }, statusBannerText: { color: theme.colors.text, fontSize: 13, fontWeight: '700', + flex: 1, }, linkCard: { marginTop: 16,