fix(web): DM_CHANNEL_UPDATE и DM_CHANNEL_DELETE обновляют список бесед
Сервер присылал эти события и раньше, но клиент перечитывал список бесед только по `DM_CHANNEL_CREATE` и READY: добавление участника, переименование группы и выход из неё не появлялись в сайдбаре до перезагрузки, а удалённая беседа оставалась открытой. Теперь все три события перечитывают список (`DM_CHANNEL_UPDATE` меняет состав, имя и аватар группы сразу), а `DM_CHANNEL_DELETE` дополнительно убирает беседу из стора сессии и закрывает её, если она была открыта. Тесты в `web/tests/friends.test.tsx` падают на старом коде: переименование группы видно второму участнику, удалённая беседа исчезает и закрывается.
This commit is contained in:
@@ -378,11 +378,21 @@ export function dispatchGatewayEvent(event: GatewayDispatch): void {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case 'DM_CHANNEL_CREATE': {
|
case 'DM_CHANNEL_CREATE':
|
||||||
// В событии только channel_id: список бесед забираем по REST.
|
case 'DM_CHANNEL_UPDATE':
|
||||||
|
case 'DM_CHANNEL_DELETE': {
|
||||||
|
// В событии только channel_id: список бесед забираем по REST. CREATE —
|
||||||
|
// новая беседа, UPDATE — изменился состав или имя группы, DELETE —
|
||||||
|
// беседа больше не наша (нас удалили). Список читается целиком, потому
|
||||||
|
// что от состава зависят и имя, и аватар, и шапка беседы.
|
||||||
const direct = parseDmChannelEvent(event.d);
|
const direct = parseDmChannelEvent(event.d);
|
||||||
if (direct !== null) {
|
if (direct !== null) {
|
||||||
void session.reloadDmChannels();
|
void session.reloadDmChannels().then(() => {
|
||||||
|
if (event.t === 'DM_CHANNEL_DELETE') {
|
||||||
|
// Удалённая беседа могла быть открыта: уводим в список бесед.
|
||||||
|
useSessionStore.getState().removeDmChannel(direct.channel_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ interface SessionState {
|
|||||||
reloadDmChannels: () => Promise<void>;
|
reloadDmChannels: () => Promise<void>;
|
||||||
/** CHANNEL_DELETE: удаляет комнату и переносит выбор на первую доступную. */
|
/** CHANNEL_DELETE: удаляет комнату и переносит выбор на первую доступную. */
|
||||||
removeChannel: (guildId: string, channelId: string) => void;
|
removeChannel: (guildId: string, channelId: string) => void;
|
||||||
|
/** DM_CHANNEL_DELETE: беседа пропала — убирает её из списка и из выбора. */
|
||||||
|
removeDmChannel: (channelId: string) => void;
|
||||||
/** GUILD_CHANNELS_SYNC: перечитать видимые комнаты сервера (права изменились). */
|
/** GUILD_CHANNELS_SYNC: перечитать видимые комнаты сервера (права изменились). */
|
||||||
reloadGuildChannels: (guildId: string) => Promise<void>;
|
reloadGuildChannels: (guildId: string) => Promise<void>;
|
||||||
/** То же, но с объединением частых вызовов в один запрос. */
|
/** То же, но с объединением частых вызовов в один запрос. */
|
||||||
@@ -582,6 +584,15 @@ export const useSessionStore = create<SessionState>((set, get) => ({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
removeDmChannel: (channelId) => {
|
||||||
|
const state = get();
|
||||||
|
set({
|
||||||
|
dmChannels: state.dmChannels.filter((channel) => channel.id !== channelId),
|
||||||
|
// Если пропала открытая беседа, показываем список бесед раздела друзей.
|
||||||
|
selectedChannelId: state.selectedChannelId === channelId ? null : state.selectedChannelId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
firstChannelId: (guildId) => {
|
firstChannelId: (guildId) => {
|
||||||
const guild = get().guilds.find((item) => item.id === guildId);
|
const guild = get().guilds.find((item) => item.id === guildId);
|
||||||
return guild === undefined ? null : firstVisibleChannelId(guild);
|
return guild === undefined ? null : firstVisibleChannelId(guild);
|
||||||
|
|||||||
@@ -743,6 +743,64 @@ describe('события шлюза о друзьях', () => {
|
|||||||
expect(screen.getByTestId('dm-status-dm-1')).toHaveAttribute('data-status', 'online');
|
expect(screen.getByTestId('dm-status-dm-1')).toHaveAttribute('data-status', 'online');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('DM_CHANNEL_UPDATE перечитывает список бесед', async () => {
|
||||||
|
// Состав и имя группы меняются событием: сервер шлёт только channel_id,
|
||||||
|
// поэтому список бесед перечитывается целиком (AGENT.md 7.8).
|
||||||
|
let renamed = false;
|
||||||
|
installFetch(
|
||||||
|
baseRoutes(empty, [
|
||||||
|
{
|
||||||
|
match: '/api/v1/users/@me/channels',
|
||||||
|
response: () =>
|
||||||
|
json({
|
||||||
|
channels: renamed ? [directChannel('dm-1', 'Аня и Борис')] : [],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
const gateway = installGatewaySocket(makeReadySnapshot([]));
|
||||||
|
|
||||||
|
renderApp('/app/friends');
|
||||||
|
await screen.findByTestId('guild-rail');
|
||||||
|
await gateway.greet();
|
||||||
|
expect(await screen.findByText('Личных бесед пока нет.')).toBeVisible();
|
||||||
|
|
||||||
|
renamed = true;
|
||||||
|
await dispatchEvent(gateway, 'DM_CHANNEL_UPDATE', { channel_id: 'dm-1' });
|
||||||
|
|
||||||
|
expect(await screen.findByTestId('dm-link-dm-1')).toHaveTextContent('Аня и Борис');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('DM_CHANNEL_DELETE убирает беседу и закрывает её', async () => {
|
||||||
|
let removed = false;
|
||||||
|
installFetch(
|
||||||
|
baseRoutes(empty, [
|
||||||
|
{
|
||||||
|
match: '/api/v1/users/@me/channels',
|
||||||
|
response: () => json({ channels: removed ? [] : [directChannel('dm-1')] }),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
const gateway = installGatewaySocket(
|
||||||
|
makeReadySnapshot(
|
||||||
|
[],
|
||||||
|
[{ id: 'dm-1', name: 'Боб', recipient_id: 'user-2', status: 'online' }],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
renderApp('/app/friends/dm-1');
|
||||||
|
await screen.findByTestId('guild-rail');
|
||||||
|
await gateway.greet();
|
||||||
|
expect(await screen.findByTestId('dm-link-dm-1')).toBeVisible();
|
||||||
|
|
||||||
|
removed = true;
|
||||||
|
await dispatchEvent(gateway, 'DM_CHANNEL_DELETE', { channel_id: 'dm-1' });
|
||||||
|
|
||||||
|
// Беседа исчезла из списка, а открытая беседа закрылась: показываем список.
|
||||||
|
await waitFor(() => expect(screen.queryByTestId('dm-link-dm-1')).not.toBeInTheDocument());
|
||||||
|
expect(await screen.findByText('Личных бесед пока нет.')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
it('RELATIONSHIP_UPDATE перечитывает друзей', async () => {
|
it('RELATIONSHIP_UPDATE перечитывает друзей', async () => {
|
||||||
let hasFriend = false;
|
let hasFriend = false;
|
||||||
installFetch(baseRoutes(() => (hasFriend ? { ...empty, friends: [bob] } : empty)));
|
installFetch(baseRoutes(() => (hasFriend ? { ...empty, friends: [bob] } : empty)));
|
||||||
|
|||||||
Reference in New Issue
Block a user