@webinex/chatify 1.0.0-rc3 → 1.0.1-rc3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/esm/index.js.map +1 -1
  2. package/package.json +92 -92
  3. package/src/ChatifyContext.tsx +41 -41
  4. package/src/core/action.tsx +405 -405
  5. package/src/core/client.ts +143 -143
  6. package/src/core/index.ts +17 -17
  7. package/src/core/models.tsx +82 -82
  8. package/src/core/reducer.tsx +87 -87
  9. package/src/core/state.ts +92 -92
  10. package/src/core/useAddChatMutation.tsx +22 -22
  11. package/src/core/useAddMemberMutation.tsx +25 -25
  12. package/src/core/useFetchNextMutation.tsx +28 -28
  13. package/src/core/useGetAccountsQuery.tsx +33 -33
  14. package/src/core/useGetChatListQuery.tsx +33 -33
  15. package/src/core/useGetChatQuery.tsx +34 -34
  16. package/src/core/useGetMessagesQuery.tsx +35 -35
  17. package/src/core/useReadMonitor.tsx +32 -32
  18. package/src/core/useRemoveMemberMutation.tsx +23 -23
  19. package/src/core/useSendMutation.tsx +24 -24
  20. package/src/core/useSignalRMonitor.tsx +66 -66
  21. package/src/core/useUpdateChatNameMutation.tsx +23 -23
  22. package/src/index.ts +3 -3
  23. package/src/styles/aside.scss +94 -94
  24. package/src/styles/chat.scss +222 -222
  25. package/src/styles/index.scss +79 -79
  26. package/src/styles/keyframes.scss +14 -14
  27. package/src/styles/mixins.scss +20 -20
  28. package/src/styles/settings.scss +5 -5
  29. package/src/styles/variables.scss +10 -10
  30. package/src/ui/AddChatButton.tsx +15 -15
  31. package/src/ui/Avatar.tsx +18 -18
  32. package/src/ui/ChatList/ChatList.tsx +42 -42
  33. package/src/ui/ChatList/ChatListItemBox.tsx +22 -22
  34. package/src/ui/ChatList/ChatListItemLastMessage.tsx +13 -13
  35. package/src/ui/ChatList/ChatListItemLastMessageAuthor.tsx +20 -20
  36. package/src/ui/ChatList/ChatListItemLastMessageContent.tsx +21 -21
  37. package/src/ui/ChatList/ChatListItemName.tsx +13 -13
  38. package/src/ui/ChatList/ChatListItemUnreadCount.tsx +12 -12
  39. package/src/ui/ChatList/index.ts +7 -7
  40. package/src/ui/ChatName.tsx +10 -10
  41. package/src/ui/ChatPanel/ChatBody.tsx +44 -44
  42. package/src/ui/ChatPanel/ChatHeader.tsx +33 -33
  43. package/src/ui/ChatPanel/ChatHeaderMembers.tsx +26 -26
  44. package/src/ui/ChatPanel/ChatHeaderName.tsx +67 -67
  45. package/src/ui/ChatPanel/ChatMembersButton.tsx +21 -21
  46. package/src/ui/ChatPanel/ChatMembersPanel.tsx +117 -117
  47. package/src/ui/ChatPanel/ChatPanel.tsx +27 -27
  48. package/src/ui/ChatPanel/index.ts +6 -6
  49. package/src/ui/Chatify.tsx +148 -148
  50. package/src/ui/CreateChatPanel/CreateChatForm.tsx +36 -36
  51. package/src/ui/CreateChatPanel/CreateChatMemberInput.tsx +66 -66
  52. package/src/ui/CreateChatPanel/CreateChatNameInput.tsx +25 -25
  53. package/src/ui/CreateChatPanel/CreateChatPanel.tsx +33 -33
  54. package/src/ui/CreateChatPanel/index.ts +4 -4
  55. package/src/ui/Icon.tsx +33 -33
  56. package/src/ui/InputBox/InputBox.tsx +38 -38
  57. package/src/ui/InputBox/InputFilesBox.tsx +9 -9
  58. package/src/ui/InputBox/InputSubmitButtonBox.tsx +17 -17
  59. package/src/ui/InputBox/InputTextBox.tsx +50 -50
  60. package/src/ui/InputBox/index.ts +4 -4
  61. package/src/ui/Layout/Aside.tsx +10 -10
  62. package/src/ui/Layout/Body.tsx +10 -10
  63. package/src/ui/Layout/Footer.tsx +3 -3
  64. package/src/ui/Layout/Header.tsx +10 -10
  65. package/src/ui/Layout/Main.tsx +15 -15
  66. package/src/ui/Layout/index.ts +5 -5
  67. package/src/ui/Message/MessageAuthor.tsx +13 -13
  68. package/src/ui/Message/MessageBox.tsx +20 -20
  69. package/src/ui/Message/MessageContent.tsx +13 -13
  70. package/src/ui/Message/MessageInfoBox.tsx +25 -25
  71. package/src/ui/Message/MessageRow.tsx +24 -24
  72. package/src/ui/Message/MessageText.tsx +7 -7
  73. package/src/ui/Message/index.ts +6 -6
  74. package/src/ui/MessageReadObserver.tsx +45 -45
  75. package/src/ui/MessageSkeleton.tsx +19 -19
  76. package/src/ui/Next.tsx +48 -48
  77. package/src/ui/SendingMessage/SendingMessageBox.tsx +16 -16
  78. package/src/ui/SendingMessage/SendingMessageContent.tsx +13 -13
  79. package/src/ui/SendingMessage/SendingMessageInfoBox.tsx +13 -13
  80. package/src/ui/SendingMessage/SendingMessageText.tsx +7 -7
  81. package/src/ui/SendingMessage/index.ts +4 -4
  82. package/src/ui/SystemMessage/SystemMessageBox.tsx +20 -20
  83. package/src/ui/SystemMessage/SystemMessageRow.tsx +43 -43
  84. package/src/ui/SystemMessage/index.ts +2 -2
  85. package/src/ui/customize.tsx +45 -45
  86. package/src/ui/index.ts +11 -11
  87. package/src/ui/localizer.tsx +80 -80
  88. package/src/util/index.ts +2 -2
  89. package/src/util/uniqBy.tsx +17 -17
  90. package/src/util/uniqId.tsx +3 -3
@@ -1,405 +1,405 @@
1
- import type { State } from './state';
2
- import { Account, Chat, ChatListItem, Message, ReadEvent, File } from './models';
3
-
4
- export const ACTIONS = {
5
- update: (state: State, { mutation }: { mutation: (state: State) => State }) => {
6
- mutation(state);
7
- },
8
-
9
- messages_fetch: (state: State, { chatId }: { chatId: string }) => {
10
- state.query.messages[chatId] = {
11
- ...state.query.messages[chatId],
12
- uninitialized: false,
13
- isFetching: true,
14
- };
15
- },
16
-
17
- messages_fulfilled: (
18
- state: State,
19
- { chatId, messages, hasMore }: { chatId: string; messages: Message[]; hasMore: boolean },
20
- ) => {
21
- const qState = state.query.messages[chatId];
22
-
23
- qState.error = undefined;
24
- qState.isFetching = false;
25
- qState.data = messages.map((x) => ({ type: 'message', ...x }));
26
-
27
- if (hasMore) {
28
- qState.data!.push({ type: 'next' });
29
- }
30
- },
31
-
32
- messages_rejected: (state: State, { chatId, error }: { chatId: string; error: any }) => {
33
- const qState = state.query.messages[chatId];
34
- qState.error = error;
35
- qState.isFetching = false;
36
- },
37
-
38
- fetchNext_fetch: (state: State, { chatId }: { chatId: string }) => {
39
- state.mutation.fetchNext = { isFetching: true };
40
- },
41
-
42
- fetchNext_fulfilled: (
43
- state: State,
44
- { chatId, messages, hasMore }: { chatId: string; messages: Message[]; hasMore: boolean },
45
- ) => {
46
- state.mutation.fetchNext = { isFetching: false, error: undefined };
47
- state.query.messages[chatId].data = state.query.messages[chatId].data!.filter((x) => x.type !== 'next');
48
- state.query.messages[chatId].data!.push(...messages.map((x) => ({ type: 'message' as const, ...x })));
49
-
50
- if (hasMore) state.query.messages[chatId].data!.push({ type: 'next' });
51
- },
52
-
53
- fetchNext_rejected: (state: State, { chatId, error }: { chatId: string; error: any }) => {
54
- state.mutation.fetchNext = { isFetching: false, error };
55
- },
56
-
57
- chatList_fetch: (state: State) => {
58
- state.query.chatList.isFetching = true;
59
- state.query.chatList.uninitialized = false;
60
- },
61
-
62
- chatList_fulfilled: (state: State, { chats }: { chats: ChatListItem[] }) => {
63
- chats = chats.sort((a, b) => -a.message.id.substring(47).localeCompare(b.message.id.substring(47)));
64
-
65
- state.query.chatList.isFetching = false;
66
- state.query.chatList.error = undefined;
67
- state.query.chatList.data = chats;
68
-
69
- if (state.ui.chatId === undefined && chats.length > 0) {
70
- state.ui.view = 'chat';
71
- state.ui.chatId = chats[0].id;
72
- }
73
- },
74
-
75
- chatList_rejected: (state: State, { error }: { error: any }) => {
76
- state.query.chatList.isFetching = false;
77
- state.query.chatList.error = error;
78
- },
79
-
80
- removeMember_fetch: (state: State) => {
81
- state.mutation.removeMember = { isFetching: true };
82
- },
83
-
84
- removeMember_fulfilled: (state: State) => {
85
- state.mutation.removeMember = { isFetching: false, error: undefined };
86
- },
87
-
88
- removeMember_rejected: (state: State, { error }: { error: any }) => {
89
- state.mutation.removeMember = { isFetching: false, error };
90
- },
91
-
92
- addMember_fetch: (state: State) => {
93
- state.mutation.addMember = { isFetching: true };
94
- },
95
-
96
- addMember_fulfilled: (state: State) => {
97
- state.mutation.addMember = { isFetching: false, error: undefined };
98
- },
99
-
100
- addMember_rejected: (state: State, { error }: { error: any }) => {
101
- state.mutation.addMember = { isFetching: false, error };
102
- },
103
-
104
- updateChatName_fetch: (state: State) => {
105
- state.mutation.updateChatName = { isFetching: true };
106
- },
107
-
108
- updateChatName_fulfilled: (state: State) => {
109
- state.mutation.updateChatName = { isFetching: false, error: undefined };
110
- },
111
-
112
- updateChatName_rejected: (state: State, { error }: { error: any }) => {
113
- state.mutation.updateChatName = { isFetching: false, error };
114
- },
115
-
116
- addChat_fetch: (state: State) => {
117
- state.mutation.addChat = { isFetching: true };
118
- },
119
-
120
- addChat_fulfilled: (state: State) => {
121
- state.mutation.addChat = { isFetching: false, error: undefined };
122
- },
123
-
124
- addChat_rejected: (state: State, { error }: { error: any }) => {
125
- state.mutation.addChat = { isFetching: false, error };
126
- },
127
-
128
- accounts_fetch: (state: State) => {
129
- state.query.accounts = { isFetching: true, uninitialized: false };
130
- },
131
-
132
- accounts_fulfilled: (state: State, { accounts }: { accounts: Account[] }) => {
133
- state.query.accounts.data = accounts;
134
- state.query.accounts.isFetching = false;
135
- state.query.accounts.error = undefined;
136
- },
137
-
138
- accounts_rejected: (state: State, { error }: { error: any }) => {
139
- state.query.accounts.isFetching = false;
140
- state.query.accounts.error = error;
141
- },
142
-
143
- chat_open: (state: State, { chatId }: { chatId: string }) => {
144
- state.ui.chatId = chatId;
145
- state.ui.view = 'chat';
146
- },
147
-
148
- new_chat_open: (state: State) => {
149
- state.ui.chatId = undefined;
150
- state.ui.view = 'new-chat';
151
- },
152
-
153
- toggle_members_view: (state: State) => {
154
- state.ui.showMembers = !state.ui.showMembers;
155
- },
156
-
157
- message_received: (
158
- state: State,
159
- { message, requestId, me }: { message: Message; requestId: string; me: string },
160
- ) => {
161
- const messagesState = state.query.messages[message.chatId];
162
-
163
- if (messagesState?.data) {
164
- messagesState.data.unshift({ type: 'message', ...message });
165
- messagesState.data = messagesState.data.filter(
166
- (x) => x.type !== 'sending' || x.requestId !== requestId,
167
- );
168
- }
169
-
170
- const chatListItemState = state.query.chatList?.data?.find((x) => x.id === message.chatId);
171
-
172
- if (chatListItemState) {
173
- chatListItemState.message = message;
174
- }
175
-
176
- if (chatListItemState && message.sentBy.id !== me) {
177
- chatListItemState.totalUnreadCount++;
178
- }
179
-
180
- if (chatListItemState && message.sentBy.id === me) {
181
- chatListItemState.lastReadMessageId = message.id;
182
- chatListItemState.totalUnreadCount = 0;
183
- }
184
- },
185
-
186
- chat_created: (state: State, { chat }: { chat: ChatListItem; requestId?: string }) => {
187
- state.query.chatList?.data?.unshift(chat);
188
- },
189
-
190
- read: (state: State, { id, timestamp }: { id: string; timestamp: number }) => {
191
- const queuedSameChat = state.queue.read.queued.filter((x) => x.substring(0, 36) === id.substring(0, 36));
192
-
193
- if (queuedSameChat.some((x) => x.localeCompare(id) >= 0)) {
194
- return;
195
- }
196
-
197
- state.queue.read.queued.filter((x) => queuedSameChat.includes(x));
198
- state.queue.read.queued.push(id);
199
- state.queue.read.timestamp = timestamp;
200
- },
201
-
202
- read_send: (state: State, { id }: { id: string }) => {
203
- state.queue.read.queued = state.queue.read.queued.filter((x) => x !== id);
204
- state.queue.read.processing.push(id);
205
- },
206
-
207
- read_reject: (state: State, { id, timestamp }: { id: string; timestamp: number }) => {
208
- state.queue.read.queued.push(id);
209
- state.queue.read.processing = state.queue.read.processing.filter((x) => x !== id);
210
- state.queue.read.timestamp = timestamp;
211
- },
212
-
213
- read_received: (state: State, { events }: { events: ReadEvent[] }) => {
214
- state.queue.read.processing = state.queue.read.processing.filter((item) => {
215
- const event = events.find((x) => x.chatId === item.substring(0, 36));
216
- return !event || event.newLastReadMessageId.localeCompare(item) >= 0;
217
- });
218
-
219
- for (const event of events) {
220
- state.queue.read.processing = state.queue.read.processing.filter(
221
- (x) => x.substring(0, 36) !== event.chatId || x.localeCompare(event.newLastReadMessageId) > 0,
222
- );
223
- const chatListItem = state.query.chatList.data?.find((x) => x.id === event.chatId);
224
-
225
- if (chatListItem) {
226
- chatListItem.lastReadMessageId = event.newLastReadMessageId;
227
- chatListItem.totalUnreadCount = chatListItem.totalUnreadCount - event.readCount;
228
- }
229
- }
230
- },
231
-
232
- member_added: (
233
- state: State,
234
- {
235
- chatId,
236
- chatName,
237
- account,
238
- me,
239
- message,
240
- withHistory,
241
- }: {
242
- chatId: string;
243
- chatName: string;
244
- account: Account;
245
- me: string;
246
- message: Message;
247
- withHistory: boolean;
248
- },
249
- ) => {
250
- const { chats, messages, chatList } = state.query;
251
- chats[chatId]?.data?.members.push(account);
252
- const chatListItem = chatList?.data?.find((x) => x.id === chatId);
253
-
254
- if (account.id !== me && chatListItem != null) {
255
- chatListItem.message = message;
256
- chatListItem.totalUnreadCount++;
257
- }
258
-
259
- messages[chatId]?.data?.unshift({ type: 'message', ...message });
260
-
261
- if (account.id === me && chatList.data !== undefined && !chatListItem) {
262
- chatList.data.unshift({
263
- id: chatId,
264
- name: chatName,
265
- message,
266
- totalUnreadCount: 1,
267
- active: true,
268
- lastReadMessageId: null,
269
- });
270
- }
271
-
272
- if (account.id === me && chatListItem) {
273
- chatListItem.active = true;
274
- chatListItem.message = message;
275
- chatListItem.totalUnreadCount++;
276
- }
277
-
278
- return state;
279
- },
280
-
281
- member_removed: (
282
- state: State,
283
- {
284
- chatId,
285
- accountId,
286
- deleteHistory,
287
- me,
288
- message,
289
- }: { chatId: string; accountId: string; deleteHistory: boolean; me: string; message: Message },
290
- ) => {
291
- const { chats, chatList } = state.query;
292
- const chat = chats[chatId];
293
-
294
- if (chat?.data) {
295
- chat.data.members = chat.data.members.filter((x) => x.id !== accountId);
296
- }
297
-
298
- if (accountId === me && state.query.chats[chatId].data) {
299
- delete state.query.chats[chatId];
300
- }
301
-
302
- if (accountId === me && deleteHistory && chatList?.data) {
303
- chatList.data = chatList.data.filter((x) => x.id !== chatId);
304
-
305
- if (state.ui.chatId === chatId) {
306
- state.ui.chatId = chatList.data.at(0)?.id;
307
- }
308
- }
309
-
310
- if (accountId === me && !deleteHistory && chatList?.data) {
311
- const listItem = chatList.data.find((x) => x.id === chatId)!;
312
- listItem.active = false;
313
- }
314
-
315
- if ((accountId !== me || !deleteHistory) && state.query.messages[chatId].data) {
316
- state.query.messages[chatId].data!.unshift({ ...message, type: 'message' });
317
- }
318
-
319
- if ((accountId !== me || !deleteHistory) && state.query.chatList.data) {
320
- const listItem = state.query.chatList.data.find((x) => x.id === chatId)!;
321
- listItem.message = message;
322
- listItem.totalUnreadCount++;
323
- }
324
-
325
- if (accountId === me && deleteHistory && state.query.messages[chatId].data) {
326
- delete state.query.messages[chatId];
327
- }
328
- },
329
-
330
- chat_fetch: (state: State, { chatId }: { chatId: string }) => {
331
- state.query.chats[chatId] = { isFetching: true, uninitialized: false, data: undefined, error: undefined };
332
- },
333
-
334
- chat_fulfilled: (state: State, { chat }: { chat: Chat }) => {
335
- const chatState = state.query.chats[chat.id];
336
- chatState.isFetching = false;
337
- chatState.error = undefined;
338
- chatState.data = chat;
339
- },
340
-
341
- chat_rejected: (state: State, { chatId, error }: { chatId: string; error: any }) => {
342
- const chatState = state.query.chats[chatId];
343
- chatState.isFetching = false;
344
- chatState.error = error;
345
- },
346
-
347
- send_fetch: (
348
- state: State,
349
- {
350
- chatId,
351
- text,
352
- files,
353
- requestId,
354
- }: {
355
- chatId: string;
356
- text: string;
357
- files: File[];
358
- requestId: string;
359
- },
360
- ) => {
361
- state.mutation.send = { isFetching: true, error: undefined };
362
- state.query.messages[chatId].data?.push({ type: 'sending', text, files, requestId });
363
- },
364
-
365
- send_fulfilled: (state: State, { chatId, requestId }: { chatId: string; requestId: string }) => {
366
- state.mutation.send = { isFetching: false, error: undefined };
367
- },
368
-
369
- send_rejected: (
370
- state: State,
371
- { chatId, requestId, error }: { chatId: string; requestId: string; error: any },
372
- ) => {
373
- state.mutation.send = { isFetching: false, error };
374
- state.query.messages[chatId].data = state.query.messages[chatId].data?.filter(
375
- (x) => x.type !== 'sending' || x.requestId !== requestId,
376
- );
377
- },
378
-
379
- chatNameChanged_received: (
380
- state: State,
381
- { chatId, newName, message }: { chatId: string; newName: string; message: Message },
382
- ) => {
383
- if (state.query.chatList.data) {
384
- const chatListItem = state.query.chatList.data.find((x) => x.id === chatId);
385
- chatListItem!.name = newName;
386
- chatListItem!.message = message;
387
- chatListItem!.totalUnreadCount++;
388
- }
389
-
390
- if (state.query.chats[chatId].data) {
391
- state.query.chats[chatId].data!.name = newName;
392
- }
393
-
394
- if (state.query.messages[chatId].data) {
395
- state.query.messages[chatId].data!.unshift({ type: 'message', ...message });
396
- }
397
- },
398
- };
399
-
400
- export type Action = {
401
- [K in keyof typeof ACTIONS]: {
402
- type: K;
403
- data: Parameters<(typeof ACTIONS)[K]>[1];
404
- };
405
- }[keyof typeof ACTIONS];
1
+ import type { State } from './state';
2
+ import { Account, Chat, ChatListItem, Message, ReadEvent, File } from './models';
3
+
4
+ export const ACTIONS = {
5
+ update: (state: State, { mutation }: { mutation: (state: State) => State }) => {
6
+ mutation(state);
7
+ },
8
+
9
+ messages_fetch: (state: State, { chatId }: { chatId: string }) => {
10
+ state.query.messages[chatId] = {
11
+ ...state.query.messages[chatId],
12
+ uninitialized: false,
13
+ isFetching: true,
14
+ };
15
+ },
16
+
17
+ messages_fulfilled: (
18
+ state: State,
19
+ { chatId, messages, hasMore }: { chatId: string; messages: Message[]; hasMore: boolean },
20
+ ) => {
21
+ const qState = state.query.messages[chatId];
22
+
23
+ qState.error = undefined;
24
+ qState.isFetching = false;
25
+ qState.data = messages.map((x) => ({ type: 'message', ...x }));
26
+
27
+ if (hasMore) {
28
+ qState.data!.push({ type: 'next' });
29
+ }
30
+ },
31
+
32
+ messages_rejected: (state: State, { chatId, error }: { chatId: string; error: any }) => {
33
+ const qState = state.query.messages[chatId];
34
+ qState.error = error;
35
+ qState.isFetching = false;
36
+ },
37
+
38
+ fetchNext_fetch: (state: State, { chatId }: { chatId: string }) => {
39
+ state.mutation.fetchNext = { isFetching: true };
40
+ },
41
+
42
+ fetchNext_fulfilled: (
43
+ state: State,
44
+ { chatId, messages, hasMore }: { chatId: string; messages: Message[]; hasMore: boolean },
45
+ ) => {
46
+ state.mutation.fetchNext = { isFetching: false, error: undefined };
47
+ state.query.messages[chatId].data = state.query.messages[chatId].data!.filter((x) => x.type !== 'next');
48
+ state.query.messages[chatId].data!.push(...messages.map((x) => ({ type: 'message' as const, ...x })));
49
+
50
+ if (hasMore) state.query.messages[chatId].data!.push({ type: 'next' });
51
+ },
52
+
53
+ fetchNext_rejected: (state: State, { chatId, error }: { chatId: string; error: any }) => {
54
+ state.mutation.fetchNext = { isFetching: false, error };
55
+ },
56
+
57
+ chatList_fetch: (state: State) => {
58
+ state.query.chatList.isFetching = true;
59
+ state.query.chatList.uninitialized = false;
60
+ },
61
+
62
+ chatList_fulfilled: (state: State, { chats }: { chats: ChatListItem[] }) => {
63
+ chats = chats.sort((a, b) => -a.message.id.substring(47).localeCompare(b.message.id.substring(47)));
64
+
65
+ state.query.chatList.isFetching = false;
66
+ state.query.chatList.error = undefined;
67
+ state.query.chatList.data = chats;
68
+
69
+ if (state.ui.chatId === undefined && chats.length > 0) {
70
+ state.ui.view = 'chat';
71
+ state.ui.chatId = chats[0].id;
72
+ }
73
+ },
74
+
75
+ chatList_rejected: (state: State, { error }: { error: any }) => {
76
+ state.query.chatList.isFetching = false;
77
+ state.query.chatList.error = error;
78
+ },
79
+
80
+ removeMember_fetch: (state: State) => {
81
+ state.mutation.removeMember = { isFetching: true };
82
+ },
83
+
84
+ removeMember_fulfilled: (state: State) => {
85
+ state.mutation.removeMember = { isFetching: false, error: undefined };
86
+ },
87
+
88
+ removeMember_rejected: (state: State, { error }: { error: any }) => {
89
+ state.mutation.removeMember = { isFetching: false, error };
90
+ },
91
+
92
+ addMember_fetch: (state: State) => {
93
+ state.mutation.addMember = { isFetching: true };
94
+ },
95
+
96
+ addMember_fulfilled: (state: State) => {
97
+ state.mutation.addMember = { isFetching: false, error: undefined };
98
+ },
99
+
100
+ addMember_rejected: (state: State, { error }: { error: any }) => {
101
+ state.mutation.addMember = { isFetching: false, error };
102
+ },
103
+
104
+ updateChatName_fetch: (state: State) => {
105
+ state.mutation.updateChatName = { isFetching: true };
106
+ },
107
+
108
+ updateChatName_fulfilled: (state: State) => {
109
+ state.mutation.updateChatName = { isFetching: false, error: undefined };
110
+ },
111
+
112
+ updateChatName_rejected: (state: State, { error }: { error: any }) => {
113
+ state.mutation.updateChatName = { isFetching: false, error };
114
+ },
115
+
116
+ addChat_fetch: (state: State) => {
117
+ state.mutation.addChat = { isFetching: true };
118
+ },
119
+
120
+ addChat_fulfilled: (state: State) => {
121
+ state.mutation.addChat = { isFetching: false, error: undefined };
122
+ },
123
+
124
+ addChat_rejected: (state: State, { error }: { error: any }) => {
125
+ state.mutation.addChat = { isFetching: false, error };
126
+ },
127
+
128
+ accounts_fetch: (state: State) => {
129
+ state.query.accounts = { isFetching: true, uninitialized: false };
130
+ },
131
+
132
+ accounts_fulfilled: (state: State, { accounts }: { accounts: Account[] }) => {
133
+ state.query.accounts.data = accounts;
134
+ state.query.accounts.isFetching = false;
135
+ state.query.accounts.error = undefined;
136
+ },
137
+
138
+ accounts_rejected: (state: State, { error }: { error: any }) => {
139
+ state.query.accounts.isFetching = false;
140
+ state.query.accounts.error = error;
141
+ },
142
+
143
+ chat_open: (state: State, { chatId }: { chatId: string }) => {
144
+ state.ui.chatId = chatId;
145
+ state.ui.view = 'chat';
146
+ },
147
+
148
+ new_chat_open: (state: State) => {
149
+ state.ui.chatId = undefined;
150
+ state.ui.view = 'new-chat';
151
+ },
152
+
153
+ toggle_members_view: (state: State) => {
154
+ state.ui.showMembers = !state.ui.showMembers;
155
+ },
156
+
157
+ message_received: (
158
+ state: State,
159
+ { message, requestId, me }: { message: Message; requestId: string; me: string },
160
+ ) => {
161
+ const messagesState = state.query.messages[message.chatId];
162
+
163
+ if (messagesState?.data) {
164
+ messagesState.data.unshift({ type: 'message', ...message });
165
+ messagesState.data = messagesState.data.filter(
166
+ (x) => x.type !== 'sending' || x.requestId !== requestId,
167
+ );
168
+ }
169
+
170
+ const chatListItemState = state.query.chatList?.data?.find((x) => x.id === message.chatId);
171
+
172
+ if (chatListItemState) {
173
+ chatListItemState.message = message;
174
+ }
175
+
176
+ if (chatListItemState && message.sentBy.id !== me) {
177
+ chatListItemState.totalUnreadCount++;
178
+ }
179
+
180
+ if (chatListItemState && message.sentBy.id === me) {
181
+ chatListItemState.lastReadMessageId = message.id;
182
+ chatListItemState.totalUnreadCount = 0;
183
+ }
184
+ },
185
+
186
+ chat_created: (state: State, { chat }: { chat: ChatListItem; requestId?: string }) => {
187
+ state.query.chatList?.data?.unshift(chat);
188
+ },
189
+
190
+ read: (state: State, { id, timestamp }: { id: string; timestamp: number }) => {
191
+ const queuedSameChat = state.queue.read.queued.filter((x) => x.substring(0, 36) === id.substring(0, 36));
192
+
193
+ if (queuedSameChat.some((x) => x.localeCompare(id) >= 0)) {
194
+ return;
195
+ }
196
+
197
+ state.queue.read.queued.filter((x) => queuedSameChat.includes(x));
198
+ state.queue.read.queued.push(id);
199
+ state.queue.read.timestamp = timestamp;
200
+ },
201
+
202
+ read_send: (state: State, { id }: { id: string }) => {
203
+ state.queue.read.queued = state.queue.read.queued.filter((x) => x !== id);
204
+ state.queue.read.processing.push(id);
205
+ },
206
+
207
+ read_reject: (state: State, { id, timestamp }: { id: string; timestamp: number }) => {
208
+ state.queue.read.queued.push(id);
209
+ state.queue.read.processing = state.queue.read.processing.filter((x) => x !== id);
210
+ state.queue.read.timestamp = timestamp;
211
+ },
212
+
213
+ read_received: (state: State, { events }: { events: ReadEvent[] }) => {
214
+ state.queue.read.processing = state.queue.read.processing.filter((item) => {
215
+ const event = events.find((x) => x.chatId === item.substring(0, 36));
216
+ return !event || event.newLastReadMessageId.localeCompare(item) >= 0;
217
+ });
218
+
219
+ for (const event of events) {
220
+ state.queue.read.processing = state.queue.read.processing.filter(
221
+ (x) => x.substring(0, 36) !== event.chatId || x.localeCompare(event.newLastReadMessageId) > 0,
222
+ );
223
+ const chatListItem = state.query.chatList.data?.find((x) => x.id === event.chatId);
224
+
225
+ if (chatListItem) {
226
+ chatListItem.lastReadMessageId = event.newLastReadMessageId;
227
+ chatListItem.totalUnreadCount = chatListItem.totalUnreadCount - event.readCount;
228
+ }
229
+ }
230
+ },
231
+
232
+ member_added: (
233
+ state: State,
234
+ {
235
+ chatId,
236
+ chatName,
237
+ account,
238
+ me,
239
+ message,
240
+ withHistory,
241
+ }: {
242
+ chatId: string;
243
+ chatName: string;
244
+ account: Account;
245
+ me: string;
246
+ message: Message;
247
+ withHistory: boolean;
248
+ },
249
+ ) => {
250
+ const { chats, messages, chatList } = state.query;
251
+ chats[chatId]?.data?.members.push(account);
252
+ const chatListItem = chatList?.data?.find((x) => x.id === chatId);
253
+
254
+ if (account.id !== me && chatListItem != null) {
255
+ chatListItem.message = message;
256
+ chatListItem.totalUnreadCount++;
257
+ }
258
+
259
+ messages[chatId]?.data?.unshift({ type: 'message', ...message });
260
+
261
+ if (account.id === me && chatList.data !== undefined && !chatListItem) {
262
+ chatList.data.unshift({
263
+ id: chatId,
264
+ name: chatName,
265
+ message,
266
+ totalUnreadCount: 1,
267
+ active: true,
268
+ lastReadMessageId: null,
269
+ });
270
+ }
271
+
272
+ if (account.id === me && chatListItem) {
273
+ chatListItem.active = true;
274
+ chatListItem.message = message;
275
+ chatListItem.totalUnreadCount++;
276
+ }
277
+
278
+ return state;
279
+ },
280
+
281
+ member_removed: (
282
+ state: State,
283
+ {
284
+ chatId,
285
+ accountId,
286
+ deleteHistory,
287
+ me,
288
+ message,
289
+ }: { chatId: string; accountId: string; deleteHistory: boolean; me: string; message: Message },
290
+ ) => {
291
+ const { chats, chatList } = state.query;
292
+ const chat = chats[chatId];
293
+
294
+ if (chat?.data) {
295
+ chat.data.members = chat.data.members.filter((x) => x.id !== accountId);
296
+ }
297
+
298
+ if (accountId === me && state.query.chats[chatId].data) {
299
+ delete state.query.chats[chatId];
300
+ }
301
+
302
+ if (accountId === me && deleteHistory && chatList?.data) {
303
+ chatList.data = chatList.data.filter((x) => x.id !== chatId);
304
+
305
+ if (state.ui.chatId === chatId) {
306
+ state.ui.chatId = chatList.data.at(0)?.id;
307
+ }
308
+ }
309
+
310
+ if (accountId === me && !deleteHistory && chatList?.data) {
311
+ const listItem = chatList.data.find((x) => x.id === chatId)!;
312
+ listItem.active = false;
313
+ }
314
+
315
+ if ((accountId !== me || !deleteHistory) && state.query.messages[chatId].data) {
316
+ state.query.messages[chatId].data!.unshift({ ...message, type: 'message' });
317
+ }
318
+
319
+ if ((accountId !== me || !deleteHistory) && state.query.chatList.data) {
320
+ const listItem = state.query.chatList.data.find((x) => x.id === chatId)!;
321
+ listItem.message = message;
322
+ listItem.totalUnreadCount++;
323
+ }
324
+
325
+ if (accountId === me && deleteHistory && state.query.messages[chatId].data) {
326
+ delete state.query.messages[chatId];
327
+ }
328
+ },
329
+
330
+ chat_fetch: (state: State, { chatId }: { chatId: string }) => {
331
+ state.query.chats[chatId] = { isFetching: true, uninitialized: false, data: undefined, error: undefined };
332
+ },
333
+
334
+ chat_fulfilled: (state: State, { chat }: { chat: Chat }) => {
335
+ const chatState = state.query.chats[chat.id];
336
+ chatState.isFetching = false;
337
+ chatState.error = undefined;
338
+ chatState.data = chat;
339
+ },
340
+
341
+ chat_rejected: (state: State, { chatId, error }: { chatId: string; error: any }) => {
342
+ const chatState = state.query.chats[chatId];
343
+ chatState.isFetching = false;
344
+ chatState.error = error;
345
+ },
346
+
347
+ send_fetch: (
348
+ state: State,
349
+ {
350
+ chatId,
351
+ text,
352
+ files,
353
+ requestId,
354
+ }: {
355
+ chatId: string;
356
+ text: string;
357
+ files: File[];
358
+ requestId: string;
359
+ },
360
+ ) => {
361
+ state.mutation.send = { isFetching: true, error: undefined };
362
+ state.query.messages[chatId].data?.push({ type: 'sending', text, files, requestId });
363
+ },
364
+
365
+ send_fulfilled: (state: State, { chatId, requestId }: { chatId: string; requestId: string }) => {
366
+ state.mutation.send = { isFetching: false, error: undefined };
367
+ },
368
+
369
+ send_rejected: (
370
+ state: State,
371
+ { chatId, requestId, error }: { chatId: string; requestId: string; error: any },
372
+ ) => {
373
+ state.mutation.send = { isFetching: false, error };
374
+ state.query.messages[chatId].data = state.query.messages[chatId].data?.filter(
375
+ (x) => x.type !== 'sending' || x.requestId !== requestId,
376
+ );
377
+ },
378
+
379
+ chatNameChanged_received: (
380
+ state: State,
381
+ { chatId, newName, message }: { chatId: string; newName: string; message: Message },
382
+ ) => {
383
+ if (state.query.chatList.data) {
384
+ const chatListItem = state.query.chatList.data.find((x) => x.id === chatId);
385
+ chatListItem!.name = newName;
386
+ chatListItem!.message = message;
387
+ chatListItem!.totalUnreadCount++;
388
+ }
389
+
390
+ if (state.query.chats[chatId].data) {
391
+ state.query.chats[chatId].data!.name = newName;
392
+ }
393
+
394
+ if (state.query.messages[chatId].data) {
395
+ state.query.messages[chatId].data!.unshift({ type: 'message', ...message });
396
+ }
397
+ },
398
+ };
399
+
400
+ export type Action = {
401
+ [K in keyof typeof ACTIONS]: {
402
+ type: K;
403
+ data: Parameters<(typeof ACTIONS)[K]>[1];
404
+ };
405
+ }[keyof typeof ACTIONS];