@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.
- package/package.json +90 -90
- package/src/ChatifyContext.tsx +41 -41
- package/src/core/action.tsx +319 -319
- package/src/core/client.ts +137 -137
- package/src/core/index.ts +14 -14
- package/src/core/models.tsx +75 -75
- package/src/core/reducer.tsx +86 -86
- package/src/core/state.ts +38 -38
- package/src/core/useAccounts.tsx +11 -11
- package/src/core/useAddChat.tsx +12 -12
- package/src/core/useAddMember.tsx +12 -12
- package/src/core/useAddMessage.tsx +40 -40
- package/src/core/useChat.tsx +12 -12
- package/src/core/useChatList.tsx +10 -10
- package/src/core/useLoadMore.tsx +42 -42
- package/src/core/useMessages.tsx +23 -23
- package/src/core/useMutation.tsx +48 -48
- package/src/core/useQuery.tsx +45 -45
- package/src/core/useReadMonitor.tsx +31 -31
- package/src/core/useRemoveMember.tsx +12 -12
- package/src/core/useSignalRMonitor.tsx +52 -52
- package/src/index.ts +3 -3
- package/src/ui/AddChat.tsx +55 -55
- package/src/ui/AddChatButton.tsx +19 -19
- package/src/ui/Aside.tsx +41 -41
- package/src/ui/Avatar.tsx +18 -18
- package/src/ui/Chat.tsx +26 -26
- package/src/ui/ChatBody.tsx +43 -43
- package/src/ui/ChatHeader.tsx +33 -33
- package/src/ui/ChatHeaderMembers.tsx +30 -30
- package/src/ui/ChatHeaderSettingsButton.tsx +26 -26
- package/src/ui/ChatListItem.tsx +101 -101
- package/src/ui/ChatName.tsx +15 -15
- package/src/ui/ChatSettings.tsx +59 -59
- package/src/ui/Chatify.tsx +106 -106
- package/src/ui/Footer.tsx +7 -7
- package/src/ui/Header.tsx +14 -14
- package/src/ui/Icon.tsx +21 -21
- package/src/ui/InputForm.tsx +51 -51
- package/src/ui/LoadMore.tsx +40 -40
- package/src/ui/Main.tsx +16 -16
- package/src/ui/Message.tsx +118 -118
- package/src/ui/MessageSkeleton.tsx +19 -19
- package/src/ui/SendingMessage.tsx +50 -50
- package/src/ui/SystemMessage.tsx +32 -32
- package/src/ui/index.ts +11 -11
- package/src/ui/localizer.tsx +68 -68
- package/src/ui/styles/aside.scss +94 -94
- package/src/ui/styles/index.scss +248 -248
- package/src/ui/styles/keyframes.scss +14 -14
- package/src/ui/styles/mixins.scss +20 -20
- package/src/ui/styles/variables.scss +10 -10
- package/src/util/customize.tsx +45 -45
- package/src/util/index.ts +3 -3
- package/src/util/uniqBy.tsx +17 -17
- 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
|
+
};
|
package/src/core/useAccounts.tsx
CHANGED
|
@@ -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
|
+
}
|
package/src/core/useAddChat.tsx
CHANGED
|
@@ -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
|
+
}
|
package/src/core/useChat.tsx
CHANGED
|
@@ -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
|
+
}
|
package/src/core/useChatList.tsx
CHANGED
|
@@ -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
|
+
}
|
package/src/core/useLoadMore.tsx
CHANGED
|
@@ -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
|
+
}
|
package/src/core/useMessages.tsx
CHANGED
|
@@ -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
|
+
}
|
package/src/core/useMutation.tsx
CHANGED
|
@@ -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
|
+
}
|
package/src/core/useQuery.tsx
CHANGED
|
@@ -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
|
+
}
|