feat(web): друзья, личные беседы и вход только по приглашению
Раздел «Друзья» по запросу пользователя: - первая иконка рейки — «Друзья», серверы ниже за разделителем; личные беседы не засоряют список серверов; - сайдбар друзей: беседы с точками статуса и бейджами непрочитанного, список друзей с кликом на беседу; - контент: пустое состояние «Добавь друзей для общения» с поиском, поиск по логину (debounce), добавление в друзья, входящие и исходящие заявки, меню «…» (удалить/заблокировать), поповер последнего входа в часовом поясе пользователя (по умолчанию МСК), блок «Присоединиться по приглашению» с предпросмотром сервера; - личный чат на существующих компонентах: права по can_send канала, заголовок с именем и статусом собеседника, поиск, пины, ack и typing работают; - realtime: READY dm_channels, события DM_CHANNEL_CREATE, RELATIONSHIP_UPDATE, PRESENCE_UPDATE и MESSAGE_CREATE в беседах; - новый пользователь: «У вас пока нет серверов», создание сервера и вход по приглашению (автовступления нет); - 239 тестов Vitest (было 219), тест паритета ключей i18n (527 ключей).
This commit is contained in:
+39
-9
@@ -7,6 +7,7 @@ import { GatewayOp } from '@/api/gateway';
|
||||
import type { Message } from '@/api/messages';
|
||||
import { routes } from '@/router';
|
||||
import { setQueryClient } from '@/lib/queryClient';
|
||||
import { useFriendsStore } from '@/stores/friends';
|
||||
import { useGatewayStore } from '@/stores/gateway';
|
||||
import { useMessagesStore } from '@/stores/messages';
|
||||
import { useSessionStore } from '@/stores/session';
|
||||
@@ -120,19 +121,17 @@ export function resetStores(): void {
|
||||
useSessionStore.getState().reset();
|
||||
useMessagesStore.getState().reset();
|
||||
useVoiceStore.getState().reset();
|
||||
useFriendsStore.getState().reset();
|
||||
// Окно настроек — обычное состояние UI: между тестами оно должно быть закрыто.
|
||||
useUiStore.setState({
|
||||
guildCreateOpen: false,
|
||||
settingsOpen: false,
|
||||
settingsSection: 'profile',
|
||||
settingsProfileTab: 'main',
|
||||
});
|
||||
useGatewayStore.setState({
|
||||
status: 'idle',
|
||||
heartbeatIntervalMs: null,
|
||||
sessionId: null,
|
||||
attempts: 0,
|
||||
invalidated: false,
|
||||
});
|
||||
// `reset` закрывает и общее соединение шлюза: иначе колбэки прошлого теста
|
||||
// (переподключение, статус) меняли бы стор уже следующего.
|
||||
useGatewayStore.getState().reset();
|
||||
}
|
||||
|
||||
/** Рендер приложения на заданном маршруте с изолированными сторами. */
|
||||
@@ -206,8 +205,24 @@ interface SnapshotGuildInput {
|
||||
}[];
|
||||
}
|
||||
|
||||
/** Личная беседа в снапшоте READY. */
|
||||
export interface SnapshotDmInput {
|
||||
id: string;
|
||||
/** Имя собеседника (сервер отдаёт display_name в поле `name`). */
|
||||
name: string;
|
||||
recipient_id?: string;
|
||||
icon_file_id?: string;
|
||||
status?: string;
|
||||
last_message_id?: string;
|
||||
last_message_at?: string;
|
||||
can_send?: boolean;
|
||||
}
|
||||
|
||||
/** Снапшот READY в формате шлюза. */
|
||||
export function makeReadySnapshot(guilds: SnapshotGuildInput[] = []) {
|
||||
export function makeReadySnapshot(
|
||||
guilds: SnapshotGuildInput[] = [],
|
||||
dmChannels: SnapshotDmInput[] = [],
|
||||
) {
|
||||
return {
|
||||
user: {
|
||||
id: 'user-1',
|
||||
@@ -237,7 +252,22 @@ export function makeReadySnapshot(guilds: SnapshotGuildInput[] = []) {
|
||||
my_role_ids: [],
|
||||
my_permissions: guild.my_permissions ?? [],
|
||||
})),
|
||||
dm_channels: [],
|
||||
dm_channels: dmChannels.map((channel) => ({
|
||||
id: channel.id,
|
||||
type: 'dm',
|
||||
name: channel.name,
|
||||
position: 0,
|
||||
...(channel.recipient_id === undefined ? {} : { recipient_id: channel.recipient_id }),
|
||||
...(channel.icon_file_id === undefined ? {} : { icon_file_id: channel.icon_file_id }),
|
||||
...(channel.status === undefined ? {} : { status: channel.status }),
|
||||
...(channel.last_message_id === undefined
|
||||
? {}
|
||||
: { last_message_id: channel.last_message_id }),
|
||||
...(channel.last_message_at === undefined
|
||||
? {}
|
||||
: { last_message_at: channel.last_message_at }),
|
||||
can_send: channel.can_send ?? true,
|
||||
})),
|
||||
read_states: [],
|
||||
session_id: 'session-1',
|
||||
heartbeat_interval_ms: 45_000,
|
||||
|
||||
Reference in New Issue
Block a user