From ce07b5492096b03926dc8adbab5264614e9d847b Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Wed, 22 Oct 2025 19:42:13 +0200 Subject: [PATCH] ``` refactor: improve UI polish with empty state, spacing, and remove status messages ``` --- packages/app/src/app/index.tsx | 127 ++++++++++-------- packages/app/src/styles/theme.ts | 1 + .../server/src/server/websocket-server.ts | 24 ---- 3 files changed, 73 insertions(+), 79 deletions(-) diff --git a/packages/app/src/app/index.tsx b/packages/app/src/app/index.tsx index 82f4e4e1f..60b08e429 100644 --- a/packages/app/src/app/index.tsx +++ b/packages/app/src/app/index.tsx @@ -23,7 +23,7 @@ import { router } from "expo-router"; import { activateKeepAwakeAsync, deactivateKeepAwake } from "expo-keep-awake"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { StyleSheet, useUnistyles } from "react-native-unistyles"; -import { theme as defaultTheme } from "../styles/theme"; +import { theme as defaultTheme, theme } from "../styles/theme"; // Simple unique ID generator let messageIdCounter = 0; @@ -154,7 +154,7 @@ function RealtimeCircle({ volume, onClose }: RealtimeCircleProps) { // Update volume scale when volume changes useEffect(() => { // Map volume (0-1) to scale range (MIN_SCALE - MAX_SCALE) - const targetScale = MIN_SCALE + (volume * (MAX_SCALE - MIN_SCALE)); + const targetScale = MIN_SCALE + volume * (MAX_SCALE - MIN_SCALE); volumeScale.value = withSpring(targetScale, { damping: 15, stiffness: 150, @@ -199,7 +199,13 @@ function RealtimeCircle({ volume, onClose }: RealtimeCircleProps) { justifyContent: "center", }} > - + {/* Outer pulsating circle */} [ - ...prev, - { - type: "activity", - id: generateMessageId(), - timestamp: Date.now(), - activityType: "success", - message: "Realtime mode started - speak anytime!", - }, - ]); } catch (error: any) { console.error("[App] Failed to start realtime mode:", error); - setMessages((prev) => [ - ...prev, - { - type: "activity", - id: generateMessageId(), - timestamp: Date.now(), - activityType: "error", - message: `Failed to start realtime mode: ${error.message}`, - }, - ]); } } else { // Stop realtime mode @@ -1024,17 +1012,6 @@ export default function VoiceAssistantScreen() { }, }; ws.send(modeMessage); - - setMessages((prev) => [ - ...prev, - { - type: "activity", - id: generateMessageId(), - timestamp: Date.now(), - activityType: "info", - message: "Realtime mode stopped", - }, - ]); } catch (error: any) { console.error("[App] Failed to stop realtime mode:", error); } @@ -1138,10 +1115,22 @@ export default function VoiceAssistantScreen() { + {messages.length === 0 && !currentAssistantMessage && ( + + OnTheGo + + What would you like to work on? + + + )} + {messages.map((msg) => { if (msg.type === "user") { return ( @@ -1226,8 +1215,19 @@ export default function VoiceAssistantScreen() { > {isRealtimeMode ? ( // Realtime mode - show volume meter and mute button - - + + - + @@ -1408,25 +1406,42 @@ const styles = StyleSheet.create((theme) => ({ paddingBottom: theme.spacing[4], flexGrow: 1, }, + emptyStateContainer: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, + emptyState: { + alignItems: "center", + justifyContent: "center", + paddingHorizontal: theme.spacing[6], + }, + emptyStateTitle: { + fontSize: theme.fontSize["4xl"], + fontWeight: "700", + color: theme.colors.foreground, + marginBottom: theme.spacing[2], + }, + emptyStateSubtitle: { + fontSize: theme.fontSize.lg, + color: theme.colors.mutedForeground, + textAlign: "center", + }, inputAreaWrapper: { borderTopRightRadius: theme.borderRadius["2xl"], borderTopLeftRadius: theme.borderRadius["2xl"], - paddingTop: theme.spacing[2], - paddingHorizontal: theme.spacing[2], borderTopWidth: theme.borderWidth[1], borderTopColor: theme.colors.border, backgroundColor: theme.colors.muted, }, - inputArea: { - borderRadius: theme.borderRadius["2xl"], - paddingHorizontal: theme.spacing[2], - paddingVertical: theme.spacing[3], - }, + inputArea: {}, textInput: { + paddingTop: theme.spacing[4], + paddingHorizontal: theme.spacing[4], + borderRadius: theme.borderRadius["2xl"], + paddingVertical: theme.spacing[3], backgroundColor: "transparent", color: theme.colors.foreground, - paddingHorizontal: 0, - paddingVertical: 0, fontSize: theme.fontSize.lg, marginBottom: theme.spacing[3], maxHeight: 128, @@ -1437,6 +1452,8 @@ const styles = StyleSheet.create((theme) => ({ justifyContent: "flex-end", gap: theme.spacing[2], marginBottom: theme.spacing[1], + paddingHorizontal: theme.spacing[4], + paddingBottom: theme.spacing[2], }, realtimeButton: { width: 40, diff --git a/packages/app/src/styles/theme.ts b/packages/app/src/styles/theme.ts index 9b4115b74..91bc014ff 100644 --- a/packages/app/src/styles/theme.ts +++ b/packages/app/src/styles/theme.ts @@ -170,6 +170,7 @@ const commonTheme = { xl: 18, "2xl": 20, "3xl": 24, + "4xl": 32, }, fontWeight: { diff --git a/packages/server/src/server/websocket-server.ts b/packages/server/src/server/websocket-server.ts index 927920eae..b5bd96ccf 100644 --- a/packages/server/src/server/websocket-server.ts +++ b/packages/server/src/server/websocket-server.ts @@ -82,30 +82,6 @@ export class VoiceAssistantWebSocketServer { })` ); - // Send welcome message - this.sendToClient( - ws, - wrapSessionMessage({ - type: "status", - payload: { - status: "connected", - message: "WebSocket connection established", - }, - }) - ); - - // Send conversation confirmation (whether new or loaded) - this.sendToClient( - ws, - wrapSessionMessage({ - type: "conversation_loaded", - payload: { - conversationId: session.getConversationId(), - messageCount: initialMessages?.length || 0, - }, - }) - ); - // Set up message handler ws.on("message", (data) => { this.handleMessage(ws, data);