fix(web): robust date parsing and merge strategy in message store
This commit is contained in:
+166
-114
@@ -1,6 +1,13 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { api } from "../lib/api.ts";
|
import { api } from "../lib/api.ts";
|
||||||
|
|
||||||
|
function parseDate(iso: string): number {
|
||||||
|
if (!iso) return 0;
|
||||||
|
const normalized = iso.includes("T") ? iso : iso.replace(" ", "T");
|
||||||
|
const t = new Date(normalized).getTime();
|
||||||
|
return isNaN(t) ? 0 : t;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MessageEmbed {
|
export interface MessageEmbed {
|
||||||
id?: string;
|
id?: string;
|
||||||
url: string;
|
url: string;
|
||||||
@@ -59,7 +66,7 @@ export interface MessageState {
|
|||||||
messagesByChannel: Record<string, Message[]>;
|
messagesByChannel: Record<string, Message[]>;
|
||||||
pinnedMessagesByChannel: Record<string, Message[]>;
|
pinnedMessagesByChannel: Record<string, Message[]>;
|
||||||
searchResultsByChannel: Record<string, SearchResultMessage[]>;
|
searchResultsByChannel: Record<string, SearchResultMessage[]>;
|
||||||
selectedMessageIds: Record<string, Set<string>>; // ponytail: per-channel bulk selection
|
selectedMessageIds: Record<string, Set<string>>;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
isLoadingOlder: boolean;
|
isLoadingOlder: boolean;
|
||||||
hasMoreByChannel: Record<string, boolean>;
|
hasMoreByChannel: Record<string, boolean>;
|
||||||
@@ -95,24 +102,34 @@ export const useMessageStore = create<MessageState>((set, get) => ({
|
|||||||
error: null,
|
error: null,
|
||||||
|
|
||||||
fetchMessages: async (channelId, before) => {
|
fetchMessages: async (channelId, before) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
set({ isLoading: true, error: null });
|
set({ isLoading: true, error: null });
|
||||||
try {
|
try {
|
||||||
const params = before ? `?before=${encodeURIComponent(before)}` : "";
|
const params = before ? `?before=${encodeURIComponent(before)}` : "";
|
||||||
const messages = await api.get<Message[]>(
|
const messages = await api.get<Message[]>(
|
||||||
`/channels/${channelId}/messages${params}`,
|
`/channels/${chId}/messages${params}`,
|
||||||
);
|
);
|
||||||
const list = Array.isArray(messages) ? messages : [];
|
const list = Array.isArray(messages) ? messages : [];
|
||||||
set((state) => ({
|
set((state) => {
|
||||||
messagesByChannel: {
|
const existing = state.messagesByChannel[chId] || [];
|
||||||
...state.messagesByChannel,
|
const map = new Map<string, Message>();
|
||||||
[channelId]: list,
|
existing.forEach((m) => map.set(m.id, m));
|
||||||
},
|
list.forEach((m) => map.set(m.id, { ...m, channel_id: (m.channel_id || chId).toLowerCase() }));
|
||||||
hasMoreByChannel: {
|
const merged = Array.from(map.values()).sort(
|
||||||
...state.hasMoreByChannel,
|
(a, b) => parseDate(a.created_at) - parseDate(b.created_at)
|
||||||
[channelId]: list.length >= 50,
|
);
|
||||||
},
|
return {
|
||||||
isLoading: false,
|
messagesByChannel: {
|
||||||
}));
|
...state.messagesByChannel,
|
||||||
|
[chId]: merged,
|
||||||
|
},
|
||||||
|
hasMoreByChannel: {
|
||||||
|
...state.hasMoreByChannel,
|
||||||
|
[chId]: list.length >= 50,
|
||||||
|
},
|
||||||
|
isLoading: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
set({
|
set({
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
@@ -125,116 +142,136 @@ export const useMessageStore = create<MessageState>((set, get) => ({
|
|||||||
},
|
},
|
||||||
|
|
||||||
fetchOlderMessages: async (channelId) => {
|
fetchOlderMessages: async (channelId) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
const state = get();
|
const state = get();
|
||||||
if (state.isLoadingOlder || state.hasMoreByChannel[channelId] === false) return;
|
if (state.isLoadingOlder || state.hasMoreByChannel[chId] === false) return;
|
||||||
const existing = state.messagesByChannel[channelId] || [];
|
const existing = state.messagesByChannel[chId] || [];
|
||||||
if (existing.length === 0) return;
|
if (existing.length === 0) return;
|
||||||
// ponytail: existing is now oldest-first, so existing[0] is the true oldest
|
|
||||||
const oldestId = existing[0].id;
|
const oldestId = existing[0].id;
|
||||||
set({ isLoadingOlder: true });
|
set({ isLoadingOlder: true });
|
||||||
try {
|
try {
|
||||||
const older = await api.get<Message[]>(
|
const older = await api.get<Message[]>(
|
||||||
`/channels/${channelId}/messages?before=${encodeURIComponent(oldestId)}`,
|
`/channels/${chId}/messages?before=${encodeURIComponent(oldestId)}`,
|
||||||
);
|
);
|
||||||
const list = Array.isArray(older) ? older : [];
|
const list = Array.isArray(older) ? older : [];
|
||||||
set((state) => ({
|
set((state) => {
|
||||||
messagesByChannel: {
|
const map = new Map<string, Message>();
|
||||||
...state.messagesByChannel,
|
[...list, ...existing].forEach((m) => map.set(m.id, { ...m, channel_id: (m.channel_id || chId).toLowerCase() }));
|
||||||
[channelId]: [...list, ...existing],
|
const merged = Array.from(map.values()).sort(
|
||||||
},
|
(a, b) => parseDate(a.created_at) - parseDate(b.created_at)
|
||||||
hasMoreByChannel: {
|
);
|
||||||
...state.hasMoreByChannel,
|
return {
|
||||||
[channelId]: list.length >= 50,
|
messagesByChannel: {
|
||||||
},
|
...state.messagesByChannel,
|
||||||
isLoadingOlder: false,
|
[chId]: merged,
|
||||||
}));
|
},
|
||||||
|
hasMoreByChannel: {
|
||||||
|
...state.hasMoreByChannel,
|
||||||
|
[chId]: list.length >= 50,
|
||||||
|
},
|
||||||
|
isLoadingOlder: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
set({ isLoadingOlder: false });
|
set({ isLoadingOlder: false });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
searchMessages: async (channelId, query) => {
|
searchMessages: async (channelId, query) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
const results = await api.get<SearchResultMessage[]>(
|
const results = await api.get<SearchResultMessage[]>(
|
||||||
`/channels/${channelId}/messages/search?q=${encodeURIComponent(query)}`,
|
`/channels/${chId}/messages/search?q=${encodeURIComponent(query)}`,
|
||||||
);
|
);
|
||||||
|
const list = Array.isArray(results) ? results : [];
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
searchResultsByChannel: {
|
searchResultsByChannel: {
|
||||||
...state.searchResultsByChannel,
|
...state.searchResultsByChannel,
|
||||||
[channelId]: Array.isArray(results) ? results : [],
|
[chId]: list,
|
||||||
},
|
|
||||||
}));
|
|
||||||
return Array.isArray(results) ? results : [];
|
|
||||||
},
|
|
||||||
|
|
||||||
sendMessage: async (channelId, content, replyTo) => {
|
|
||||||
const body: { content: string; reply_to?: string } = { content };
|
|
||||||
if (replyTo) body.reply_to = replyTo;
|
|
||||||
// Add locally so the message appears immediately even if WS lags.
|
|
||||||
// addMessage dedupes, so a late WS event won't double it.
|
|
||||||
const message = await api.post<Message>(
|
|
||||||
`/channels/${channelId}/messages`,
|
|
||||||
body,
|
|
||||||
);
|
|
||||||
// ponytail: local append as fallback for WS MESSAGE_CREATE; remove if WS reliability improves
|
|
||||||
get().addMessage(message);
|
|
||||||
return message;
|
|
||||||
},
|
|
||||||
|
|
||||||
pinMessage: async (channelId, messageId) => {
|
|
||||||
const updated = await api.put<Message>(`/channels/${channelId}/messages/${messageId}/pin`, {});
|
|
||||||
get().updateMessage(updated);
|
|
||||||
},
|
|
||||||
|
|
||||||
unpinMessage: async (channelId, messageId) => {
|
|
||||||
const updated = await api.delete<Message>(`/channels/${channelId}/messages/${messageId}/pin`);
|
|
||||||
get().updateMessage(updated);
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchPinnedMessages: async (channelId) => {
|
|
||||||
const pinned = await api.get<Message[]>(`/channels/${channelId}/messages/pinned`);
|
|
||||||
const list = Array.isArray(pinned) ? pinned : [];
|
|
||||||
set((state) => ({
|
|
||||||
pinnedMessagesByChannel: {
|
|
||||||
...state.pinnedMessagesByChannel,
|
|
||||||
[channelId]: list,
|
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
return list;
|
return list;
|
||||||
},
|
},
|
||||||
|
|
||||||
addMessage: (message) =>
|
sendMessage: async (channelId, content, replyTo) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
|
const body: { content: string; reply_to?: string } = { content };
|
||||||
|
if (replyTo) body.reply_to = replyTo;
|
||||||
|
const message = await api.post<Message>(
|
||||||
|
`/channels/${chId}/messages`,
|
||||||
|
body,
|
||||||
|
);
|
||||||
|
const normalizedMessage = { ...message, channel_id: (message.channel_id || chId).toLowerCase() };
|
||||||
|
get().addMessage(normalizedMessage);
|
||||||
|
return normalizedMessage;
|
||||||
|
},
|
||||||
|
|
||||||
|
pinMessage: async (channelId, messageId) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
|
const updated = await api.put<Message>(`/channels/${chId}/messages/${messageId}/pin`, {});
|
||||||
|
const normalizedMessage = { ...updated, channel_id: (updated.channel_id || chId).toLowerCase() };
|
||||||
|
get().updateMessage(normalizedMessage);
|
||||||
|
},
|
||||||
|
|
||||||
|
unpinMessage: async (channelId, messageId) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
|
const updated = await api.delete<Message>(`/channels/${chId}/messages/${messageId}/pin`);
|
||||||
|
const normalizedMessage = { ...updated, channel_id: (updated.channel_id || chId).toLowerCase() };
|
||||||
|
get().updateMessage(normalizedMessage);
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchPinnedMessages: async (channelId) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
|
const pinned = await api.get<Message[]>(`/channels/${chId}/messages/pinned`);
|
||||||
|
const list = Array.isArray(pinned) ? pinned : [];
|
||||||
|
set((state) => ({
|
||||||
|
pinnedMessagesByChannel: {
|
||||||
|
...state.pinnedMessagesByChannel,
|
||||||
|
[chId]: list,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
return list;
|
||||||
|
},
|
||||||
|
|
||||||
|
addMessage: (message) => {
|
||||||
|
const chId = (message.channel_id || "").toLowerCase();
|
||||||
|
const normalizedMessage = { ...message, channel_id: chId };
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const list = state.messagesByChannel[message.channel_id] || [];
|
const list = state.messagesByChannel[chId] || [];
|
||||||
if (list.some((m) => m.id === message.id)) {
|
if (list.some((m) => m.id === normalizedMessage.id)) {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
messagesByChannel: {
|
messagesByChannel: {
|
||||||
...state.messagesByChannel,
|
...state.messagesByChannel,
|
||||||
[message.channel_id]: [...list, message],
|
[chId]: [...list, normalizedMessage].sort(
|
||||||
|
(a, b) => parseDate(a.created_at) - parseDate(b.created_at)
|
||||||
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
|
|
||||||
updateMessage: (message) =>
|
updateMessage: (message) => {
|
||||||
|
const chId = (message.channel_id || "").toLowerCase();
|
||||||
|
const normalizedMessage = { ...message, channel_id: chId };
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const list = state.messagesByChannel[message.channel_id] || [];
|
const list = state.messagesByChannel[chId] || [];
|
||||||
const updatedList = list.map((m) =>
|
const updatedList = list.map((m) =>
|
||||||
m.id === message.id ? message : m,
|
m.id === normalizedMessage.id ? normalizedMessage : m,
|
||||||
);
|
);
|
||||||
|
|
||||||
const pinnedList = state.pinnedMessagesByChannel[message.channel_id] || [];
|
const pinnedList = state.pinnedMessagesByChannel[chId] || [];
|
||||||
let updatedPinned = [...pinnedList];
|
let updatedPinned = [...pinnedList];
|
||||||
if (message.pinned) {
|
if (normalizedMessage.pinned) {
|
||||||
if (!pinnedList.some((m) => m.id === message.id)) {
|
if (!pinnedList.some((m) => m.id === normalizedMessage.id)) {
|
||||||
updatedPinned = [message, ...pinnedList].sort(
|
updatedPinned = [normalizedMessage, ...pinnedList].sort(
|
||||||
(a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
|
(a, b) => parseDate(b.created_at) - parseDate(a.created_at)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
updatedPinned = pinnedList.map((m) => (m.id === message.id ? message : m));
|
updatedPinned = pinnedList.map((m) => (m.id === normalizedMessage.id ? normalizedMessage : m));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updatedPinned = pinnedList.filter((m) => m.id !== message.id);
|
updatedPinned = pinnedList.filter((m) => m.id !== normalizedMessage.id);
|
||||||
}
|
}
|
||||||
if (updatedPinned.length > 5) {
|
if (updatedPinned.length > 5) {
|
||||||
updatedPinned = updatedPinned.slice(0, 5);
|
updatedPinned = updatedPinned.slice(0, 5);
|
||||||
@@ -243,34 +280,38 @@ export const useMessageStore = create<MessageState>((set, get) => ({
|
|||||||
return {
|
return {
|
||||||
messagesByChannel: {
|
messagesByChannel: {
|
||||||
...state.messagesByChannel,
|
...state.messagesByChannel,
|
||||||
[message.channel_id]: updatedList,
|
[chId]: updatedList,
|
||||||
},
|
},
|
||||||
pinnedMessagesByChannel: {
|
pinnedMessagesByChannel: {
|
||||||
...state.pinnedMessagesByChannel,
|
...state.pinnedMessagesByChannel,
|
||||||
[message.channel_id]: updatedPinned,
|
[chId]: updatedPinned,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
|
|
||||||
removeMessage: (channelId, messageId) =>
|
removeMessage: (channelId, messageId) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const list = state.messagesByChannel[channelId] || [];
|
const list = state.messagesByChannel[chId] || [];
|
||||||
const pinnedList = state.pinnedMessagesByChannel[channelId] || [];
|
const pinnedList = state.pinnedMessagesByChannel[chId] || [];
|
||||||
return {
|
return {
|
||||||
messagesByChannel: {
|
messagesByChannel: {
|
||||||
...state.messagesByChannel,
|
...state.messagesByChannel,
|
||||||
[channelId]: list.filter((m) => m.id !== messageId),
|
[chId]: list.filter((m) => m.id !== messageId),
|
||||||
},
|
},
|
||||||
pinnedMessagesByChannel: {
|
pinnedMessagesByChannel: {
|
||||||
...state.pinnedMessagesByChannel,
|
...state.pinnedMessagesByChannel,
|
||||||
[channelId]: pinnedList.filter((m) => m.id !== messageId),
|
[chId]: pinnedList.filter((m) => m.id !== messageId),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
|
|
||||||
addReaction: (channelId, messageId, emoji, userId) =>
|
addReaction: (channelId, messageId, emoji, userId) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const list = state.messagesByChannel[channelId] || [];
|
const list = state.messagesByChannel[chId] || [];
|
||||||
const updatedList = list.map((m) => {
|
const updatedList = list.map((m) => {
|
||||||
if (m.id !== messageId) return m;
|
if (m.id !== messageId) return m;
|
||||||
const reactions = m.reactions ? [...m.reactions] : [];
|
const reactions = m.reactions ? [...m.reactions] : [];
|
||||||
@@ -288,14 +329,16 @@ export const useMessageStore = create<MessageState>((set, get) => ({
|
|||||||
return {
|
return {
|
||||||
messagesByChannel: {
|
messagesByChannel: {
|
||||||
...state.messagesByChannel,
|
...state.messagesByChannel,
|
||||||
[channelId]: updatedList,
|
[chId]: updatedList,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
|
|
||||||
removeReaction: (channelId, messageId, emoji, userId) =>
|
removeReaction: (channelId, messageId, emoji, userId) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const list = state.messagesByChannel[channelId] || [];
|
const list = state.messagesByChannel[chId] || [];
|
||||||
const updatedList = list.map((m) => {
|
const updatedList = list.map((m) => {
|
||||||
if (m.id !== messageId) return m;
|
if (m.id !== messageId) return m;
|
||||||
if (!m.reactions) return m;
|
if (!m.reactions) return m;
|
||||||
@@ -311,25 +354,27 @@ export const useMessageStore = create<MessageState>((set, get) => ({
|
|||||||
return {
|
return {
|
||||||
messagesByChannel: {
|
messagesByChannel: {
|
||||||
...state.messagesByChannel,
|
...state.messagesByChannel,
|
||||||
[channelId]: updatedList,
|
[chId]: updatedList,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
|
|
||||||
bulkDeleteMessages: async (channelId, messageIds) => {
|
bulkDeleteMessages: async (channelId, messageIds) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
const res = await api.post<{ deleted: number }>(
|
const res = await api.post<{ deleted: number }>(
|
||||||
`/channels/${channelId}/messages/bulk-delete`,
|
`/channels/${chId}/messages/bulk-delete`,
|
||||||
{ messages: messageIds },
|
{ messages: messageIds },
|
||||||
);
|
);
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const list = state.messagesByChannel[channelId] || [];
|
const list = state.messagesByChannel[chId] || [];
|
||||||
const ids = new Set(messageIds);
|
const ids = new Set(messageIds);
|
||||||
const selected = { ...state.selectedMessageIds };
|
const selected = { ...state.selectedMessageIds };
|
||||||
delete selected[channelId];
|
delete selected[chId];
|
||||||
return {
|
return {
|
||||||
messagesByChannel: {
|
messagesByChannel: {
|
||||||
...state.messagesByChannel,
|
...state.messagesByChannel,
|
||||||
[channelId]: list.filter((m) => !ids.has(m.id)),
|
[chId]: list.filter((m) => !ids.has(m.id)),
|
||||||
},
|
},
|
||||||
selectedMessageIds: selected,
|
selectedMessageIds: selected,
|
||||||
};
|
};
|
||||||
@@ -337,9 +382,10 @@ export const useMessageStore = create<MessageState>((set, get) => ({
|
|||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleSelectedMessage: (channelId, messageId) =>
|
toggleSelectedMessage: (channelId, messageId) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const current = state.selectedMessageIds[channelId] || new Set<string>();
|
const current = state.selectedMessageIds[chId] || new Set<string>();
|
||||||
const next = new Set(current);
|
const next = new Set(current);
|
||||||
if (next.has(messageId)) {
|
if (next.has(messageId)) {
|
||||||
next.delete(messageId);
|
next.delete(messageId);
|
||||||
@@ -349,21 +395,25 @@ export const useMessageStore = create<MessageState>((set, get) => ({
|
|||||||
return {
|
return {
|
||||||
selectedMessageIds: {
|
selectedMessageIds: {
|
||||||
...state.selectedMessageIds,
|
...state.selectedMessageIds,
|
||||||
[channelId]: next,
|
[chId]: next,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
|
|
||||||
clearSelectedMessages: (channelId) =>
|
clearSelectedMessages: (channelId) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const next = { ...state.selectedMessageIds };
|
const next = { ...state.selectedMessageIds };
|
||||||
delete next[channelId];
|
delete next[chId];
|
||||||
return { selectedMessageIds: next };
|
return { selectedMessageIds: next };
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
|
|
||||||
createPoll: async (channelId, question, options) => {
|
createPoll: async (channelId, question, options) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
const resp = await api.post<Poll>("/polls", {
|
const resp = await api.post<Poll>("/polls", {
|
||||||
channel_id: channelId,
|
channel_id: chId,
|
||||||
question,
|
question,
|
||||||
options,
|
options,
|
||||||
});
|
});
|
||||||
@@ -374,9 +424,10 @@ export const useMessageStore = create<MessageState>((set, get) => ({
|
|||||||
await api.post(`/polls/${pollId}/vote`, { option_id: optionId });
|
await api.post(`/polls/${pollId}/vote`, { option_id: optionId });
|
||||||
},
|
},
|
||||||
|
|
||||||
updatePoll: (channelId, poll) =>
|
updatePoll: (channelId, poll) => {
|
||||||
|
const chId = channelId.toLowerCase();
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const messages = state.messagesByChannel[channelId];
|
const messages = state.messagesByChannel[chId];
|
||||||
if (!messages) return state;
|
if (!messages) return state;
|
||||||
const updated = messages.map((m) =>
|
const updated = messages.map((m) =>
|
||||||
m.poll?.id === poll.id ? { ...m, poll } : m,
|
m.poll?.id === poll.id ? { ...m, poll } : m,
|
||||||
@@ -384,8 +435,9 @@ export const useMessageStore = create<MessageState>((set, get) => ({
|
|||||||
return {
|
return {
|
||||||
messagesByChannel: {
|
messagesByChannel: {
|
||||||
...state.messagesByChannel,
|
...state.messagesByChannel,
|
||||||
[channelId]: updated,
|
[chId]: updated,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user