@weavy/uikit-react 13.0.0 → 14.0.0
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/changelog.md +24 -0
- package/dist/cjs/index.js +3 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/Chat.d.ts +1 -1
- package/dist/cjs/types/components/Messages.d.ts +3 -1
- package/dist/cjs/types/hooks/useConversations.d.ts +1 -1
- package/dist/cjs/types/hooks/useMutateMessage.d.ts +1 -1
- package/dist/cjs/types/hooks/useMutateRead.d.ts +1 -0
- package/dist/cjs/types/types/Chat.d.ts +1 -0
- package/dist/cjs/types/types/Message.d.ts +2 -0
- package/dist/cjs/types/types/types.d.ts +47 -4
- package/dist/cjs/types/ui/Spinner.d.ts +2 -1
- package/dist/css/weavy-chat.css +1540 -954
- package/dist/css/weavy-messenger.css +1901 -1298
- package/dist/css/weavy.css +1696 -1093
- package/dist/esm/index.js +3 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/Chat.d.ts +1 -1
- package/dist/esm/types/components/Messages.d.ts +3 -1
- package/dist/esm/types/hooks/useConversations.d.ts +1 -1
- package/dist/esm/types/hooks/useMutateMessage.d.ts +1 -1
- package/dist/esm/types/hooks/useMutateRead.d.ts +1 -0
- package/dist/esm/types/types/Chat.d.ts +1 -0
- package/dist/esm/types/types/Message.d.ts +2 -0
- package/dist/esm/types/types/types.d.ts +47 -4
- package/dist/esm/types/ui/Spinner.d.ts +2 -1
- package/dist/index.d.ts +2 -1
- package/package.json +2 -2
- package/src/client/WeavyClient.ts +12 -17
- package/src/components/Attachment.tsx +5 -5
- package/src/components/Chat.tsx +6 -5
- package/src/components/Conversation.tsx +26 -20
- package/src/components/ConversationBadge.tsx +7 -5
- package/src/components/ConversationForm.tsx +1 -1
- package/src/components/ConversationList.tsx +59 -11
- package/src/components/ConversationListItem.tsx +71 -54
- package/src/components/FileBrowser.tsx +53 -50
- package/src/components/MeetingCard.tsx +35 -13
- package/src/components/Meetings.tsx +1 -1
- package/src/components/Message.tsx +41 -41
- package/src/components/Messages.tsx +61 -60
- package/src/components/Messenger.tsx +7 -2
- package/src/components/NewConversation.tsx +1 -1
- package/src/components/PdfViewer.tsx +5 -5
- package/src/components/Preview.tsx +2 -2
- package/src/components/Reactions.tsx +11 -5
- package/src/components/SearchUsers.tsx +19 -9
- package/src/components/SeenBy.tsx +13 -7
- package/src/components/Typing.tsx +11 -12
- package/src/contexts/UserContext.tsx +1 -1
- package/src/contexts/WeavyContext.tsx +3 -3
- package/src/hooks/useConversations.ts +15 -5
- package/src/hooks/useMutateMessage.ts +1 -5
- package/src/hooks/useMutateRead.ts +5 -3
- package/src/hooks/usePresence.ts +2 -3
- package/src/hooks/useReactions.ts +11 -12
- package/src/scss/theme/_alert.scss +61 -63
- package/src/scss/theme/_appbar.scss +105 -30
- package/src/scss/theme/_avatar.scss +23 -28
- package/src/scss/theme/_badge.scss +26 -18
- package/src/scss/theme/_buttons.scss +107 -52
- package/src/scss/theme/_card.scss +102 -4
- package/src/scss/theme/_checkbox.scss +16 -20
- package/src/scss/theme/_code-vscode-dark.scss +3 -3
- package/src/scss/theme/_code-vscode-light.scss +3 -3
- package/src/scss/theme/_code.scss +0 -2
- package/src/scss/theme/_comment-editor-cm.scss +97 -0
- package/src/scss/theme/_comment-editor.scss +129 -0
- package/src/scss/theme/_comments.scss +66 -0
- package/src/scss/theme/_content.scss +33 -5
- package/src/scss/theme/_conversations.scss +19 -78
- package/src/scss/theme/_dropdown.scss +102 -15
- package/src/scss/theme/_embed.scss +135 -0
- package/src/scss/theme/_facepile.scss +11 -0
- package/src/scss/theme/_filebrowser.scss +1 -1
- package/src/scss/theme/_files.scss +76 -47
- package/src/scss/theme/_grid.scss +8 -0
- package/src/scss/theme/_icons.scss +155 -19
- package/src/scss/theme/_image-grid.scss +7 -10
- package/src/scss/theme/_input.scss +160 -0
- package/src/scss/theme/_item.scss +169 -0
- package/src/scss/theme/_list.scss +57 -0
- package/src/scss/theme/_meeting.scss +11 -0
- package/src/scss/theme/_message-editor-cm.scss +95 -0
- package/src/scss/theme/_message-editor.scss +65 -19
- package/src/scss/theme/_messages.scss +51 -105
- package/src/scss/theme/_meta.scss +12 -0
- package/src/scss/theme/_overlays.scss +31 -76
- package/src/scss/theme/_pager.scss +5 -1
- package/src/scss/theme/_pane.scss +13 -2
- package/src/scss/theme/_panels.scss +33 -28
- package/src/scss/theme/_picker-list.scss +5 -3
- package/src/scss/theme/_placeholder.scss +19 -0
- package/src/scss/theme/_poll.scss +49 -0
- package/src/scss/theme/_post-editor-cm.scss +100 -0
- package/src/scss/theme/_post-editor.scss +127 -0
- package/src/scss/theme/_post.scss +83 -0
- package/src/scss/theme/_preview-code.scss +11 -2
- package/src/scss/theme/_preview-embed.scss +8 -2
- package/src/scss/theme/_preview-image.scss +8 -26
- package/src/scss/theme/_preview-media.scss +1 -0
- package/src/scss/theme/_preview-pdf.scss +10 -15
- package/src/scss/theme/_preview.scss +57 -79
- package/src/scss/theme/_reactions.scss +48 -17
- package/src/scss/theme/_sheet.scss +59 -0
- package/src/scss/theme/_sidebar.scss +86 -0
- package/src/scss/theme/_spinner.scss +11 -7
- package/src/scss/theme/_tab.scss +72 -0
- package/src/scss/theme/_tables.scss +70 -23
- package/src/scss/theme/_toasts.scss +56 -26
- package/src/scss/theme/_type.scss +41 -0
- package/src/scss/theme/{mixins → base}/_backdrop.scss +0 -0
- package/src/scss/theme/{bootstrap/mixins → base}/_breakpoints.scss +9 -0
- package/src/scss/theme/base/_colors.scss +315 -0
- package/src/scss/theme/base/_md.scss +19 -0
- package/src/scss/theme/base/_palette.scss +130 -0
- package/src/scss/theme/{mixins → base}/_position.scss +5 -5
- package/src/scss/theme/base/_reboot.scss +51 -0
- package/src/scss/theme/base/_scroll.scss +180 -0
- package/src/scss/theme/base/_svg.scss +49 -0
- package/src/scss/theme/base/_text.scss +23 -0
- package/src/scss/theme/base/_vars.scss +203 -0
- package/src/scss/theme/{fonts → base/fonts}/_fontmapping-roboto.scss +0 -0
- package/src/scss/theme/{fonts → base/fonts}/_fontmapping-segoe-ui.scss +0 -0
- package/src/scss/theme/base/fonts/_index.scss +2 -0
- package/src/scss/weavy-chat.scss +10 -4
- package/src/scss/weavy-messenger.scss +37 -21
- package/src/types/Chat.ts +2 -1
- package/src/types/Message.ts +3 -1
- package/src/types/types.ts +56 -5
- package/src/ui/Icon.tsx +1 -1
- package/src/ui/Spinner.tsx +3 -2
- package/src/utils/infiniteScroll.js +11 -2
- package/src/utils/postal-parent.js +398 -0
- package/src/utils/promise.js +187 -0
- package/src/utils/scrollbarDetection.js +68 -9
- package/src/utils/utils.js +547 -0
- package/src/scss/theme/_attachments.scss +0 -74
- package/src/scss/theme/_cm-editor.scss +0 -42
- package/src/scss/theme/_colors.scss +0 -520
- package/src/scss/theme/_config.scss +0 -6
- package/src/scss/theme/_inputs.scss +0 -28
- package/src/scss/theme/_nav.scss +0 -52
- package/src/scss/theme/_palette.scss +0 -165
- package/src/scss/theme/_preview-icon.scss +0 -41
- package/src/scss/theme/_reboot.scss +0 -41
- package/src/scss/theme/_root.scss +0 -2
- package/src/scss/theme/_scroll.scss +0 -55
- package/src/scss/theme/_search.scss +0 -68
- package/src/scss/theme/_turbo.scss +0 -17
- package/src/scss/theme/_variables.scss +0 -139
- package/src/scss/theme/bootstrap/_accordion.scss +0 -146
- package/src/scss/theme/bootstrap/_alert.scss +0 -71
- package/src/scss/theme/bootstrap/_badge.scss +0 -38
- package/src/scss/theme/bootstrap/_breadcrumb.scss +0 -40
- package/src/scss/theme/bootstrap/_button-group.scss +0 -142
- package/src/scss/theme/bootstrap/_buttons.scss +0 -186
- package/src/scss/theme/bootstrap/_card.scss +0 -234
- package/src/scss/theme/bootstrap/_carousel.scss +0 -229
- package/src/scss/theme/bootstrap/_close.scss +0 -40
- package/src/scss/theme/bootstrap/_containers.scss +0 -41
- package/src/scss/theme/bootstrap/_dropdown.scss +0 -248
- package/src/scss/theme/bootstrap/_forms.scss +0 -9
- package/src/scss/theme/bootstrap/_functions.scss +0 -302
- package/src/scss/theme/bootstrap/_grid.scss +0 -33
- package/src/scss/theme/bootstrap/_helpers.scss +0 -10
- package/src/scss/theme/bootstrap/_images.scss +0 -42
- package/src/scss/theme/bootstrap/_list-group.scss +0 -191
- package/src/scss/theme/bootstrap/_maps.scss +0 -54
- package/src/scss/theme/bootstrap/_mixins.scss +0 -43
- package/src/scss/theme/bootstrap/_modal.scss +0 -237
- package/src/scss/theme/bootstrap/_nav.scss +0 -172
- package/src/scss/theme/bootstrap/_navbar.scss +0 -276
- package/src/scss/theme/bootstrap/_offcanvas.scss +0 -143
- package/src/scss/theme/bootstrap/_pagination.scss +0 -109
- package/src/scss/theme/bootstrap/_placeholders.scss +0 -51
- package/src/scss/theme/bootstrap/_popover.scss +0 -196
- package/src/scss/theme/bootstrap/_progress.scss +0 -59
- package/src/scss/theme/bootstrap/_reboot.scss +0 -610
- package/src/scss/theme/bootstrap/_root.scss +0 -73
- package/src/scss/theme/bootstrap/_spinners.scss +0 -85
- package/src/scss/theme/bootstrap/_tables.scss +0 -164
- package/src/scss/theme/bootstrap/_toasts.scss +0 -70
- package/src/scss/theme/bootstrap/_tooltip.scss +0 -120
- package/src/scss/theme/bootstrap/_transitions.scss +0 -27
- package/src/scss/theme/bootstrap/_type.scss +0 -106
- package/src/scss/theme/bootstrap/_utilities.scss +0 -647
- package/src/scss/theme/bootstrap/_variables.scss +0 -1633
- package/src/scss/theme/bootstrap/forms/_floating-labels.scss +0 -74
- package/src/scss/theme/bootstrap/forms/_form-check.scss +0 -175
- package/src/scss/theme/bootstrap/forms/_form-control.scss +0 -194
- package/src/scss/theme/bootstrap/forms/_form-range.scss +0 -91
- package/src/scss/theme/bootstrap/forms/_form-select.scss +0 -71
- package/src/scss/theme/bootstrap/forms/_form-text.scss +0 -11
- package/src/scss/theme/bootstrap/forms/_input-group.scss +0 -129
- package/src/scss/theme/bootstrap/forms/_labels.scss +0 -36
- package/src/scss/theme/bootstrap/forms/_validation.scss +0 -12
- package/src/scss/theme/bootstrap/helpers/_clearfix.scss +0 -3
- package/src/scss/theme/bootstrap/helpers/_color-bg.scss +0 -10
- package/src/scss/theme/bootstrap/helpers/_colored-links.scss +0 -12
- package/src/scss/theme/bootstrap/helpers/_position.scss +0 -36
- package/src/scss/theme/bootstrap/helpers/_ratio.scss +0 -26
- package/src/scss/theme/bootstrap/helpers/_stacks.scss +0 -15
- package/src/scss/theme/bootstrap/helpers/_stretched-link.scss +0 -15
- package/src/scss/theme/bootstrap/helpers/_text-truncation.scss +0 -7
- package/src/scss/theme/bootstrap/helpers/_visually-hidden.scss +0 -8
- package/src/scss/theme/bootstrap/helpers/_vr.scss +0 -8
- package/src/scss/theme/bootstrap/mixins/_alert.scss +0 -15
- package/src/scss/theme/bootstrap/mixins/_backdrop.scss +0 -14
- package/src/scss/theme/bootstrap/mixins/_banner.scss +0 -9
- package/src/scss/theme/bootstrap/mixins/_border-radius.scss +0 -78
- package/src/scss/theme/bootstrap/mixins/_box-shadow.scss +0 -18
- package/src/scss/theme/bootstrap/mixins/_buttons.scss +0 -70
- package/src/scss/theme/bootstrap/mixins/_caret.scss +0 -64
- package/src/scss/theme/bootstrap/mixins/_clearfix.scss +0 -9
- package/src/scss/theme/bootstrap/mixins/_color-scheme.scss +0 -7
- package/src/scss/theme/bootstrap/mixins/_container.scss +0 -11
- package/src/scss/theme/bootstrap/mixins/_deprecate.scss +0 -10
- package/src/scss/theme/bootstrap/mixins/_forms.scss +0 -152
- package/src/scss/theme/bootstrap/mixins/_gradients.scss +0 -47
- package/src/scss/theme/bootstrap/mixins/_grid.scss +0 -151
- package/src/scss/theme/bootstrap/mixins/_image.scss +0 -16
- package/src/scss/theme/bootstrap/mixins/_list-group.scss +0 -24
- package/src/scss/theme/bootstrap/mixins/_lists.scss +0 -7
- package/src/scss/theme/bootstrap/mixins/_pagination.scss +0 -10
- package/src/scss/theme/bootstrap/mixins/_reset-text.scss +0 -17
- package/src/scss/theme/bootstrap/mixins/_resize.scss +0 -6
- package/src/scss/theme/bootstrap/mixins/_table-variants.scss +0 -24
- package/src/scss/theme/bootstrap/mixins/_text-truncate.scss +0 -8
- package/src/scss/theme/bootstrap/mixins/_transition.scss +0 -26
- package/src/scss/theme/bootstrap/mixins/_utilities.scss +0 -97
- package/src/scss/theme/bootstrap/mixins/_visually-hidden.scss +0 -29
- package/src/scss/theme/bootstrap/utilities/_api.scss +0 -47
- package/src/scss/theme/bootstrap/vendor/_rfs.scss +0 -354
- package/src/scss/theme/bs/_badge.scss +0 -20
- package/src/scss/theme/bs/_buttons.scss +0 -185
- package/src/scss/theme/bs/_dropdown.scss +0 -86
- package/src/scss/theme/bs/_forms.scss +0 -161
- package/src/scss/theme/bs/_list-group.scss +0 -73
- package/src/scss/theme/bs/_tables.scss +0 -46
- package/src/scss/theme/fonts/_index.scss +0 -2
- package/src/scss/theme/mixins/_palette.scss +0 -165
- package/src/scss/theme/mixins/_scrollbar.scss +0 -110
|
@@ -17,11 +17,13 @@ type Props = {
|
|
|
17
17
|
id: number,
|
|
18
18
|
members: MembersResult | undefined,
|
|
19
19
|
displayName?: string,
|
|
20
|
-
avatarUrl?: string
|
|
20
|
+
avatarUrl?: string,
|
|
21
|
+
lastMessageId: number | null,
|
|
22
|
+
chatRoom: boolean
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
const Messages = ({ id, members, displayName, avatarUrl }: Props) => {
|
|
24
|
-
var reverseScroller:IntersectionObserver | null;
|
|
25
|
+
const Messages = ({ id, members, displayName, avatarUrl, lastMessageId, chatRoom }: Props) => {
|
|
26
|
+
var reverseScroller: IntersectionObserver | null;
|
|
25
27
|
|
|
26
28
|
const { user } = useContext(UserContext);
|
|
27
29
|
|
|
@@ -31,7 +33,7 @@ const Messages = ({ id, members, displayName, avatarUrl }: Props) => {
|
|
|
31
33
|
const readMoreRef = useRef<any>();
|
|
32
34
|
const messagesEndRef = useRef<any>();
|
|
33
35
|
const [resolveScrollerFetch, setResolveScrollerFetch] = useState<Function | null>()
|
|
34
|
-
|
|
36
|
+
|
|
35
37
|
const { dispatch, on, events } = useEvents();
|
|
36
38
|
|
|
37
39
|
const { isLoading, isError, data, error, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage } = useMessages(id, {});
|
|
@@ -44,7 +46,6 @@ const Messages = ({ id, members, displayName, avatarUrl }: Props) => {
|
|
|
44
46
|
useLayoutEffect(() => {
|
|
45
47
|
//if (id && !isLoading && !isLoadingMembers && !isLoadingConversation) {
|
|
46
48
|
if (id && !isLoading) {
|
|
47
|
-
//console.log("useLayoutEffect scroller", id)
|
|
48
49
|
// scroll to bottom when selecting a conversation
|
|
49
50
|
scrollParentToBottom(messagesEndRef.current);
|
|
50
51
|
|
|
@@ -63,7 +64,7 @@ const Messages = ({ id, members, displayName, avatarUrl }: Props) => {
|
|
|
63
64
|
})
|
|
64
65
|
}
|
|
65
66
|
})
|
|
66
|
-
|
|
67
|
+
|
|
67
68
|
} else {
|
|
68
69
|
reverseScroller?.disconnect();
|
|
69
70
|
reverseScroller = null;
|
|
@@ -85,60 +86,63 @@ const Messages = ({ id, members, displayName, avatarUrl }: Props) => {
|
|
|
85
86
|
}, [data]);
|
|
86
87
|
|
|
87
88
|
useEffect(() => {
|
|
89
|
+
// mark conversation as read
|
|
90
|
+
if (id && lastMessageId) {
|
|
91
|
+
readMessageMutation.mutate({ id: id, read: true, messageId: lastMessageId })
|
|
92
|
+
}
|
|
93
|
+
}, [id, lastMessageId])
|
|
88
94
|
|
|
95
|
+
useEffect(() => {
|
|
89
96
|
if (id) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
readMessageMutation.mutate({ id: id, read: true })
|
|
95
|
-
|
|
96
|
-
client?.subscribe(`a${id}`, "message-inserted", handleRealtimeMessage);
|
|
97
|
-
client?.subscribe(`a${id}`, "conversation-read", handleRealtimeSeenBy);
|
|
98
|
-
client?.subscribe(`a${id}`, "reaction-inserted", handleRealtimeReactionInserted);
|
|
99
|
-
client?.subscribe(`a${id}`, "reaction-deleted", handleRealtimeReactionDeleted);
|
|
97
|
+
client?.subscribe(`a${id}`, "message_created", handleRealtimeMessage);
|
|
98
|
+
client?.subscribe(`a${id}`, "conversation_marked", handleRealtimeSeenBy);
|
|
99
|
+
client?.subscribe(`a${id}`, "reaction_added", handleRealtimeReactionInserted);
|
|
100
|
+
client?.subscribe(`a${id}`, "reaction_removed", handleRealtimeReactionDeleted);
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
return () => {
|
|
103
|
-
|
|
104
|
+
|
|
105
|
+
//window.removeEventListener('focus', handleFocus, false);
|
|
104
106
|
|
|
105
107
|
if (id) {
|
|
106
108
|
// remove additional pages in cache. Only get first page
|
|
107
109
|
let qd = queryClient.getQueryData(["messages", id]);
|
|
108
|
-
|
|
110
|
+
|
|
109
111
|
if (qd) {
|
|
110
|
-
|
|
112
|
+
|
|
111
113
|
queryClient.setQueryData(["messages", id], (data: any) => ({
|
|
112
|
-
pages: data?.pages.slice(0,1),
|
|
113
|
-
pageParams: [undefined]
|
|
114
|
-
}));
|
|
114
|
+
pages: data?.pages.slice(0, 1),
|
|
115
|
+
pageParams: [undefined]
|
|
116
|
+
}));
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
client?.unsubscribe(`a${id}`, "
|
|
118
|
-
client?.unsubscribe(`a${id}`, "
|
|
119
|
-
client?.unsubscribe(`a${id}`, "
|
|
120
|
-
client?.unsubscribe(`a${id}`, "
|
|
119
|
+
client?.unsubscribe(`a${id}`, "message_created", handleRealtimeMessage);
|
|
120
|
+
client?.unsubscribe(`a${id}`, "conversation_marked", handleRealtimeSeenBy);
|
|
121
|
+
client?.unsubscribe(`a${id}`, "reaction_added", handleRealtimeReactionInserted);
|
|
122
|
+
client?.unsubscribe(`a${id}`, "reaction_removed", handleRealtimeReactionDeleted);
|
|
121
123
|
}
|
|
122
124
|
}
|
|
123
125
|
}, [id]);
|
|
124
126
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
|
|
128
|
+
const handleRealtimeReactionInserted = useCallback((realtimeEvent: RealtimeReaction) => {
|
|
129
|
+
dispatch("reaction_added_" + realtimeEvent.entity.id, realtimeEvent);
|
|
127
130
|
}, [id]);
|
|
128
131
|
|
|
129
|
-
const handleRealtimeReactionDeleted = useCallback((
|
|
130
|
-
dispatch("
|
|
132
|
+
const handleRealtimeReactionDeleted = useCallback((realtimeEvent: RealtimeReaction) => {
|
|
133
|
+
dispatch("reaction_deleted_" + realtimeEvent.entity.id, realtimeEvent);
|
|
131
134
|
}, [id]);
|
|
132
135
|
|
|
133
136
|
// handle new message from post form
|
|
134
137
|
const handleNewMessage = (text: string, attachments: [], meetings: []) => {
|
|
135
138
|
addMessageMutation.mutate({ id: id, text: text, userId: user.id, attachments: attachments, meetings: meetings }, {
|
|
136
|
-
onSuccess: () => {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// mark conversation as read
|
|
140
|
-
setTimeout(() => { readMessageMutation.mutate({ id: id, read: true }) }, 1000);
|
|
139
|
+
onSuccess: (data: MessageType) => {
|
|
140
|
+
// mark conversation as read
|
|
141
|
+
readMessageMutation.mutate({ id: id, read: true, messageId: data.id });
|
|
141
142
|
|
|
143
|
+
requestAnimationFrame(() => scrollParentToBottom(messagesEndRef.current, true))
|
|
144
|
+
}
|
|
145
|
+
});
|
|
142
146
|
|
|
143
147
|
requestAnimationFrame(() => scrollParentToBottom(readMoreRef.current, true));
|
|
144
148
|
}
|
|
@@ -146,7 +150,7 @@ const Messages = ({ id, members, displayName, avatarUrl }: Props) => {
|
|
|
146
150
|
const handleRealtimeSeenBy = async (data: any) => {
|
|
147
151
|
let isAtBottom = isParentAtBottom(readMoreRef.current);
|
|
148
152
|
// how to do this better?
|
|
149
|
-
queryClient.invalidateQueries(["members", id])
|
|
153
|
+
queryClient.invalidateQueries(["members", id]);
|
|
150
154
|
|
|
151
155
|
if (isAtBottom) {
|
|
152
156
|
requestAnimationFrame(() => scrollParentToBottom(readMoreRef.current, true));
|
|
@@ -154,11 +158,14 @@ const Messages = ({ id, members, displayName, avatarUrl }: Props) => {
|
|
|
154
158
|
}
|
|
155
159
|
|
|
156
160
|
// real time insert-message handler
|
|
157
|
-
const handleRealtimeMessage = useCallback((
|
|
158
|
-
if (message.app_id !== id || message.
|
|
161
|
+
const handleRealtimeMessage = useCallback((realtimeEvent: RealtimeMessage) => {
|
|
162
|
+
if (realtimeEvent.message.app_id !== id || realtimeEvent.message.created_by_id === user.id) return;
|
|
163
|
+
|
|
164
|
+
// set created_by
|
|
165
|
+
realtimeEvent.message.created_by = realtimeEvent.actor;
|
|
159
166
|
|
|
160
167
|
// mark conversation as read
|
|
161
|
-
readMessageMutation.mutate({ id: id, read: true })
|
|
168
|
+
readMessageMutation.mutate({ id: id, read: true, messageId: realtimeEvent.message.id })
|
|
162
169
|
|
|
163
170
|
const previousData = queryClient.getQueryData<any>(['messages', id]);
|
|
164
171
|
|
|
@@ -166,13 +173,13 @@ const Messages = ({ id, members, displayName, avatarUrl }: Props) => {
|
|
|
166
173
|
let isAtBottom = isParentAtBottom(readMoreRef.current);
|
|
167
174
|
|
|
168
175
|
const newPagesArray = previousData.pages.map((page: any, i: number) => {
|
|
169
|
-
//
|
|
176
|
+
// add realtime message
|
|
177
|
+
|
|
170
178
|
if (i === 0) {
|
|
171
179
|
page.data = [
|
|
172
180
|
...page.data,
|
|
173
|
-
message
|
|
181
|
+
realtimeEvent.message
|
|
174
182
|
]
|
|
175
|
-
|
|
176
183
|
}
|
|
177
184
|
return page;
|
|
178
185
|
|
|
@@ -191,15 +198,9 @@ const Messages = ({ id, members, displayName, avatarUrl }: Props) => {
|
|
|
191
198
|
|
|
192
199
|
}, [id]);
|
|
193
200
|
|
|
194
|
-
const handleFocus = useCallback(() => {
|
|
195
|
-
if (!id) return;
|
|
196
|
-
|
|
197
|
-
readMessageMutation.mutate({ id: id, read: true })
|
|
198
|
-
}, [id]);
|
|
199
|
-
|
|
200
201
|
let messageHeader = <div className="wy-avatar-header">
|
|
201
202
|
{avatarUrl && displayName && <Avatar src={avatarUrl} name={displayName} id={id} size={128} /> || ''}
|
|
202
|
-
{displayName && <div className="wy-
|
|
203
|
+
{displayName && <div className="wy-headline">{displayName}</div> || ''}
|
|
203
204
|
</div>;
|
|
204
205
|
|
|
205
206
|
let loadMoreButton = <Button.UI onClick={() => fetchNextPage()} disabled={!hasNextPage || isFetchingNextPage} className="wy-message-readmore">Load more</Button.UI>;
|
|
@@ -207,19 +208,19 @@ const Messages = ({ id, members, displayName, avatarUrl }: Props) => {
|
|
|
207
208
|
let messages = (
|
|
208
209
|
<>
|
|
209
210
|
<div className="wy-message-readmore" ref={readMoreRef}>
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
211
|
+
{isFetchingNextPage
|
|
212
|
+
? 'Loading more...'
|
|
213
|
+
: hasNextPage
|
|
214
|
+
? loadMoreButton
|
|
215
|
+
: messageHeader}
|
|
216
|
+
|
|
216
217
|
</div>
|
|
217
218
|
{data && members && data.pages && data.pages.map((group, i) => {
|
|
218
219
|
// Reverse key since page loading is reversed
|
|
219
220
|
return <React.Fragment key={data.pages.length - i}>
|
|
220
221
|
{
|
|
221
222
|
group.data?.map((item: MessageType) => {
|
|
222
|
-
|
|
223
|
+
|
|
223
224
|
return <Message
|
|
224
225
|
key={item.id}
|
|
225
226
|
id={item.id}
|
|
@@ -229,15 +230,15 @@ const Messages = ({ id, members, displayName, avatarUrl }: Props) => {
|
|
|
229
230
|
avatar={item.created_by.avatar_url}
|
|
230
231
|
name={item.created_by.display_name}
|
|
231
232
|
created_at={item.created_at}
|
|
233
|
+
created_by={item.created_by.display_name}
|
|
232
234
|
attachments={item.attachments}
|
|
233
235
|
meeting={item.meeting}
|
|
234
236
|
parentId={id}
|
|
235
237
|
reactions={item.reactions}
|
|
238
|
+
chatRoom={chatRoom}
|
|
236
239
|
//reactions_count={item.reactions_count}
|
|
237
240
|
seenBy={(members.data && members.data.length > 0) ? members.data.filter((member) => {
|
|
238
|
-
|
|
239
|
-
const nothingLaterRead = !data.pages.map((p) => p.data).flat().find((message: MessageType) => { return message.id > item.id && member.read_at >= message.created_at });
|
|
240
|
-
return hasRead && nothingLaterRead && member.id !== user.id;
|
|
241
|
+
return member.marked_id === item.id && member.id !== user.id;
|
|
241
242
|
}) : []}
|
|
242
243
|
/>
|
|
243
244
|
})
|
|
@@ -260,7 +261,7 @@ const Messages = ({ id, members, displayName, avatarUrl }: Props) => {
|
|
|
260
261
|
<div id="container" className="wy-messages">
|
|
261
262
|
{messages}
|
|
262
263
|
</div>
|
|
263
|
-
<div className="wy-message-editor">
|
|
264
|
+
<div className="wy-footerbar wy-footerbar-sticky wy-message-editor wy-message-editor-bottom">
|
|
264
265
|
<ConversationForm key={id} conversationId={id} handleInsert={handleNewMessage} />
|
|
265
266
|
</div>
|
|
266
267
|
</>
|
|
@@ -4,8 +4,13 @@ import Conversation from './Conversation';
|
|
|
4
4
|
import ConversationList from './ConversationList';
|
|
5
5
|
import { Messenger } from '../types/Messenger';
|
|
6
6
|
import { WeavyContext } from '../contexts/WeavyContext';
|
|
7
|
+
import classNames from 'classnames';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
type Props = {
|
|
10
|
+
className?: string,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const Messenger: FC<Messenger> = ({ className }: Props) => {
|
|
9
14
|
|
|
10
15
|
const { client } = useContext(WeavyContext);
|
|
11
16
|
|
|
@@ -15,7 +20,7 @@ const Messenger: FC<Messenger> = () => {
|
|
|
15
20
|
|
|
16
21
|
return (
|
|
17
22
|
<MessengerProvider>
|
|
18
|
-
<div className="wy-messenger-provider">
|
|
23
|
+
<div className={classNames("wy-messenger-provider", className)}>
|
|
19
24
|
|
|
20
25
|
<div className="wy-messenger-sidebar wy-scroll-y">
|
|
21
26
|
<ConversationList />
|
|
@@ -29,7 +29,7 @@ const NewConversation = () => {
|
|
|
29
29
|
<Button.UI onClick={handleOpen}><Icon.UI name="plus" /></Button.UI>
|
|
30
30
|
|
|
31
31
|
<Overlay.UI isOpen={modalOpen} className="wy-modal">
|
|
32
|
-
<header className="wy-appbars">
|
|
32
|
+
<header className="wy-appbars" data-adjust-scrollbar-top>
|
|
33
33
|
<nav className="wy-appbar">
|
|
34
34
|
<Button.UI onClick={handleClose}><Icon.UI name='close' /></Button.UI>
|
|
35
35
|
<div className="wy-appbar-text">Create conversation</div>
|
|
@@ -248,19 +248,19 @@ const PdfViewer = ({ src, pdfCMapsUrl, pdfWorkerUrl }: Props) => {
|
|
|
248
248
|
|
|
249
249
|
return (
|
|
250
250
|
<div className="wy-content-pdf" data-controller="pdf" data-pdf-url-value="">
|
|
251
|
-
<div className="wy-
|
|
252
|
-
<nav className="wy-
|
|
253
|
-
<div className="wy-
|
|
251
|
+
<div className="wy-toolbars-bottom">
|
|
252
|
+
<nav className="wy-toolbar">
|
|
253
|
+
<div className="wy-toolbar-buttons">
|
|
254
254
|
<input type="text" className="wy-input" ref={pageNumberRef} onChange={updatePage} onClick={select} data-pdf-target="pageNumber"/>
|
|
255
255
|
<span>/</span>
|
|
256
256
|
<span ref={totalPagesRef}>1</span>
|
|
257
257
|
</div>
|
|
258
|
-
<div className="wy-
|
|
258
|
+
<div className="wy-toolbar-buttons">
|
|
259
259
|
<button className="wy-button wy-button-icon btn-zoom-out" onClick={zoomOut} title="Zoom out"><Icon.UI name="minus" /></button>
|
|
260
260
|
<input type="text" className="wy-input" ref={zoomLevelRef} onChange={updateZoom} onClick={select} value="100%" data-pdf-target="zoomLevel"/>
|
|
261
261
|
<button className="wy-button wy-button-icon btn-zoom-in" onClick={zoomIn} title="Zoom in"><Icon.UI name="plus" /></button>
|
|
262
262
|
</div>
|
|
263
|
-
<div className="wy-
|
|
263
|
+
<div className="wy-toolbar-buttons">
|
|
264
264
|
<button className="wy-button wy-button-icon btn-fit-page" onClick={fitToWidth} title="Fit to width"><Icon.UI name="fit-width" /></button>
|
|
265
265
|
<button className="wy-button wy-button-icon" onClick={fitToPage} title="Fit to screen"><Icon.UI name="fit-screen" /></button>
|
|
266
266
|
</div>
|
|
@@ -200,14 +200,14 @@ export const PreviewText = ({ src, html = false, code = false }: TextProps) => {
|
|
|
200
200
|
code ?
|
|
201
201
|
<div className="wy-content-code wy-code" dangerouslySetInnerHTML={{ __html: textContent }}></div>
|
|
202
202
|
:
|
|
203
|
-
<div className="wy-document">
|
|
203
|
+
<div className="wy-document wy-light">
|
|
204
204
|
<div className="wy-content-html" dangerouslySetInnerHTML={{ __html: textContent }}></div>
|
|
205
205
|
</div>
|
|
206
206
|
:
|
|
207
207
|
code ?
|
|
208
208
|
<div className="wy-content-code">{textContent}</div>
|
|
209
209
|
:
|
|
210
|
-
<div className="wy-document">
|
|
210
|
+
<div className="wy-document wy-light">
|
|
211
211
|
<pre className="wy-content-text">{textContent}</pre>
|
|
212
212
|
</div>
|
|
213
213
|
}
|
|
@@ -50,7 +50,7 @@ export const ReactionsMenu = ({ id, reactions }: ReactionMenuProps) => {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
const handleReaction = async (e: any) => {
|
|
53
|
-
// check if the reaction already exists for the user
|
|
53
|
+
// check if the reaction already exists for the user
|
|
54
54
|
const existing = reactionsList.find((r) => r.has_reacted)
|
|
55
55
|
const emoji = e.target.dataset.emoji;
|
|
56
56
|
|
|
@@ -69,7 +69,7 @@ export const ReactionsMenu = ({ id, reactions }: ReactionMenuProps) => {
|
|
|
69
69
|
|
|
70
70
|
return (
|
|
71
71
|
<div className={classNames({ "wy-active": visible })} style={{ position: 'relative' }}>
|
|
72
|
-
<Button.UI onClick={toggleReactionMenu}><Icon.UI name="emoticon-plus" size={1} /></Button.UI>
|
|
72
|
+
<Button.UI className="wy-reaction-menu-button" onClick={toggleReactionMenu}><Icon.UI name="emoticon-plus" size={1.25/1.5} /></Button.UI>
|
|
73
73
|
<div className="wy-reaction-menu wy-dropdown-menu" style={{ display: visible ? 'block' : 'none', position: 'absolute', top: '-3.25rem' }}>
|
|
74
74
|
<div className="wy-reaction-picker">
|
|
75
75
|
{emojis?.map((r: string, i: number) => {
|
|
@@ -83,13 +83,19 @@ export const ReactionsMenu = ({ id, reactions }: ReactionMenuProps) => {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
export const ReactionsList = ({ id, reactions }: ReactionsProps) => {
|
|
86
|
-
const { reactionsList } = useReactions(id, reactions);
|
|
87
|
-
|
|
86
|
+
const { reactionsList } = useReactions(id, reactions);
|
|
87
|
+
|
|
88
|
+
let reactionCount = reactionsList.reduce(
|
|
89
|
+
(previousValue, currentItem) => previousValue + currentItem.count,
|
|
90
|
+
0,
|
|
91
|
+
);
|
|
92
|
+
|
|
88
93
|
return (
|
|
89
94
|
<>
|
|
90
|
-
{reactionsList && reactionsList.map((r: ReactionGroup, i: number) => {
|
|
95
|
+
{!!reactionsList && reactionsList.map((r: ReactionGroup, i: number) => {
|
|
91
96
|
return <span key={i} className="wy-reaction" title={r.count.toString()}>{r.content}</span> //r.has_reacted
|
|
92
97
|
})}
|
|
98
|
+
{reactionCount > 1 && <span className="wy-reaction-count">{reactionCount}</span>}
|
|
93
99
|
</>
|
|
94
100
|
)
|
|
95
101
|
}
|
|
@@ -22,6 +22,13 @@ const SearchUsers = ({handleSubmit, buttonTitle}: SearchUsersProps) => {
|
|
|
22
22
|
const throttledCb = useDebounce(() => refetch(), 250);
|
|
23
23
|
useEffect(throttledCb, [text])
|
|
24
24
|
|
|
25
|
+
const toggleChecked = (e: any) => {
|
|
26
|
+
let checkbox = e.currentTarget.querySelector("input[type=checkbox]");
|
|
27
|
+
if (checkbox !== e.target && !e.target.matches("label, input[type=checkbox]")) {
|
|
28
|
+
checkbox?.click();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
25
32
|
const isChecked = (id: number): boolean => {
|
|
26
33
|
return selected.find((u) => { return u.id === id }) != null;
|
|
27
34
|
}
|
|
@@ -42,25 +49,28 @@ const SearchUsers = ({handleSubmit, buttonTitle}: SearchUsersProps) => {
|
|
|
42
49
|
|
|
43
50
|
return (
|
|
44
51
|
<div className="wy-search wy-scroll-y">
|
|
45
|
-
<div className="wy-
|
|
46
|
-
<
|
|
47
|
-
|
|
52
|
+
<div className="wy-pane-group">
|
|
53
|
+
<div className="wy-search-form wy-input-group">
|
|
54
|
+
<input className="wy-search-input wy-input wy-input-filled" value={text} onChange={(e) => setText(e.target.value)} name="text" placeholder='Search...' />
|
|
55
|
+
<Button.UI type="reset" onClick={() => clear()}><Icon.UI name="close-circle" /></Button.UI>
|
|
56
|
+
<Button.UI><Icon.UI name="magnify" /></Button.UI>
|
|
57
|
+
</div>
|
|
48
58
|
</div>
|
|
49
59
|
|
|
50
60
|
<div className="wy-pane-group">
|
|
51
61
|
{data && (!data.data || data.data.length === 0) &&
|
|
52
|
-
<div className="wy-
|
|
62
|
+
<div className="wy-table-no-result">Your search did not match any people.</div>
|
|
53
63
|
}
|
|
54
|
-
<table className="wy-search-result-table">
|
|
64
|
+
<table className="wy-table wy-table-hover wy-search-result-table">
|
|
55
65
|
<tbody>
|
|
56
66
|
{data && data.data && data.data.length > 0 && data.data.map((user: MemberType) => {
|
|
57
67
|
return (
|
|
58
|
-
<tr key={user.id}
|
|
59
|
-
<td className="wy-
|
|
68
|
+
<tr key={user.id} onClick={toggleChecked}>
|
|
69
|
+
<td className="wy-table-cell-icon">
|
|
60
70
|
<Avatar src={user.avatar_url} size={24} id={user.id} presence={user.presence} name={user.display_name} />
|
|
61
71
|
</td>
|
|
62
|
-
<td><label htmlFor={'chk' + user.id}>{user.display_name}</label></td>
|
|
63
|
-
<td className="wy-
|
|
72
|
+
<td className="wy-table-cell-text"><label htmlFor={'chk' + user.id}>{user.display_name}</label></td>
|
|
73
|
+
<td className="wy-table-cell-icon"><input type="checkbox" id={'chk' + user.id} checked={isChecked(user.id)} onChange={(e) => handleSelected(e, user)} /></td>
|
|
64
74
|
</tr>
|
|
65
75
|
)
|
|
66
76
|
})}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import Avatar from "./Avatar";
|
|
3
|
-
import dayjs
|
|
3
|
+
import dayjs from 'dayjs';
|
|
4
4
|
|
|
5
5
|
type Props = {
|
|
6
6
|
id: number,
|
|
@@ -10,14 +10,20 @@ type Props = {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const SeenBy = ({ seenBy }: Props) => {
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
return (
|
|
15
|
-
|
|
16
|
-
{seenBy && seenBy.length > 0 &&
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
<>
|
|
16
|
+
{seenBy && seenBy.length > 0 &&
|
|
17
|
+
<div className="wy-readby-status">
|
|
18
|
+
{seenBy.map((member: MemberType) => {
|
|
19
|
+
const date = dayjs.utc(member.marked_at).tz(dayjs.tz.guess());
|
|
20
|
+
return (<Avatar name={`Seen by ${member.display_name} at ${date.format('LLLL')}`} src={member.avatar_url} size={16} key={member.id} />)
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
</div>
|
|
19
24
|
}
|
|
20
|
-
|
|
25
|
+
</>
|
|
26
|
+
|
|
21
27
|
)
|
|
22
28
|
}
|
|
23
29
|
|
|
@@ -27,11 +27,11 @@ const Typing = ({ children, id, context }: Props) => {
|
|
|
27
27
|
setActiveTypers([]);
|
|
28
28
|
|
|
29
29
|
client?.subscribe(`a${id}`, "typing", handleTyping);
|
|
30
|
-
client?.subscribe(`a${id}`, "
|
|
30
|
+
client?.subscribe(`a${id}`, "message_created", handleStopTyping);
|
|
31
31
|
|
|
32
32
|
return () => {
|
|
33
33
|
client?.unsubscribe(`a${id}`, "typing", handleTyping);
|
|
34
|
-
client?.unsubscribe(`a${id}`, "
|
|
34
|
+
client?.unsubscribe(`a${id}`, "message_created", handleStopTyping);
|
|
35
35
|
}
|
|
36
36
|
}, [id])
|
|
37
37
|
|
|
@@ -87,29 +87,28 @@ const Typing = ({ children, id, context }: Props) => {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
const setTypers = (
|
|
90
|
+
const setTypers = (actor: MemberTypingType) => {
|
|
91
91
|
|
|
92
92
|
// remove existing typing events by this user (can only type in one conversation at a time)
|
|
93
93
|
activeTypers.forEach(function (item: any, index: number) {
|
|
94
|
-
if (item.member.id ===
|
|
94
|
+
if (item.member.id === actor.id) {
|
|
95
95
|
setActiveTypers(activeTypers.splice(index, 1));
|
|
96
96
|
}
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
// track time when we received this event
|
|
100
|
-
|
|
101
|
-
setActiveTypers([...activeTypers,
|
|
100
|
+
actor.time = Date.now();
|
|
101
|
+
setActiveTypers([...activeTypers, actor]);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
const handleTyping = useCallback((
|
|
105
|
-
if (
|
|
106
|
-
setTypers(
|
|
104
|
+
const handleTyping = useCallback((realtimeEvent: RealtimeTyping) => {
|
|
105
|
+
if (realtimeEvent.conversation.id === id && realtimeEvent.actor.id !== user.id) {
|
|
106
|
+
setTypers(realtimeEvent.actor);
|
|
107
107
|
}
|
|
108
|
-
|
|
109
108
|
}, [id, context, activeTypers]);
|
|
110
109
|
|
|
111
|
-
const handleStopTyping = useCallback((
|
|
112
|
-
if (
|
|
110
|
+
const handleStopTyping = useCallback((realtimeEvent: RealtimeMessage) => {
|
|
111
|
+
if (realtimeEvent.message.app_id === id) {
|
|
113
112
|
setActiveTypers([]);
|
|
114
113
|
}
|
|
115
114
|
}, [id, context, activeTypers]);
|
|
@@ -2,7 +2,7 @@ import React, { createContext} from "react";
|
|
|
2
2
|
import useUser from "../hooks/useUser";
|
|
3
3
|
|
|
4
4
|
export const UserContext = createContext<UserContextProps>({
|
|
5
|
-
user: { id: -1, username: "anonymous", name: "Anonymous", email: "",
|
|
5
|
+
user: { id: -1, uid: "", username: "anonymous", name: "Anonymous", email: "", display_name: "", presence: "", avatar_url: "" }
|
|
6
6
|
});
|
|
7
7
|
|
|
8
8
|
type Props = {
|
|
@@ -7,7 +7,7 @@ import utc from 'dayjs/plugin/utc';
|
|
|
7
7
|
import timezone from 'dayjs/plugin/timezone';
|
|
8
8
|
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
|
9
9
|
import PreviewProvider from "./PreviewContext";
|
|
10
|
-
import detectScrollbars from '../utils/scrollbarDetection';
|
|
10
|
+
import { detectScrollbars, detectScrollbarAdjustments } from '../utils/scrollbarDetection';
|
|
11
11
|
|
|
12
12
|
dayjs.extend(relativeTime);
|
|
13
13
|
dayjs.extend(utc);
|
|
@@ -41,14 +41,14 @@ const WeavyProvider = ({ children, client, options }: WeavyProviderProperties) =
|
|
|
41
41
|
teamsAuthenticationUrl: undefined,
|
|
42
42
|
enableCloudFiles: true,
|
|
43
43
|
enableScrollbarDetection: true,
|
|
44
|
-
filebrowserUrl: "https://filebrowser.
|
|
44
|
+
filebrowserUrl: "https://filebrowser.weavy.io/v14/",
|
|
45
45
|
reactions: ['😍', '😎', '😉', '😜', '👍']
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
let opts = { ...defaultOptions, ...options }
|
|
49
49
|
|
|
50
50
|
if (opts.enableScrollbarDetection) {
|
|
51
|
-
detectScrollbars();
|
|
51
|
+
detectScrollbars().then(() => detectScrollbarAdjustments());
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
if(!client){
|
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
import { useContext } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useInfiniteQuery } from "react-query";
|
|
3
3
|
import { WeavyContext } from "../contexts/WeavyContext";
|
|
4
4
|
|
|
5
5
|
/// GET all conversations
|
|
6
|
-
export default function useConversations() {
|
|
6
|
+
export default function useConversations(options: any) {
|
|
7
7
|
const { client } = useContext(WeavyContext);
|
|
8
|
+
const PAGE_SIZE = 25;
|
|
8
9
|
|
|
9
10
|
if (!client) {
|
|
10
11
|
throw new Error('useConversations must be used within an WeavyProvider');
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
const getConversations = async () => {
|
|
14
|
-
|
|
14
|
+
const getConversations = async (opt: any) => {
|
|
15
|
+
let skip = opt.pageParam || 0;
|
|
16
|
+
const response = await client.get("/api/conversations?contextual=false&skip=" + skip + "&top=" + PAGE_SIZE);
|
|
15
17
|
const data = await response.json();
|
|
16
18
|
return data;
|
|
17
19
|
};
|
|
18
20
|
|
|
21
|
+
var opts = {
|
|
22
|
+
...options,
|
|
23
|
+
getNextPageParam: (lastPage:any, pages:any) => {
|
|
24
|
+
if (lastPage?.end < lastPage?.count) {
|
|
25
|
+
return pages.length * PAGE_SIZE;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
19
29
|
|
|
20
|
-
return
|
|
30
|
+
return useInfiniteQuery<ConversationsResult>("conversations", getConversations, opts);
|
|
21
31
|
}
|
|
@@ -34,22 +34,18 @@ export default function useMutateMessage() {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
return useMutation(mutateMessage, {
|
|
37
|
-
onSuccess: (data:
|
|
37
|
+
onSuccess: (data: MessageType, variables: any, context: any) => {
|
|
38
38
|
|
|
39
39
|
const previousData = queryClient.getQueryData<any>(['messages', variables.id]);
|
|
40
40
|
if (previousData && previousData.pages) {
|
|
41
41
|
// update cache
|
|
42
42
|
const newPagesArray = previousData.pages.map((page: any, i: number) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
43
|
// remove temp message
|
|
47
44
|
if (i === 0) {
|
|
48
45
|
page.data = [
|
|
49
46
|
...page.data.filter((message: MessageType) => message.id !== context.tempId),
|
|
50
47
|
data
|
|
51
48
|
]
|
|
52
|
-
|
|
53
49
|
}
|
|
54
50
|
|
|
55
51
|
return page;
|
|
@@ -15,11 +15,13 @@ export default function useMutateRead() {
|
|
|
15
15
|
|
|
16
16
|
type MutateProps = {
|
|
17
17
|
id: number | null,
|
|
18
|
-
read: boolean
|
|
18
|
+
read: boolean,
|
|
19
|
+
messageId: number | null
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
const mutateRead = async ({ id, read }: MutateProps) => {
|
|
22
|
-
const
|
|
22
|
+
const mutateRead = async ({ id, read, messageId }: MutateProps) => {
|
|
23
|
+
const url = read ? `/api/conversations/${id}/read?messageId=${messageId}`: `/api/conversations/${id}/read`;
|
|
24
|
+
const response = await client.post(url, !read ? "DELETE": "PUT", "")
|
|
23
25
|
return response.json();
|
|
24
26
|
};
|
|
25
27
|
|
package/src/hooks/usePresence.ts
CHANGED
|
@@ -10,11 +10,10 @@ export default function usePresence() {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
useEffect(() => {
|
|
13
|
-
client.subscribe(
|
|
13
|
+
client.subscribe(null, "online", handlePresenceChange)
|
|
14
14
|
}, []);
|
|
15
15
|
|
|
16
|
-
const handlePresenceChange = (data: any) => {
|
|
17
|
-
|
|
16
|
+
const handlePresenceChange = (data: any) => {
|
|
18
17
|
if (Array.isArray(data)) {
|
|
19
18
|
document.querySelectorAll(".wy-presence").forEach(function (item) {
|
|
20
19
|
item.classList.remove("wy-presence-active");
|