@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/Message.tsx
CHANGED
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
import { useMe } from '../ChatifyContext';
|
|
2
|
-
import { FC, Ref, RefObject, useEffect, useMemo, useRef } from 'react';
|
|
3
|
-
import { Message, useDispatch, useSelect } from '../core';
|
|
4
|
-
import { customize } from '../util';
|
|
5
|
-
import { useLocalizer } from './localizer';
|
|
6
|
-
import { Avatar } from './Avatar';
|
|
7
|
-
|
|
8
|
-
export interface MessageBoxProps {
|
|
9
|
-
message: Message;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface MessageRowProps extends MessageBoxProps {
|
|
13
|
-
containerRef?: Ref<HTMLDivElement>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const MessageAuthor = customize<FC<MessageBoxProps>, string>('MessageAuthor', (props) => {
|
|
17
|
-
const { sentBy } = props.message;
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<div className="wxchtf-message-author">
|
|
21
|
-
<Avatar account={sentBy} />
|
|
22
|
-
</div>
|
|
23
|
-
);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
export const MessageText = customize<FC<MessageBoxProps>, string>('MessageText', (props) => {
|
|
27
|
-
const { text } = props.message;
|
|
28
|
-
return <div className="wxchtf-message-text">{text}</div>;
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
export const MessageInfoBox = customize<FC<MessageBoxProps>, string>('MessageInfoBox', (props) => {
|
|
32
|
-
const { message } = props;
|
|
33
|
-
const { id, sentBy, sentAt, read } = message;
|
|
34
|
-
const me = useMe();
|
|
35
|
-
const my = sentBy.id === me;
|
|
36
|
-
const localizer = useLocalizer();
|
|
37
|
-
const reading = useSelect((x) => x.read.queued.includes(id), []);
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<div className="wxchtf-message-info-box">
|
|
41
|
-
<div className="wxchtf-message-sent-at">{localizer.timestamp(sentAt)}</div>
|
|
42
|
-
{read !== undefined && !my && (
|
|
43
|
-
<div className="wxchtf-message-read-box">
|
|
44
|
-
{reading ? localizer.message.reading() : localizer.message.read()}
|
|
45
|
-
</div>
|
|
46
|
-
)}
|
|
47
|
-
</div>
|
|
48
|
-
);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
export const MessageContent = customize<FC<MessageBoxProps>, string>('MessageContent', (props) => {
|
|
52
|
-
return (
|
|
53
|
-
<div className="wxchtf-message-content">
|
|
54
|
-
<MessageText {...props} />
|
|
55
|
-
<MessageInfoBox {...props} />
|
|
56
|
-
</div>
|
|
57
|
-
);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
export const MessageRow = customize<FC<MessageRowProps>, string>('MessageRow', (props) => {
|
|
61
|
-
const { message, containerRef } = props;
|
|
62
|
-
const { sentBy } = message;
|
|
63
|
-
const me = useMe();
|
|
64
|
-
const my = sentBy.id === me;
|
|
65
|
-
|
|
66
|
-
return (
|
|
67
|
-
<div ref={containerRef} className={'wxchtf-message' + (my ? ' --my' : '')}>
|
|
68
|
-
<MessageAuthor {...props} />
|
|
69
|
-
<MessageContent {...props} />
|
|
70
|
-
</div>
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
export const MessageBox = customize<FC<MessageBoxProps>, string>('MessageBox', (props) => {
|
|
75
|
-
const ref = useRef<HTMLDivElement | null>(null);
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<>
|
|
79
|
-
<MessageReadObserver messageRef={ref} {...props} />
|
|
80
|
-
<MessageRow containerRef={ref} {...props} />
|
|
81
|
-
</>
|
|
82
|
-
);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
export interface MessageReadObserverProps extends MessageBoxProps {
|
|
86
|
-
messageRef: RefObject<HTMLDivElement | null>;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export const MessageReadObserver = customize<FC<MessageReadObserverProps>, string>(
|
|
90
|
-
'MessageReadObserver',
|
|
91
|
-
(props) => {
|
|
92
|
-
const { message, messageRef } = props;
|
|
93
|
-
const dispatch = useDispatch();
|
|
94
|
-
const readRef = useRef(false);
|
|
95
|
-
|
|
96
|
-
const observer = useMemo(
|
|
97
|
-
() =>
|
|
98
|
-
new IntersectionObserver(([entry]) => {
|
|
99
|
-
if (entry.isIntersecting && !readRef.current && !message.read) {
|
|
100
|
-
readRef.current = true;
|
|
101
|
-
dispatch({ type: 'read', data: { id: message.id, timestamp: new Date().getTime() } });
|
|
102
|
-
}
|
|
103
|
-
}),
|
|
104
|
-
|
|
105
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
106
|
-
[],
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
useEffect(() => {
|
|
110
|
-
observer.observe(messageRef.current!);
|
|
111
|
-
return () => observer.disconnect();
|
|
112
|
-
|
|
113
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
114
|
-
}, [observer]);
|
|
115
|
-
|
|
116
|
-
return <></>;
|
|
117
|
-
},
|
|
118
|
-
);
|
|
1
|
+
import { useMe } from '../ChatifyContext';
|
|
2
|
+
import { FC, Ref, RefObject, useEffect, useMemo, useRef } from 'react';
|
|
3
|
+
import { Message, useDispatch, useSelect } from '../core';
|
|
4
|
+
import { customize } from '../util';
|
|
5
|
+
import { useLocalizer } from './localizer';
|
|
6
|
+
import { Avatar } from './Avatar';
|
|
7
|
+
|
|
8
|
+
export interface MessageBoxProps {
|
|
9
|
+
message: Message;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface MessageRowProps extends MessageBoxProps {
|
|
13
|
+
containerRef?: Ref<HTMLDivElement>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const MessageAuthor = customize<FC<MessageBoxProps>, string>('MessageAuthor', (props) => {
|
|
17
|
+
const { sentBy } = props.message;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="wxchtf-message-author">
|
|
21
|
+
<Avatar account={sentBy} />
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export const MessageText = customize<FC<MessageBoxProps>, string>('MessageText', (props) => {
|
|
27
|
+
const { text } = props.message;
|
|
28
|
+
return <div className="wxchtf-message-text">{text}</div>;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export const MessageInfoBox = customize<FC<MessageBoxProps>, string>('MessageInfoBox', (props) => {
|
|
32
|
+
const { message } = props;
|
|
33
|
+
const { id, sentBy, sentAt, read } = message;
|
|
34
|
+
const me = useMe();
|
|
35
|
+
const my = sentBy.id === me;
|
|
36
|
+
const localizer = useLocalizer();
|
|
37
|
+
const reading = useSelect((x) => x.read.queued.includes(id), []);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div className="wxchtf-message-info-box">
|
|
41
|
+
<div className="wxchtf-message-sent-at">{localizer.timestamp(sentAt)}</div>
|
|
42
|
+
{read !== undefined && !my && (
|
|
43
|
+
<div className="wxchtf-message-read-box">
|
|
44
|
+
{reading ? localizer.message.reading() : localizer.message.read()}
|
|
45
|
+
</div>
|
|
46
|
+
)}
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export const MessageContent = customize<FC<MessageBoxProps>, string>('MessageContent', (props) => {
|
|
52
|
+
return (
|
|
53
|
+
<div className="wxchtf-message-content">
|
|
54
|
+
<MessageText {...props} />
|
|
55
|
+
<MessageInfoBox {...props} />
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export const MessageRow = customize<FC<MessageRowProps>, string>('MessageRow', (props) => {
|
|
61
|
+
const { message, containerRef } = props;
|
|
62
|
+
const { sentBy } = message;
|
|
63
|
+
const me = useMe();
|
|
64
|
+
const my = sentBy.id === me;
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div ref={containerRef} className={'wxchtf-message' + (my ? ' --my' : '')}>
|
|
68
|
+
<MessageAuthor {...props} />
|
|
69
|
+
<MessageContent {...props} />
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
export const MessageBox = customize<FC<MessageBoxProps>, string>('MessageBox', (props) => {
|
|
75
|
+
const ref = useRef<HTMLDivElement | null>(null);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<>
|
|
79
|
+
<MessageReadObserver messageRef={ref} {...props} />
|
|
80
|
+
<MessageRow containerRef={ref} {...props} />
|
|
81
|
+
</>
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
export interface MessageReadObserverProps extends MessageBoxProps {
|
|
86
|
+
messageRef: RefObject<HTMLDivElement | null>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const MessageReadObserver = customize<FC<MessageReadObserverProps>, string>(
|
|
90
|
+
'MessageReadObserver',
|
|
91
|
+
(props) => {
|
|
92
|
+
const { message, messageRef } = props;
|
|
93
|
+
const dispatch = useDispatch();
|
|
94
|
+
const readRef = useRef(false);
|
|
95
|
+
|
|
96
|
+
const observer = useMemo(
|
|
97
|
+
() =>
|
|
98
|
+
new IntersectionObserver(([entry]) => {
|
|
99
|
+
if (entry.isIntersecting && !readRef.current && !message.read) {
|
|
100
|
+
readRef.current = true;
|
|
101
|
+
dispatch({ type: 'read', data: { id: message.id, timestamp: new Date().getTime() } });
|
|
102
|
+
}
|
|
103
|
+
}),
|
|
104
|
+
|
|
105
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
106
|
+
[],
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
observer.observe(messageRef.current!);
|
|
111
|
+
return () => observer.disconnect();
|
|
112
|
+
|
|
113
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
114
|
+
}, [observer]);
|
|
115
|
+
|
|
116
|
+
return <></>;
|
|
117
|
+
},
|
|
118
|
+
);
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { Skeleton } from 'antd';
|
|
2
|
-
|
|
3
|
-
export interface MessageSkeletonProps {
|
|
4
|
-
count?: number;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function MessageSkeleton(props: MessageSkeletonProps) {
|
|
8
|
-
const { count = 1 } = props;
|
|
9
|
-
return (
|
|
10
|
-
<>
|
|
11
|
-
{Array.from(Array(count)).map((_, index) => (
|
|
12
|
-
<div className="wxchtf-message-skeleton" key={index}>
|
|
13
|
-
<Skeleton.Avatar className="wxchtf-avatar" active />
|
|
14
|
-
<Skeleton.Button active className="wxchtf-content" />
|
|
15
|
-
</div>
|
|
16
|
-
))}
|
|
17
|
-
</>
|
|
18
|
-
);
|
|
19
|
-
}
|
|
1
|
+
import { Skeleton } from 'antd';
|
|
2
|
+
|
|
3
|
+
export interface MessageSkeletonProps {
|
|
4
|
+
count?: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function MessageSkeleton(props: MessageSkeletonProps) {
|
|
8
|
+
const { count = 1 } = props;
|
|
9
|
+
return (
|
|
10
|
+
<>
|
|
11
|
+
{Array.from(Array(count)).map((_, index) => (
|
|
12
|
+
<div className="wxchtf-message-skeleton" key={index}>
|
|
13
|
+
<Skeleton.Avatar className="wxchtf-avatar" active />
|
|
14
|
+
<Skeleton.Button active className="wxchtf-content" />
|
|
15
|
+
</div>
|
|
16
|
+
))}
|
|
17
|
+
</>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
import { FC } from 'react';
|
|
2
|
-
import { File } from '../core';
|
|
3
|
-
import { customize } from '../util';
|
|
4
|
-
import { useLocalizer } from './localizer';
|
|
5
|
-
|
|
6
|
-
export interface SendingMessageProps {
|
|
7
|
-
text: string;
|
|
8
|
-
files: File[];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const SendingMessageText = customize<FC<SendingMessageProps>, string>(
|
|
12
|
-
'SendingMessageText',
|
|
13
|
-
(props) => {
|
|
14
|
-
const { text } = props;
|
|
15
|
-
return <div className="wxchtf-sending-message-text">{text}</div>;
|
|
16
|
-
},
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
export const SendingMessageInfoBox = customize<FC<SendingMessageProps>, string>(
|
|
20
|
-
'SendingMessageInfoBox',
|
|
21
|
-
() => {
|
|
22
|
-
const localizer = useLocalizer();
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<div className="wxchtf-sending-message-info-box">
|
|
26
|
-
<div className="wxchtf-sending-message-sending-box">{localizer.message.sending()}</div>
|
|
27
|
-
</div>
|
|
28
|
-
);
|
|
29
|
-
},
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
export const SendingMessageContent = customize<FC<SendingMessageProps>, string>(
|
|
33
|
-
'SendingMessageContent',
|
|
34
|
-
(props) => {
|
|
35
|
-
return (
|
|
36
|
-
<div className="wxchtf-sending-message-content">
|
|
37
|
-
<SendingMessageText {...props} />
|
|
38
|
-
<SendingMessageInfoBox {...props} />
|
|
39
|
-
</div>
|
|
40
|
-
);
|
|
41
|
-
},
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
export const SendingMessage = customize<FC<SendingMessageProps>, string>('SendingMessage', (props) => {
|
|
45
|
-
return (
|
|
46
|
-
<div className="wxchtf-sending-message">
|
|
47
|
-
<SendingMessageContent {...props} />
|
|
48
|
-
</div>
|
|
49
|
-
);
|
|
50
|
-
});
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { File } from '../core';
|
|
3
|
+
import { customize } from '../util';
|
|
4
|
+
import { useLocalizer } from './localizer';
|
|
5
|
+
|
|
6
|
+
export interface SendingMessageProps {
|
|
7
|
+
text: string;
|
|
8
|
+
files: File[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const SendingMessageText = customize<FC<SendingMessageProps>, string>(
|
|
12
|
+
'SendingMessageText',
|
|
13
|
+
(props) => {
|
|
14
|
+
const { text } = props;
|
|
15
|
+
return <div className="wxchtf-sending-message-text">{text}</div>;
|
|
16
|
+
},
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
export const SendingMessageInfoBox = customize<FC<SendingMessageProps>, string>(
|
|
20
|
+
'SendingMessageInfoBox',
|
|
21
|
+
() => {
|
|
22
|
+
const localizer = useLocalizer();
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className="wxchtf-sending-message-info-box">
|
|
26
|
+
<div className="wxchtf-sending-message-sending-box">{localizer.message.sending()}</div>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
},
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export const SendingMessageContent = customize<FC<SendingMessageProps>, string>(
|
|
33
|
+
'SendingMessageContent',
|
|
34
|
+
(props) => {
|
|
35
|
+
return (
|
|
36
|
+
<div className="wxchtf-sending-message-content">
|
|
37
|
+
<SendingMessageText {...props} />
|
|
38
|
+
<SendingMessageInfoBox {...props} />
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export const SendingMessage = customize<FC<SendingMessageProps>, string>('SendingMessage', (props) => {
|
|
45
|
+
return (
|
|
46
|
+
<div className="wxchtf-sending-message">
|
|
47
|
+
<SendingMessageContent {...props} />
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
});
|
package/src/ui/SystemMessage.tsx
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { FC } from 'react';
|
|
2
|
-
import { customize } from '../util';
|
|
3
|
-
import { Localizer, useLocalizer } from './localizer';
|
|
4
|
-
|
|
5
|
-
export interface SystemMessageProps {
|
|
6
|
-
text: string;
|
|
7
|
-
sentAt: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function formatSystemMessage(localizer: Localizer, text: string): React.ReactNode {
|
|
11
|
-
if (text === 'chatify://chat-created') {
|
|
12
|
-
return localizer.system.chatCreated();
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (text.startsWith('chatify://member-added::')) {
|
|
16
|
-
return localizer.system.memberAdded();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return text;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const SystemMessage = customize<FC<SystemMessageProps>, string>('SystemMessage', (props) => {
|
|
23
|
-
const { text, sentAt } = props;
|
|
24
|
-
const localizer = useLocalizer();
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<div className="wxchtf-system-message">
|
|
28
|
-
<div className="wxchtf-text">{formatSystemMessage(localizer, text)}</div>
|
|
29
|
-
<div className="wxchtf-timestamp">{localizer.timestamp(sentAt)}</div>
|
|
30
|
-
</div>
|
|
31
|
-
);
|
|
32
|
-
});
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { customize } from '../util';
|
|
3
|
+
import { Localizer, useLocalizer } from './localizer';
|
|
4
|
+
|
|
5
|
+
export interface SystemMessageProps {
|
|
6
|
+
text: string;
|
|
7
|
+
sentAt: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function formatSystemMessage(localizer: Localizer, text: string): React.ReactNode {
|
|
11
|
+
if (text === 'chatify://chat-created') {
|
|
12
|
+
return localizer.system.chatCreated();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (text.startsWith('chatify://member-added::')) {
|
|
16
|
+
return localizer.system.memberAdded();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return text;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const SystemMessage = customize<FC<SystemMessageProps>, string>('SystemMessage', (props) => {
|
|
23
|
+
const { text, sentAt } = props;
|
|
24
|
+
const localizer = useLocalizer();
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className="wxchtf-system-message">
|
|
28
|
+
<div className="wxchtf-text">{formatSystemMessage(localizer, text)}</div>
|
|
29
|
+
<div className="wxchtf-timestamp">{localizer.timestamp(sentAt)}</div>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
});
|
package/src/ui/index.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export * from './Chatify';
|
|
2
|
-
export * from './Avatar';
|
|
3
|
-
export * from './localizer';
|
|
4
|
-
export * from './AddChatButton';
|
|
5
|
-
export * from './Header';
|
|
6
|
-
export * from './ChatHeader';
|
|
7
|
-
export * from './ChatHeaderMembers';
|
|
8
|
-
export * from './ChatListItem';
|
|
9
|
-
export * from './Message';
|
|
10
|
-
export * from './SystemMessage';
|
|
11
|
-
export * from './Icon';
|
|
1
|
+
export * from './Chatify';
|
|
2
|
+
export * from './Avatar';
|
|
3
|
+
export * from './localizer';
|
|
4
|
+
export * from './AddChatButton';
|
|
5
|
+
export * from './Header';
|
|
6
|
+
export * from './ChatHeader';
|
|
7
|
+
export * from './ChatHeaderMembers';
|
|
8
|
+
export * from './ChatListItem';
|
|
9
|
+
export * from './Message';
|
|
10
|
+
export * from './SystemMessage';
|
|
11
|
+
export * from './Icon';
|
package/src/ui/localizer.tsx
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
import dayjs from 'dayjs';
|
|
2
|
-
import React, { useContext } from 'react';
|
|
3
|
-
|
|
4
|
-
export interface Localizer {
|
|
5
|
-
timestamp: (value: string) => string;
|
|
6
|
-
system: {
|
|
7
|
-
chatCreated: () => string;
|
|
8
|
-
memberAdded: () => string;
|
|
9
|
-
};
|
|
10
|
-
message: {
|
|
11
|
-
read: () => React.ReactNode;
|
|
12
|
-
reading: () => React.ReactNode;
|
|
13
|
-
sending: () => React.ReactNode;
|
|
14
|
-
};
|
|
15
|
-
addChat: {
|
|
16
|
-
title: () => React.ReactNode;
|
|
17
|
-
name: {
|
|
18
|
-
label: () => React.ReactNode;
|
|
19
|
-
required: () => string;
|
|
20
|
-
max250: () => string;
|
|
21
|
-
};
|
|
22
|
-
members: {
|
|
23
|
-
label: () => React.ReactNode;
|
|
24
|
-
required: () => string;
|
|
25
|
-
};
|
|
26
|
-
submitBtn: () => React.ReactNode;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
input: {
|
|
30
|
-
placeholder: () => string;
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export const LocalizerContext = React.createContext<Localizer>(null!);
|
|
35
|
-
|
|
36
|
-
export function useLocalizer() {
|
|
37
|
-
return useContext(LocalizerContext);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const defaultLocalizer: Localizer = {
|
|
41
|
-
timestamp: (value: string) => dayjs(value).format('HH:mm'),
|
|
42
|
-
message: {
|
|
43
|
-
read: () => 'Read',
|
|
44
|
-
reading: () => 'Reading...',
|
|
45
|
-
sending: () => 'Sending...',
|
|
46
|
-
},
|
|
47
|
-
addChat: {
|
|
48
|
-
title: () => 'Add chat',
|
|
49
|
-
name: {
|
|
50
|
-
label: () => 'Name',
|
|
51
|
-
required: () => `Name is required`,
|
|
52
|
-
max250: () => 'Max 250 characters allowed',
|
|
53
|
-
},
|
|
54
|
-
members: {
|
|
55
|
-
label: () => 'Members',
|
|
56
|
-
required: () => 'Members is required',
|
|
57
|
-
},
|
|
58
|
-
submitBtn: () => 'Submit',
|
|
59
|
-
},
|
|
60
|
-
system: {
|
|
61
|
-
chatCreated: () => 'Chat created',
|
|
62
|
-
memberAdded: () => 'New member added',
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
input: {
|
|
66
|
-
placeholder: () => 'Start typing (Ctrl/⌘Cmd + Enter to send) ...',
|
|
67
|
-
},
|
|
68
|
-
};
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import React, { useContext } from 'react';
|
|
3
|
+
|
|
4
|
+
export interface Localizer {
|
|
5
|
+
timestamp: (value: string) => string;
|
|
6
|
+
system: {
|
|
7
|
+
chatCreated: () => string;
|
|
8
|
+
memberAdded: () => string;
|
|
9
|
+
};
|
|
10
|
+
message: {
|
|
11
|
+
read: () => React.ReactNode;
|
|
12
|
+
reading: () => React.ReactNode;
|
|
13
|
+
sending: () => React.ReactNode;
|
|
14
|
+
};
|
|
15
|
+
addChat: {
|
|
16
|
+
title: () => React.ReactNode;
|
|
17
|
+
name: {
|
|
18
|
+
label: () => React.ReactNode;
|
|
19
|
+
required: () => string;
|
|
20
|
+
max250: () => string;
|
|
21
|
+
};
|
|
22
|
+
members: {
|
|
23
|
+
label: () => React.ReactNode;
|
|
24
|
+
required: () => string;
|
|
25
|
+
};
|
|
26
|
+
submitBtn: () => React.ReactNode;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
input: {
|
|
30
|
+
placeholder: () => string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const LocalizerContext = React.createContext<Localizer>(null!);
|
|
35
|
+
|
|
36
|
+
export function useLocalizer() {
|
|
37
|
+
return useContext(LocalizerContext);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const defaultLocalizer: Localizer = {
|
|
41
|
+
timestamp: (value: string) => dayjs(value).format('HH:mm'),
|
|
42
|
+
message: {
|
|
43
|
+
read: () => 'Read',
|
|
44
|
+
reading: () => 'Reading...',
|
|
45
|
+
sending: () => 'Sending...',
|
|
46
|
+
},
|
|
47
|
+
addChat: {
|
|
48
|
+
title: () => 'Add chat',
|
|
49
|
+
name: {
|
|
50
|
+
label: () => 'Name',
|
|
51
|
+
required: () => `Name is required`,
|
|
52
|
+
max250: () => 'Max 250 characters allowed',
|
|
53
|
+
},
|
|
54
|
+
members: {
|
|
55
|
+
label: () => 'Members',
|
|
56
|
+
required: () => 'Members is required',
|
|
57
|
+
},
|
|
58
|
+
submitBtn: () => 'Submit',
|
|
59
|
+
},
|
|
60
|
+
system: {
|
|
61
|
+
chatCreated: () => 'Chat created',
|
|
62
|
+
memberAdded: () => 'New member added',
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
input: {
|
|
66
|
+
placeholder: () => 'Start typing (Ctrl/⌘Cmd + Enter to send) ...',
|
|
67
|
+
},
|
|
68
|
+
};
|