Files
CRM_Montzar/apps/web/src/lib/userAvatars.ts
T

97 lines
3.1 KiB
TypeScript

import { reactive } from 'vue';
import type { OrgUserDto } from '@mento/shared';
import { api } from '@/lib/api';
import { useAuthStore } from '@/stores/auth';
export const DEFAULT_USER_AVATAR = '/brand/user-avatar.png';
/** userId → آیا عکس دارد (از فهرست کاربران سازمان) */
const hasAvatarById = reactive<Record<string, boolean>>({});
/** userId → object URL عکس */
const urlById = reactive<Record<string, string>>({});
const pending = new Map<string, Promise<void>>();
let usersLoad: Promise<void> | null = null;
let usersScope: string | null = null;
function resetCache() {
for (const url of Object.values(urlById)) URL.revokeObjectURL(url);
for (const key of Object.keys(urlById)) delete urlById[key];
for (const key of Object.keys(hasAvatarById)) delete hasAvatarById[key];
pending.clear();
usersLoad = null;
}
function ensureScope() {
const auth = useAuthStore();
const scope = auth.user ? `${auth.user.id}:${auth.user.organizationId}` : null;
if (scope !== usersScope) {
resetCache();
usersScope = scope;
}
return auth;
}
function loadUserFlags(): Promise<void> {
if (usersLoad) return usersLoad;
usersLoad = api
.get<OrgUserDto[]>('/users')
.then(({ data }) => {
for (const u of data ?? []) hasAvatarById[u.id] = Boolean(u.hasAvatar);
})
.catch(() => undefined);
return usersLoad;
}
function fetchAvatar(userId: string): Promise<void> {
const existing = pending.get(userId);
if (existing) return existing;
const job = api
.get<Blob>(`/users/${encodeURIComponent(userId)}/avatar`, { responseType: 'blob' })
.then(({ data }) => {
if (!data || data.size === 0 || data.type.includes('json')) return;
urlById[userId] = URL.createObjectURL(data);
})
.catch(() => {
hasAvatarById[userId] = false;
});
pending.set(userId, job);
return job;
}
/** اطلاع از وضعیت عکس کاربرانی که API برگردانده (مثلاً اعضای گفتگو). */
export function primeUserAvatar(userId: string, hasAvatar: boolean | undefined) {
ensureScope();
if (hasAvatar === undefined) return;
hasAvatarById[userId] = hasAvatar;
}
/** آدرس عکس پروفایل کاربر؛ تا بارگذاری نشده عکس پیش‌فرض برمی‌گردد. */
export function userAvatarSrc(userId: string | null | undefined): string {
if (!userId) return DEFAULT_USER_AVATAR;
const auth = ensureScope();
if (auth.user?.id === userId) return auth.avatarSrc;
const url = urlById[userId];
if (url) return url;
const flag = hasAvatarById[userId];
if (flag === undefined) {
void loadUserFlags().then(() => {
if (hasAvatarById[userId]) void fetchAvatar(userId);
});
} else if (flag) {
void fetchAvatar(userId);
}
return DEFAULT_USER_AVATAR;
}
/** بعد از تغییر عکس یک کاربر (مثلاً در تنظیمات تیم) کش را تازه کن. */
export function invalidateUserAvatar(userId: string) {
const url = urlById[userId];
if (url) URL.revokeObjectURL(url);
delete urlById[userId];
delete hasAvatarById[userId];
pending.delete(userId);
usersLoad = null;
}