void list.refetch()} />
) : emojis.length === 0 ? (
{t('settings.servers.emojis.empty')}
diff --git a/web/src/stores/gateway.ts b/web/src/stores/gateway.ts
index 47d72e6..ba1b3b9 100644
--- a/web/src/stores/gateway.ts
+++ b/web/src/stores/gateway.ts
@@ -1,6 +1,6 @@
import { create } from 'zustand';
-import { guildEmojisQueryKey } from '@/api/emojis';
+import { guildEmojisQueryKey, type GuildEmoji } from '@/api/emojis';
import {
connectGateway,
disconnectGateway,
@@ -314,12 +314,12 @@ export function dispatchGatewayEvent(event: GatewayDispatch): void {
return;
}
case 'GUILD_EMOJIS_UPDATE': {
- // Набор эмодзи сервера изменился: заменяем его в снапшоте и помечаем
- // REST-кэш устаревшим — форма списка одна и та же.
+ // Набор эмодзи сервера изменился целиком: кладём его и в снапшот, и в
+ // кэш REST-запроса раздела настроек — повторный запрос не нужен (7.12).
const update = parseGuildEmojisUpdate(event.d);
if (update !== null) {
session.setGuildEmojis(update.guild_id, update.emojis);
- void invalidateEmojiQueries(update.guild_id);
+ void applyGuildEmojis(update.guild_id, update.emojis);
}
return;
}
@@ -569,12 +569,16 @@ async function invalidateGuildQueries(guildId: string): Promise {
]);
}
-/** Инвалидация списка эмодзи сервера после `GUILD_EMOJIS_UPDATE`. */
-async function invalidateEmojiQueries(guildId: string): Promise {
+/**
+ * Кладёт набор эмодзи из `GUILD_EMOJIS_UPDATE` в кэш REST-запроса: событие
+ * несёт список целиком, поэтому повторный запрос не нужен, а его отказ под
+ * нагрузкой (429) оставлял раздел настроек со старым списком (AGENT.md 7.12).
+ */
+async function applyGuildEmojis(guildId: string, emojis: GuildEmoji[]): Promise {
// Ключ запроса берём из статического импорта: `api/emojis` не зависит от
// сторов, поэтому цикла «стор → query-client → стор» здесь нет.
const { getQueryClient } = await import('@/lib/queryClient');
- await getQueryClient().invalidateQueries({ queryKey: guildEmojisQueryKey(guildId) });
+ getQueryClient().setQueryData(guildEmojisQueryKey(guildId), emojis);
}
/**
diff --git a/web/tests/guildEmojisSettings.test.tsx b/web/tests/guildEmojisSettings.test.tsx
index 2165049..c47c7ca 100644
--- a/web/tests/guildEmojisSettings.test.tsx
+++ b/web/tests/guildEmojisSettings.test.tsx
@@ -6,7 +6,9 @@ import {
apiError,
findRequest,
installFetch,
+ installGatewaySocket,
json,
+ makeReadySnapshot,
makeUser,
recordedRequests,
renderApp,
@@ -18,8 +20,9 @@ import {
/**
* Секция «Эмодзи» в настройках сервера (AGENT.md 7.12): список загруженных
* эмодзи со счётчиком, загрузка с клиентскими проверками, переименование и
- * удаление. Лента и пикер видят изменения сразу — запросы эмодзи
- * инвалидируются, а снапшот обновляет событие `GUILD_EMOJIS_UPDATE`.
+ * удаление. Лента и пикер видят изменения сразу: событие `GUILD_EMOJIS_UPDATE`
+ * несёт набор целиком и попадает прямо в кэш запроса, а свои операции
+ * инвалидируют список.
*/
const user = makeUser();
@@ -351,4 +354,31 @@ describe('настройки сервера: секция «Эмодзи»', ()
expect(findRequest(fetchMock, { url: '/emojis/e-1', method: 'DELETE' })).toBeDefined();
});
});
+ it('показывает эмодзи из GUILD_EMOJIS_UPDATE, даже если список по REST не отдался', async () => {
+ // Под нагрузкой ручка списка отвечает 429 (AGENT.md 8.6), а раздел должен
+ // обновляться снапшотом из события шлюза, а не повтором запроса (7.12).
+ const gateway = installGatewaySocket(
+ makeReadySnapshot([{ id: 'g-1', name: 'Сервер', my_permissions: ['MANAGE_EMOJIS'] }]),
+ );
+ installFetch(
+ routes({
+ extra: [
+ {
+ match: '/api/v1/guilds/g-1/emojis',
+ method: 'GET',
+ response: () => apiError('ratelimit.hit', 429),
+ },
+ ],
+ }),
+ );
+ renderApp('/settings/servers/g-1');
+ const dialog = await screen.findByRole('dialog', { name: 'Настройки' });
+ await gateway.greet();
+
+ const fresh = { id: 'e-9', name: 'fresh', file_id: '920', animated: false, url: '/files/920' };
+ gateway.dispatch('GUILD_EMOJIS_UPDATE', { guild_id: 'g-1', emojis: [pepe, fresh] });
+
+ expect(await within(dialog).findByTestId('guild-emoji-e-1')).toHaveTextContent(':pepe:');
+ expect(within(dialog).getByTestId('guild-emoji-e-9')).toHaveTextContent(':fresh:');
+ });
});