@weavy/uikit-react 12.1.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.
Files changed (280) hide show
  1. package/changelog.md +60 -0
  2. package/dist/cjs/index.js +28 -6
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/types/client/WeavyClient.d.ts +8 -1
  5. package/dist/cjs/types/components/Attachment.d.ts +2 -1
  6. package/dist/cjs/types/components/Chat.d.ts +1 -1
  7. package/dist/cjs/types/components/Messages.d.ts +3 -1
  8. package/dist/cjs/types/components/PdfViewer.d.ts +3 -1
  9. package/dist/cjs/types/components/Preview.d.ts +8 -10
  10. package/dist/cjs/types/contexts/PreviewContext.d.ts +2 -1
  11. package/dist/cjs/types/contexts/WeavyContext.d.ts +2 -3
  12. package/dist/cjs/types/hooks/useConversations.d.ts +1 -1
  13. package/dist/cjs/types/hooks/useMutateMessage.d.ts +1 -1
  14. package/dist/cjs/types/hooks/useMutateRead.d.ts +1 -0
  15. package/dist/cjs/types/types/Chat.d.ts +2 -1
  16. package/dist/cjs/types/types/Message.d.ts +2 -0
  17. package/dist/cjs/types/types/types.d.ts +63 -9
  18. package/dist/cjs/types/ui/Spinner.d.ts +10 -0
  19. package/dist/cjs/types/utils/fileUtilities.d.ts +1 -4
  20. package/dist/css/weavy-chat.css +1803 -1041
  21. package/dist/css/weavy-messenger.css +2141 -1360
  22. package/dist/css/weavy.css +1943 -1162
  23. package/dist/esm/index.js +28 -6
  24. package/dist/esm/index.js.map +1 -1
  25. package/dist/esm/types/client/WeavyClient.d.ts +8 -1
  26. package/dist/esm/types/components/Attachment.d.ts +2 -1
  27. package/dist/esm/types/components/Chat.d.ts +1 -1
  28. package/dist/esm/types/components/Messages.d.ts +3 -1
  29. package/dist/esm/types/components/PdfViewer.d.ts +3 -1
  30. package/dist/esm/types/components/Preview.d.ts +8 -10
  31. package/dist/esm/types/contexts/PreviewContext.d.ts +2 -1
  32. package/dist/esm/types/contexts/WeavyContext.d.ts +2 -3
  33. package/dist/esm/types/hooks/useConversations.d.ts +1 -1
  34. package/dist/esm/types/hooks/useMutateMessage.d.ts +1 -1
  35. package/dist/esm/types/hooks/useMutateRead.d.ts +1 -0
  36. package/dist/esm/types/types/Chat.d.ts +2 -1
  37. package/dist/esm/types/types/Message.d.ts +2 -0
  38. package/dist/esm/types/types/types.d.ts +63 -9
  39. package/dist/esm/types/ui/Spinner.d.ts +10 -0
  40. package/dist/esm/types/utils/fileUtilities.d.ts +1 -4
  41. package/dist/index.d.ts +15 -7
  42. package/package.json +2 -2
  43. package/rollup.config.js +3 -1
  44. package/src/client/WeavyClient.ts +105 -29
  45. package/src/components/Attachment.tsx +8 -7
  46. package/src/components/Chat.tsx +8 -7
  47. package/src/components/Conversation.tsx +29 -23
  48. package/src/components/ConversationBadge.tsx +7 -5
  49. package/src/components/ConversationForm.tsx +1 -1
  50. package/src/components/ConversationList.tsx +59 -11
  51. package/src/components/ConversationListItem.tsx +71 -54
  52. package/src/components/FileBrowser.tsx +53 -50
  53. package/src/components/MeetingCard.tsx +35 -13
  54. package/src/components/Meetings.tsx +1 -1
  55. package/src/components/Message.tsx +41 -41
  56. package/src/components/Messages.tsx +62 -61
  57. package/src/components/Messenger.tsx +7 -2
  58. package/src/components/NewConversation.tsx +1 -1
  59. package/src/components/PdfViewer.tsx +93 -88
  60. package/src/components/Preview.tsx +115 -54
  61. package/src/components/Reactions.tsx +11 -5
  62. package/src/components/SearchUsers.tsx +21 -11
  63. package/src/components/SeenBy.tsx +13 -7
  64. package/src/components/Typing.tsx +11 -12
  65. package/src/contexts/PreviewContext.tsx +90 -16
  66. package/src/contexts/UserContext.tsx +1 -1
  67. package/src/contexts/WeavyContext.tsx +10 -7
  68. package/src/hooks/useBadge.ts +2 -6
  69. package/src/hooks/useChat.ts +3 -14
  70. package/src/hooks/useConversation.ts +1 -7
  71. package/src/hooks/useConversations.ts +15 -11
  72. package/src/hooks/useFileUploader.ts +6 -8
  73. package/src/hooks/useMembers.ts +1 -7
  74. package/src/hooks/useMessages.ts +1 -7
  75. package/src/hooks/useMutateChat.ts +6 -11
  76. package/src/hooks/useMutateConversation.ts +7 -10
  77. package/src/hooks/useMutateConversationName.ts +10 -12
  78. package/src/hooks/useMutateDeleteReaction.ts +3 -8
  79. package/src/hooks/useMutateExternalBlobs.ts +6 -11
  80. package/src/hooks/useMutateMeeting.ts +6 -11
  81. package/src/hooks/useMutateMembers.ts +8 -13
  82. package/src/hooks/useMutateMessage.ts +9 -18
  83. package/src/hooks/useMutatePinned.ts +3 -8
  84. package/src/hooks/useMutateReaction.ts +6 -12
  85. package/src/hooks/useMutateRead.ts +5 -12
  86. package/src/hooks/useMutateRemoveMembers.ts +7 -12
  87. package/src/hooks/useMutateTyping.ts +6 -11
  88. package/src/hooks/usePresence.ts +2 -3
  89. package/src/hooks/useReactions.ts +11 -12
  90. package/src/hooks/useSearchUsers.ts +1 -6
  91. package/src/hooks/useUser.ts +3 -14
  92. package/src/scss/theme/_alert.scss +61 -63
  93. package/src/scss/theme/_appbar.scss +105 -28
  94. package/src/scss/theme/_avatar.scss +23 -28
  95. package/src/scss/theme/_badge.scss +26 -18
  96. package/src/scss/theme/_buttons.scss +107 -52
  97. package/src/scss/theme/_card.scss +102 -4
  98. package/src/scss/theme/_checkbox.scss +16 -20
  99. package/src/scss/theme/_code-vscode-dark.scss +184 -0
  100. package/src/scss/theme/_code-vscode-light.scss +179 -0
  101. package/src/scss/theme/_code.scss +9 -114
  102. package/src/scss/theme/_comment-editor-cm.scss +97 -0
  103. package/src/scss/theme/_comment-editor.scss +129 -0
  104. package/src/scss/theme/_comments.scss +66 -0
  105. package/src/scss/theme/_content.scss +33 -5
  106. package/src/scss/theme/_conversations.scss +19 -78
  107. package/src/scss/theme/_dropdown.scss +102 -15
  108. package/src/scss/theme/_embed.scss +135 -0
  109. package/src/scss/theme/_facepile.scss +11 -0
  110. package/src/scss/theme/_filebrowser.scss +1 -1
  111. package/src/scss/theme/_files.scss +77 -48
  112. package/src/scss/theme/_grid.scss +8 -0
  113. package/src/scss/theme/_icons.scss +155 -19
  114. package/src/scss/theme/_image-grid.scss +7 -10
  115. package/src/scss/theme/_input.scss +160 -0
  116. package/src/scss/theme/_item.scss +169 -0
  117. package/src/scss/theme/_list.scss +57 -0
  118. package/src/scss/theme/_meeting.scss +11 -0
  119. package/src/scss/theme/_message-editor-cm.scss +95 -0
  120. package/src/scss/theme/_message-editor.scss +66 -20
  121. package/src/scss/theme/_messages.scss +51 -105
  122. package/src/scss/theme/_meta.scss +12 -0
  123. package/src/scss/theme/_overlays.scss +31 -76
  124. package/src/scss/theme/_pager.scss +5 -1
  125. package/src/scss/theme/_pane.scss +13 -2
  126. package/src/scss/theme/_panels.scss +34 -25
  127. package/src/scss/theme/_picker-list.scss +5 -3
  128. package/src/scss/theme/_placeholder.scss +19 -0
  129. package/src/scss/theme/_poll.scss +49 -0
  130. package/src/scss/theme/_post-editor-cm.scss +100 -0
  131. package/src/scss/theme/_post-editor.scss +127 -0
  132. package/src/scss/theme/_post.scss +83 -0
  133. package/src/scss/theme/_preview-code.scss +14 -0
  134. package/src/scss/theme/_preview-embed.scss +11 -5
  135. package/src/scss/theme/_preview-image.scss +8 -26
  136. package/src/scss/theme/_preview-media.scss +1 -0
  137. package/src/scss/theme/_preview-pdf.scss +10 -15
  138. package/src/scss/theme/_preview-text.scss +1 -1
  139. package/src/scss/theme/_preview.scss +59 -76
  140. package/src/scss/theme/_reactions.scss +48 -17
  141. package/src/scss/theme/_sheet.scss +59 -0
  142. package/src/scss/theme/_sidebar.scss +86 -0
  143. package/src/scss/theme/_spinner.scss +11 -7
  144. package/src/scss/theme/_tab.scss +72 -0
  145. package/src/scss/theme/_tables.scss +70 -23
  146. package/src/scss/theme/_toasts.scss +56 -26
  147. package/src/scss/theme/_type.scss +41 -0
  148. package/src/scss/theme/{mixins → base}/_backdrop.scss +0 -0
  149. package/src/scss/theme/{bootstrap/mixins → base}/_breakpoints.scss +9 -0
  150. package/src/scss/theme/base/_colors.scss +315 -0
  151. package/src/scss/theme/base/_md.scss +19 -0
  152. package/src/scss/theme/base/_palette.scss +130 -0
  153. package/src/scss/theme/{mixins → base}/_position.scss +5 -5
  154. package/src/scss/theme/base/_reboot.scss +51 -0
  155. package/src/scss/theme/base/_scroll.scss +180 -0
  156. package/src/scss/theme/base/_svg.scss +49 -0
  157. package/src/scss/theme/base/_text.scss +23 -0
  158. package/src/scss/theme/base/_vars.scss +203 -0
  159. package/src/scss/theme/{fonts → base/fonts}/_fontmapping-roboto.scss +0 -0
  160. package/src/scss/theme/{fonts → base/fonts}/_fontmapping-segoe-ui.scss +0 -0
  161. package/src/scss/theme/base/fonts/_index.scss +2 -0
  162. package/src/scss/weavy-chat.scss +11 -4
  163. package/src/scss/weavy-messenger.scss +38 -21
  164. package/src/types/Chat.ts +2 -1
  165. package/src/types/Message.ts +3 -1
  166. package/src/types/types.ts +72 -10
  167. package/src/ui/Icon.tsx +1 -1
  168. package/src/ui/Spinner.tsx +19 -0
  169. package/src/utils/fileUtilities.ts +11 -125
  170. package/src/utils/infiniteScroll.js +11 -2
  171. package/src/utils/postal-parent.js +398 -0
  172. package/src/utils/promise.js +187 -0
  173. package/src/utils/scrollbarDetection.js +68 -9
  174. package/src/utils/utils.js +547 -0
  175. package/src/scss/theme/_attachments.scss +0 -74
  176. package/src/scss/theme/_cm-editor.scss +0 -42
  177. package/src/scss/theme/_colors.scss +0 -520
  178. package/src/scss/theme/_config.scss +0 -6
  179. package/src/scss/theme/_inputs.scss +0 -28
  180. package/src/scss/theme/_nav.scss +0 -52
  181. package/src/scss/theme/_palette.scss +0 -165
  182. package/src/scss/theme/_preview-icon.scss +0 -41
  183. package/src/scss/theme/_reboot.scss +0 -41
  184. package/src/scss/theme/_root.scss +0 -2
  185. package/src/scss/theme/_scroll.scss +0 -55
  186. package/src/scss/theme/_search.scss +0 -68
  187. package/src/scss/theme/_turbo.scss +0 -17
  188. package/src/scss/theme/_variables.scss +0 -139
  189. package/src/scss/theme/bootstrap/_accordion.scss +0 -146
  190. package/src/scss/theme/bootstrap/_alert.scss +0 -71
  191. package/src/scss/theme/bootstrap/_badge.scss +0 -38
  192. package/src/scss/theme/bootstrap/_breadcrumb.scss +0 -40
  193. package/src/scss/theme/bootstrap/_button-group.scss +0 -142
  194. package/src/scss/theme/bootstrap/_buttons.scss +0 -186
  195. package/src/scss/theme/bootstrap/_card.scss +0 -234
  196. package/src/scss/theme/bootstrap/_carousel.scss +0 -229
  197. package/src/scss/theme/bootstrap/_close.scss +0 -40
  198. package/src/scss/theme/bootstrap/_containers.scss +0 -41
  199. package/src/scss/theme/bootstrap/_dropdown.scss +0 -248
  200. package/src/scss/theme/bootstrap/_forms.scss +0 -9
  201. package/src/scss/theme/bootstrap/_functions.scss +0 -302
  202. package/src/scss/theme/bootstrap/_grid.scss +0 -33
  203. package/src/scss/theme/bootstrap/_helpers.scss +0 -10
  204. package/src/scss/theme/bootstrap/_images.scss +0 -42
  205. package/src/scss/theme/bootstrap/_list-group.scss +0 -191
  206. package/src/scss/theme/bootstrap/_maps.scss +0 -54
  207. package/src/scss/theme/bootstrap/_mixins.scss +0 -43
  208. package/src/scss/theme/bootstrap/_modal.scss +0 -237
  209. package/src/scss/theme/bootstrap/_nav.scss +0 -172
  210. package/src/scss/theme/bootstrap/_navbar.scss +0 -276
  211. package/src/scss/theme/bootstrap/_offcanvas.scss +0 -143
  212. package/src/scss/theme/bootstrap/_pagination.scss +0 -109
  213. package/src/scss/theme/bootstrap/_placeholders.scss +0 -51
  214. package/src/scss/theme/bootstrap/_popover.scss +0 -196
  215. package/src/scss/theme/bootstrap/_progress.scss +0 -59
  216. package/src/scss/theme/bootstrap/_reboot.scss +0 -610
  217. package/src/scss/theme/bootstrap/_root.scss +0 -73
  218. package/src/scss/theme/bootstrap/_spinners.scss +0 -85
  219. package/src/scss/theme/bootstrap/_tables.scss +0 -164
  220. package/src/scss/theme/bootstrap/_toasts.scss +0 -70
  221. package/src/scss/theme/bootstrap/_tooltip.scss +0 -120
  222. package/src/scss/theme/bootstrap/_transitions.scss +0 -27
  223. package/src/scss/theme/bootstrap/_type.scss +0 -106
  224. package/src/scss/theme/bootstrap/_utilities.scss +0 -647
  225. package/src/scss/theme/bootstrap/_variables.scss +0 -1633
  226. package/src/scss/theme/bootstrap/forms/_floating-labels.scss +0 -74
  227. package/src/scss/theme/bootstrap/forms/_form-check.scss +0 -175
  228. package/src/scss/theme/bootstrap/forms/_form-control.scss +0 -194
  229. package/src/scss/theme/bootstrap/forms/_form-range.scss +0 -91
  230. package/src/scss/theme/bootstrap/forms/_form-select.scss +0 -71
  231. package/src/scss/theme/bootstrap/forms/_form-text.scss +0 -11
  232. package/src/scss/theme/bootstrap/forms/_input-group.scss +0 -129
  233. package/src/scss/theme/bootstrap/forms/_labels.scss +0 -36
  234. package/src/scss/theme/bootstrap/forms/_validation.scss +0 -12
  235. package/src/scss/theme/bootstrap/helpers/_clearfix.scss +0 -3
  236. package/src/scss/theme/bootstrap/helpers/_color-bg.scss +0 -10
  237. package/src/scss/theme/bootstrap/helpers/_colored-links.scss +0 -12
  238. package/src/scss/theme/bootstrap/helpers/_position.scss +0 -36
  239. package/src/scss/theme/bootstrap/helpers/_ratio.scss +0 -26
  240. package/src/scss/theme/bootstrap/helpers/_stacks.scss +0 -15
  241. package/src/scss/theme/bootstrap/helpers/_stretched-link.scss +0 -15
  242. package/src/scss/theme/bootstrap/helpers/_text-truncation.scss +0 -7
  243. package/src/scss/theme/bootstrap/helpers/_visually-hidden.scss +0 -8
  244. package/src/scss/theme/bootstrap/helpers/_vr.scss +0 -8
  245. package/src/scss/theme/bootstrap/mixins/_alert.scss +0 -15
  246. package/src/scss/theme/bootstrap/mixins/_backdrop.scss +0 -14
  247. package/src/scss/theme/bootstrap/mixins/_banner.scss +0 -9
  248. package/src/scss/theme/bootstrap/mixins/_border-radius.scss +0 -78
  249. package/src/scss/theme/bootstrap/mixins/_box-shadow.scss +0 -18
  250. package/src/scss/theme/bootstrap/mixins/_buttons.scss +0 -70
  251. package/src/scss/theme/bootstrap/mixins/_caret.scss +0 -64
  252. package/src/scss/theme/bootstrap/mixins/_clearfix.scss +0 -9
  253. package/src/scss/theme/bootstrap/mixins/_color-scheme.scss +0 -7
  254. package/src/scss/theme/bootstrap/mixins/_container.scss +0 -11
  255. package/src/scss/theme/bootstrap/mixins/_deprecate.scss +0 -10
  256. package/src/scss/theme/bootstrap/mixins/_forms.scss +0 -152
  257. package/src/scss/theme/bootstrap/mixins/_gradients.scss +0 -47
  258. package/src/scss/theme/bootstrap/mixins/_grid.scss +0 -151
  259. package/src/scss/theme/bootstrap/mixins/_image.scss +0 -16
  260. package/src/scss/theme/bootstrap/mixins/_list-group.scss +0 -24
  261. package/src/scss/theme/bootstrap/mixins/_lists.scss +0 -7
  262. package/src/scss/theme/bootstrap/mixins/_pagination.scss +0 -10
  263. package/src/scss/theme/bootstrap/mixins/_reset-text.scss +0 -17
  264. package/src/scss/theme/bootstrap/mixins/_resize.scss +0 -6
  265. package/src/scss/theme/bootstrap/mixins/_table-variants.scss +0 -24
  266. package/src/scss/theme/bootstrap/mixins/_text-truncate.scss +0 -8
  267. package/src/scss/theme/bootstrap/mixins/_transition.scss +0 -26
  268. package/src/scss/theme/bootstrap/mixins/_utilities.scss +0 -97
  269. package/src/scss/theme/bootstrap/mixins/_visually-hidden.scss +0 -29
  270. package/src/scss/theme/bootstrap/utilities/_api.scss +0 -47
  271. package/src/scss/theme/bootstrap/vendor/_rfs.scss +0 -354
  272. package/src/scss/theme/bs/_badge.scss +0 -20
  273. package/src/scss/theme/bs/_buttons.scss +0 -185
  274. package/src/scss/theme/bs/_dropdown.scss +0 -86
  275. package/src/scss/theme/bs/_forms.scss +0 -161
  276. package/src/scss/theme/bs/_list-group.scss +0 -73
  277. package/src/scss/theme/bs/_tables.scss +0 -46
  278. package/src/scss/theme/fonts/_index.scss +0 -2
  279. package/src/scss/theme/mixins/_palette.scss +0 -165
  280. package/src/scss/theme/mixins/_scrollbar.scss +0 -110
@@ -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-search-form wy-pane-group">
46
- <Button.UI><Icon.UI name="magnify" /></Button.UI>
47
- <input className="wy-search-input" value={text} onChange={(e) => setText(e.target.value)} name="text" placeholder='Search...' />
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
- {data && data.data.length === 0 &&
52
- <div className="wy-search-no-result">Your search did not match any people.</div>
61
+ {data && (!data.data || data.data.length === 0) &&
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
- {data && data.data.length > 0 && data.data.map((user: MemberType) => {
66
+ {data && data.data && data.data.length > 0 && data.data.map((user: MemberType) => {
57
67
  return (
58
- <tr key={user.id} className="wy-search-result-table-checkbox">
59
- <td className="wy-search-result-table-icon">
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-search-result-table-icon"><input type="checkbox" id={'chk' + user.id} checked={isChecked(user.id)} onChange={(e) => handleSelected(e, user)} /></td>
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 from '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
- <div className="wy-readby-status">
16
- {seenBy && seenBy.length > 0 && seenBy.map((member: MemberType) => {
17
- const date = dayjs.utc(member.read_at).tz(dayjs.tz.guess());
18
- return (<Avatar name={`Seen by ${member.name} at ${date.format('LLLL')}`} src={member.avatar_url} size={16} key={member.id}/>)})
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
- </div>
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}`, "message-inserted", handleStopTyping);
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}`, "message-inserted", handleStopTyping);
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 = (data: any) => {
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 === data.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
- data.member.time = Date.now();
101
- setActiveTypers([...activeTypers, data.member]);
100
+ actor.time = Date.now();
101
+ setActiveTypers([...activeTypers, actor]);
102
102
  }
103
103
 
104
- const handleTyping = useCallback((data: any) => {
105
- if (data.conversation.id === id && data.member.id !== user.id) {
106
- setTypers(data);
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((data: any) => {
112
- if (data.app_id === id) {
110
+ const handleStopTyping = useCallback((realtimeEvent: RealtimeMessage) => {
111
+ if (realtimeEvent.message.app_id === id) {
113
112
  setActiveTypers([]);
114
113
  }
115
114
  }, [id, context, activeTypers]);
@@ -2,17 +2,25 @@ import React, { createContext, useEffect, useState } from "react";
2
2
  import Overlay from '../ui/Overlay';
3
3
  import Button from '../ui/Button';
4
4
  import Icon from '../ui/Icon';
5
+ import Preview from '../components/Preview';
6
+ import { getIcon, getExtension } from "../utils/fileUtilities";
5
7
 
6
8
  export const PreviewContext = createContext<PreviewContextProps>({
7
9
  openPreview: Function,
8
10
  closePreview: Function
9
11
  });
10
12
 
13
+ function allowedKeyTarget(e: any) {
14
+ var notInputField = !e.target.matches("input, textarea, select") && !e.target.closest('[contenteditable="true"]');
15
+ return notInputField;
16
+ }
17
+
11
18
  type Props = {
19
+ client: any,
12
20
  children: React.ReactNode
13
21
  }
14
22
 
15
- const PreviewProvider = ({ children }: Props) => {
23
+ const PreviewProvider = ({ client, children }: Props) => {
16
24
  const [modalPreviewOpen, setModalPreviewOpen] = useState<boolean>(false);
17
25
  const [attachments, setAttachments] = useState<AttachmentType[]>([]);
18
26
  const [activeAttachment, setActiveAttchment] = useState<AttachmentType>();
@@ -53,51 +61,117 @@ const PreviewProvider = ({ children }: Props) => {
53
61
  }
54
62
 
55
63
  const handleDownload = () => {
56
- window.open(`${activeAttachment?.download_url}&d=1`, "_blank");
64
+ window.open(`${activeAttachment?.download_url}&d=1`, "_top");
65
+ }
66
+
67
+ const handleExternal = () => {
68
+ window.open(`${activeAttachment?.external_url}`, "_blank");
69
+ }
70
+
71
+ const handleApplication = () => {
72
+ // TODO: Change to webdav-url
73
+ window.open(`${activeAttachment?.application_url}`, "_top");
74
+ }
75
+
76
+ // Keyboard handlers
77
+ useEffect(() => {
78
+ if (activeAttachment) {
79
+ const handleKey = (e: any) => {
80
+ if (e.which === 27) { // ESC
81
+ e.stopImmediatePropagation();
82
+ closePreview()
83
+ } else if (e.which === 37) { // LEFT
84
+ if (allowedKeyTarget(e)) {
85
+ setCurrentAttachmentId(previousAttachmentId);
86
+ }
87
+ } else if (e.which === 39) { // RIGHT
88
+ if (allowedKeyTarget(e)) {
89
+ setCurrentAttachmentId(nextAttachmentId);
90
+ }
91
+ }
92
+ };
93
+
94
+ document.addEventListener("keyup", handleKey);
95
+
96
+ return () => {
97
+ // cleanup
98
+ document.removeEventListener("keyup", handleKey);
99
+ }
100
+ }
101
+
102
+ }, [activeAttachment])
103
+
104
+
105
+ let activeExt: string = getExtension(activeAttachment?.name || '');
106
+ let activeIcon: string = getIcon(activeAttachment?.name || '').icon;
107
+
108
+ let activeSrc = activeAttachment?.preview_url || activeAttachment?.download_url || '';
109
+
110
+ // Let GIF and SVG display raw content
111
+ let animatedImage = activeAttachment?.preview_format === "image" && (activeExt === ".gif" || activeExt === ".svg");
112
+ if (animatedImage) {
113
+ activeSrc = activeAttachment?.download_url || '';
57
114
  }
58
115
 
59
116
  return (
60
117
  <>
61
-
62
118
  <PreviewContext.Provider value={{ openPreview: openPreview, closePreview: closePreview }}>
63
119
  {children}
64
120
  </PreviewContext.Provider>
65
121
 
66
- <Overlay.UI isOpen={modalPreviewOpen} className={'wy-dark'}>
67
- <header className={'wy-appbars'}>
68
- <nav className={'wy-appbar'}>
122
+ <Overlay.UI isOpen={modalPreviewOpen} className="wy-dark">
123
+ <header className="wy-appbars">
124
+ <nav className="wy-appbar">
69
125
  <Button.UI onClick={closePreview}><Icon.UI name='close' /></Button.UI>
70
- <div className={'wy-appbar-text'}>
126
+ <div className="wy-appbar-text">
71
127
  {activeAttachment &&
72
128
  <span>{activeAttachment.name}</span>
73
129
  }
74
130
 
75
131
  </div>
76
- <Button.UI onClick={handleDownload}><Icon.UI name='download' /></Button.UI>
132
+ <div className="wy-appbar-buttons">
133
+ {activeAttachment?.application_url ?
134
+ <Button.UI onClick={handleApplication} title={`Open in app`}><Icon.UI name={activeIcon} /></Button.UI>
135
+ :
136
+ activeAttachment?.external_url ?
137
+ <Button.UI onClick={handleExternal} title={`Open in ${activeAttachment.provider}`}><Icon.UI name={activeIcon} /></Button.UI>
138
+ :
139
+ <Button.UI onClick={handleDownload}><Icon.UI name='download' /></Button.UI>
140
+ }
141
+ </div>
142
+
77
143
  </nav>
78
144
  </header>
79
- <div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100%" }}>
80
-
145
+ <div className="wy-preview wy-scroll-y wy-scroll-x">
81
146
  {activeAttachment &&
82
147
  <>
83
148
  {previousAttachmentId &&
84
- <Button.UI onClick={handlePrevious} style={{ position: "absolute", top: "50%", left: "0" }}><Icon.UI name="previous" /></Button.UI>
149
+ <nav className="wy-nav-prev"><Button.UI onClick={handlePrevious}><Icon.UI name="previous" /></Button.UI></nav>
85
150
  }
86
151
  {nextAttachmentId &&
87
- <Button.UI onClick={handleNext} style={{ position: "absolute", top: "50%", right: "0", }}><Icon.UI name="next" /></Button.UI>
152
+ <nav className="wy-nav-next"><Button.UI onClick={handleNext}><Icon.UI name="next" /></Button.UI></nav>
88
153
  }
89
-
90
- <img style={{ maxWidth: "100%" }} src={activeAttachment.preview_url} />
154
+ <Preview
155
+ client={client}
156
+ src={activeSrc}
157
+ link={activeAttachment.external_url}
158
+ format={activeAttachment.preview_format}
159
+ name={activeAttachment.name}
160
+ icon={activeIcon}
161
+ width={activeAttachment.width}
162
+ height={activeAttachment.height}
163
+ mediaType={activeAttachment.media_type}
164
+ provider={activeAttachment.provider}
165
+ />
91
166
  </>
92
167
  }
93
-
94
168
  </div>
95
169
  </Overlay.UI>
96
170
 
97
171
  </>
98
172
 
99
-
100
173
  )
101
174
  };
102
175
 
103
176
  export default PreviewProvider;
177
+
@@ -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: "", title: "", presence: "", avatar_url: "" }
5
+ user: { id: -1, uid: "", username: "anonymous", name: "Anonymous", email: "", display_name: "", presence: "", avatar_url: "" }
6
6
  });
7
7
 
8
8
  type Props = {
@@ -7,8 +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 WeavyClient from "../client/WeavyClient";
11
- import detectScrollbars from '../utils/scrollbarDetection';
10
+ import { detectScrollbars, detectScrollbarAdjustments } from '../utils/scrollbarDetection';
12
11
 
13
12
  dayjs.extend(relativeTime);
14
13
  dayjs.extend(utc);
@@ -21,7 +20,7 @@ export const WeavyContext = createContext<WeavyContextProps>({
21
20
  options: {}
22
21
  });
23
22
 
24
- type Props = {
23
+ type WeavyProviderProperties = {
25
24
  children: React.ReactNode,
26
25
  client: WeavyClient,
27
26
  options?: WeavyContextOptions
@@ -35,21 +34,25 @@ const queryClient = new QueryClient({
35
34
  },
36
35
  })
37
36
 
38
- const WeavyProvider = ({ children, client, options }: Props) => {
37
+ const WeavyProvider = ({ children, client, options }: WeavyProviderProperties) => {
39
38
 
40
39
  let defaultOptions: WeavyContextOptions = {
41
40
  zoomAuthenticationUrl: undefined,
42
41
  teamsAuthenticationUrl: undefined,
43
42
  enableCloudFiles: true,
44
43
  enableScrollbarDetection: true,
45
- filebrowserUrl: "https://filebrowser.weavycloud.com/index10.html",
44
+ filebrowserUrl: "https://filebrowser.weavy.io/v14/",
46
45
  reactions: ['😍', '😎', '😉', '😜', '👍']
47
46
  };
48
47
 
49
48
  let opts = { ...defaultOptions, ...options }
50
49
 
51
50
  if (opts.enableScrollbarDetection) {
52
- detectScrollbars();
51
+ detectScrollbars().then(() => detectScrollbarAdjustments());
52
+ }
53
+
54
+ if(!client){
55
+ queryClient.clear();
53
56
  }
54
57
 
55
58
  return (
@@ -58,7 +61,7 @@ const WeavyProvider = ({ children, client, options }: Props) => {
58
61
  <QueryClientProvider client={queryClient}>
59
62
  <WeavyContext.Provider value={{ client, options: opts }}>
60
63
  <UserProvider client={client}>
61
- <PreviewProvider>
64
+ <PreviewProvider client={client}>
62
65
  {children}
63
66
  </PreviewProvider>
64
67
  </UserProvider>
@@ -12,12 +12,8 @@ export default function useBadge() {
12
12
 
13
13
  const getBadge = async () => {
14
14
 
15
- const response = await fetch(client.url + "/api/conversations/badge", {
16
- headers: {
17
- "content-type": "application/json",
18
- "Authorization": "Bearer " + await client.tokenFactory()
19
- }
20
- });
15
+ const response = await client.get("/api/conversations/badge")
16
+
21
17
  if(response.ok){
22
18
  const data = await response.json();
23
19
  return data;
@@ -11,20 +11,9 @@ export default function useChat(id: string, options: any) {
11
11
  }
12
12
 
13
13
  const getConversation = async () => {
14
-
15
- const response = await fetch(client.url + "/api/apps/idf/" + id, {
16
- headers: {
17
- "content-type": "application/json",
18
- "Authorization": "Bearer " + await client.tokenFactory()
19
- }
20
- });
21
- if(response.ok){
22
- const data = await response.json();
23
- return data;
24
- }
25
-
26
- return null;
27
-
14
+ const response = await client.get("/api/apps/" + id);
15
+ const data = await response.json();
16
+ return data;
28
17
  };
29
18
 
30
19
 
@@ -11,13 +11,7 @@ export default function useConversation(id: number | null, options: any) {
11
11
  }
12
12
 
13
13
  const getConversation = async () => {
14
-
15
- const response = await fetch(client.url + "/api/conversations/" + id, {
16
- headers: {
17
- "content-type": "application/json",
18
- "Authorization": "Bearer " + await client.tokenFactory()
19
- }
20
- });
14
+ const response = await client.get("/api/conversations/" + id);
21
15
  const data = await response.json();
22
16
  return data;
23
17
  };
@@ -1,27 +1,31 @@
1
1
  import { useContext } from "react";
2
- import { useQuery } from "react-query";
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
-
15
- const response = await fetch(client.url + "/api/conversations?contextual=false", {
16
- headers: {
17
- "content-type": "application/json",
18
- "Authorization": "Bearer " + await client.tokenFactory()
19
- }
20
- });
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);
21
17
  const data = await response.json();
22
18
  return data;
23
19
  };
24
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
+ };
25
29
 
26
- return useQuery<ConversationsResult>("conversations", getConversations);
30
+ return useInfiniteQuery<ConversationsResult>("conversations", getConversations, opts);
27
31
  }
@@ -17,13 +17,11 @@ export default function useFileUploader(callback: Function) {
17
17
  const formData = new FormData();
18
18
  formData.append('blob', request.file);
19
19
 
20
- var response = await fetch(client.url + "/api/blobs", {
21
- method: 'POST',
22
- body: formData,
23
- headers: {
24
- "Authorization": "Bearer " + await client.tokenFactory()
25
- }
26
- });
20
+
21
+ const response = await client.post("/api/blobs",
22
+ "POST",
23
+ formData,
24
+ "");
27
25
 
28
26
  var uploaded = await response.json();
29
27
  callback(uploaded);
@@ -32,7 +30,7 @@ export default function useFileUploader(callback: Function) {
32
30
  onError: (e: any) => {
33
31
 
34
32
  },
35
- onSuccess: (data: any, variables: any) => {
33
+ onSuccess: (data: any, variables: any) => {
36
34
  },
37
35
  }
38
36
  );
@@ -11,13 +11,7 @@ export default function useMembers(id: number | null, options: any) {
11
11
  }
12
12
 
13
13
  const getConversationMembers = async () => {
14
-
15
- const response = await fetch(client.url + "/api/apps/" + id + "/members", {
16
- headers: {
17
- "content-type": "application/json",
18
- "Authorization": "Bearer " + await client.tokenFactory()
19
- }
20
- });
14
+ const response = await client.get("/api/apps/" + id + "/members");
21
15
  const data = await response.json();
22
16
  return data;
23
17
  };
@@ -13,13 +13,7 @@ export default function useMessages(id: number | null, options: any) {
13
13
 
14
14
  const getMessages = async (opt: any) => {
15
15
  let skip = opt.pageParam || 0;
16
-
17
- const response = await fetch(client.url + "/api/apps/" + id + "/messages?orderby=createdat+desc&skip=" + skip + "&top=" + PAGE_SIZE, {
18
- headers: {
19
- "content-type": "application/json",
20
- "Authorization": "Bearer " + await client.tokenFactory()
21
- }
22
- });
16
+ const response = await client.get("/api/apps/" + id + "/messages?orderby=createdat+desc&skip=" + skip + "&top=" + PAGE_SIZE);
23
17
  let result = await response.json();
24
18
  result.data = result.data?.reverse() || [];
25
19
  return result;
@@ -21,17 +21,12 @@ export default function useMutateChat() {
21
21
  // create new conversation
22
22
  const mutateChat = async ({ identifier, members }: MutateProps) => {
23
23
 
24
- const response = await fetch(client.url + "/api/apps/", {
25
- method: "POST",
26
- body: JSON.stringify({
24
+ const response = await client.post("/api/apps/",
25
+ "POST",
26
+ JSON.stringify({
27
27
  identifier: identifier,
28
- members: members
29
- }),
30
- headers: {
31
- "content-type": "application/json",
32
- "Authorization": "Bearer " + await client.tokenFactory()
33
- }
34
- });
28
+ members: members
29
+ }));
35
30
 
36
31
  return response.json();
37
32
  };
@@ -39,6 +34,6 @@ export default function useMutateChat() {
39
34
  return useMutation(mutateChat, {
40
35
  onSuccess: () => {
41
36
  //queryClient.invalidateQueries("conversations");
42
- }
37
+ }
43
38
  });
44
39
  }
@@ -20,21 +20,18 @@ export default function useMutateConversation() {
20
20
  // create new conversation
21
21
  const mutateConversation = async ({ members }: MutateProps) => {
22
22
 
23
- const response = await fetch(client.url + "/api/conversations/", {
24
- method: "POST",
25
- body: JSON.stringify({ members: members }),
26
- headers: {
27
- "content-type": "application/json",
28
- "Authorization": "Bearer " + await client.tokenFactory()
29
- }
30
- });
23
+ const response = await client.post("/api/conversations/",
24
+ "POST",
25
+ JSON.stringify({
26
+ members: members
27
+ }));
31
28
 
32
29
  return response.json();
33
30
  };
34
31
 
35
32
  return useMutation(mutateConversation, {
36
33
  onSuccess: () => {
37
- queryClient.invalidateQueries("conversations");
38
- }
34
+ queryClient.invalidateQueries("conversations");
35
+ }
39
36
  });
40
37
  }
@@ -14,28 +14,26 @@ export default function useMutateConversationName() {
14
14
 
15
15
 
16
16
  type MutateProps = {
17
- id: number | null,
17
+ id: number | null,
18
18
  name: string
19
19
  }
20
20
 
21
21
  const mutateConversationName = async ({ id, name }: MutateProps) => {
22
22
 
23
- const response = await fetch(client.url + "/api/apps/" + id, {
24
- method: "PATCH",
25
- body: JSON.stringify({ name: name }),
26
- headers: {
27
- "content-type": "application/json",
28
- "Authorization": "Bearer " + await client.tokenFactory()
29
- }
30
- });
23
+ const response = await client.post("/api/apps/" + id,
24
+ "PATCH",
25
+ JSON.stringify({
26
+ type: 'ChatRoom',
27
+ name: name
28
+ }));
31
29
 
32
30
  return response.json();
33
31
  };
34
32
 
35
33
  return useMutation(mutateConversationName, {
36
34
  onSuccess: (data: any, variables: any, context: any) => {
37
- queryClient.invalidateQueries("conversations");
38
- queryClient.invalidateQueries(["conversation", variables.id]);
39
- }
35
+ queryClient.invalidateQueries("conversations");
36
+ queryClient.invalidateQueries(["conversation", variables.id]);
37
+ }
40
38
  });
41
39
  }
@@ -22,14 +22,9 @@ export default function useMutateDeleteReaction() {
22
22
  const mutateDeleteReaction = async ({ id, reaction }: MutateProps) => {
23
23
 
24
24
  // remove the existing reaction
25
- const response = await fetch(client.url + "/api/messages/" + id + "/reactions/", {
26
- method: "DELETE",
27
- headers: {
28
- "content-type": "application/json",
29
- "Authorization": "Bearer " + await client.tokenFactory()
30
- }
31
- });
32
-
25
+ const response = await client.post("/api/messages/" + id + "/reactions/",
26
+ "DELETE",
27
+ "");
33
28
 
34
29
  return response;
35
30
  };