Files
Oxicloud/frontend/src/lib/utils/avatar.ts
T
DioCrafts b3bde0d896 feat(shares): show external users with avatar, email and a badge (#500)
Two related parity gaps from the VanillaJS → Svelte migration (issue #500):
internal-vs-external users weren't badged, and external users in a share's
member list rendered as a bare UUID with a static icon — no avatar, no email.
Both share one root cause: there was no shared user vignette and no resolver
for non-directory (external) users (the system address book lists internal
users only, and ShareDialog hardcoded isExternal=false).

- lib/api/endpoints/users.ts: resolveUser(id) — cached GET /api/users/{id}
  (the authenticated per-user profile lookup) → {name, email, image,
  isExternal}; returns null when the profile isn't visible so callers keep
  their fallback label.
- lib/components/UserVignette.svelte: reusable identity chip — avatar (photo
  or coloured initials), name, email, and a building-circle-xmark badge for
  external users; resolves lazily and falls back to a caller-supplied label.
- lib/utils/avatar.ts: userInitials() + avatarColorIndex() extracted from
  AppShell (now shared by both — no duplicated logic) so vignette and account
  button render identically.
- ShareDialog: user member rows now render <UserVignette>; groups keep their
  icon+label. Drops the dead hardcoded isExternal.

Backend already exposes everything (UserDto.email/image/is_external via
GET /api/users/{id}); no backend change. Frontend gate green (svelte-check
0/0, eslint, stylelint, prettier) + 47 Vitest.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:23:54 +02:00

26 lines
1000 B
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Shared avatar helpers — initials + a deterministic colour bucket — so the
* user vignette (shares, recipients) and the app-shell account button render
* identically. Mirrors the original `userVignette` `_initials` / `_colorIndex`.
*/
/** Up-to-two-letter initials from a display label (name or email). */
export function userInitials(label: string | null | undefined): string {
const base = (label ?? '').trim();
if (!base) return '?';
const parts = base.split(/\s+/);
if (parts.length >= 2) return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
return base.slice(0, 2).toUpperCase();
}
/**
* Deterministic colour bucket 0–4 from an id, so the same user always gets the
* same avatar colour (matches the original `userVignette._colorIndex`).
*/
export function avatarColorIndex(id: string | null | undefined): number {
let hash = 0;
const s = id ?? '';
for (let i = 0; i < s.length; i++) hash = (hash * 31 + s.charCodeAt(i)) | 0;
return Math.abs(hash) % 5;
}