add group chat

This commit is contained in:
Виталий Лавшонок
2025-11-23 10:30:31 +03:00
parent abb7301c16
commit 390f1f52c8
28 changed files with 414 additions and 217 deletions

View File

@@ -19,7 +19,8 @@ export interface ChatMessage {
interface FetchMessagesParams {
groupId: number;
limit?: number;
afterMessageId?: number | null;
afterMessageId?: number;
timeoutSeconds?: number;
}
interface SendMessageParams {
@@ -33,8 +34,7 @@ interface SendMessageParams {
interface GroupChatState {
messages: Record<number, ChatMessage[]>; // по группам
hasMore: Record<number, boolean>; // есть ли ещё старые сообщения
isInitialLoaded: Record<number, boolean>; // загружена ли первая порция
lastMessage: Record<number, number>;
fetchMessages: {
status: Status;
@@ -48,8 +48,7 @@ interface GroupChatState {
const initialState: GroupChatState = {
messages: {},
hasMore: {},
isInitialLoaded: {},
lastMessage: {},
fetchMessages: {
status: 'idle',
error: undefined,
@@ -73,13 +72,13 @@ export const fetchGroupMessages = createAsyncThunk(
params: {
limit: params.limit,
afterMessageId: params.afterMessageId,
timeoutSeconds: params.timeoutSeconds,
},
});
return {
groupId: params.groupId,
messages: response.data as ChatMessage[],
afterMessageId: params.afterMessageId,
};
} catch (err: any) {
return rejectWithValue(
@@ -117,8 +116,18 @@ const groupChatSlice = createSlice({
reducers: {
clearChat(state, action: PayloadAction<number>) {
delete state.messages[action.payload];
delete state.hasMore[action.payload];
delete state.isInitialLoaded[action.payload];
},
setGroupChatStatus: (
state,
action: PayloadAction<{
key: keyof GroupChatState;
status: Status;
}>,
) => {
const { key, status } = action.payload;
if (state[key]) {
(state[key] as any).status = status;
}
},
},
extraReducers: (builder) => {
@@ -134,28 +143,22 @@ const groupChatSlice = createSlice({
action: PayloadAction<{
groupId: number;
messages: ChatMessage[];
afterMessageId?: number | null;
}>,
) => {
const { groupId, messages, afterMessageId } = action.payload;
const { groupId, messages } = action.payload;
const existing = state.messages[groupId] || [];
// первичная загрузка
if (!afterMessageId) {
state.messages[groupId] = messages;
state.isInitialLoaded[groupId] = true;
state.hasMore[groupId] = messages.length > 0;
}
// догружаем старые (scroll up)
else if (afterMessageId) {
const ids = new Set(existing.map((m) => m.id));
const filtered = messages.filter((m) => !ids.has(m.id));
console.log('messages', messages);
console.log('filtered', filtered);
console.log('ids', ids);
console.log('existing', existing);
state.messages[groupId] = [...existing, ...filtered];
state.hasMore[groupId] = filtered.length > 0;
const ids = new Set(existing.map((m) => m.id));
const filtered = messages.filter((m) => !ids.has(m.id));
state.messages[groupId] = [...existing, ...filtered].sort(
(a, b) => a.id - b.id,
);
if (state.messages[groupId].length) {
state.lastMessage[groupId] =
state.messages[groupId][
state.messages[groupId].length - 1
].id;
}
state.fetchMessages.status = 'successful';
@@ -190,5 +193,5 @@ const groupChatSlice = createSlice({
},
});
export const { clearChat } = groupChatSlice.actions;
export const { clearChat, setGroupChatStatus } = groupChatSlice.actions;
export const groupChatReducer = groupChatSlice.reducer;