feat: avatars for humans (#273)

* implement avatar backend functionality

* add avatar endpoints

* typo

* implement client side avatar upload and handling

* fixes

* Update go/internal/handler/handler.go

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update js/desktop/src/lib/avatar-image.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* bug in order

* remove unused component

* fix invalid migration

* fix syntax errors

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit was merged in pull request #273.
This commit is contained in:
Arjun Patel
2026-06-11 15:30:21 -07:00
committed by GitHub
co-authored by coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
parent ca3bbf204e
commit 4facc6b371
25 changed files with 739 additions and 256 deletions
+20
View File
@@ -0,0 +1,20 @@
import { useQuery, skipToken } from '@tanstack/react-query';
import { apiClient } from '@/api/client';
/**
* Resolves an avatar object id to a signed download URL. Mirrors
* {@link import('./use-download-url').useDownloadUrl} — React Query handles
* caching and de-duping, so many avatars sharing an id make a single request.
*/
export function useAvatarUrl(
objectId: string | null | undefined,
): string | undefined {
const { data } = useQuery({
queryKey: ['avatar-url', objectId],
queryFn: objectId
? () => apiClient.getAvatarDownloadUrl(objectId)
: skipToken,
staleTime: 1000 * 60 * 60, // 1 hour — signed URLs valid for 24h
});
return data;
}
@@ -5,6 +5,7 @@ export interface HumanPresence {
humanId: string;
email: string;
emailPrefix: string;
avatarObjectId: string | null;
}
/**
@@ -42,6 +43,7 @@ export function usePresencePositions(
humanId: human.id,
email: human.email,
emailPrefix: human.email_prefix,
avatarObjectId: human.avatar_object_id ?? null,
};
if (existing) {
existing.push(presence);