@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
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { useClient } from '../ChatifyContext';
|
|
2
|
-
import { RemoveMemberRequest } from './models';
|
|
3
|
-
import { useMutation } from './useMutation';
|
|
4
|
-
|
|
5
|
-
export function useRemoveMember() {
|
|
6
|
-
const client = useClient();
|
|
7
|
-
|
|
8
|
-
return useMutation({
|
|
9
|
-
key: 'remove-member',
|
|
10
|
-
queryFn: (args: RemoveMemberRequest) => client.removeMember(args),
|
|
11
|
-
});
|
|
12
|
-
}
|
|
1
|
+
import { useClient } from '../ChatifyContext';
|
|
2
|
+
import { RemoveMemberRequest } from './models';
|
|
3
|
+
import { useMutation } from './useMutation';
|
|
4
|
+
|
|
5
|
+
export function useRemoveMember() {
|
|
6
|
+
const client = useClient();
|
|
7
|
+
|
|
8
|
+
return useMutation({
|
|
9
|
+
key: 'remove-member',
|
|
10
|
+
queryFn: (args: RemoveMemberRequest) => client.removeMember(args),
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
import { useDispatch } from './reducer';
|
|
3
|
-
import { Account, ChatListItem, Message, ReadEvent } from './models';
|
|
4
|
-
import { ChatifyClient } from './client';
|
|
5
|
-
|
|
6
|
-
export function useSignalRMonitor(me: string, client: ChatifyClient) {
|
|
7
|
-
const dispatch = useDispatch();
|
|
8
|
-
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
let unsubscribes: Array<() => void> = [];
|
|
11
|
-
|
|
12
|
-
client.connect().then(() => {
|
|
13
|
-
unsubscribes.push(
|
|
14
|
-
client.subscribe('chatify://new-message', ([message, requestId]: [Message, string]) => {
|
|
15
|
-
dispatch({ type: 'message_received', data: { message, requestId } });
|
|
16
|
-
}),
|
|
17
|
-
);
|
|
18
|
-
unsubscribes.push(
|
|
19
|
-
client.subscribe('chatify://chat-created', ([chat, requestId]: [ChatListItem, string]) => {
|
|
20
|
-
dispatch({ type: 'chat_created', data: { chat, requestId } });
|
|
21
|
-
}),
|
|
22
|
-
);
|
|
23
|
-
unsubscribes.push(
|
|
24
|
-
client.subscribe('chatify://read', ([events]: [ReadEvent[]]) => {
|
|
25
|
-
dispatch({ type: 'read_received', data: { events } });
|
|
26
|
-
}),
|
|
27
|
-
);
|
|
28
|
-
unsubscribes.push(
|
|
29
|
-
client.subscribe(
|
|
30
|
-
'chatify://member-added',
|
|
31
|
-
([chatId, chatName, account, message]: [string, string, Account, Message]) => {
|
|
32
|
-
dispatch({ type: 'member_added', data: { chatId, chatName, account, me, message } });
|
|
33
|
-
},
|
|
34
|
-
),
|
|
35
|
-
);
|
|
36
|
-
unsubscribes.push(
|
|
37
|
-
client.subscribe(
|
|
38
|
-
'chatify://member-removed',
|
|
39
|
-
([chatId, accountId, deleteHistory]: [string, string, boolean]) => {
|
|
40
|
-
dispatch({ type: 'member_removed', data: { chatId, accountId, deleteHistory, me } });
|
|
41
|
-
},
|
|
42
|
-
),
|
|
43
|
-
);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
return () => {
|
|
47
|
-
unsubscribes.forEach((x) => x());
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
51
|
-
}, []);
|
|
52
|
-
}
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useDispatch } from './reducer';
|
|
3
|
+
import { Account, ChatListItem, Message, ReadEvent } from './models';
|
|
4
|
+
import { ChatifyClient } from './client';
|
|
5
|
+
|
|
6
|
+
export function useSignalRMonitor(me: string, client: ChatifyClient) {
|
|
7
|
+
const dispatch = useDispatch();
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
let unsubscribes: Array<() => void> = [];
|
|
11
|
+
|
|
12
|
+
client.connect().then(() => {
|
|
13
|
+
unsubscribes.push(
|
|
14
|
+
client.subscribe('chatify://new-message', ([message, requestId]: [Message, string]) => {
|
|
15
|
+
dispatch({ type: 'message_received', data: { message, requestId } });
|
|
16
|
+
}),
|
|
17
|
+
);
|
|
18
|
+
unsubscribes.push(
|
|
19
|
+
client.subscribe('chatify://chat-created', ([chat, requestId]: [ChatListItem, string]) => {
|
|
20
|
+
dispatch({ type: 'chat_created', data: { chat, requestId } });
|
|
21
|
+
}),
|
|
22
|
+
);
|
|
23
|
+
unsubscribes.push(
|
|
24
|
+
client.subscribe('chatify://read', ([events]: [ReadEvent[]]) => {
|
|
25
|
+
dispatch({ type: 'read_received', data: { events } });
|
|
26
|
+
}),
|
|
27
|
+
);
|
|
28
|
+
unsubscribes.push(
|
|
29
|
+
client.subscribe(
|
|
30
|
+
'chatify://member-added',
|
|
31
|
+
([chatId, chatName, account, message]: [string, string, Account, Message]) => {
|
|
32
|
+
dispatch({ type: 'member_added', data: { chatId, chatName, account, me, message } });
|
|
33
|
+
},
|
|
34
|
+
),
|
|
35
|
+
);
|
|
36
|
+
unsubscribes.push(
|
|
37
|
+
client.subscribe(
|
|
38
|
+
'chatify://member-removed',
|
|
39
|
+
([chatId, accountId, deleteHistory]: [string, string, boolean]) => {
|
|
40
|
+
dispatch({ type: 'member_removed', data: { chatId, accountId, deleteHistory, me } });
|
|
41
|
+
},
|
|
42
|
+
),
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return () => {
|
|
47
|
+
unsubscribes.forEach((x) => x());
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
51
|
+
}, []);
|
|
52
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './ui';
|
|
2
|
-
export * from './core';
|
|
3
|
-
export * from './ChatifyContext';
|
|
1
|
+
export * from './ui';
|
|
2
|
+
export * from './core';
|
|
3
|
+
export * from './ChatifyContext';
|
package/src/ui/AddChat.tsx
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
import { Button, Form, Input, Select } from 'antd';
|
|
2
|
-
import { useAccounts, useAddChat } from '../core';
|
|
3
|
-
import { useCallback, useMemo } from 'react';
|
|
4
|
-
import { uniqId } from '../util';
|
|
5
|
-
import { useLocalizer } from './localizer';
|
|
6
|
-
|
|
7
|
-
type FormValue = {
|
|
8
|
-
name: string;
|
|
9
|
-
members: string[];
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export function AddChat() {
|
|
13
|
-
const [add, { isFetching: adding }] = useAddChat();
|
|
14
|
-
const { data: accounts, isFetching } = useAccounts();
|
|
15
|
-
const localizer = useLocalizer();
|
|
16
|
-
const options = useMemo(() => accounts?.map((x) => ({ value: x.id, label: x.name })), [accounts]);
|
|
17
|
-
|
|
18
|
-
const onSubmit = useCallback(
|
|
19
|
-
(value: FormValue) =>
|
|
20
|
-
add({
|
|
21
|
-
name: value.name,
|
|
22
|
-
members: value.members,
|
|
23
|
-
requestId: uniqId(),
|
|
24
|
-
}),
|
|
25
|
-
[add],
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<div className="wxchtf-add-chat">
|
|
30
|
-
<h3>{localizer.addChat.title()}</h3>
|
|
31
|
-
<Form onFinish={onSubmit} layout="vertical" disabled={adding}>
|
|
32
|
-
<Form.Item
|
|
33
|
-
name="name"
|
|
34
|
-
label={localizer.addChat.name.label()}
|
|
35
|
-
rules={[
|
|
36
|
-
{ required: true, message: localizer.addChat.name.required() },
|
|
37
|
-
{ max: 250, message: localizer.addChat.name.max250() },
|
|
38
|
-
]}
|
|
39
|
-
>
|
|
40
|
-
<Input />
|
|
41
|
-
</Form.Item>
|
|
42
|
-
<Form.Item
|
|
43
|
-
name="members"
|
|
44
|
-
label={localizer.addChat.members.label()}
|
|
45
|
-
rules={[{ required: true, message: localizer.addChat.members.required() }]}
|
|
46
|
-
>
|
|
47
|
-
<Select mode="multiple" loading={isFetching} options={options} />
|
|
48
|
-
</Form.Item>
|
|
49
|
-
<Button loading={adding} type="primary" block htmlType="submit">
|
|
50
|
-
{localizer.addChat.submitBtn()}
|
|
51
|
-
</Button>
|
|
52
|
-
</Form>
|
|
53
|
-
</div>
|
|
54
|
-
);
|
|
55
|
-
}
|
|
1
|
+
import { Button, Form, Input, Select } from 'antd';
|
|
2
|
+
import { useAccounts, useAddChat } from '../core';
|
|
3
|
+
import { useCallback, useMemo } from 'react';
|
|
4
|
+
import { uniqId } from '../util';
|
|
5
|
+
import { useLocalizer } from './localizer';
|
|
6
|
+
|
|
7
|
+
type FormValue = {
|
|
8
|
+
name: string;
|
|
9
|
+
members: string[];
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function AddChat() {
|
|
13
|
+
const [add, { isFetching: adding }] = useAddChat();
|
|
14
|
+
const { data: accounts, isFetching } = useAccounts();
|
|
15
|
+
const localizer = useLocalizer();
|
|
16
|
+
const options = useMemo(() => accounts?.map((x) => ({ value: x.id, label: x.name })), [accounts]);
|
|
17
|
+
|
|
18
|
+
const onSubmit = useCallback(
|
|
19
|
+
(value: FormValue) =>
|
|
20
|
+
add({
|
|
21
|
+
name: value.name,
|
|
22
|
+
members: value.members,
|
|
23
|
+
requestId: uniqId(),
|
|
24
|
+
}),
|
|
25
|
+
[add],
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className="wxchtf-add-chat">
|
|
30
|
+
<h3>{localizer.addChat.title()}</h3>
|
|
31
|
+
<Form onFinish={onSubmit} layout="vertical" disabled={adding}>
|
|
32
|
+
<Form.Item
|
|
33
|
+
name="name"
|
|
34
|
+
label={localizer.addChat.name.label()}
|
|
35
|
+
rules={[
|
|
36
|
+
{ required: true, message: localizer.addChat.name.required() },
|
|
37
|
+
{ max: 250, message: localizer.addChat.name.max250() },
|
|
38
|
+
]}
|
|
39
|
+
>
|
|
40
|
+
<Input />
|
|
41
|
+
</Form.Item>
|
|
42
|
+
<Form.Item
|
|
43
|
+
name="members"
|
|
44
|
+
label={localizer.addChat.members.label()}
|
|
45
|
+
rules={[{ required: true, message: localizer.addChat.members.required() }]}
|
|
46
|
+
>
|
|
47
|
+
<Select mode="multiple" loading={isFetching} options={options} />
|
|
48
|
+
</Form.Item>
|
|
49
|
+
<Button loading={adding} type="primary" block htmlType="submit">
|
|
50
|
+
{localizer.addChat.submitBtn()}
|
|
51
|
+
</Button>
|
|
52
|
+
</Form>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
package/src/ui/AddChatButton.tsx
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { Button } from 'antd';
|
|
2
|
-
import { useDispatch } from '../core';
|
|
3
|
-
import { FC, useCallback } from 'react';
|
|
4
|
-
import { customize } from '../util';
|
|
5
|
-
|
|
6
|
-
const _AddChatButton: FC = () => {
|
|
7
|
-
const dispatch = useDispatch();
|
|
8
|
-
const onAddChat = useCallback(() => dispatch({ type: 'new_chat_open', data: undefined }), [dispatch]);
|
|
9
|
-
|
|
10
|
-
return (
|
|
11
|
-
<Button onClick={onAddChat} icon={'+'} type="link">
|
|
12
|
-
Add chat
|
|
13
|
-
</Button>
|
|
14
|
-
);
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
_AddChatButton.displayName = 'AddChatButton';
|
|
18
|
-
|
|
19
|
-
export const AddChatButton = customize('AddChatButton', _AddChatButton);
|
|
1
|
+
import { Button } from 'antd';
|
|
2
|
+
import { useDispatch } from '../core';
|
|
3
|
+
import { FC, useCallback } from 'react';
|
|
4
|
+
import { customize } from '../util';
|
|
5
|
+
|
|
6
|
+
const _AddChatButton: FC = () => {
|
|
7
|
+
const dispatch = useDispatch();
|
|
8
|
+
const onAddChat = useCallback(() => dispatch({ type: 'new_chat_open', data: undefined }), [dispatch]);
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<Button onClick={onAddChat} icon={'+'} type="link">
|
|
12
|
+
Add chat
|
|
13
|
+
</Button>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
_AddChatButton.displayName = 'AddChatButton';
|
|
18
|
+
|
|
19
|
+
export const AddChatButton = customize('AddChatButton', _AddChatButton);
|
package/src/ui/Aside.tsx
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { Menu, MenuProps } from 'antd';
|
|
2
|
-
import { useCallback, useEffect, useMemo } from 'react';
|
|
3
|
-
import type { MenuItemType } from 'antd/es/menu/hooks/useItems';
|
|
4
|
-
import { useChatList, useDispatch, useSelect } from '../core';
|
|
5
|
-
import { ChatListItemBox } from './ChatListItem';
|
|
6
|
-
|
|
7
|
-
export function Aside() {
|
|
8
|
-
const activeChatId = useSelect((x) => x.ui.chatId, []);
|
|
9
|
-
const dispatch = useDispatch();
|
|
10
|
-
const selected = useMemo(() => (activeChatId ? [activeChatId] : []), [activeChatId]);
|
|
11
|
-
|
|
12
|
-
const onSelect = useCallback(
|
|
13
|
-
(info: Parameters<NonNullable<MenuProps['onSelect']>>[0]) =>
|
|
14
|
-
dispatch({ type: 'chat_open', data: { chatId: info.selectedKeys[0] } }),
|
|
15
|
-
[dispatch],
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
const { data: chats } = useChatList();
|
|
19
|
-
|
|
20
|
-
const items = useMemo<MenuItemType[]>(() => {
|
|
21
|
-
if (!chats) {
|
|
22
|
-
return [];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return chats.map((x) => ({ key: x.id, label: <ChatListItemBox chat={x} /> }));
|
|
26
|
-
}, [chats]);
|
|
27
|
-
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
if (!activeChatId && chats && chats.length > 0) {
|
|
30
|
-
dispatch({ type: 'chat_open', data: { chatId: chats[0].id } });
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
34
|
-
}, [chats, dispatch]);
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<div className="wxchtf-aside">
|
|
38
|
-
{chats && <Menu className="wxchtf-chats" selectedKeys={selected} onSelect={onSelect} items={items} />}
|
|
39
|
-
</div>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
1
|
+
import { Menu, MenuProps } from 'antd';
|
|
2
|
+
import { useCallback, useEffect, useMemo } from 'react';
|
|
3
|
+
import type { MenuItemType } from 'antd/es/menu/hooks/useItems';
|
|
4
|
+
import { useChatList, useDispatch, useSelect } from '../core';
|
|
5
|
+
import { ChatListItemBox } from './ChatListItem';
|
|
6
|
+
|
|
7
|
+
export function Aside() {
|
|
8
|
+
const activeChatId = useSelect((x) => x.ui.chatId, []);
|
|
9
|
+
const dispatch = useDispatch();
|
|
10
|
+
const selected = useMemo(() => (activeChatId ? [activeChatId] : []), [activeChatId]);
|
|
11
|
+
|
|
12
|
+
const onSelect = useCallback(
|
|
13
|
+
(info: Parameters<NonNullable<MenuProps['onSelect']>>[0]) =>
|
|
14
|
+
dispatch({ type: 'chat_open', data: { chatId: info.selectedKeys[0] } }),
|
|
15
|
+
[dispatch],
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const { data: chats } = useChatList();
|
|
19
|
+
|
|
20
|
+
const items = useMemo<MenuItemType[]>(() => {
|
|
21
|
+
if (!chats) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return chats.map((x) => ({ key: x.id, label: <ChatListItemBox chat={x} /> }));
|
|
26
|
+
}, [chats]);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!activeChatId && chats && chats.length > 0) {
|
|
30
|
+
dispatch({ type: 'chat_open', data: { chatId: chats[0].id } });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
34
|
+
}, [chats, dispatch]);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className="wxchtf-aside">
|
|
38
|
+
{chats && <Menu className="wxchtf-chats" selectedKeys={selected} onSelect={onSelect} items={items} />}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
package/src/ui/Avatar.tsx
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { FC } from 'react';
|
|
2
|
-
import { Account } from '../core';
|
|
3
|
-
import { Avatar as AntdAvatar } from 'antd';
|
|
4
|
-
import { customize } from '../util';
|
|
5
|
-
|
|
6
|
-
export interface AvatarProps {
|
|
7
|
-
account: Account;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const _Avatar: FC<AvatarProps> = (props: AvatarProps) => {
|
|
11
|
-
const { account } = props;
|
|
12
|
-
|
|
13
|
-
return <AntdAvatar src={account.avatar} />;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
_Avatar.displayName = 'Avatar';
|
|
17
|
-
|
|
18
|
-
export const Avatar = customize('Avatar', _Avatar);
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { Account } from '../core';
|
|
3
|
+
import { Avatar as AntdAvatar } from 'antd';
|
|
4
|
+
import { customize } from '../util';
|
|
5
|
+
|
|
6
|
+
export interface AvatarProps {
|
|
7
|
+
account: Account;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const _Avatar: FC<AvatarProps> = (props: AvatarProps) => {
|
|
11
|
+
const { account } = props;
|
|
12
|
+
|
|
13
|
+
return <AntdAvatar src={account.avatar} />;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
_Avatar.displayName = 'Avatar';
|
|
17
|
+
|
|
18
|
+
export const Avatar = customize('Avatar', _Avatar);
|
package/src/ui/Chat.tsx
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import { ChatHeader } from './ChatHeader';
|
|
2
|
-
import { ChatBody } from './ChatBody';
|
|
3
|
-
import { InputForm } from './InputForm';
|
|
4
|
-
import { useChat } from '../core';
|
|
5
|
-
import { useMe } from '../ChatifyContext';
|
|
6
|
-
|
|
7
|
-
export interface ChatProps {
|
|
8
|
-
id: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function Chat(props: ChatProps) {
|
|
12
|
-
const { id } = props;
|
|
13
|
-
const me = useMe();
|
|
14
|
-
const { data: chat } = useChat({ chatId: id });
|
|
15
|
-
const isMember = chat?.members.map((x) => x.id).includes(me) === true;
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<>
|
|
19
|
-
<div className="wxchtf-chat">
|
|
20
|
-
<ChatHeader id={id} />
|
|
21
|
-
<ChatBody id={id} />
|
|
22
|
-
</div>
|
|
23
|
-
{isMember && <InputForm />}
|
|
24
|
-
</>
|
|
25
|
-
);
|
|
26
|
-
}
|
|
1
|
+
import { ChatHeader } from './ChatHeader';
|
|
2
|
+
import { ChatBody } from './ChatBody';
|
|
3
|
+
import { InputForm } from './InputForm';
|
|
4
|
+
import { useChat } from '../core';
|
|
5
|
+
import { useMe } from '../ChatifyContext';
|
|
6
|
+
|
|
7
|
+
export interface ChatProps {
|
|
8
|
+
id: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function Chat(props: ChatProps) {
|
|
12
|
+
const { id } = props;
|
|
13
|
+
const me = useMe();
|
|
14
|
+
const { data: chat } = useChat({ chatId: id });
|
|
15
|
+
const isMember = chat?.members.map((x) => x.id).includes(me) === true;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<div className="wxchtf-chat">
|
|
20
|
+
<ChatHeader id={id} />
|
|
21
|
+
<ChatBody id={id} />
|
|
22
|
+
</div>
|
|
23
|
+
{isMember && <InputForm />}
|
|
24
|
+
</>
|
|
25
|
+
);
|
|
26
|
+
}
|
package/src/ui/ChatBody.tsx
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
import { MessageBox } from './Message';
|
|
2
|
-
import { useMessages } from '../core';
|
|
3
|
-
import { LoadMore } from './LoadMore';
|
|
4
|
-
import { SendingMessage } from './SendingMessage';
|
|
5
|
-
import { SystemMessage } from './SystemMessage';
|
|
6
|
-
import { MessageSkeleton } from './MessageSkeleton';
|
|
7
|
-
|
|
8
|
-
export interface ChatBodyProps {
|
|
9
|
-
id: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function ChatBody(props: ChatBodyProps) {
|
|
13
|
-
const { id } = props;
|
|
14
|
-
const { data: messages } = useMessages({ chatId: id });
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<div className="wxchtf-chat-body">
|
|
18
|
-
{!messages && <MessageSkeleton count={5} />}
|
|
19
|
-
{messages?.map((x, index) => {
|
|
20
|
-
if (x.type === 'message' && x.sentBy.id === 'chatify::system') {
|
|
21
|
-
return <SystemMessage text={x.text} sentAt={x.sentAt} />;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
switch (x.type) {
|
|
25
|
-
case 'message': {
|
|
26
|
-
return <MessageBox message={x} key={x.id} />;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
case 'sending': {
|
|
30
|
-
return <SendingMessage key={x.requestId} text={x.text} files={x.files} />;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
case 'more': {
|
|
34
|
-
return <LoadMore key={index} chatId={id} />;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
default:
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
})}
|
|
41
|
-
</div>
|
|
42
|
-
);
|
|
43
|
-
}
|
|
1
|
+
import { MessageBox } from './Message';
|
|
2
|
+
import { useMessages } from '../core';
|
|
3
|
+
import { LoadMore } from './LoadMore';
|
|
4
|
+
import { SendingMessage } from './SendingMessage';
|
|
5
|
+
import { SystemMessage } from './SystemMessage';
|
|
6
|
+
import { MessageSkeleton } from './MessageSkeleton';
|
|
7
|
+
|
|
8
|
+
export interface ChatBodyProps {
|
|
9
|
+
id: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function ChatBody(props: ChatBodyProps) {
|
|
13
|
+
const { id } = props;
|
|
14
|
+
const { data: messages } = useMessages({ chatId: id });
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className="wxchtf-chat-body">
|
|
18
|
+
{!messages && <MessageSkeleton count={5} />}
|
|
19
|
+
{messages?.map((x, index) => {
|
|
20
|
+
if (x.type === 'message' && x.sentBy.id === 'chatify::system') {
|
|
21
|
+
return <SystemMessage text={x.text} sentAt={x.sentAt} />;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
switch (x.type) {
|
|
25
|
+
case 'message': {
|
|
26
|
+
return <MessageBox message={x} key={x.id} />;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
case 'sending': {
|
|
30
|
+
return <SendingMessage key={x.requestId} text={x.text} files={x.files} />;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
case 'more': {
|
|
34
|
+
return <LoadMore key={index} chatId={id} />;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
default:
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
})}
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
package/src/ui/ChatHeader.tsx
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { useChat, useSelect, ChatListItem } from '../core';
|
|
2
|
-
import { Col, Row, Space } from 'antd';
|
|
3
|
-
import { ChatHeaderMembers } from './ChatHeaderMembers';
|
|
4
|
-
import { ChatSettingsButton } from './ChatHeaderSettingsButton';
|
|
5
|
-
import { ChatName } from './ChatName';
|
|
6
|
-
|
|
7
|
-
export interface ChatHeaderProps {
|
|
8
|
-
id: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function ChatHeader(props: ChatHeaderProps) {
|
|
12
|
-
const { id } = props;
|
|
13
|
-
const { data: chat } = useChat({ chatId: id });
|
|
14
|
-
const chatListItem = useSelect(
|
|
15
|
-
(x) => x.query['chat-list'].data?.find((x: ChatListItem) => x.id === id),
|
|
16
|
-
[id],
|
|
17
|
-
);
|
|
18
|
-
const name = chat?.name ?? chatListItem?.name;
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<div className="wxchtf-chat-header">
|
|
22
|
-
<Row justify="space-between" align="middle">
|
|
23
|
-
<Col className="wxchtf-chat-name">{name && <ChatName name={name} />}</Col>
|
|
24
|
-
<Col className="wxchtf-chat-members">
|
|
25
|
-
<Space align="center">
|
|
26
|
-
<ChatHeaderMembers id={id} />
|
|
27
|
-
<ChatSettingsButton id={id} />
|
|
28
|
-
</Space>
|
|
29
|
-
</Col>
|
|
30
|
-
</Row>
|
|
31
|
-
</div>
|
|
32
|
-
);
|
|
33
|
-
}
|
|
1
|
+
import { useChat, useSelect, ChatListItem } from '../core';
|
|
2
|
+
import { Col, Row, Space } from 'antd';
|
|
3
|
+
import { ChatHeaderMembers } from './ChatHeaderMembers';
|
|
4
|
+
import { ChatSettingsButton } from './ChatHeaderSettingsButton';
|
|
5
|
+
import { ChatName } from './ChatName';
|
|
6
|
+
|
|
7
|
+
export interface ChatHeaderProps {
|
|
8
|
+
id: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function ChatHeader(props: ChatHeaderProps) {
|
|
12
|
+
const { id } = props;
|
|
13
|
+
const { data: chat } = useChat({ chatId: id });
|
|
14
|
+
const chatListItem = useSelect(
|
|
15
|
+
(x) => x.query['chat-list'].data?.find((x: ChatListItem) => x.id === id),
|
|
16
|
+
[id],
|
|
17
|
+
);
|
|
18
|
+
const name = chat?.name ?? chatListItem?.name;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="wxchtf-chat-header">
|
|
22
|
+
<Row justify="space-between" align="middle">
|
|
23
|
+
<Col className="wxchtf-chat-name">{name && <ChatName name={name} />}</Col>
|
|
24
|
+
<Col className="wxchtf-chat-members">
|
|
25
|
+
<Space align="center">
|
|
26
|
+
<ChatHeaderMembers id={id} />
|
|
27
|
+
<ChatSettingsButton id={id} />
|
|
28
|
+
</Space>
|
|
29
|
+
</Col>
|
|
30
|
+
</Row>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|