From 9911f1c5c84e1d153d18a1186ef77c39ef011829 Mon Sep 17 00:00:00 2001 From: Sivert <316918682+grytchat@users.noreply.github.com> Date: Sun, 1 Nov 2026 13:00:46 +0100 Subject: [PATCH] feat(friends): send each friend's avatar in the friend list The phone's friends tab spans every server and showed an owl for anyone it had no member list or DM for, while the DM showed their picture. Co-Authored-By: Claude Opus 5.5 --- src/db/sqlite/friends.ts | 10 ++++++---- src/socket/handlers/friends.ts | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/db/sqlite/friends.ts b/src/db/sqlite/friends.ts index 45c37636..a379e6a6 100644 --- a/src/db/sqlite/friends.ts +++ b/src/db/sqlite/friends.ts @@ -15,6 +15,7 @@ export interface FriendPerson { /** Null when their member row is gone. */ serverUserId: string | null; nickname: string | null; + avatarFileId: string | null; /** When the friendship, or the request, was made. */ at: string; } @@ -34,12 +35,13 @@ function cutoff(now = Date.now()): string { return toIso(new Date(now - FRIEND_REQUEST_TTL_MS)); } -type PersonRow = { gryt_user_id: string; server_user_id: string | null; nickname: string | null; at: string }; +type PersonRow = { gryt_user_id: string; server_user_id: string | null; nickname: string | null; avatar_file_id: string | null; at: string }; const toPerson = (r: PersonRow): FriendPerson => ({ grytUserId: r.gryt_user_id, serverUserId: r.server_user_id, nickname: r.nickname, + avatarFileId: r.avatar_file_id, at: r.at, }); @@ -59,7 +61,7 @@ export async function hasAnyFriend(grytUserId: string): Promise { export async function listFriends(grytUserId: string): Promise { const rows = getSqliteDb() .prepare( - `SELECT f.other AS gryt_user_id, u.server_user_id, u.nickname, f.created_at AS at + `SELECT f.other AS gryt_user_id, u.server_user_id, u.nickname, u.avatar_file_id, f.created_at AS at FROM (SELECT CASE WHEN a_gryt_user_id = ? THEN b_gryt_user_id ELSE a_gryt_user_id END AS other, created_at FROM friendships WHERE a_gryt_user_id = ? OR b_gryt_user_id = ?) f LEFT JOIN users u ON u.gryt_user_id = f.other @@ -110,7 +112,7 @@ export async function listFriendRequests(grytUserId: string): Promise ? ORDER BY r.created_at`, @@ -118,7 +120,7 @@ export async function listFriendRequests(grytUserId: string): Promise ? ORDER BY r.created_at`, diff --git a/src/socket/handlers/friends.ts b/src/socket/handlers/friends.ts index e640e675..97ef6538 100644 --- a/src/socket/handlers/friends.ts +++ b/src/socket/handlers/friends.ts @@ -40,13 +40,14 @@ const NOT_A_MEMBER = { error: "unknown_member", message: "That person is not a m export interface FriendView { serverUserId: string; nickname: string | null; + avatarFileId: string | null; at: string; } const view = (people: FriendPerson[]): FriendView[] => people .filter((p): p is FriendPerson & { serverUserId: string } => !!p.serverUserId) - .map((p) => ({ serverUserId: p.serverUserId, nickname: p.nickname, at: p.at })); + .map((p) => ({ serverUserId: p.serverUserId, nickname: p.nickname, avatarFileId: p.avatarFileId, at: p.at })); /** What one member is shown. A request from somebody either of them blocked isn't. */ export async function friendListFor(grytUserId: string): Promise<{ friends: FriendView[]; incoming: FriendView[]; outgoing: FriendView[] }> {