fuzzyworld

north-star / commit

removed extra list of friends

1ca0cf1b8cebdf1ed1ebffa3dc027988c29248a8 · Pavel Safronov · 2026-03-18 23:04:14 -0700 · parents 3266273722d4

removed extra list of friends
 mobile/src/components/arrows_panel.tsx     |  64 ++---------------
 mobile/src/features/friends/sync.ts        |  11 +--
 mobile/src/screens/room_session_screen.tsx | 109 +++++++++++++++--------------
 3 files changed, 70 insertions(+), 114 deletions(-)

diff --git a/mobile/src/components/arrows_panel.tsx b/mobile/src/components/arrows_panel.tsx
index eec655c..872cbab 100644
--- a/mobile/src/components/arrows_panel.tsx
+++ b/mobile/src/components/arrows_panel.tsx
@@ -6,19 +6,17 @@ export type ArrowFriend = {
     name: string;
     color: string;
     arrowDeg: number | null;
-    dist: string | null;
 };
 
 type ArrowsPanelProps = {
     friends: ArrowFriend[];
 };
 
-// Arrow needle tip pointing up (north), centered at (0,0).
-// Drawn in a ±1 unit coordinate space, scaled via SVG transform.
-const NEEDLE_TIP = 'M 0 -36 L 8 -10 L 0 -16 L -8 -10 Z';
-const NEEDLE_TAIL = 'M 0 16 L 5 6 L 0 10 L -5 6 Z';
+// Needle pointing up (north), centered at (0,0), sized for the larger compass.
+const NEEDLE_TIP = 'M 0 -105 L 22 -30 L 0 -50 L -22 -30 Z';
+const NEEDLE_TAIL = 'M 0 45 L 14 16 L 0 28 L -14 16 Z';
 
-const COMPASS_SIZE = 260;
+const COMPASS_SIZE = 310;
 const CENTER = COMPASS_SIZE / 2;
 
 export function ArrowsPanel({ friends }: ArrowsPanelProps) {
@@ -49,8 +47,8 @@ export function ArrowsPanel({ friends }: ArrowsPanelProps) {
                     {/* Cardinal tick marks */}
                     {[0, 90, 180, 270].map(deg => {
                         const rad = (deg - 90) * (Math.PI / 180);
-                        const inner = CENTER - 20;
-                        const outer = CENTER - 6;
+                        const inner = CENTER - 26;
+                        const outer = CENTER - 8;
                         return (
                             <Line
                                 key={deg}
@@ -64,7 +62,7 @@ export function ArrowsPanel({ friends }: ArrowsPanelProps) {
                         );
                     })}
                     {/* Center dot */}
-                    <Circle cx={CENTER} cy={CENTER} r={6} fill="rgba(0,0,0,0.15)" />
+                    <Circle cx={CENTER} cy={CENTER} r={8} fill="rgba(0,0,0,0.18)" />
 
                     {/* One needle per friend */}
                     {friends.map((friend) => {
@@ -92,22 +90,6 @@ export function ArrowsPanel({ friends }: ArrowsPanelProps) {
                     )}
                 </Svg>
             </View>
-
-            {/* Legend: one row per friend */}
-            <View style={styles.legend}>
-                {friends.map((friend) => (
-                    <View key={friend.id} style={styles.legendRow}>
-                        <View style={[styles.legendDot, { backgroundColor: friend.color }]} />
-                        <Text style={styles.legendName} numberOfLines={1}>{friend.name}</Text>
-                        {friend.dist !== null && (
-                            <Text style={styles.legendDist}>{friend.dist}</Text>
-                        )}
-                        {friend.arrowDeg === null && (
-                            <Text style={styles.legendNoPos}>no position</Text>
-                        )}
-                    </View>
-                ))}
-            </View>
         </View>
     );
 }
@@ -115,44 +97,12 @@ export function ArrowsPanel({ friends }: ArrowsPanelProps) {
 const styles = StyleSheet.create({
     wrap: {
         alignItems: 'center',
-        gap: 16,
         paddingVertical: 4
     },
     compassOuter: {
         borderRadius: COMPASS_SIZE / 2,
         overflow: 'hidden'
     },
-    legend: {
-        alignSelf: 'stretch',
-        gap: 8,
-        paddingHorizontal: 4
-    },
-    legendRow: {
-        flexDirection: 'row',
-        alignItems: 'center',
-        gap: 8
-    },
-    legendDot: {
-        width: 12,
-        height: 12,
-        borderRadius: 6,
-        flexShrink: 0
-    },
-    legendName: {
-        flex: 1,
-        fontSize: 14,
-        fontWeight: '600',
-        color: '#2e2a23'
-    },
-    legendDist: {
-        fontSize: 13,
-        color: '#7a7469'
-    },
-    legendNoPos: {
-        fontSize: 12,
-        color: '#9a8f84',
-        fontStyle: 'italic'
-    },
     emptyWrap: {
         paddingHorizontal: 8,
         paddingVertical: 12
diff --git a/mobile/src/features/friends/sync.ts b/mobile/src/features/friends/sync.ts
index c27155a..eec2e00 100644
--- a/mobile/src/features/friends/sync.ts
+++ b/mobile/src/features/friends/sync.ts
@@ -79,9 +79,12 @@ export const subscribeToFriends = ({
         async (snapshot) => {
             try {
                 const value = snapshot.val() as Record<string, MobileFriendDbRecord> | null;
-                const raw = value ? Object.values(value) : [];
+                const raw = value ? Object.entries(value) : [];
+
+                const resolved = await Promise.all(raw.map(async ([key, friend]): Promise<MobileFriendRecord | null> => {
+                    // Filter self by Firebase key — most reliable, works for any write format
+                    if (key === selfUid) return null;
 
-                const resolved = await Promise.all(raw.map(async (friend): Promise<MobileFriendRecord | null> => {
                     if (friend.encryptedState) {
                         if (!roomKey) return null;
                         try {
@@ -106,10 +109,10 @@ export const subscribeToFriends = ({
                         }
                     }
 
-                    return friend;
+                    return { ...friend, id: key };
                 }));
 
-                const nextFriends = resolved.filter((item): item is MobileFriendRecord => item !== null && item.id !== selfUid);
+                const nextFriends = resolved.filter((item): item is MobileFriendRecord => item !== null);
                 nextFriends.sort((a, b) => {
                     const aName = (a.name || '').toLowerCase();
                     const bName = (b.name || '').toLowerCase();
diff --git a/mobile/src/screens/room_session_screen.tsx b/mobile/src/screens/room_session_screen.tsx
index 31a0c9a..8f1a0c7 100644
--- a/mobile/src/screens/room_session_screen.tsx
+++ b/mobile/src/screens/room_session_screen.tsx
@@ -11,7 +11,7 @@ import {
 import AsyncStorage from '@react-native-async-storage/async-storage';
 import QRCode from 'react-native-qrcode-svg';
 import { ArrowsPanel, type ArrowFriend } from '../components/arrows_panel';
-import { formatDistance, getBearingDeg, getDistanceMeters, getRelativeBearingDeg } from '../core/bearing';
+import { getBearingDeg, getRelativeBearingDeg } from '../core/bearing';
 import { parseRoomFromHash } from '../core/room';
 import type { SymmetricKey } from '../core/crypto';
 import { watchHeading } from '../platform/heading';
@@ -78,10 +78,7 @@ export function RoomSessionScreen({
                 const arrowDeg = bearingDeg !== null
                     ? getRelativeBearingDeg(bearingDeg, headingDeg)
                     : null;
-                const dist = position && f.position
-                    ? formatDistance(getDistanceMeters(position, f.position))
-                    : null;
-                return { id: f.id, name: f.name || f.id, color: f.color ?? '#888888', arrowDeg, dist };
+                return { id: f.id, name: f.name || f.id, color: f.color ?? '#888888', arrowDeg };
             });
     }, [orderedFriends, hiddenFriendIds, position, headingDeg]);
 
@@ -264,54 +261,52 @@ export function RoomSessionScreen({
                     {locationStatus === 'watching' && !position && <Text style={styles.valueMuted}>waiting for GPS…</Text>}
                 </View>
                 <ArrowsPanel friends={arrowFriends} />
+                {/* Friend rows with controls */}
+                <View style={styles.friendsList}>
+                    {orderedFriends.map((friend, index) => {
+                        const isHidden = hiddenFriendIds.has(friend.id);
+                        const hasPos = !!friend.position;
+                        return (
+                            <View key={friend.id} style={styles.friendRow}>
+                                <View style={[styles.friendDot, { backgroundColor: friend.color ?? '#888888', opacity: isHidden ? 0.35 : 1 }]} />
+                                <Text style={[styles.value, styles.friendName, isHidden && styles.friendNameHidden]} numberOfLines={1}>
+                                    {friend.name || friend.id}
+                                </Text>
+                                {!hasPos && !isHidden && <Text style={styles.noPos}>no pos</Text>}
+                                <Pressable
+                                    onPress={() => toggleHidden(friend.id)}
+                                    style={({ pressed }) => [styles.hideButton, pressed && styles.buttonPressed]}
+                                >
+                                    <Text style={styles.hideButtonText}>{isHidden ? 'show' : 'hide'}</Text>
+                                </Pressable>
+                                {orderedFriends.length > 1 && (
+                                    <View style={styles.moveButtons}>
+                                        <Pressable
+                                            onPress={() => moveUp(index)}
+                                            disabled={index === 0}
+                                            style={[styles.moveButton, index === 0 && styles.moveButtonDim]}
+                                        >
+                                            <Text style={styles.moveButtonText}>▲</Text>
+                                        </Pressable>
+                                        <Pressable
+                                            onPress={() => moveDown(index)}
+                                            disabled={index === orderedFriends.length - 1}
+                                            style={[styles.moveButton, index === orderedFriends.length - 1 && styles.moveButtonDim]}
+                                        >
+                                            <Text style={styles.moveButtonText}>▼</Text>
+                                        </Pressable>
+                                    </View>
+                                )}
+                            </View>
+                        );
+                    })}
+                    {orderedFriends.length === 0 && firebaseSession && (
+                        <Text style={styles.valueMuted}>No friends in room yet.</Text>
+                    )}
+                </View>
                 {!!syncError && <Text style={styles.errorText}>{syncError}</Text>}
             </View>
 
-            {/* ── Friends list ── */}
-            <View style={styles.card}>
-                <Text style={styles.label}>
-                    {orderedFriends.length > 0 ? `Friends (${orderedFriends.length})` : 'Friends'}
-                </Text>
-                {orderedFriends.map((friend, index) => {
-                    const isHidden = hiddenFriendIds.has(friend.id);
-                    return (
-                        <View key={friend.id} style={styles.friendRow}>
-                            <View style={[styles.friendDot, { backgroundColor: friend.color ?? '#888888', opacity: isHidden ? 0.35 : 1 }]} />
-                            <Text style={[styles.value, styles.friendName, isHidden && styles.friendNameHidden]}>
-                                {friend.name || friend.id}
-                            </Text>
-                            <Pressable
-                                onPress={() => toggleHidden(friend.id)}
-                                style={({ pressed }) => [styles.hideButton, pressed && styles.buttonPressed]}
-                            >
-                                <Text style={styles.hideButtonText}>{isHidden ? 'show' : 'hide'}</Text>
-                            </Pressable>
-                            {orderedFriends.length > 1 && (
-                                <View style={styles.moveButtons}>
-                                    <Pressable
-                                        onPress={() => moveUp(index)}
-                                        disabled={index === 0}
-                                        style={[styles.moveButton, index === 0 && styles.moveButtonDim]}
-                                    >
-                                        <Text style={styles.moveButtonText}>▲</Text>
-                                    </Pressable>
-                                    <Pressable
-                                        onPress={() => moveDown(index)}
-                                        disabled={index === orderedFriends.length - 1}
-                                        style={[styles.moveButton, index === orderedFriends.length - 1 && styles.moveButtonDim]}
-                                    >
-                                        <Text style={styles.moveButtonText}>▼</Text>
-                                    </Pressable>
-                                </View>
-                            )}
-                        </View>
-                    );
-                })}
-                {orderedFriends.length === 0 && firebaseSession && (
-                    <Text style={styles.valueMuted}>No friends in room yet.</Text>
-                )}
-            </View>
-
             {/* ── Invite QR ── */}
             <View style={styles.card}>
                 <Pressable
@@ -393,6 +388,10 @@ const styles = StyleSheet.create({
         gap: 10,
         paddingHorizontal: 4
     },
+    friendsList: {
+        gap: 10,
+        paddingHorizontal: 4
+    },
     connectingRowInline: {
         flexDirection: 'row',
         alignItems: 'center',
@@ -418,12 +417,11 @@ const styles = StyleSheet.create({
         color: '#2e2a23'
     },
     secondaryButton: {
-        borderWidth: 1,
-        borderColor: '#7a4f2a',
+        backgroundColor: 'rgba(255,252,245,0.92)',
         borderRadius: 10,
         alignItems: 'center',
         paddingVertical: 8,
-        paddingHorizontal: 12
+        paddingHorizontal: 14
     },
     secondaryButtonText: {
         color: '#7a4f2a',
@@ -461,6 +459,11 @@ const styles = StyleSheet.create({
     friendNameHidden: {
         opacity: 0.4
     },
+    noPos: {
+        fontSize: 11,
+        color: '#9a8f84',
+        fontStyle: 'italic'
+    },
     hideButton: {
         paddingHorizontal: 10,
         paddingVertical: 4,