@webinex/chatify 1.0.0-build2 → 1.0.0-rc2

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 (56) hide show
  1. package/package.json +90 -90
  2. package/src/ChatifyContext.tsx +41 -41
  3. package/src/core/action.tsx +319 -319
  4. package/src/core/client.ts +137 -137
  5. package/src/core/index.ts +14 -14
  6. package/src/core/models.tsx +75 -75
  7. package/src/core/reducer.tsx +86 -86
  8. package/src/core/state.ts +38 -38
  9. package/src/core/useAccounts.tsx +11 -11
  10. package/src/core/useAddChat.tsx +12 -12
  11. package/src/core/useAddMember.tsx +12 -12
  12. package/src/core/useAddMessage.tsx +40 -40
  13. package/src/core/useChat.tsx +12 -12
  14. package/src/core/useChatList.tsx +10 -10
  15. package/src/core/useLoadMore.tsx +42 -42
  16. package/src/core/useMessages.tsx +23 -23
  17. package/src/core/useMutation.tsx +48 -48
  18. package/src/core/useQuery.tsx +45 -45
  19. package/src/core/useReadMonitor.tsx +31 -31
  20. package/src/core/useRemoveMember.tsx +12 -12
  21. package/src/core/useSignalRMonitor.tsx +52 -52
  22. package/src/index.ts +3 -3
  23. package/src/ui/AddChat.tsx +55 -55
  24. package/src/ui/AddChatButton.tsx +19 -19
  25. package/src/ui/Aside.tsx +41 -41
  26. package/src/ui/Avatar.tsx +18 -18
  27. package/src/ui/Chat.tsx +26 -26
  28. package/src/ui/ChatBody.tsx +43 -43
  29. package/src/ui/ChatHeader.tsx +33 -33
  30. package/src/ui/ChatHeaderMembers.tsx +30 -30
  31. package/src/ui/ChatHeaderSettingsButton.tsx +26 -26
  32. package/src/ui/ChatListItem.tsx +101 -101
  33. package/src/ui/ChatName.tsx +15 -15
  34. package/src/ui/ChatSettings.tsx +59 -59
  35. package/src/ui/Chatify.tsx +106 -106
  36. package/src/ui/Footer.tsx +7 -7
  37. package/src/ui/Header.tsx +14 -14
  38. package/src/ui/Icon.tsx +21 -21
  39. package/src/ui/InputForm.tsx +51 -51
  40. package/src/ui/LoadMore.tsx +40 -40
  41. package/src/ui/Main.tsx +16 -16
  42. package/src/ui/Message.tsx +118 -118
  43. package/src/ui/MessageSkeleton.tsx +19 -19
  44. package/src/ui/SendingMessage.tsx +50 -50
  45. package/src/ui/SystemMessage.tsx +32 -32
  46. package/src/ui/index.ts +11 -11
  47. package/src/ui/localizer.tsx +68 -68
  48. package/src/ui/styles/aside.scss +94 -94
  49. package/src/ui/styles/index.scss +248 -248
  50. package/src/ui/styles/keyframes.scss +14 -14
  51. package/src/ui/styles/mixins.scss +20 -20
  52. package/src/ui/styles/variables.scss +10 -10
  53. package/src/util/customize.tsx +45 -45
  54. package/src/util/index.ts +3 -3
  55. package/src/util/uniqBy.tsx +17 -17
  56. package/src/util/uniqId.tsx +3 -3
package/src/core/state.ts CHANGED
@@ -1,38 +1,38 @@
1
- export interface UIState {
2
- view?: 'chat' | 'new-chat' | 'chat-settings';
3
- chatId?: string;
4
- }
5
-
6
- export interface QueryState<TData> {
7
- uninitialized: boolean;
8
- isFetching: boolean;
9
- error?: any;
10
- data?: TData;
11
- }
12
-
13
- export interface MutationState {
14
- isFetching: boolean;
15
- error?: any;
16
- }
17
-
18
- export interface State {
19
- ui: UIState;
20
- query: Record<string, QueryState<any>>;
21
- mutation: Record<string, MutationState>;
22
- read: {
23
- queued: string[];
24
- sent: string[];
25
- timestamp: number;
26
- };
27
- }
28
-
29
- export const INITIAL_STATE: State = {
30
- ui: {},
31
- query: {},
32
- mutation: {},
33
- read: {
34
- queued: [],
35
- sent: [],
36
- timestamp: 0,
37
- },
38
- };
1
+ export interface UIState {
2
+ view?: 'chat' | 'new-chat' | 'chat-settings';
3
+ chatId?: string;
4
+ }
5
+
6
+ export interface QueryState<TData> {
7
+ uninitialized: boolean;
8
+ isFetching: boolean;
9
+ error?: any;
10
+ data?: TData;
11
+ }
12
+
13
+ export interface MutationState {
14
+ isFetching: boolean;
15
+ error?: any;
16
+ }
17
+
18
+ export interface State {
19
+ ui: UIState;
20
+ query: Record<string, QueryState<any>>;
21
+ mutation: Record<string, MutationState>;
22
+ read: {
23
+ queued: string[];
24
+ sent: string[];
25
+ timestamp: number;
26
+ };
27
+ }
28
+
29
+ export const INITIAL_STATE: State = {
30
+ ui: {},
31
+ query: {},
32
+ mutation: {},
33
+ read: {
34
+ queued: [],
35
+ sent: [],
36
+ timestamp: 0,
37
+ },
38
+ };
@@ -1,11 +1,11 @@
1
- import { useClient } from '../ChatifyContext';
2
- import { useQuery } from './useQuery';
3
-
4
- export function useAccounts() {
5
- const client = useClient();
6
-
7
- return useQuery({
8
- key: 'accounts',
9
- queryFn: () => client.accounts(),
10
- });
11
- }
1
+ import { useClient } from '../ChatifyContext';
2
+ import { useQuery } from './useQuery';
3
+
4
+ export function useAccounts() {
5
+ const client = useClient();
6
+
7
+ return useQuery({
8
+ key: 'accounts',
9
+ queryFn: () => client.accounts(),
10
+ });
11
+ }
@@ -1,12 +1,12 @@
1
- import { useClient } from '../ChatifyContext';
2
- import { AddChatRequest } from './models';
3
- import { useMutation } from './useMutation';
4
-
5
- export function useAddChat() {
6
- const client = useClient();
7
-
8
- return useMutation({
9
- key: 'add-chat',
10
- queryFn: (args: AddChatRequest) => client.addChat(args),
11
- });
12
- }
1
+ import { useClient } from '../ChatifyContext';
2
+ import { AddChatRequest } from './models';
3
+ import { useMutation } from './useMutation';
4
+
5
+ export function useAddChat() {
6
+ const client = useClient();
7
+
8
+ return useMutation({
9
+ key: 'add-chat',
10
+ queryFn: (args: AddChatRequest) => client.addChat(args),
11
+ });
12
+ }
@@ -1,12 +1,12 @@
1
- import { useClient } from '../ChatifyContext';
2
- import { AddMemberRequest } from './models';
3
- import { useMutation } from './useMutation';
4
-
5
- export function useAddMember() {
6
- const client = useClient();
7
-
8
- return useMutation({
9
- key: 'add-member',
10
- queryFn: (args: AddMemberRequest) => client.addMember(args),
11
- });
12
- }
1
+ import { useClient } from '../ChatifyContext';
2
+ import { AddMemberRequest } from './models';
3
+ import { useMutation } from './useMutation';
4
+
5
+ export function useAddMember() {
6
+ const client = useClient();
7
+
8
+ return useMutation({
9
+ key: 'add-member',
10
+ queryFn: (args: AddMemberRequest) => client.addMember(args),
11
+ });
12
+ }
@@ -1,40 +1,40 @@
1
- import { useClient } from '../ChatifyContext';
2
- import { SendMessageRequest } from './models';
3
- import { QueryState } from './state';
4
- import { MessageState } from './useMessages';
5
- import { useMutation } from './useMutation';
6
-
7
- export interface UseAddMessageArgs {
8
- chatId: string;
9
- }
10
-
11
- export function useAddMessage(args: UseAddMessageArgs) {
12
- const { chatId } = args;
13
- const client = useClient();
14
-
15
- return useMutation<unknown, Omit<SendMessageRequest, 'chatId'>>({
16
- key: 'add.' + chatId,
17
- queryFn: (args) => client.send({ chatId, ...args }),
18
- onQueryStarted: async (args, { updateState }) => {
19
- updateState((state) => {
20
- state = {
21
- ...state,
22
- query: {
23
- ...state.query,
24
- ['messages.' + chatId]: {
25
- ...state.query['messages.' + chatId],
26
- },
27
- },
28
- };
29
-
30
- const queryState: QueryState<MessageState[]> = state.query['messages.' + chatId];
31
- queryState.data = [
32
- { type: 'sending', text: args.text, requestId: args.requestId, files: args.files },
33
- ...queryState.data!,
34
- ];
35
-
36
- return state;
37
- });
38
- },
39
- });
40
- }
1
+ import { useClient } from '../ChatifyContext';
2
+ import { SendMessageRequest } from './models';
3
+ import { QueryState } from './state';
4
+ import { MessageState } from './useMessages';
5
+ import { useMutation } from './useMutation';
6
+
7
+ export interface UseAddMessageArgs {
8
+ chatId: string;
9
+ }
10
+
11
+ export function useAddMessage(args: UseAddMessageArgs) {
12
+ const { chatId } = args;
13
+ const client = useClient();
14
+
15
+ return useMutation<unknown, Omit<SendMessageRequest, 'chatId'>>({
16
+ key: 'add.' + chatId,
17
+ queryFn: (args) => client.send({ chatId, ...args }),
18
+ onQueryStarted: async (args, { updateState }) => {
19
+ updateState((state) => {
20
+ state = {
21
+ ...state,
22
+ query: {
23
+ ...state.query,
24
+ ['messages.' + chatId]: {
25
+ ...state.query['messages.' + chatId],
26
+ },
27
+ },
28
+ };
29
+
30
+ const queryState: QueryState<MessageState[]> = state.query['messages.' + chatId];
31
+ queryState.data = [
32
+ { type: 'sending', text: args.text, requestId: args.requestId, files: args.files },
33
+ ...queryState.data!,
34
+ ];
35
+
36
+ return state;
37
+ });
38
+ },
39
+ });
40
+ }
@@ -1,12 +1,12 @@
1
- import { useClient } from '../ChatifyContext';
2
- import { useQuery } from './useQuery';
3
-
4
- export function useChat(args: { chatId: string }) {
5
- const { chatId } = args;
6
- const client = useClient();
7
-
8
- return useQuery({
9
- key: 'chat.' + chatId,
10
- queryFn: () => client.chat(chatId),
11
- });
12
- }
1
+ import { useClient } from '../ChatifyContext';
2
+ import { useQuery } from './useQuery';
3
+
4
+ export function useChat(args: { chatId: string }) {
5
+ const { chatId } = args;
6
+ const client = useClient();
7
+
8
+ return useQuery({
9
+ key: 'chat.' + chatId,
10
+ queryFn: () => client.chat(chatId),
11
+ });
12
+ }
@@ -1,10 +1,10 @@
1
- import { useClient } from '../ChatifyContext';
2
- import { useQuery } from './useQuery';
3
-
4
- export function useChatList() {
5
- const client = useClient();
6
- return useQuery({
7
- key: 'chat-list',
8
- queryFn: () => client.chats(),
9
- });
10
- }
1
+ import { useClient } from '../ChatifyContext';
2
+ import { useQuery } from './useQuery';
3
+
4
+ export function useChatList() {
5
+ const client = useClient();
6
+ return useQuery({
7
+ key: 'chat-list',
8
+ queryFn: () => client.chats(),
9
+ });
10
+ }
@@ -1,42 +1,42 @@
1
- import { useClient } from '../ChatifyContext';
2
- import { useMutation } from './useMutation';
3
- import { MessageState } from './useMessages';
4
- import { useStateRef } from './reducer';
5
- import { QueryState } from './state';
6
-
7
- export function useLoadMore({ chatId }: { chatId: string }) {
8
- const state = useStateRef();
9
- const client = useClient();
10
-
11
- return useMutation({
12
- key: 'more.' + chatId,
13
- queryFn: () =>
14
- client.messages({
15
- chatId,
16
- skip: state.current.query['messages.' + chatId].data.filter((x: MessageState) => x.type === 'message')
17
- .length,
18
- take: 15,
19
- }),
20
-
21
- onQueryStarted: async (_, { promise, updateState }) => {
22
- const result = await promise;
23
- updateState((state) => {
24
- state = { ...state };
25
- state.query = { ...state.query };
26
- state.query['messages.' + chatId] = { ...state.query['messages.' + chatId] };
27
- const value: QueryState<MessageState[]> = state.query['messages.' + chatId];
28
- value.data = value.data?.filter((x) => x.type !== 'more');
29
- value.data = [
30
- ...value.data!,
31
- ...result.map((x) => ({ type: 'message' as const, ...x, proceed: true })),
32
- ];
33
-
34
- if (result.length === 15) {
35
- value.data = [...value.data!, { type: 'more' }];
36
- }
37
-
38
- return state;
39
- });
40
- },
41
- });
42
- }
1
+ import { useClient } from '../ChatifyContext';
2
+ import { useMutation } from './useMutation';
3
+ import { MessageState } from './useMessages';
4
+ import { useStateRef } from './reducer';
5
+ import { QueryState } from './state';
6
+
7
+ export function useLoadMore({ chatId }: { chatId: string }) {
8
+ const state = useStateRef();
9
+ const client = useClient();
10
+
11
+ return useMutation({
12
+ key: 'more.' + chatId,
13
+ queryFn: () =>
14
+ client.messages({
15
+ chatId,
16
+ skip: state.current.query['messages.' + chatId].data.filter((x: MessageState) => x.type === 'message')
17
+ .length,
18
+ take: 15,
19
+ }),
20
+
21
+ onQueryStarted: async (_, { promise, updateState }) => {
22
+ const result = await promise;
23
+ updateState((state) => {
24
+ state = { ...state };
25
+ state.query = { ...state.query };
26
+ state.query['messages.' + chatId] = { ...state.query['messages.' + chatId] };
27
+ const value: QueryState<MessageState[]> = state.query['messages.' + chatId];
28
+ value.data = value.data?.filter((x) => x.type !== 'more');
29
+ value.data = [
30
+ ...value.data!,
31
+ ...result.map((x) => ({ type: 'message' as const, ...x, proceed: true })),
32
+ ];
33
+
34
+ if (result.length === 15) {
35
+ value.data = [...value.data!, { type: 'more' }];
36
+ }
37
+
38
+ return state;
39
+ });
40
+ },
41
+ });
42
+ }
@@ -1,23 +1,23 @@
1
- import { useClient } from '../ChatifyContext';
2
- import { File, Message } from './models';
3
- import { QueryState } from './state';
4
- import { useQuery } from './useQuery';
5
-
6
- export type MessageState =
7
- | ({ type: 'message' } & Message)
8
- | { type: 'more' }
9
- | { type: 'sending'; text: string; files: File[]; requestId: string };
10
-
11
- export function useMessages(args: { chatId: string }): QueryState<MessageState[]> {
12
- const { chatId } = args;
13
- const client = useClient();
14
-
15
- return useQuery({
16
- key: 'messages.' + chatId,
17
- queryFn: () =>
18
- client.messages({ chatId, skip: 0, take: 15 }).then((x) => {
19
- const result = x.map((message) => ({ type: 'message' as const, ...message, proceed: true }));
20
- return x.length === 15 ? [...result, { type: 'more' as const }] : result;
21
- }),
22
- });
23
- }
1
+ import { useClient } from '../ChatifyContext';
2
+ import { File, Message } from './models';
3
+ import { QueryState } from './state';
4
+ import { useQuery } from './useQuery';
5
+
6
+ export type MessageState =
7
+ | ({ type: 'message' } & Message)
8
+ | { type: 'more' }
9
+ | { type: 'sending'; text: string; files: File[]; requestId: string };
10
+
11
+ export function useMessages(args: { chatId: string }): QueryState<MessageState[]> {
12
+ const { chatId } = args;
13
+ const client = useClient();
14
+
15
+ return useQuery({
16
+ key: 'messages.' + chatId,
17
+ queryFn: () =>
18
+ client.messages({ chatId, skip: 0, take: 15 }).then((x) => {
19
+ const result = x.map((message) => ({ type: 'message' as const, ...message, proceed: true }));
20
+ return x.length === 15 ? [...result, { type: 'more' as const }] : result;
21
+ }),
22
+ });
23
+ }
@@ -1,48 +1,48 @@
1
- import { useCallback } from 'react';
2
- import { useDispatch, useSelect, useStateRef } from './reducer';
3
- import { MutationState, State } from './state';
4
-
5
- export interface MutationApi<TResult> {
6
- promise: Promise<TResult>;
7
- state: State;
8
- updateState: (mutation: (state: State) => State) => void;
9
- }
10
-
11
- export interface UseMutationArgs<TResult = unknown, TArgs = void> {
12
- key: string;
13
- queryFn: (args: TArgs) => Promise<TResult>;
14
- onQueryStarted?: (args: TArgs, api: MutationApi<TResult>) => void;
15
- }
16
-
17
- const INITIAL_MUTATION_STATE: MutationState = { isFetching: false, error: undefined };
18
-
19
- export function useMutation<TResult = unknown, TArgs = void>(args: UseMutationArgs<TResult, TArgs>) {
20
- const { key, queryFn, onQueryStarted } = args;
21
- const dispatch = useDispatch();
22
- const state = useStateRef();
23
- const mutationState = useSelect((x) => x.mutation[args.key], []);
24
-
25
- const invoke = useCallback((args: TArgs) => {
26
- dispatch({ type: 'mutation_fetch', data: { key } });
27
- const promise = queryFn(args);
28
-
29
- promise
30
- .then(() => {
31
- dispatch({ type: 'mutation_resolve', data: { key } });
32
- })
33
- .catch((error) => dispatch({ type: 'mutation_reject', data: { key, error } }));
34
-
35
- onQueryStarted &&
36
- onQueryStarted(args, {
37
- promise,
38
- state: state.current,
39
- updateState: (mutation: (state: State) => State) => {
40
- dispatch({ type: 'update', data: { mutation } });
41
- },
42
- });
43
-
44
- // eslint-disable-next-line react-hooks/exhaustive-deps
45
- }, []);
46
-
47
- return [invoke, mutationState ?? INITIAL_MUTATION_STATE] as const;
48
- }
1
+ import { useCallback } from 'react';
2
+ import { useDispatch, useSelect, useStateRef } from './reducer';
3
+ import { MutationState, State } from './state';
4
+
5
+ export interface MutationApi<TResult> {
6
+ promise: Promise<TResult>;
7
+ state: State;
8
+ updateState: (mutation: (state: State) => State) => void;
9
+ }
10
+
11
+ export interface UseMutationArgs<TResult = unknown, TArgs = void> {
12
+ key: string;
13
+ queryFn: (args: TArgs) => Promise<TResult>;
14
+ onQueryStarted?: (args: TArgs, api: MutationApi<TResult>) => void;
15
+ }
16
+
17
+ const INITIAL_MUTATION_STATE: MutationState = { isFetching: false, error: undefined };
18
+
19
+ export function useMutation<TResult = unknown, TArgs = void>(args: UseMutationArgs<TResult, TArgs>) {
20
+ const { key, queryFn, onQueryStarted } = args;
21
+ const dispatch = useDispatch();
22
+ const state = useStateRef();
23
+ const mutationState = useSelect((x) => x.mutation[args.key], []);
24
+
25
+ const invoke = useCallback((args: TArgs) => {
26
+ dispatch({ type: 'mutation_fetch', data: { key } });
27
+ const promise = queryFn(args);
28
+
29
+ promise
30
+ .then(() => {
31
+ dispatch({ type: 'mutation_resolve', data: { key } });
32
+ })
33
+ .catch((error) => dispatch({ type: 'mutation_reject', data: { key, error } }));
34
+
35
+ onQueryStarted &&
36
+ onQueryStarted(args, {
37
+ promise,
38
+ state: state.current,
39
+ updateState: (mutation: (state: State) => State) => {
40
+ dispatch({ type: 'update', data: { mutation } });
41
+ },
42
+ });
43
+
44
+ // eslint-disable-next-line react-hooks/exhaustive-deps
45
+ }, []);
46
+
47
+ return [invoke, mutationState ?? INITIAL_MUTATION_STATE] as const;
48
+ }
@@ -1,45 +1,45 @@
1
- import { useEffect } from 'react';
2
- import { useDispatch, useSelect, useStateRef } from './reducer';
3
- import { QueryState, State } from './state';
4
-
5
- interface UseQueryArgs<TData> {
6
- key: string;
7
- queryFn: () => Promise<TData>;
8
- }
9
-
10
- const INITIAL_QUERY_STATE: QueryState<any> = {
11
- uninitialized: false,
12
- isFetching: true,
13
- data: undefined,
14
- error: undefined,
15
- };
16
-
17
- function select<TData>(state: State, key: string): QueryState<TData> | undefined {
18
- return (state.query as any)[key];
19
- }
20
-
21
- function useQueryState<TData>(key: string): QueryState<TData> | undefined {
22
- return useSelect((x) => select<TData>(x, key), []);
23
- }
24
-
25
- export function useQuery<TData>(args: UseQueryArgs<TData>): QueryState<TData> {
26
- const { key, queryFn } = args;
27
- const state = useStateRef();
28
- const dispatch = useDispatch();
29
- const queryState = useQueryState<TData>(args.key);
30
-
31
- useEffect(() => {
32
- if (select(state.current, key)) {
33
- return;
34
- }
35
-
36
- dispatch({ type: 'query_fetch', data: { key } });
37
- queryFn()
38
- .then((data) => dispatch({ type: 'query_resolve', data: { key, data: data as any } }))
39
- .catch((error) => dispatch({ type: 'query_reject', data: { key, error } }));
40
-
41
- // eslint-disable-next-line react-hooks/exhaustive-deps
42
- }, []);
43
-
44
- return queryState ?? INITIAL_QUERY_STATE;
45
- }
1
+ import { useEffect } from 'react';
2
+ import { useDispatch, useSelect, useStateRef } from './reducer';
3
+ import { QueryState, State } from './state';
4
+
5
+ interface UseQueryArgs<TData> {
6
+ key: string;
7
+ queryFn: () => Promise<TData>;
8
+ }
9
+
10
+ const INITIAL_QUERY_STATE: QueryState<any> = {
11
+ uninitialized: false,
12
+ isFetching: true,
13
+ data: undefined,
14
+ error: undefined,
15
+ };
16
+
17
+ function select<TData>(state: State, key: string): QueryState<TData> | undefined {
18
+ return (state.query as any)[key];
19
+ }
20
+
21
+ function useQueryState<TData>(key: string): QueryState<TData> | undefined {
22
+ return useSelect((x) => select<TData>(x, key), []);
23
+ }
24
+
25
+ export function useQuery<TData>(args: UseQueryArgs<TData>): QueryState<TData> {
26
+ const { key, queryFn } = args;
27
+ const state = useStateRef();
28
+ const dispatch = useDispatch();
29
+ const queryState = useQueryState<TData>(args.key);
30
+
31
+ useEffect(() => {
32
+ if (select(state.current, key)) {
33
+ return;
34
+ }
35
+
36
+ dispatch({ type: 'query_fetch', data: { key } });
37
+ queryFn()
38
+ .then((data) => dispatch({ type: 'query_resolve', data: { key, data: data as any } }))
39
+ .catch((error) => dispatch({ type: 'query_reject', data: { key, error } }));
40
+
41
+ // eslint-disable-next-line react-hooks/exhaustive-deps
42
+ }, []);
43
+
44
+ return queryState ?? INITIAL_QUERY_STATE;
45
+ }
@@ -1,31 +1,31 @@
1
- import { useEffect, useRef } from 'react';
2
- import { useClient } from '../ChatifyContext';
3
- import { useDispatch, useSelect } from './reducer';
4
-
5
- export function useReadMonitor() {
6
- const client = useClient();
7
- const [queued, timestamp] = useSelect((x) => [x.read.queued, x.read.timestamp], []);
8
- const dispatch = useDispatch();
9
- const timer = useRef<NodeJS.Timeout | null>(null);
10
-
11
- useEffect(() => {
12
- if (queued.length === 0) {
13
- return;
14
- }
15
-
16
- if (timer.current) {
17
- clearTimeout(timer.current);
18
- }
19
-
20
- timer.current = setTimeout(() => {
21
- dispatch({ type: 'read_send', data: { ids: queued } });
22
- client
23
- .read({ ids: queued })
24
- .catch(() =>
25
- dispatch({ type: 'read_reject', data: { ids: queued, timestamp: new Date().getTime() } }),
26
- );
27
- }, 100);
28
-
29
- // eslint-disable-next-line react-hooks/exhaustive-deps
30
- }, [timestamp]);
31
- }
1
+ import { useEffect, useRef } from 'react';
2
+ import { useClient } from '../ChatifyContext';
3
+ import { useDispatch, useSelect } from './reducer';
4
+
5
+ export function useReadMonitor() {
6
+ const client = useClient();
7
+ const [queued, timestamp] = useSelect((x) => [x.read.queued, x.read.timestamp], []);
8
+ const dispatch = useDispatch();
9
+ const timer = useRef<NodeJS.Timeout | null>(null);
10
+
11
+ useEffect(() => {
12
+ if (queued.length === 0) {
13
+ return;
14
+ }
15
+
16
+ if (timer.current) {
17
+ clearTimeout(timer.current);
18
+ }
19
+
20
+ timer.current = setTimeout(() => {
21
+ dispatch({ type: 'read_send', data: { ids: queued } });
22
+ client
23
+ .read({ ids: queued })
24
+ .catch(() =>
25
+ dispatch({ type: 'read_reject', data: { ids: queued, timestamp: new Date().getTime() } }),
26
+ );
27
+ }, 100);
28
+
29
+ // eslint-disable-next-line react-hooks/exhaustive-deps
30
+ }, [timestamp]);
31
+ }