@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/ui/Chatify.tsx
CHANGED
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
import { CSSProperties, FC } from 'react';
|
|
2
|
-
import { useReadMonitor } from '../core';
|
|
3
|
-
import { Aside } from './Aside';
|
|
4
|
-
import { Main } from './Main';
|
|
5
|
-
import { Localizer, LocalizerContext, defaultLocalizer } from './localizer';
|
|
6
|
-
import { Avatar } from './Avatar';
|
|
7
|
-
import { CustomizeProvider } from '../util';
|
|
8
|
-
import { ChatHeaderMembers } from './ChatHeaderMembers';
|
|
9
|
-
import { ChatSettingsButton } from './ChatHeaderSettingsButton';
|
|
10
|
-
import { Header } from './Header';
|
|
11
|
-
import { AddChatButton } from './AddChatButton';
|
|
12
|
-
import { Footer } from './Footer';
|
|
13
|
-
import { ChatName } from './ChatName';
|
|
14
|
-
import {
|
|
15
|
-
ChatListItemBox,
|
|
16
|
-
ChatListItemLastMessage,
|
|
17
|
-
ChatListItemLastMessageAuthor,
|
|
18
|
-
ChatListItemLastMessageContent,
|
|
19
|
-
ChatListItemName,
|
|
20
|
-
ChatListItemUnreadCount,
|
|
21
|
-
} from './ChatListItem';
|
|
22
|
-
import {
|
|
23
|
-
SendingMessage,
|
|
24
|
-
SendingMessageContent,
|
|
25
|
-
SendingMessageInfoBox,
|
|
26
|
-
SendingMessageText,
|
|
27
|
-
} from './SendingMessage';
|
|
28
|
-
import {
|
|
29
|
-
MessageAuthor,
|
|
30
|
-
MessageContent,
|
|
31
|
-
MessageBox,
|
|
32
|
-
MessageInfoBox,
|
|
33
|
-
MessageReadObserver,
|
|
34
|
-
MessageRow,
|
|
35
|
-
MessageText,
|
|
36
|
-
} from './Message';
|
|
37
|
-
import { Icon } from './Icon';
|
|
38
|
-
import { SystemMessage } from './SystemMessage';
|
|
39
|
-
|
|
40
|
-
export interface ChatifyCustomizeValue {
|
|
41
|
-
Avatar?: typeof Avatar.Component | null;
|
|
42
|
-
Header?: typeof Header.Component | null;
|
|
43
|
-
Footer?: typeof Footer.Component | null;
|
|
44
|
-
AddChatButton?: typeof AddChatButton.Component | null;
|
|
45
|
-
ChatHeaderMembers?: typeof ChatHeaderMembers.Component | null;
|
|
46
|
-
ChatSettingsButton?: typeof ChatSettingsButton.Component | null;
|
|
47
|
-
ChatName?: typeof ChatName.Component | null;
|
|
48
|
-
ChatListItemBox?: typeof ChatListItemBox.Component | null;
|
|
49
|
-
ChatListItemName?: typeof ChatListItemName.Component | null;
|
|
50
|
-
ChatListItemUnreadCount?: typeof ChatListItemUnreadCount.Component | null;
|
|
51
|
-
ChatListItemLastMessage?: typeof ChatListItemLastMessage.Component | null;
|
|
52
|
-
ChatListItemLastMessageAuthor?: typeof ChatListItemLastMessageAuthor.Component | null;
|
|
53
|
-
ChatListItemLastMessageContent?: typeof ChatListItemLastMessageContent.Component | null;
|
|
54
|
-
SystemMessage?: typeof SystemMessage.Component | null;
|
|
55
|
-
SendingMessage?: typeof SendingMessage.Component | null;
|
|
56
|
-
SendingMessageBody?: typeof SendingMessageContent.Component | null;
|
|
57
|
-
SendingMessageInfoBox?: typeof SendingMessageInfoBox.Component | null;
|
|
58
|
-
SendingMessageText?: typeof SendingMessageText.Component | null;
|
|
59
|
-
MessageBox?: typeof MessageBox.Component | null;
|
|
60
|
-
MessageReadObserver?: typeof MessageReadObserver.Component | null;
|
|
61
|
-
MessageRow?: typeof MessageRow.Component | null;
|
|
62
|
-
MessageContent?: typeof MessageContent.Component | null;
|
|
63
|
-
MessageInfoBox?: typeof MessageInfoBox.Component | null;
|
|
64
|
-
MessageText?: typeof MessageText.Component | null;
|
|
65
|
-
MessageAuthor?: typeof MessageAuthor.Component | null;
|
|
66
|
-
Icon?: typeof Icon.Component | null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface ChatifyProps {
|
|
70
|
-
className?: string;
|
|
71
|
-
style?: CSSProperties;
|
|
72
|
-
localizer?: Localizer;
|
|
73
|
-
customize?: ChatifyCustomizeValue;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function Content(props: Pick<ChatifyProps, 'className' | 'style'>) {
|
|
77
|
-
const { className = '', style } = props;
|
|
78
|
-
useReadMonitor();
|
|
79
|
-
|
|
80
|
-
return (
|
|
81
|
-
<div className={className + ' wxchtf-chatify'} style={style}>
|
|
82
|
-
<Header />
|
|
83
|
-
<div className="wxchtf-body">
|
|
84
|
-
<Aside />
|
|
85
|
-
<Main />
|
|
86
|
-
</div>
|
|
87
|
-
<Footer />
|
|
88
|
-
</div>
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const EMPTY_CUSTOMIZE = {};
|
|
93
|
-
|
|
94
|
-
export const Chatify: FC<ChatifyProps> = (props) => {
|
|
95
|
-
const { localizer = defaultLocalizer, customize = EMPTY_CUSTOMIZE } = props;
|
|
96
|
-
|
|
97
|
-
return (
|
|
98
|
-
<LocalizerContext.Provider value={localizer}>
|
|
99
|
-
<CustomizeProvider value={customize}>
|
|
100
|
-
<Content {...props} />
|
|
101
|
-
</CustomizeProvider>
|
|
102
|
-
</LocalizerContext.Provider>
|
|
103
|
-
);
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
Chatify.displayName = 'Chatify';
|
|
1
|
+
import { CSSProperties, FC } from 'react';
|
|
2
|
+
import { useReadMonitor } from '../core';
|
|
3
|
+
import { Aside } from './Aside';
|
|
4
|
+
import { Main } from './Main';
|
|
5
|
+
import { Localizer, LocalizerContext, defaultLocalizer } from './localizer';
|
|
6
|
+
import { Avatar } from './Avatar';
|
|
7
|
+
import { CustomizeProvider } from '../util';
|
|
8
|
+
import { ChatHeaderMembers } from './ChatHeaderMembers';
|
|
9
|
+
import { ChatSettingsButton } from './ChatHeaderSettingsButton';
|
|
10
|
+
import { Header } from './Header';
|
|
11
|
+
import { AddChatButton } from './AddChatButton';
|
|
12
|
+
import { Footer } from './Footer';
|
|
13
|
+
import { ChatName } from './ChatName';
|
|
14
|
+
import {
|
|
15
|
+
ChatListItemBox,
|
|
16
|
+
ChatListItemLastMessage,
|
|
17
|
+
ChatListItemLastMessageAuthor,
|
|
18
|
+
ChatListItemLastMessageContent,
|
|
19
|
+
ChatListItemName,
|
|
20
|
+
ChatListItemUnreadCount,
|
|
21
|
+
} from './ChatListItem';
|
|
22
|
+
import {
|
|
23
|
+
SendingMessage,
|
|
24
|
+
SendingMessageContent,
|
|
25
|
+
SendingMessageInfoBox,
|
|
26
|
+
SendingMessageText,
|
|
27
|
+
} from './SendingMessage';
|
|
28
|
+
import {
|
|
29
|
+
MessageAuthor,
|
|
30
|
+
MessageContent,
|
|
31
|
+
MessageBox,
|
|
32
|
+
MessageInfoBox,
|
|
33
|
+
MessageReadObserver,
|
|
34
|
+
MessageRow,
|
|
35
|
+
MessageText,
|
|
36
|
+
} from './Message';
|
|
37
|
+
import { Icon } from './Icon';
|
|
38
|
+
import { SystemMessage } from './SystemMessage';
|
|
39
|
+
|
|
40
|
+
export interface ChatifyCustomizeValue {
|
|
41
|
+
Avatar?: typeof Avatar.Component | null;
|
|
42
|
+
Header?: typeof Header.Component | null;
|
|
43
|
+
Footer?: typeof Footer.Component | null;
|
|
44
|
+
AddChatButton?: typeof AddChatButton.Component | null;
|
|
45
|
+
ChatHeaderMembers?: typeof ChatHeaderMembers.Component | null;
|
|
46
|
+
ChatSettingsButton?: typeof ChatSettingsButton.Component | null;
|
|
47
|
+
ChatName?: typeof ChatName.Component | null;
|
|
48
|
+
ChatListItemBox?: typeof ChatListItemBox.Component | null;
|
|
49
|
+
ChatListItemName?: typeof ChatListItemName.Component | null;
|
|
50
|
+
ChatListItemUnreadCount?: typeof ChatListItemUnreadCount.Component | null;
|
|
51
|
+
ChatListItemLastMessage?: typeof ChatListItemLastMessage.Component | null;
|
|
52
|
+
ChatListItemLastMessageAuthor?: typeof ChatListItemLastMessageAuthor.Component | null;
|
|
53
|
+
ChatListItemLastMessageContent?: typeof ChatListItemLastMessageContent.Component | null;
|
|
54
|
+
SystemMessage?: typeof SystemMessage.Component | null;
|
|
55
|
+
SendingMessage?: typeof SendingMessage.Component | null;
|
|
56
|
+
SendingMessageBody?: typeof SendingMessageContent.Component | null;
|
|
57
|
+
SendingMessageInfoBox?: typeof SendingMessageInfoBox.Component | null;
|
|
58
|
+
SendingMessageText?: typeof SendingMessageText.Component | null;
|
|
59
|
+
MessageBox?: typeof MessageBox.Component | null;
|
|
60
|
+
MessageReadObserver?: typeof MessageReadObserver.Component | null;
|
|
61
|
+
MessageRow?: typeof MessageRow.Component | null;
|
|
62
|
+
MessageContent?: typeof MessageContent.Component | null;
|
|
63
|
+
MessageInfoBox?: typeof MessageInfoBox.Component | null;
|
|
64
|
+
MessageText?: typeof MessageText.Component | null;
|
|
65
|
+
MessageAuthor?: typeof MessageAuthor.Component | null;
|
|
66
|
+
Icon?: typeof Icon.Component | null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ChatifyProps {
|
|
70
|
+
className?: string;
|
|
71
|
+
style?: CSSProperties;
|
|
72
|
+
localizer?: Localizer;
|
|
73
|
+
customize?: ChatifyCustomizeValue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function Content(props: Pick<ChatifyProps, 'className' | 'style'>) {
|
|
77
|
+
const { className = '', style } = props;
|
|
78
|
+
useReadMonitor();
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div className={className + ' wxchtf-chatify'} style={style}>
|
|
82
|
+
<Header />
|
|
83
|
+
<div className="wxchtf-body">
|
|
84
|
+
<Aside />
|
|
85
|
+
<Main />
|
|
86
|
+
</div>
|
|
87
|
+
<Footer />
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const EMPTY_CUSTOMIZE = {};
|
|
93
|
+
|
|
94
|
+
export const Chatify: FC<ChatifyProps> = (props) => {
|
|
95
|
+
const { localizer = defaultLocalizer, customize = EMPTY_CUSTOMIZE } = props;
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<LocalizerContext.Provider value={localizer}>
|
|
99
|
+
<CustomizeProvider value={customize}>
|
|
100
|
+
<Content {...props} />
|
|
101
|
+
</CustomizeProvider>
|
|
102
|
+
</LocalizerContext.Provider>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
Chatify.displayName = 'Chatify';
|
package/src/ui/Footer.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { FC } from 'react';
|
|
2
|
-
import { customize } from '../util';
|
|
3
|
-
|
|
4
|
-
const _Footer: FC = () => <div className="wxchtf-footer"></div>;
|
|
5
|
-
_Footer.displayName = 'Footer';
|
|
6
|
-
|
|
7
|
-
export const Footer = customize('Footer', _Footer);
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { customize } from '../util';
|
|
3
|
+
|
|
4
|
+
const _Footer: FC = () => <div className="wxchtf-footer"></div>;
|
|
5
|
+
_Footer.displayName = 'Footer';
|
|
6
|
+
|
|
7
|
+
export const Footer = customize('Footer', _Footer);
|
package/src/ui/Header.tsx
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { FC } from 'react';
|
|
2
|
-
import { AddChatButton } from './AddChatButton';
|
|
3
|
-
import { customize } from '../util';
|
|
4
|
-
|
|
5
|
-
const _Header: FC = () => {
|
|
6
|
-
return (
|
|
7
|
-
<div className="wxchtf-header">
|
|
8
|
-
<AddChatButton />
|
|
9
|
-
</div>
|
|
10
|
-
);
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
_Header.displayName = 'Header';
|
|
14
|
-
export const Header = customize('Header', _Header);
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { AddChatButton } from './AddChatButton';
|
|
3
|
+
import { customize } from '../util';
|
|
4
|
+
|
|
5
|
+
const _Header: FC = () => {
|
|
6
|
+
return (
|
|
7
|
+
<div className="wxchtf-header">
|
|
8
|
+
<AddChatButton />
|
|
9
|
+
</div>
|
|
10
|
+
);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
_Header.displayName = 'Header';
|
|
14
|
+
export const Header = customize('Header', _Header);
|
package/src/ui/Icon.tsx
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { FC } from 'react';
|
|
2
|
-
import { customize } from '../util';
|
|
3
|
-
import SendOutlined from '@ant-design/icons/SendOutlined';
|
|
4
|
-
import FileAddOutlined from '@ant-design/icons/FileAddOutlined';
|
|
5
|
-
|
|
6
|
-
export type IconType = 'attach' | 'send';
|
|
7
|
-
|
|
8
|
-
export interface IconProps {
|
|
9
|
-
type: IconType;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const ICONS: Record<IconType, React.ComponentType> = {
|
|
13
|
-
attach: FileAddOutlined,
|
|
14
|
-
send: SendOutlined,
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export const Icon = customize<FC<IconProps>, string>('Icon', (props) => {
|
|
18
|
-
const { type } = props;
|
|
19
|
-
const IconComponent = ICONS[type];
|
|
20
|
-
return IconComponent ? <IconComponent /> : null;
|
|
21
|
-
});
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { customize } from '../util';
|
|
3
|
+
import SendOutlined from '@ant-design/icons/SendOutlined';
|
|
4
|
+
import FileAddOutlined from '@ant-design/icons/FileAddOutlined';
|
|
5
|
+
|
|
6
|
+
export type IconType = 'attach' | 'send';
|
|
7
|
+
|
|
8
|
+
export interface IconProps {
|
|
9
|
+
type: IconType;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const ICONS: Record<IconType, React.ComponentType> = {
|
|
13
|
+
attach: FileAddOutlined,
|
|
14
|
+
send: SendOutlined,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const Icon = customize<FC<IconProps>, string>('Icon', (props) => {
|
|
18
|
+
const { type } = props;
|
|
19
|
+
const IconComponent = ICONS[type];
|
|
20
|
+
return IconComponent ? <IconComponent /> : null;
|
|
21
|
+
});
|
package/src/ui/InputForm.tsx
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
import { Button, Input } from 'antd';
|
|
2
|
-
import { useCallback, useState } from 'react';
|
|
3
|
-
import isHotkey from 'is-hotkey';
|
|
4
|
-
import { useAddMessage } from '../core/useAddMessage';
|
|
5
|
-
import { uniqId } from '../util';
|
|
6
|
-
import { useSelect } from '../core';
|
|
7
|
-
import { useLocalizer } from './localizer';
|
|
8
|
-
import { Icon } from './Icon';
|
|
9
|
-
|
|
10
|
-
const isSend = isHotkey('ctrl+enter');
|
|
11
|
-
|
|
12
|
-
export function InputForm() {
|
|
13
|
-
const chatId = useSelect((x) => x.ui.chatId!, []);
|
|
14
|
-
const [add] = useAddMessage({ chatId });
|
|
15
|
-
const [text, setText] = useState('');
|
|
16
|
-
const localizer = useLocalizer();
|
|
17
|
-
|
|
18
|
-
const onSend = useCallback(() => {
|
|
19
|
-
setText('');
|
|
20
|
-
add({ text: text, files: [], requestId: uniqId() });
|
|
21
|
-
|
|
22
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
23
|
-
}, [chatId, text]);
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<div className="wxchtf-input-form">
|
|
27
|
-
<div className="wxchtf-input-box">
|
|
28
|
-
<div className="wxchtf-add-files-box">
|
|
29
|
-
<Button type="link" icon={<Icon type="attach" />} className="wxchtf-add-files-btn" />
|
|
30
|
-
</div>
|
|
31
|
-
<div className="wxchtf-text-input-box">
|
|
32
|
-
<Input.TextArea
|
|
33
|
-
value={text}
|
|
34
|
-
onChange={(e) => setText(e.target.value)}
|
|
35
|
-
autoSize={{ maxRows: 5 }}
|
|
36
|
-
className="wxchtf-text-input-textarea"
|
|
37
|
-
placeholder={localizer.input.placeholder()}
|
|
38
|
-
onKeyDown={(event) => {
|
|
39
|
-
if (isSend(event)) {
|
|
40
|
-
onSend();
|
|
41
|
-
}
|
|
42
|
-
}}
|
|
43
|
-
/>
|
|
44
|
-
</div>
|
|
45
|
-
<div className="wxchtf-submit-btn-box">
|
|
46
|
-
<Button onClick={onSend} type="link" icon={<Icon type="send" />} className="wxchtf-submit-btn" />
|
|
47
|
-
</div>
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
50
|
-
);
|
|
51
|
-
}
|
|
1
|
+
import { Button, Input } from 'antd';
|
|
2
|
+
import { useCallback, useState } from 'react';
|
|
3
|
+
import isHotkey from 'is-hotkey';
|
|
4
|
+
import { useAddMessage } from '../core/useAddMessage';
|
|
5
|
+
import { uniqId } from '../util';
|
|
6
|
+
import { useSelect } from '../core';
|
|
7
|
+
import { useLocalizer } from './localizer';
|
|
8
|
+
import { Icon } from './Icon';
|
|
9
|
+
|
|
10
|
+
const isSend = isHotkey('ctrl+enter');
|
|
11
|
+
|
|
12
|
+
export function InputForm() {
|
|
13
|
+
const chatId = useSelect((x) => x.ui.chatId!, []);
|
|
14
|
+
const [add] = useAddMessage({ chatId });
|
|
15
|
+
const [text, setText] = useState('');
|
|
16
|
+
const localizer = useLocalizer();
|
|
17
|
+
|
|
18
|
+
const onSend = useCallback(() => {
|
|
19
|
+
setText('');
|
|
20
|
+
add({ text: text, files: [], requestId: uniqId() });
|
|
21
|
+
|
|
22
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
23
|
+
}, [chatId, text]);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className="wxchtf-input-form">
|
|
27
|
+
<div className="wxchtf-input-box">
|
|
28
|
+
<div className="wxchtf-add-files-box">
|
|
29
|
+
<Button type="link" icon={<Icon type="attach" />} className="wxchtf-add-files-btn" />
|
|
30
|
+
</div>
|
|
31
|
+
<div className="wxchtf-text-input-box">
|
|
32
|
+
<Input.TextArea
|
|
33
|
+
value={text}
|
|
34
|
+
onChange={(e) => setText(e.target.value)}
|
|
35
|
+
autoSize={{ maxRows: 5 }}
|
|
36
|
+
className="wxchtf-text-input-textarea"
|
|
37
|
+
placeholder={localizer.input.placeholder()}
|
|
38
|
+
onKeyDown={(event) => {
|
|
39
|
+
if (isSend(event)) {
|
|
40
|
+
onSend();
|
|
41
|
+
}
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
</div>
|
|
45
|
+
<div className="wxchtf-submit-btn-box">
|
|
46
|
+
<Button onClick={onSend} type="link" icon={<Icon type="send" />} className="wxchtf-submit-btn" />
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
}
|
package/src/ui/LoadMore.tsx
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
-
import { useLoadMore } from '../core';
|
|
3
|
-
import { MessageSkeleton } from './MessageSkeleton';
|
|
4
|
-
|
|
5
|
-
export interface LoadMoreProps {
|
|
6
|
-
chatId: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function LoadMore(props: LoadMoreProps) {
|
|
10
|
-
const { chatId } = props;
|
|
11
|
-
const [loadMore, { isFetching }] = useLoadMore({ chatId });
|
|
12
|
-
const [intersecting, setIntersecting] = useState(false);
|
|
13
|
-
const ref = useRef<HTMLDivElement | null>(null);
|
|
14
|
-
|
|
15
|
-
const observer = useMemo(
|
|
16
|
-
() => new IntersectionObserver(([entry]) => setIntersecting((prev) => prev || entry.isIntersecting)),
|
|
17
|
-
[],
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
observer.observe(ref.current!);
|
|
22
|
-
return () => observer.disconnect();
|
|
23
|
-
}, [observer]);
|
|
24
|
-
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
if (!intersecting) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
loadMore();
|
|
31
|
-
|
|
32
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
33
|
-
}, [intersecting]);
|
|
34
|
-
|
|
35
|
-
if (isFetching) {
|
|
36
|
-
return <MessageSkeleton count={4} />;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return <div ref={ref} />;
|
|
40
|
-
}
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { useLoadMore } from '../core';
|
|
3
|
+
import { MessageSkeleton } from './MessageSkeleton';
|
|
4
|
+
|
|
5
|
+
export interface LoadMoreProps {
|
|
6
|
+
chatId: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function LoadMore(props: LoadMoreProps) {
|
|
10
|
+
const { chatId } = props;
|
|
11
|
+
const [loadMore, { isFetching }] = useLoadMore({ chatId });
|
|
12
|
+
const [intersecting, setIntersecting] = useState(false);
|
|
13
|
+
const ref = useRef<HTMLDivElement | null>(null);
|
|
14
|
+
|
|
15
|
+
const observer = useMemo(
|
|
16
|
+
() => new IntersectionObserver(([entry]) => setIntersecting((prev) => prev || entry.isIntersecting)),
|
|
17
|
+
[],
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
observer.observe(ref.current!);
|
|
22
|
+
return () => observer.disconnect();
|
|
23
|
+
}, [observer]);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!intersecting) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
loadMore();
|
|
31
|
+
|
|
32
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
33
|
+
}, [intersecting]);
|
|
34
|
+
|
|
35
|
+
if (isFetching) {
|
|
36
|
+
return <MessageSkeleton count={4} />;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return <div ref={ref} />;
|
|
40
|
+
}
|
package/src/ui/Main.tsx
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { useSelect } from '../core';
|
|
2
|
-
import { AddChat } from './AddChat';
|
|
3
|
-
import { Chat } from './Chat';
|
|
4
|
-
import { ChatSettings } from './ChatSettings';
|
|
5
|
-
|
|
6
|
-
export function Main() {
|
|
7
|
-
const { view, chatId } = useSelect((x) => ({ view: x.ui.view, chatId: x.ui.chatId }), []);
|
|
8
|
-
|
|
9
|
-
return (
|
|
10
|
-
<div className="wxchtf-main">
|
|
11
|
-
{view === 'chat' && chatId && <Chat id={chatId} key={chatId} />}
|
|
12
|
-
{view === 'new-chat' && <AddChat />}
|
|
13
|
-
{view === 'chat-settings' && <ChatSettings />}
|
|
14
|
-
</div>
|
|
15
|
-
);
|
|
16
|
-
}
|
|
1
|
+
import { useSelect } from '../core';
|
|
2
|
+
import { AddChat } from './AddChat';
|
|
3
|
+
import { Chat } from './Chat';
|
|
4
|
+
import { ChatSettings } from './ChatSettings';
|
|
5
|
+
|
|
6
|
+
export function Main() {
|
|
7
|
+
const { view, chatId } = useSelect((x) => ({ view: x.ui.view, chatId: x.ui.chatId }), []);
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div className="wxchtf-main">
|
|
11
|
+
{view === 'chat' && chatId && <Chat id={chatId} key={chatId} />}
|
|
12
|
+
{view === 'new-chat' && <AddChat />}
|
|
13
|
+
{view === 'chat-settings' && <ChatSettings />}
|
|
14
|
+
</div>
|
|
15
|
+
);
|
|
16
|
+
}
|