fix(files): аватары и баннеры доступны авторизованным пользователям

Аватар нельзя было показать в списке друзей: файл без комнаты был доступен
только загрузившему. Добавлено назначение файла (`files.purpose`: attachment |
avatar | banner, миграция 00006): аватары и баннеры отдаются любому
авторизованному пользователю, вложения по-прежнему проверяются по правам
комнаты, а непривязанные загрузки остаются приватными.
This commit is contained in:
2026-09-20 01:39:04 +03:00
parent 20eed227ca
commit d02ef7527a
35 changed files with 2330 additions and 191 deletions
+53
View File
@@ -0,0 +1,53 @@
import type { UserStatus } from '@/api/types';
import { DEFAULT_TIMEZONE } from '@/lib/timezones';
/**
* Присутствие в разделе друзей.
*
* Цвета точки статуса отличаются от панели пользователя (там «не беспокоить»
* красный, как в Discord): в списке друзей `dnd` — серый, а `invisible` —
* тёмно-голубой. У других пользователей «невидимка» приходит как `offline`,
* поэтому тёмно-голубой виден только у себя.
*/
export const presenceDotClass: Record<UserStatus, string> = {
online: 'bg-success',
offline: 'bg-danger',
idle: 'bg-warning',
dnd: 'bg-fg-muted',
invisible: 'bg-info',
};
/**
* Время последнего входа в часовом поясе пользователя из настроек
* (`user.timezone`, по умолчанию Москва).
*
* Возвращает `null`, если дата не пришла или не разбирается: вызывающий код
* показывает «Не в сети давно».
*/
export function formatLastSeen(
lastSeenAt: string | undefined,
timezone: string | undefined,
locale: string,
): string | null {
if (lastSeenAt === undefined || lastSeenAt === '') {
return null;
}
const date = new Date(lastSeenAt);
if (Number.isNaN(date.getTime())) {
return null;
}
try {
return new Intl.DateTimeFormat(locale, {
dateStyle: 'short',
timeStyle: 'short',
timeZone: timezone === undefined || timezone === '' ? DEFAULT_TIMEZONE : timezone,
}).format(date);
} catch {
// Неизвестный пояс (например, устаревшее значение в настройках) — МСК.
return new Intl.DateTimeFormat(locale, {
dateStyle: 'short',
timeStyle: 'short',
timeZone: DEFAULT_TIMEZONE,
}).format(date);
}
}