diff --git a/web/src/stores/gateway.ts b/web/src/stores/gateway.ts index 05b0e65..0abd83d 100644 --- a/web/src/stores/gateway.ts +++ b/web/src/stores/gateway.ts @@ -295,6 +295,9 @@ export function dispatchGatewayEvent(event: GatewayDispatch): void { if (guildId !== null) { void invalidateMembershipQueries(guildId); session.scheduleGuildChannelsReload(guildId); + // Свои права после выдачи роли обновляет только сводка сервера: без + // неё кнопки управления в шапке появлялись лишь после перезагрузки. + void addGuildFromRest(guildId); } return; } @@ -305,6 +308,8 @@ export function dispatchGatewayEvent(event: GatewayDispatch): void { if (role !== null) { void invalidateMembershipQueries(role.guild_id); session.scheduleGuildChannelsReload(role.guild_id); + // Права роли могли измениться и у меня: обновляем сводку сервера. + void addGuildFromRest(role.guild_id); } return; } diff --git a/web/tests/guildHeaderAndSounds.test.tsx b/web/tests/guildHeaderAndSounds.test.tsx index 7579949..1028ca0 100644 --- a/web/tests/guildHeaderAndSounds.test.tsx +++ b/web/tests/guildHeaderAndSounds.test.tsx @@ -33,6 +33,12 @@ const user = makeUser(); const channels = [{ id: 'c-general', name: 'общий', type: 'text' as const, position: 0 }]; +/** + * Права, которые отдаёт REST-сводка сервера: тест выдачи роли меняет их и + * проверяет, что клиент перечитал сводку по событию MEMBER_UPDATE. + */ +let restPermissions: string[] | null = null; + function guildSummary(overrides: Record = {}) { return { id: 'g-1', @@ -79,7 +85,17 @@ function routes(guild: Record = guildDetail()): FetchRoute[] { method: 'POST', response: () => json({ guild: guildDetail({ icon_file_id: '777', banner_file_id: '555' }) }), }, - { match: '/api/v1/users/@me/guilds', response: () => json({ guilds: [guildSummary(guild)] }) }, + { + match: '/api/v1/users/@me/guilds', + response: () => + json({ + guilds: [ + guildSummary( + restPermissions === null ? guild : { ...guild, my_permissions: restPermissions }, + ), + ], + }), + }, { match: '/api/v1/users/@me', response: () => json({ user }) }, { match: '/api/v1/instance', response: () => json({ instance: { name: 'glchat-test' } }) }, { match: '/api/v1/guilds/g-1/members', response: () => json({ members: [] }) }, @@ -122,6 +138,7 @@ async function bootApp(guild: Record = guildDetail()) { beforeEach(() => { resetStores(); + restPermissions = null; }); describe('оформление сервера в шапке и рейке', () => { @@ -217,6 +234,25 @@ describe('приглашение из шапки сервера', () => { await screen.findByTestId('guild-header'); expect(screen.queryByTestId('guild-invite')).toBeNull(); }); + + it('появляет кнопки после выдачи роли, без перезагрузки', async () => { + // Роль выдали уже в открытом клиенте: сводка сервера перечитывается по + // MEMBER_UPDATE, иначе кнопки управления ждали бы перезагрузки (AGENT.md 11.6). + const { gateway } = await bootApp(guildDetail({ my_permissions: [], owner_id: 'user-9' })); + await screen.findByTestId('guild-header'); + expect(screen.queryByTestId('guild-invite')).toBeNull(); + + restPermissions = ['MANAGE_GUILD', 'CREATE_INVITES']; + act(() => { + gateway.dispatch('MEMBER_UPDATE', { guild_id: 'g-1', user_id: 'user-1' }); + }); + + await waitFor(() => { + expect(screen.getByTestId('guild-invite')).toBeInTheDocument(); + }); + expect(screen.getByLabelText('Настройки сервера')).toBeInTheDocument(); + expect(screen.getByLabelText('Создать комнату')).toBeInTheDocument(); + }); }); describe('встроенные звуки интерфейса', () => {