@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
@@ -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
+ });
@@ -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
+ }
@@ -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
+ }