web: fix auth session races, ws token drift, and unread clear behavior
Some checks failed
CI / test (push) Failing after 2m20s

This commit is contained in:
Codex
2026-03-09 02:17:14 +03:00
parent 4fa657ff7a
commit ad2e0ede42
6 changed files with 96 additions and 12 deletions

View File

@@ -1,6 +1,7 @@
import { create } from "zustand";
import { loginRequest, meRequest, refreshRequest } from "../api/auth";
import type { AuthUser } from "../chat/types";
import { useChatStore } from "./chatStore";
interface AuthState {
accessToken: string | null;
@@ -25,7 +26,7 @@ export const useAuthStore = create<AuthState>((set, get) => ({
setTokens: (accessToken, refreshToken) => {
localStorage.setItem(ACCESS_KEY, accessToken);
localStorage.setItem(REFRESH_KEY, refreshToken);
set({ accessToken, refreshToken });
set({ accessToken, refreshToken, me: null });
},
login: async (email, password, otpCode, recoveryCode) => {
set({ loading: true });
@@ -38,8 +39,15 @@ export const useAuthStore = create<AuthState>((set, get) => ({
}
},
loadMe: async () => {
const tokenAtStart = get().accessToken;
if (!tokenAtStart) {
set({ me: null });
return;
}
const me = await meRequest();
set({ me });
if (get().accessToken === tokenAtStart) {
set({ me });
}
},
refresh: async () => {
const token = get().refreshToken;
@@ -53,6 +61,7 @@ export const useAuthStore = create<AuthState>((set, get) => ({
logout: () => {
localStorage.removeItem(ACCESS_KEY);
localStorage.removeItem(REFRESH_KEY);
useChatStore.getState().reset();
set({ accessToken: null, refreshToken: null, me: null });
}
}));

View File

@@ -69,7 +69,7 @@ interface ChatState {
focusedMessageIdByChat: Record<number, number | null>;
loadChats: (query?: string) => Promise<void>;
setActiveChatId: (chatId: number | null) => void;
loadMessages: (chatId: number) => Promise<void>;
loadMessages: (chatId: number, options?: { markRead?: boolean }) => Promise<void>;
loadMoreMessages: (chatId: number) => Promise<void>;
prependMessage: (chatId: number, message: Message) => boolean;
addOptimisticMessage: (params: {
@@ -105,6 +105,7 @@ interface ChatState {
setDraft: (chatId: number, text: string) => void;
clearDraft: (chatId: number) => void;
setFocusedMessage: (chatId: number, messageId: number | null) => void;
reset: () => void;
}
export const useChatStore = create<ChatState>((set, get) => ({
@@ -128,7 +129,8 @@ export const useChatStore = create<ChatState>((set, get) => ({
set({ chats, activeChatId: nextActive });
},
setActiveChatId: (chatId) => set({ activeChatId: chatId }),
loadMessages: async (chatId) => {
loadMessages: async (chatId, options) => {
const markRead = options?.markRead === true;
const unreadCount = get().chats.find((c) => c.id === chatId)?.unread_count ?? 0;
const messages = await getMessages(chatId);
const sorted = [...messages].reverse();
@@ -155,12 +157,14 @@ export const useChatStore = create<ChatState>((set, get) => ({
...state.loadingMoreByChat,
[chatId]: false
},
chats: state.chats.map((chat) =>
chat.id === chatId ? { ...chat, unread_count: 0, unread_mentions_count: 0 } : chat
)
chats: markRead
? state.chats.map((chat) =>
chat.id === chatId ? { ...chat, unread_count: 0, unread_mentions_count: 0 } : chat
)
: state.chats
}));
const lastMessage = normalized[normalized.length - 1];
if (lastMessage) {
if (markRead && lastMessage) {
void updateMessageStatus(chatId, lastMessage.id, "message_read");
}
},
@@ -515,5 +519,23 @@ export const useChatStore = create<ChatState>((set, get) => ({
...state.focusedMessageIdByChat,
[chatId]: messageId
}
}))
})),
reset: () => {
saveDraftsToStorage({});
set({
chats: [],
activeChatId: null,
messagesByChat: {},
draftsByChat: {},
hasMoreByChat: {},
loadingMoreByChat: {},
typingByChat: {},
recordingVoiceByChat: {},
recordingVideoByChat: {},
replyToByChat: {},
editingByChat: {},
unreadBoundaryByChat: {},
focusedMessageIdByChat: {}
});
}
}));