Matthias Nott
2026-03-15 32ede5388bb6c66c5d5679c2d73fd6ec6b2342bf
components/SessionDrawer.tsx
....@@ -44,6 +44,7 @@
4444 function SessionRow({
4545 session,
4646 unreadCount,
47
+ isUnread,
4748 onSwitch,
4849 onLongPress,
4950 onDelete,
....@@ -53,6 +54,7 @@
5354 }: {
5455 session: WsSession;
5556 unreadCount: number;
57
+ isUnread: boolean;
5658 onSwitch: () => void;
5759 onLongPress: () => void;
5860 onDelete: () => void;
....@@ -152,16 +154,32 @@
152154
153155 {/* Name + subtitle — middle */}
154156 <View style={{ flex: 1, marginLeft: 14 }}>
155
- <Text
156
- style={{
157
- color: session.isActive ? colors.accent : colors.text,
158
- fontSize: 17,
159
- fontWeight: session.isActive ? "700" : "600",
160
- }}
161
- numberOfLines={1}
162
- >
163
- {session.name}
164
- </Text>
157
+ <View style={{ flexDirection: "row", alignItems: "center" }}>
158
+ <Text
159
+ style={{
160
+ color: session.isActive ? colors.accent : colors.text,
161
+ fontSize: 17,
162
+ fontWeight: session.isActive ? "700" : "600",
163
+ flexShrink: 1,
164
+ }}
165
+ numberOfLines={1}
166
+ >
167
+ {session.name}
168
+ </Text>
169
+ {/* Server-pushed unread dot — shown when the server signals new activity */}
170
+ {isUnread && unreadCount === 0 && (
171
+ <View
172
+ style={{
173
+ width: 8,
174
+ height: 8,
175
+ borderRadius: 4,
176
+ backgroundColor: colors.accent,
177
+ marginLeft: 6,
178
+ flexShrink: 0,
179
+ }}
180
+ />
181
+ )}
182
+ </View>
165183 <Text
166184 style={{
167185 color: colors.textMuted,
....@@ -266,6 +284,7 @@
266284 fetchProjects,
267285 projects,
268286 unreadCounts,
287
+ unreadSessions,
269288 } = useChat();
270289 const { colors } = useTheme();
271290 const [editingId, setEditingId] = useState<string | null>(null);
....@@ -276,6 +295,7 @@
276295 const [rendered, setRendered] = useState(false);
277296 const [keyboardHeight, setKeyboardHeight] = useState(0);
278297 const pickerScrollRef = useRef<ScrollView>(null);
298
+ const listRef = useRef<DraggableFlatList<WsSession>>(null);
279299
280300 useEffect(() => {
281301 const showSub = Keyboard.addListener("keyboardWillShow", (e) => {
....@@ -361,7 +381,16 @@
361381 const handleStartRename = useCallback((session: WsSession) => {
362382 Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
363383 setEditingId(session.id);
364
- }, []);
384
+ // Scroll to the item after keyboard appears so it's visible
385
+ const idx = orderedSessions.findIndex(s => s.id === session.id);
386
+ if (idx >= 0 && listRef.current) {
387
+ setTimeout(() => {
388
+ try {
389
+ (listRef.current as any)?.scrollToIndex({ index: idx, animated: true, viewPosition: 0.3 });
390
+ } catch { /* ignore if index out of range */ }
391
+ }, 400);
392
+ }
393
+ }, [orderedSessions]);
365394
366395 const handleConfirmRename = useCallback(
367396 (sessionId: string, newName: string) => {
....@@ -414,6 +443,7 @@
414443 <SessionRow
415444 session={item}
416445 unreadCount={unreadCounts[item.id] ?? 0}
446
+ isUnread={unreadSessions.has(item.id)}
417447 onSwitch={() => handleSwitch(item)}
418448 onLongPress={() => handleStartRename(item)}
419449 onDelete={() => handleRemove(item)}
....@@ -424,7 +454,7 @@
424454 </ScaleDecorator>
425455 );
426456 },
427
- [editingId, unreadCounts, colors, handleSwitch, handleStartRename, handleRemove, handleConfirmRename],
457
+ [editingId, unreadCounts, unreadSessions, colors, handleSwitch, handleStartRename, handleRemove, handleConfirmRename],
428458 );
429459
430460 const keyExtractor = useCallback((item: WsSession) => item.id, []);
....@@ -537,6 +567,7 @@
537567 </View>
538568 ) : (
539569 <DraggableFlatList
570
+ ref={listRef}
540571 data={orderedSessions}
541572 keyExtractor={keyExtractor}
542573 renderItem={renderItem}