feat(messages): счётчики упоминаний, квота приглашений и read state автора

- упоминания в сообщении увеличивают счётчик упоминаний комнаты у упомянутых
  (store.BumpMentionCount) и отправляют им READ_STATE_UPDATE на все устройства;
- отправка сообщения отмечает его прочитанным для автора;
- подтверждение прочтения сбрасывает упоминания;
- суточная квота 10 приглашений на пользователя (AGENT.md 8.6), администратор
  инстанса её обходит; httpx.NewRateLimiterWindow для произвольных окон;
- тест на рост и сброс счётчика упоминаний.
This commit is contained in:
2026-09-19 23:37:02 +03:00
parent 0e39b8e027
commit 8619f37cd5
15 changed files with 186 additions and 6 deletions
+43
View File
@@ -112,4 +112,47 @@ describe('session store', () => {
expect(state.guilds).toEqual([]);
expect(state.selectedGuildId).toBeNull();
});
it('хранит выбранный в настройках сервер отдельно от выбора в приложении', () => {
act(() => {
dispatchGatewayEvent({
op: 0,
t: 'READY',
s: 6,
d: makeReadySnapshot([
{ id: 'g-1', name: 'Main' },
{ id: 'g-2', name: 'Second' },
]),
});
useSessionStore.getState().selectChannel('g-1', 'c-1');
useSessionStore.getState().selectSettingsGuild('g-2');
});
const state = useSessionStore.getState();
expect(state.settingsGuildId).toBe('g-2');
// Выбор в приложении не пострадал.
expect(state.selectedGuildId).toBe('g-1');
act(() => {
useSessionStore.getState().reset();
});
expect(useSessionStore.getState().settingsGuildId).toBeNull();
});
it('обновляет свой никнейм на сервере', () => {
act(() => {
dispatchGatewayEvent({
op: 0,
t: 'READY',
s: 7,
d: makeReadySnapshot([{ id: 'g-1', name: 'Main', my_nickname: 'Аля' }]),
});
});
expect(useSessionStore.getState().guilds[0]?.my_nickname).toBe('Аля');
act(() => {
useSessionStore.getState().setMyNickname('g-1', 'Алиса');
});
expect(useSessionStore.getState().guilds[0]?.my_nickname).toBe('Алиса');
});
});