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:
@@ -199,6 +199,8 @@ export function dispatchGatewayEvent(event: GatewayDispatch): void {
|
||||
const message = parseMessagePayload(event.d);
|
||||
if (message !== null) {
|
||||
messages.applyMessageCreate(message);
|
||||
// В личной беседе «последнее сообщение» — источник бейджа и порядка.
|
||||
session.touchDmChannel(message.channel_id, message.id, message.created_at);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ interface SessionState {
|
||||
upsertDmChannel: (channel: Channel) => void;
|
||||
/** PRESENCE_UPDATE: статус собеседника в списке бесед и шапке чата. */
|
||||
patchDmPresence: (userId: string, status: UserStatus | undefined) => void;
|
||||
/** MESSAGE_CREATE в беседе: сдвигаем «последнее сообщение» (бейджи и порядок). */
|
||||
touchDmChannel: (channelId: string, messageId: string, at: string) => void;
|
||||
/** DM_CHANNEL_CREATE: перечитываем беседы по REST. */
|
||||
reloadDmChannels: () => Promise<void>;
|
||||
/** CHANNEL_DELETE: удаляет комнату и переносит выбор на первую доступную. */
|
||||
@@ -269,6 +271,19 @@ export const useSessionStore = create<SessionState>((set, get) => ({
|
||||
});
|
||||
},
|
||||
|
||||
touchDmChannel: (channelId, messageId, at) => {
|
||||
if (!get().dmChannels.some((channel) => channel.id === channelId)) {
|
||||
return;
|
||||
}
|
||||
set({
|
||||
dmChannels: get().dmChannels.map((channel) =>
|
||||
channel.id === channelId
|
||||
? { ...channel, last_message_id: messageId, last_message_at: at }
|
||||
: channel,
|
||||
),
|
||||
});
|
||||
},
|
||||
|
||||
reloadDmChannels: async () => {
|
||||
try {
|
||||
set({ dmChannels: await fetchDirectChannels() });
|
||||
|
||||
Reference in New Issue
Block a user