feat(userVignette): show by preference givenname/familyname
This commit is contained in:
@@ -155,14 +155,25 @@ export function createUserVignette(userId, size = 'sm', { showName = true, showE
|
||||
// longer existed in the DOM, leaving the badge invisible until
|
||||
// the next render. Creating-then-appending keeps the icon system
|
||||
// and our reveal step in agreement.
|
||||
//
|
||||
// We always fetch the email — when `showEmail` is false (the common
|
||||
// case) it's still used as the hover-tooltip on the vignette so the
|
||||
// recipient identifier stays discoverable without visual clutter.
|
||||
Promise.all([
|
||||
systemUsers.getDisplayName(userId),
|
||||
systemUsers.getPhoto(userId),
|
||||
emailEl ? systemUsers.getEmail(userId) : Promise.resolve(null),
|
||||
systemUsers.getEmail(userId),
|
||||
showOrigin ? systemUsers.getIsExternal(userId) : Promise.resolve(false)
|
||||
]).then(([name, photo, email, isExternal]) => {
|
||||
if (nameEl) nameEl.textContent = name;
|
||||
if (emailEl) emailEl.textContent = email ?? '';
|
||||
// Tooltip: surface the email on hover when it's not already
|
||||
// rendered as the visible label (showEmail mode) and isn't
|
||||
// already the displayed name (the fallback case where the user
|
||||
// has no given/family/username and the label IS the email).
|
||||
if (email && !showEmail && email !== name) {
|
||||
wrapper.title = email;
|
||||
}
|
||||
if (photo) {
|
||||
_applyPhoto(avatar, photo, name);
|
||||
} else {
|
||||
|
||||
@@ -53,6 +53,31 @@ function _nameFor(c) {
|
||||
return `${c.id.slice(0, 8)}…`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive the best display name from a `User` shape (i.e. the
|
||||
* `/api/users/{id}` payload OR the `oxicloud_user` localStorage blob).
|
||||
* Priority — matches the server-side `User::display_full()` rule sans
|
||||
* the email decoration; the `<email>` part is added in the vignette
|
||||
* layer as a tooltip when the email isn't already in the displayed
|
||||
* label:
|
||||
*
|
||||
* 1. `"Given Family"` — both names set
|
||||
* 2. `username` — handle (the typical case for password / OIDC
|
||||
* users with no profile claims)
|
||||
* 3. `email` — last resort but unambiguous
|
||||
* 4. shortened UUID — failure mode (e.g. /api/users/{id} returned
|
||||
* nothing usable)
|
||||
*
|
||||
* @param {{id?: string, given_name?: string|null, family_name?: string|null, username?: string|null, email?: string|null}} u
|
||||
* @returns {string}
|
||||
*/
|
||||
function _displayNameFromUser(u) {
|
||||
if (u.given_name && u.family_name) return `${u.given_name} ${u.family_name}`;
|
||||
if (u.username) return u.username;
|
||||
if (u.email) return u.email;
|
||||
return u.id ? `${u.id.slice(0, 8)}…` : '?';
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure both indexes are built (idempotent).
|
||||
* After loading contacts from the system address book, the current user
|
||||
@@ -93,13 +118,13 @@ async function _ensureIndex() {
|
||||
try {
|
||||
const raw = localStorage.getItem('oxicloud_user');
|
||||
if (raw) {
|
||||
const u = /** @type {{id?:string, display_name?:string, username?:string, email?:string, image?:string|null, is_external?:boolean}} */ (
|
||||
JSON.parse(raw)
|
||||
);
|
||||
const u =
|
||||
/** @type {{id?:string, given_name?:string|null, family_name?:string|null, username?:string|null, email?:string|null, image?:string|null, is_external?:boolean}} */ (
|
||||
JSON.parse(raw)
|
||||
);
|
||||
if (u?.id) {
|
||||
if (!_index.has(u.id)) {
|
||||
const name = u.display_name || u.username || u.email || `${u.id.slice(0, 8)}…`;
|
||||
_index.set(u.id, name);
|
||||
_index.set(u.id, _displayNameFromUser(u));
|
||||
}
|
||||
if (!_photoIndex.has(u.id)) {
|
||||
_photoIndex.set(u.id, u.image ?? null);
|
||||
@@ -143,7 +168,7 @@ async function _resolveMissing(userId) {
|
||||
if (!resp.ok) return;
|
||||
/** @type {User} */
|
||||
const u = await resp.json();
|
||||
_index?.set(u.id, u.username || u.email || `${u.id.slice(0, 8)}…`);
|
||||
_index?.set(u.id, _displayNameFromUser(u));
|
||||
_photoIndex?.set(u.id, u.image ?? null);
|
||||
_emailIndex?.set(u.id, u.email ?? null);
|
||||
_externalIndex?.set(u.id, !!u.is_external);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"email": {
|
||||
"invitation": {
|
||||
"subject": "{{inviter}} shared a {{kind}} with you on OxiCloud",
|
||||
"body": "{{inviter}} shared a {{kind}} with you on OxiCloud.\n\nOpen it by clicking the link below:\n{{link}}\n\nThe link works once and expires in {{ttl_hours}} hours.\nIf you didn't expect this invitation, you can safely ignore this message.\n\n— OxiCloud"
|
||||
"body": "{{inviter_full}} shared a {{kind}} with you on OxiCloud.\n\nOpen it by clicking the link below:\n{{link}}\n\nThe link works once and expires in {{ttl_hours}} hours.\nIf you didn't expect this invitation, you can safely ignore this message.\n\n— OxiCloud"
|
||||
},
|
||||
"login": {
|
||||
"subject": "Sign in to OxiCloud",
|
||||
@@ -34,7 +34,7 @@
|
||||
"notification": {
|
||||
"share": {
|
||||
"subject": "{{inviter}} shared a {{kind}} with you on OxiCloud",
|
||||
"body": "{{inviter}} shared a {{kind}} with you on OxiCloud.\n\nOpen OxiCloud to see your new share:\n{{login_link}}\n\nYou may have additional new shares from {{inviter}} — sign in to see all your shared items.\n\n— OxiCloud\n\nYou're receiving this message because you have an OxiCloud account and your share-notification preference is on. You can turn it off in your profile (Email me when someone shares with me)."
|
||||
"body": "{{inviter_full}} shared a {{kind}} with you on OxiCloud.\n\nOpen OxiCloud to see your new share:\n{{login_link}}\n\nYou may have additional new shares from {{inviter}} — sign in to see all your shared items.\n\n— OxiCloud\n\nYou're receiving this message because you have an OxiCloud account and your share-notification preference is on. You can turn it off in your profile (Email me when someone shares with me)."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user