@weavy/uikit-react 11.1.0 → 13.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 (256) hide show
  1. package/.github/workflows/publish.yml +1 -1
  2. package/README.md +3 -4
  3. package/changelog.md +57 -0
  4. package/dist/cjs/index.js +28 -6
  5. package/dist/cjs/index.js.map +1 -1
  6. package/dist/cjs/types/client/WeavyClient.d.ts +8 -1
  7. package/dist/cjs/types/components/Attachment.d.ts +2 -1
  8. package/dist/cjs/types/components/Chat.d.ts +1 -1
  9. package/dist/cjs/types/components/Image.d.ts +2 -0
  10. package/dist/cjs/types/components/PdfViewer.d.ts +8 -0
  11. package/dist/cjs/types/components/Preview.d.ts +56 -0
  12. package/dist/cjs/types/contexts/MessengerContext.d.ts +1 -2
  13. package/dist/cjs/types/contexts/PreviewContext.d.ts +2 -1
  14. package/dist/cjs/types/contexts/WeavyContext.d.ts +2 -3
  15. package/dist/cjs/types/types/Chat.d.ts +1 -1
  16. package/dist/cjs/types/types/Messenger.d.ts +0 -1
  17. package/dist/cjs/types/types/types.d.ts +18 -8
  18. package/dist/cjs/types/ui/Spinner.d.ts +9 -0
  19. package/dist/cjs/types/utils/fileUtilities.d.ts +13 -1
  20. package/dist/css/weavy-chat.css +2860 -0
  21. package/dist/css/weavy-messenger.css +3217 -0
  22. package/dist/css/weavy.css +3217 -0
  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/Image.d.ts +2 -0
  29. package/dist/esm/types/components/PdfViewer.d.ts +8 -0
  30. package/dist/esm/types/components/Preview.d.ts +56 -0
  31. package/dist/esm/types/contexts/MessengerContext.d.ts +1 -2
  32. package/dist/esm/types/contexts/PreviewContext.d.ts +2 -1
  33. package/dist/esm/types/contexts/WeavyContext.d.ts +2 -3
  34. package/dist/esm/types/types/Chat.d.ts +1 -1
  35. package/dist/esm/types/types/Messenger.d.ts +0 -1
  36. package/dist/esm/types/types/types.d.ts +18 -8
  37. package/dist/esm/types/ui/Spinner.d.ts +9 -0
  38. package/dist/esm/types/utils/fileUtilities.d.ts +13 -1
  39. package/dist/index.d.ts +15 -9
  40. package/package.json +14 -4
  41. package/rollup.config.js +3 -3
  42. package/src/client/WeavyClient.ts +105 -24
  43. package/src/components/Attachment.tsx +8 -8
  44. package/src/components/Avatar.tsx +2 -3
  45. package/src/components/Chat.tsx +13 -17
  46. package/src/components/Conversation.tsx +23 -32
  47. package/src/components/ConversationBadge.tsx +1 -2
  48. package/src/components/ConversationForm.tsx +11 -18
  49. package/src/components/ConversationList.tsx +4 -5
  50. package/src/components/ConversationListItem.tsx +11 -13
  51. package/src/components/FileBrowser.tsx +2 -3
  52. package/src/components/Image.tsx +39 -7
  53. package/src/components/MeetingCard.tsx +7 -8
  54. package/src/components/Message.tsx +13 -14
  55. package/src/components/Messages.tsx +7 -8
  56. package/src/components/Messenger.tsx +5 -6
  57. package/src/components/NewConversation.tsx +5 -7
  58. package/src/components/PdfViewer.tsx +276 -0
  59. package/src/components/Presence.tsx +2 -2
  60. package/src/components/Preview.tsx +355 -0
  61. package/src/components/Reactions.tsx +8 -8
  62. package/src/components/SearchUsers.tsx +19 -18
  63. package/src/components/SeenBy.tsx +1 -2
  64. package/src/contexts/MessengerContext.tsx +4 -12
  65. package/src/contexts/PreviewContext.tsx +89 -17
  66. package/src/contexts/WeavyContext.tsx +15 -5
  67. package/src/hooks/useBadge.ts +2 -6
  68. package/src/hooks/useChat.ts +3 -14
  69. package/src/hooks/useConversation.ts +1 -8
  70. package/src/hooks/useConversations.ts +1 -7
  71. package/src/hooks/useFileUploader.ts +6 -8
  72. package/src/hooks/useMembers.ts +1 -7
  73. package/src/hooks/useMessages.ts +1 -7
  74. package/src/hooks/useMutateChat.ts +6 -11
  75. package/src/hooks/useMutateConversation.ts +7 -10
  76. package/src/hooks/useMutateConversationName.ts +10 -12
  77. package/src/hooks/useMutateDeleteReaction.ts +3 -8
  78. package/src/hooks/useMutateExternalBlobs.ts +6 -11
  79. package/src/hooks/useMutateMeeting.ts +6 -11
  80. package/src/hooks/useMutateMembers.ts +8 -13
  81. package/src/hooks/useMutateMessage.ts +10 -15
  82. package/src/hooks/useMutatePinned.ts +3 -8
  83. package/src/hooks/useMutateReaction.ts +6 -12
  84. package/src/hooks/useMutateRead.ts +1 -10
  85. package/src/hooks/useMutateRemoveMembers.ts +7 -12
  86. package/src/hooks/useMutateTyping.ts +6 -11
  87. package/src/hooks/usePresence.ts +4 -5
  88. package/src/hooks/useReactions.ts +0 -1
  89. package/src/hooks/useSearchUsers.ts +1 -6
  90. package/src/hooks/useUser.ts +3 -14
  91. package/src/index.ts +2 -2
  92. package/src/scss/theme/_alert.scss +73 -0
  93. package/src/scss/theme/_appbar.scss +114 -0
  94. package/src/scss/theme/_attachments.scss +74 -0
  95. package/src/scss/theme/_avatar.scss +54 -0
  96. package/src/scss/theme/_badge.scss +47 -0
  97. package/src/scss/theme/_buttons.scss +96 -0
  98. package/src/scss/theme/_card.scss +7 -0
  99. package/src/scss/theme/_checkbox.scss +56 -0
  100. package/src/scss/theme/_cm-editor.scss +42 -0
  101. package/src/scss/theme/_code-vscode-dark.scss +184 -0
  102. package/src/scss/theme/_code-vscode-light.scss +179 -0
  103. package/src/scss/theme/_code.scss +12 -0
  104. package/src/scss/theme/_colors.scss +520 -0
  105. package/src/scss/theme/_config.scss +6 -0
  106. package/src/scss/theme/_content.scss +15 -0
  107. package/src/scss/theme/_conversations.scss +91 -0
  108. package/src/scss/theme/_dropdown.scss +86 -0
  109. package/src/scss/theme/_emoji.scss +5 -0
  110. package/src/scss/theme/_filebrowser.scss +26 -0
  111. package/src/scss/theme/_files.scss +140 -0
  112. package/src/scss/theme/_icons.scss +62 -0
  113. package/src/scss/theme/_image-grid.scss +63 -0
  114. package/src/scss/theme/_inputs.scss +28 -0
  115. package/src/scss/theme/_message-editor.scss +90 -0
  116. package/src/scss/theme/_messages.scss +238 -0
  117. package/src/scss/theme/_nav.scss +52 -0
  118. package/src/scss/theme/_overlays.scss +157 -0
  119. package/src/scss/theme/_pager.scss +19 -0
  120. package/src/scss/theme/_palette.scss +165 -0
  121. package/src/scss/theme/_pane.scss +16 -0
  122. package/src/scss/theme/_panels.scss +141 -0
  123. package/src/scss/theme/_picker-list.scss +37 -0
  124. package/src/scss/theme/_preview-code.scss +5 -0
  125. package/src/scss/theme/_preview-embed.scss +38 -0
  126. package/src/scss/theme/_preview-html.scss +7 -0
  127. package/src/scss/theme/_preview-icon.scss +41 -0
  128. package/src/scss/theme/_preview-image.scss +86 -0
  129. package/src/scss/theme/_preview-media.scss +28 -0
  130. package/src/scss/theme/_preview-pdf.scss +838 -0
  131. package/src/scss/theme/_preview-text.scss +5 -0
  132. package/src/scss/theme/_preview.scss +110 -0
  133. package/src/scss/theme/_reactions.scss +58 -0
  134. package/src/scss/theme/_reboot.scss +41 -0
  135. package/src/scss/theme/_root.scss +2 -0
  136. package/src/scss/theme/_scroll.scss +55 -0
  137. package/src/scss/theme/_search.scss +68 -0
  138. package/src/scss/theme/_spinner.scss +63 -0
  139. package/src/scss/theme/_tables.scss +53 -0
  140. package/src/scss/theme/_toasts.scss +47 -0
  141. package/src/scss/theme/_turbo.scss +17 -0
  142. package/src/scss/theme/_typing.scss +26 -0
  143. package/src/scss/theme/_variables.scss +139 -0
  144. package/src/scss/theme/bootstrap/_accordion.scss +146 -0
  145. package/src/scss/theme/bootstrap/_alert.scss +71 -0
  146. package/src/scss/theme/bootstrap/_badge.scss +38 -0
  147. package/src/scss/theme/bootstrap/_breadcrumb.scss +40 -0
  148. package/src/scss/theme/bootstrap/_button-group.scss +142 -0
  149. package/src/scss/theme/bootstrap/_buttons.scss +186 -0
  150. package/src/scss/theme/bootstrap/_card.scss +234 -0
  151. package/src/scss/theme/bootstrap/_carousel.scss +229 -0
  152. package/src/scss/theme/bootstrap/_close.scss +40 -0
  153. package/src/scss/theme/bootstrap/_containers.scss +41 -0
  154. package/src/scss/theme/bootstrap/_dropdown.scss +248 -0
  155. package/src/scss/theme/bootstrap/_forms.scss +9 -0
  156. package/src/scss/theme/bootstrap/_functions.scss +302 -0
  157. package/src/scss/theme/bootstrap/_grid.scss +33 -0
  158. package/src/scss/theme/bootstrap/_helpers.scss +10 -0
  159. package/src/scss/theme/bootstrap/_images.scss +42 -0
  160. package/src/scss/theme/bootstrap/_list-group.scss +191 -0
  161. package/src/scss/theme/bootstrap/_maps.scss +54 -0
  162. package/src/scss/theme/bootstrap/_mixins.scss +43 -0
  163. package/src/scss/theme/bootstrap/_modal.scss +237 -0
  164. package/src/scss/theme/bootstrap/_nav.scss +172 -0
  165. package/src/scss/theme/bootstrap/_navbar.scss +276 -0
  166. package/src/scss/theme/bootstrap/_offcanvas.scss +143 -0
  167. package/src/scss/theme/bootstrap/_pagination.scss +109 -0
  168. package/src/scss/theme/bootstrap/_placeholders.scss +51 -0
  169. package/src/scss/theme/bootstrap/_popover.scss +196 -0
  170. package/src/scss/theme/bootstrap/_progress.scss +59 -0
  171. package/src/scss/theme/bootstrap/_reboot.scss +610 -0
  172. package/src/scss/theme/bootstrap/_root.scss +73 -0
  173. package/src/scss/theme/bootstrap/_spinners.scss +85 -0
  174. package/src/scss/theme/bootstrap/_tables.scss +164 -0
  175. package/src/scss/theme/bootstrap/_toasts.scss +70 -0
  176. package/src/scss/theme/bootstrap/_tooltip.scss +120 -0
  177. package/src/scss/theme/bootstrap/_transitions.scss +27 -0
  178. package/src/scss/theme/bootstrap/_type.scss +106 -0
  179. package/src/scss/theme/bootstrap/_utilities.scss +647 -0
  180. package/src/scss/theme/bootstrap/_variables.scss +1633 -0
  181. package/src/scss/theme/bootstrap/forms/_floating-labels.scss +74 -0
  182. package/src/scss/theme/bootstrap/forms/_form-check.scss +175 -0
  183. package/src/scss/theme/bootstrap/forms/_form-control.scss +194 -0
  184. package/src/scss/theme/bootstrap/forms/_form-range.scss +91 -0
  185. package/src/scss/theme/bootstrap/forms/_form-select.scss +71 -0
  186. package/src/scss/theme/bootstrap/forms/_form-text.scss +11 -0
  187. package/src/scss/theme/bootstrap/forms/_input-group.scss +129 -0
  188. package/src/scss/theme/bootstrap/forms/_labels.scss +36 -0
  189. package/src/scss/theme/bootstrap/forms/_validation.scss +12 -0
  190. package/src/scss/theme/bootstrap/helpers/_clearfix.scss +3 -0
  191. package/src/scss/theme/bootstrap/helpers/_color-bg.scss +10 -0
  192. package/src/scss/theme/bootstrap/helpers/_colored-links.scss +12 -0
  193. package/src/scss/theme/bootstrap/helpers/_position.scss +36 -0
  194. package/src/scss/theme/bootstrap/helpers/_ratio.scss +26 -0
  195. package/src/scss/theme/bootstrap/helpers/_stacks.scss +15 -0
  196. package/src/scss/theme/bootstrap/helpers/_stretched-link.scss +15 -0
  197. package/src/scss/theme/bootstrap/helpers/_text-truncation.scss +7 -0
  198. package/src/scss/theme/bootstrap/helpers/_visually-hidden.scss +8 -0
  199. package/src/scss/theme/bootstrap/helpers/_vr.scss +8 -0
  200. package/src/scss/theme/bootstrap/mixins/_alert.scss +15 -0
  201. package/src/scss/theme/bootstrap/mixins/_backdrop.scss +14 -0
  202. package/src/scss/theme/bootstrap/mixins/_banner.scss +9 -0
  203. package/src/scss/theme/bootstrap/mixins/_border-radius.scss +78 -0
  204. package/src/scss/theme/bootstrap/mixins/_box-shadow.scss +18 -0
  205. package/src/scss/theme/bootstrap/mixins/_breakpoints.scss +127 -0
  206. package/src/scss/theme/bootstrap/mixins/_buttons.scss +70 -0
  207. package/src/scss/theme/bootstrap/mixins/_caret.scss +64 -0
  208. package/src/scss/theme/bootstrap/mixins/_clearfix.scss +9 -0
  209. package/src/scss/theme/bootstrap/mixins/_color-scheme.scss +7 -0
  210. package/src/scss/theme/bootstrap/mixins/_container.scss +11 -0
  211. package/src/scss/theme/bootstrap/mixins/_deprecate.scss +10 -0
  212. package/src/scss/theme/bootstrap/mixins/_forms.scss +152 -0
  213. package/src/scss/theme/bootstrap/mixins/_gradients.scss +47 -0
  214. package/src/scss/theme/bootstrap/mixins/_grid.scss +151 -0
  215. package/src/scss/theme/bootstrap/mixins/_image.scss +16 -0
  216. package/src/scss/theme/bootstrap/mixins/_list-group.scss +24 -0
  217. package/src/scss/theme/bootstrap/mixins/_lists.scss +7 -0
  218. package/src/scss/theme/bootstrap/mixins/_pagination.scss +10 -0
  219. package/src/scss/theme/bootstrap/mixins/_reset-text.scss +17 -0
  220. package/src/scss/theme/bootstrap/mixins/_resize.scss +6 -0
  221. package/src/scss/theme/bootstrap/mixins/_table-variants.scss +24 -0
  222. package/src/scss/theme/bootstrap/mixins/_text-truncate.scss +8 -0
  223. package/src/scss/theme/bootstrap/mixins/_transition.scss +26 -0
  224. package/src/scss/theme/bootstrap/mixins/_utilities.scss +97 -0
  225. package/src/scss/theme/bootstrap/mixins/_visually-hidden.scss +29 -0
  226. package/src/scss/theme/bootstrap/utilities/_api.scss +47 -0
  227. package/src/scss/theme/bootstrap/vendor/_rfs.scss +354 -0
  228. package/src/scss/theme/bs/_badge.scss +20 -0
  229. package/src/scss/theme/bs/_buttons.scss +185 -0
  230. package/src/scss/theme/bs/_dropdown.scss +86 -0
  231. package/src/scss/theme/bs/_forms.scss +161 -0
  232. package/src/scss/theme/bs/_list-group.scss +73 -0
  233. package/src/scss/theme/bs/_tables.scss +46 -0
  234. package/src/scss/theme/fonts/_fontmapping-roboto.scss +129 -0
  235. package/src/scss/theme/fonts/_fontmapping-segoe-ui.scss +127 -0
  236. package/src/scss/theme/fonts/_index.scss +2 -0
  237. package/src/scss/theme/mixins/_backdrop.scss +13 -0
  238. package/src/scss/theme/mixins/_palette.scss +165 -0
  239. package/src/scss/theme/mixins/_position.scss +33 -0
  240. package/src/scss/theme/mixins/_scrollbar.scss +110 -0
  241. package/src/scss/weavy-chat.scss +32 -0
  242. package/src/scss/weavy-messenger.scss +61 -0
  243. package/src/scss/weavy.scss +2 -0
  244. package/src/types/Chat.ts +1 -1
  245. package/src/types/Messenger.ts +1 -1
  246. package/src/types/types.ts +20 -11
  247. package/src/ui/Button.tsx +3 -4
  248. package/src/ui/Dropdown.tsx +4 -5
  249. package/src/ui/Icon.tsx +75 -39
  250. package/src/ui/Overlay.tsx +2 -3
  251. package/src/ui/Spinner.tsx +18 -0
  252. package/src/utils/fileUtilities.ts +166 -72
  253. package/src/utils/scrollbarDetection.js +48 -0
  254. package/dist/cjs/types/utils/styles.d.ts +0 -17
  255. package/dist/esm/types/utils/styles.d.ts +0 -17
  256. package/src/utils/styles.ts +0 -42
@@ -0,0 +1,355 @@
1
+ import React, { useEffect, useState, useCallback } from "react";
2
+ import Icon from "../ui/Icon";
3
+ import Spinner from "../ui/Spinner";
4
+ import classNames from "classnames";
5
+ import PdfViewer from "./PdfViewer";
6
+ import { checkImageLoad, imageLoaded } from "./Image";
7
+
8
+ const getStream = (response: Response) => {
9
+ if (response && response.ok && response.body) {
10
+ const reader = response.body.getReader();
11
+ return new ReadableStream({
12
+ start(controller) {
13
+ let pump: any = () => {
14
+ return reader.read().then(({ done, value }) => {
15
+ // When no more data needs to be consumed, close the stream
16
+ if (done) {
17
+ controller.close();
18
+ return;
19
+ }
20
+ // Enqueue the next data chunk into our target stream
21
+ controller.enqueue(value);
22
+ return pump();
23
+ });
24
+ }
25
+
26
+ return pump();
27
+ }
28
+ })
29
+ }
30
+ };
31
+
32
+ type ImageProps = {
33
+ src: string,
34
+ width?: number,
35
+ height?: number
36
+ }
37
+
38
+ export const PreviewImage = ({ src, width, height }: ImageProps) => {
39
+ const imageRef = useCallback((element: HTMLImageElement) => {
40
+ if (element) {
41
+ checkImageLoad(element);
42
+ }
43
+ }, [])
44
+
45
+ return (
46
+ <>
47
+ {width && height ?
48
+ <div className="wy-content-image wy-responsive-image" style={{ ["--width" as any]: width, ["--height" as any]: height }}>
49
+ <img className="wy-loading-transition" ref={imageRef} src={src} onLoad={imageLoaded} width={width} height={height} decoding="async" />
50
+ <Spinner.UI />
51
+ </div>
52
+ :
53
+ <div className="wy-content-image wy-responsive-image wy-intrinsic-image">
54
+ <img ref={imageRef} src={src} onLoad={imageLoaded} decoding="async" />
55
+ </div>
56
+ }
57
+ </>
58
+ );
59
+ }
60
+
61
+ type DocumentProps = {
62
+ src: string,
63
+ client: any
64
+ }
65
+
66
+ export const PreviewDocument = ({ src, client }: DocumentProps) => {
67
+ let pdfWorkerUrl = (new URL("/js/preview.worker.js", client.url)).toString();
68
+ let pdfCMapsUrl = (new URL("/js/cmaps/", client.url)).toString();
69
+
70
+ return (
71
+ <PdfViewer src={src} pdfWorkerUrl={pdfWorkerUrl} pdfCMapsUrl={pdfCMapsUrl} />
72
+ );
73
+ }
74
+
75
+ function mediaFallback(media: HTMLVideoElement | HTMLAudioElement) {
76
+ if (media.classList.contains("wy-loading")) {
77
+ media.classList.add("wy-loaded");
78
+ }
79
+ media.classList.add("wy-error");
80
+ // TODO: replace with react way
81
+ media.outerHTML = media.outerHTML.replace(/<(video|audio)/, "<div").replace(/(video|audio)>/, "div>");
82
+ }
83
+
84
+ function mediaLoaded(event: any) {
85
+ var src = event.target;
86
+ if (src.tagName === 'VIDEO' || src.tagName === 'AUDIO') {
87
+ if (src.classList.contains("wy-loading")) {
88
+ console.log("loaded")
89
+ src.classList.add("wy-loaded");
90
+ }
91
+ }
92
+ }
93
+
94
+ function mediaError(event: any) {
95
+ var src = event.target;
96
+ var media;
97
+ if (src.tagName === 'SOURCE') {
98
+ media = src.parentNode;
99
+ media.dataset.errors = (media.dataset.errors || 0) + 1;
100
+
101
+ if (media.querySelectorAll("source").length >= media.dataset.errors) {
102
+ console.warn(media.tagName.toLowerCase() + " source error, switching to fallback");
103
+ mediaFallback(media);
104
+ }
105
+ }
106
+ }
107
+
108
+ function codecError(event: any) {
109
+ var src = event.target;
110
+ if (src.tagName === 'VIDEO' || src.tagName === 'AUDIO') {
111
+ // Capture codec-error for video in firefox
112
+ if (src.tagName === 'VIDEO' && !src.videoWidth || src.tagName === 'AUDIO' && !src.duration) {
113
+ console.warn(src.tagName.toLowerCase() + " track not available, switching to fallback");
114
+ mediaFallback(src);
115
+ }
116
+ }
117
+ }
118
+
119
+ type MediaProps = {
120
+ format: string,
121
+ src: string,
122
+ name: string,
123
+ mediaType?: string
124
+ }
125
+
126
+ export const PreviewMedia = ({ format, src, name, mediaType }: MediaProps) => {
127
+
128
+ const [mediaElement, setMediaElement] = useState<HTMLVideoElement|HTMLAudioElement>();
129
+ const mediaRef = useCallback((element: any) => {
130
+ if (element) {
131
+ element.classList.add("wy-loading");
132
+ setMediaElement(element);
133
+ }
134
+ }, [])
135
+
136
+ useEffect(() => {
137
+ if (mediaElement) {
138
+
139
+ mediaElement.addEventListener('error', mediaError, true); // needs capturing
140
+ mediaElement.addEventListener('loadedmetadata', mediaLoaded, true); // needs capturing
141
+ mediaElement.addEventListener('loadedmetadata', codecError, true); // needs capturing
142
+
143
+ return () => {
144
+ // cleanup
145
+ mediaElement.pause();
146
+ mediaElement.removeAttribute("autoplay");
147
+ mediaElement.setAttribute("preload", "none");
148
+
149
+ mediaElement.removeEventListener('error', mediaError, true); // needs capturing
150
+ mediaElement.removeEventListener('loadedmetadata', mediaLoaded, true); // needs capturing
151
+ mediaElement.removeEventListener('loadedmetadata', codecError, true); // needs capturing
152
+ }
153
+ }
154
+
155
+ }, [mediaElement])
156
+
157
+ return (format === "video" ?
158
+ <>
159
+ <video ref={mediaRef} className="wy-content-video" controls crossOrigin="use-credentials" autoPlay>
160
+ <source src={src} type={mediaType} />
161
+ <PreviewIcon src={src} name={name} icon="file-video" download />
162
+ </video>
163
+ <Spinner.UI />
164
+ </>
165
+ :
166
+ <>
167
+ <PreviewIcon src={src} name={name} icon="file-music" download>
168
+ <audio ref={mediaRef} className="wy-content-audio" controls crossOrigin="use-credentials" autoPlay>
169
+ <source src={src} type={mediaType} />
170
+ </audio>
171
+ </PreviewIcon>
172
+ </>
173
+ );
174
+ }
175
+
176
+ type TextProps = {
177
+ src: string,
178
+ html?: boolean,
179
+ code?: boolean
180
+ }
181
+
182
+ export const PreviewText = ({ src, html = false, code = false }: TextProps) => {
183
+ const [textContent, setTextContent] = useState("");
184
+
185
+ useEffect(() => {
186
+ fetch(src)
187
+ .then(getStream)
188
+ // Create a new response out of the stream
189
+ .then(stream => new Response(stream))
190
+ // Create an object URL for the response
191
+ .then(response => response.text())
192
+ .then(text => {
193
+ setTextContent(text)
194
+ })
195
+ }, [src]);
196
+
197
+ return (
198
+ <>
199
+ {html ?
200
+ code ?
201
+ <div className="wy-content-code wy-code" dangerouslySetInnerHTML={{ __html: textContent }}></div>
202
+ :
203
+ <div className="wy-document">
204
+ <div className="wy-content-html" dangerouslySetInnerHTML={{ __html: textContent }}></div>
205
+ </div>
206
+ :
207
+ code ?
208
+ <div className="wy-content-code">{textContent}</div>
209
+ :
210
+ <div className="wy-document">
211
+ <pre className="wy-content-text">{textContent}</pre>
212
+ </div>
213
+ }
214
+ </>
215
+ );
216
+ }
217
+
218
+
219
+ type EmbedProps = {
220
+ src: string,
221
+ name: string,
222
+ icon: string,
223
+ provider?: string
224
+ }
225
+
226
+ export const PreviewEmbed = ({ src, name, icon, provider }: EmbedProps) => {
227
+ const [embedElement, setEmbedElement] = useState<HTMLObjectElement>();
228
+ const embedRef = useCallback((element: any) => {
229
+ if (element) {
230
+ element.classList.add("wy-loading");
231
+ setEmbedElement(element);
232
+ }
233
+ }, [])
234
+
235
+ useEffect(() => {
236
+ if (embedElement) {
237
+ let embedFallbackTimeout = setTimeout(function () {
238
+ console.log("fallback");
239
+ embedElement.classList.add("wy-fallback");
240
+ }, 2500)
241
+
242
+ let embedLoaded = (event: any) => {
243
+ var obj = event.target;
244
+ if (obj.tagName === 'OBJECT' && obj.classList.contains("wy-loading") && !obj.classList.contains("wy-loaded")) {
245
+ console.log("loaded");
246
+ obj.classList.add("wy-loaded");
247
+ clearTimeout(embedFallbackTimeout);
248
+ }
249
+ }
250
+
251
+ embedElement.addEventListener('load', embedLoaded, true); // needs capturing
252
+
253
+ return () => {
254
+ // cleanup
255
+ embedElement.removeEventListener('load', embedLoaded, true); // needs capturing
256
+ clearTimeout(embedFallbackTimeout);
257
+ }
258
+ }
259
+
260
+ }, [embedElement])
261
+
262
+ return (
263
+ <>
264
+ {/* iframe needs to be object to not render error pages when content is blocked */}
265
+ <object ref={embedRef} className="wy-content-iframe" data={src}></object>
266
+
267
+ <Spinner.UI />
268
+
269
+ <PreviewIcon src={src} name={name} icon={icon} provider={provider} className="wy-content-iframe-fallback" />
270
+ </>
271
+ );
272
+ }
273
+
274
+ type IconProps = {
275
+ children?: React.ReactNode,
276
+ src: string,
277
+ icon: string,
278
+ name: string,
279
+ provider?: string,
280
+ download?: boolean,
281
+ className?: string
282
+ }
283
+
284
+ export const PreviewIcon = ({ children, src, icon, name, provider, download = false, className }: IconProps) => {
285
+ return (
286
+ <div className={classNames("wy-content-icon", className)}>
287
+ <div className="wy-content-icon">
288
+ <Icon.UI name={icon} />
289
+ </div>
290
+ <div className="wy-content-name">
291
+ {provider ?
292
+ <>
293
+ <span>No preview available. </span>
294
+ <a href={src} target="_blank" title={name}>{`Open in ${provider}?`}</a>
295
+ </>
296
+ : download ?
297
+ <a href={src} target="_top" download>{name}</a>
298
+ :
299
+ <a href={src} target="_blank">{name} <Icon.UI name="open-in-new" size={1} /></a>
300
+ }
301
+ </div>
302
+ {children}
303
+ </div>
304
+ );
305
+ }
306
+
307
+ type PreviewProps = {
308
+ client: any,
309
+ src: string,
310
+ link?: string,
311
+ format: PreviewFormatType,
312
+ name: string,
313
+ icon: string,
314
+ width?: number,
315
+ height?: number,
316
+ mediaType?: string,
317
+ provider?: string
318
+ }
319
+
320
+ export const Preview = ({ client, src, link, format, name, icon, width, height, mediaType, provider }: PreviewProps) => {
321
+ return (
322
+ <>
323
+ {format === "image" &&
324
+ <PreviewImage key={src} src={src} width={width} height={height} />
325
+ }
326
+ {format === "pdf" &&
327
+ /* Key not needed since component is reused and handles cleanup */
328
+ <PreviewDocument src={src} client={client} />
329
+ }
330
+ {(format === "video" || format === "audio") &&
331
+ <PreviewMedia key={src} format={format} src={src} name={name} mediaType={mediaType} />
332
+ }
333
+ {format === "text" &&
334
+ <PreviewText key={src} src={src} />
335
+ }
336
+ {format === "code" &&
337
+ <PreviewText key={src} src={src} html code />
338
+ }
339
+ {format === "html" &&
340
+ <PreviewText key={src} src={src} html />
341
+ }
342
+ {format === "embed" &&
343
+ <PreviewEmbed key={src} src={src} name={name} icon={icon} provider={provider} />
344
+ }
345
+ {format === "none" && (
346
+ link ?
347
+ <PreviewIcon src={src} name={name} icon={icon} provider={provider} />
348
+ :
349
+ <PreviewIcon src={src} name={name} icon={icon} download />
350
+ )}
351
+ </>
352
+ )
353
+ }
354
+
355
+ export default Preview;
@@ -5,9 +5,9 @@ import Button from "../ui/Button";
5
5
  import { MessengerContext } from "../contexts/MessengerContext";
6
6
  import classNames from "classnames";
7
7
 
8
- import { prefix as wy } from "../utils/styles";
9
8
  import useReactions from "../hooks/useReactions";
10
9
  import useMutateDeleteReaction from "../hooks/useMutateDeleteReaction";
10
+ import { WeavyContext } from "../contexts/WeavyContext";
11
11
 
12
12
  type ReactionMenuProps = {
13
13
  id: number,
@@ -25,7 +25,7 @@ export const ReactionsMenu = ({ id, reactions }: ReactionMenuProps) => {
25
25
  const reactionMutation = useMutateReaction();
26
26
  const reactionDeleteMutation = useMutateDeleteReaction();
27
27
  const [visible, setVisible] = useState<boolean>(false);
28
- const { options } = useContext(MessengerContext);
28
+ const { options } = useContext(WeavyContext);
29
29
  const [reactedEmoji, setReactedEmoji] = useState<string>('');
30
30
 
31
31
  const emojis = options?.reactions;
@@ -68,12 +68,12 @@ export const ReactionsMenu = ({ id, reactions }: ReactionMenuProps) => {
68
68
  }
69
69
 
70
70
  return (
71
- <div className={wy(classNames("", { "active": visible }))} style={{ position: 'relative' }}>
72
- <Button.UI onClick={toggleReactionMenu}><Icon.UI name="emoticon-outline" size={1} /></Button.UI>
73
- <div className={wy('reaction-menu dropdown-menu')} style={{ display: visible ? 'block' : 'none', position: 'absolute', top: '-3.25rem' }}>
74
- <div className={wy('reaction-picker')}>
71
+ <div className={classNames({ "wy-active": visible })} style={{ position: 'relative' }}>
72
+ <Button.UI onClick={toggleReactionMenu}><Icon.UI name="emoticon-plus" size={1} /></Button.UI>
73
+ <div className="wy-reaction-menu wy-dropdown-menu" style={{ display: visible ? 'block' : 'none', position: 'absolute', top: '-3.25rem' }}>
74
+ <div className="wy-reaction-picker">
75
75
  {emojis?.map((r: string, i: number) => {
76
- return <Button.UI key={i} onClick={handleReaction} className={wy(classNames("button-icon reaction-button", { "active": reactedEmoji === r }))} data-emoji={r}>{r}</Button.UI> //reactedEmoji
76
+ return <Button.UI key={i} onClick={handleReaction} className={classNames("wy-button-icon wy-reaction-button", { "wy-active": reactedEmoji === r })} data-emoji={r}>{r}</Button.UI> //reactedEmoji
77
77
  })}
78
78
  </div>
79
79
  </div>
@@ -88,7 +88,7 @@ export const ReactionsList = ({ id, reactions }: ReactionsProps) => {
88
88
  return (
89
89
  <>
90
90
  {reactionsList && reactionsList.map((r: ReactionGroup, i: number) => {
91
- return <span key={i} className={wy('reaction')} title={r.count.toString()}>{r.content}</span> //r.has_reacted
91
+ return <span key={i} className="wy-reaction" title={r.count.toString()}>{r.content}</span> //r.has_reacted
92
92
  })}
93
93
  </>
94
94
  )
@@ -4,7 +4,6 @@ import useSearchUsers from "../hooks/useSearchUsers";
4
4
  import Avatar from './Avatar';
5
5
  import Button from '../ui/Button';
6
6
  import Icon from '../ui/Icon';
7
- import { prefix as wy } from "../utils/styles";
8
7
 
9
8
  type SearchUsersProps = {
10
9
  handleSubmit: any,
@@ -42,33 +41,33 @@ const SearchUsers = ({handleSubmit, buttonTitle}: SearchUsersProps) => {
42
41
  }
43
42
 
44
43
  return (
45
- <div className={wy('search scroll-y')}>
46
- <div className={wy('search-form pane-group')}>
44
+ <div className="wy-search wy-scroll-y">
45
+ <div className="wy-search-form wy-pane-group">
47
46
  <Button.UI><Icon.UI name="magnify" /></Button.UI>
48
- <input className={wy('search-input')} value={text} onChange={(e) => setText(e.target.value)} name="text" placeholder='Search...' />
47
+ <input className="wy-search-input" value={text} onChange={(e) => setText(e.target.value)} name="text" placeholder='Search...' />
49
48
  </div>
50
49
 
51
- <div className={wy('pane-group')}>
52
- {data && data.data.length === 0 &&
53
- <div className={wy('search-no-result')}>Your search did not match any people.</div>
50
+ <div className="wy-pane-group">
51
+ {data && (!data.data || data.data.length === 0) &&
52
+ <div className="wy-search-no-result">Your search did not match any people.</div>
54
53
  }
55
- <table className={wy('search-result-table')}>
54
+ <table className="wy-search-result-table">
56
55
  <tbody>
57
- {data && data.data.length > 0 && data.data.map((user: MemberType) => {
56
+ {data && data.data && data.data.length > 0 && data.data.map((user: MemberType) => {
58
57
  return (
59
- <tr key={user.id} className={wy('search-result-table-checkbox')}>
60
- <td className={wy('search-result-table-icon')}>
58
+ <tr key={user.id} className="wy-search-result-table-checkbox">
59
+ <td className="wy-search-result-table-icon">
61
60
  <Avatar src={user.avatar_url} size={24} id={user.id} presence={user.presence} name={user.display_name} />
62
61
  </td>
63
62
  <td><label htmlFor={'chk' + user.id}>{user.display_name}</label></td>
64
- <td className={wy('search-result-table-icon')}><input type="checkbox" id={'chk' + user.id} checked={isChecked(user.id)} onChange={(e) => handleSelected(e, user)} /></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>
65
64
  </tr>
66
65
  )
67
66
  })}
68
- </tbody>
67
+ </tbody>
69
68
  </table>
70
69
  </div>
71
- {/*<div className={wy('search-group')}>
70
+ {/*<div className="wy-search-group">
72
71
  <h2>Selected people</h2>
73
72
  <ul>
74
73
  {selected && selected.length > 0 && selected.map((user: UserType) => {
@@ -76,10 +75,12 @@ const SearchUsers = ({handleSubmit, buttonTitle}: SearchUsersProps) => {
76
75
  })}
77
76
  </ul>
78
77
  </div>*/}
79
- <div className={wy('footerbars')}>
80
- <div className={wy('footerbar')}>
81
- <div className={wy('pane-group')}>
82
- <button className={wy('button-primary')} type="button" onClick={() => {handleSubmit(selected); clear();}} disabled={selected.length === 0}>{buttonTitle}</button>
78
+ <div className="wy-footerbars">
79
+ <div className="wy-footerbar">
80
+ <div className="wy-pane-group">
81
+ <div className="wy-buttons">
82
+ <button className="wy-button wy-button-primary" type="button" onClick={() => {handleSubmit(selected); clear();}} disabled={selected.length === 0}>{buttonTitle}</button>
83
+ </div>
83
84
  </div>
84
85
  </div>
85
86
  </div>
@@ -1,7 +1,6 @@
1
1
  import React from "react";
2
2
  import Avatar from "./Avatar";
3
3
  import dayjs from 'dayjs';
4
- import { prefix as wy } from "../utils/styles";
5
4
 
6
5
  type Props = {
7
6
  id: number,
@@ -13,7 +12,7 @@ type Props = {
13
12
  const SeenBy = ({ seenBy }: Props) => {
14
13
 
15
14
  return (
16
- <div className={wy('readby-status')}>
15
+ <div className="wy-readby-status">
17
16
  {seenBy && seenBy.length > 0 && seenBy.map((member: MemberType) => {
18
17
  const date = dayjs.utc(member.read_at).tz(dayjs.tz.guess());
19
18
  return (<Avatar name={`Seen by ${member.name} at ${date.format('LLLL')}`} src={member.avatar_url} size={16} key={member.id}/>)})
@@ -3,22 +3,14 @@ import usePresence from "../hooks/usePresence";
3
3
 
4
4
  export const MessengerContext = createContext<MessengerContextProps>({
5
5
  selectedConversationId: null,
6
- setSelectedConversationId: (id: any) => { },
7
- options: {}
6
+ setSelectedConversationId: (id: any) => { }
8
7
  });
9
8
 
10
9
  type Props = {
11
- children: React.ReactNode,
12
- options?: MessengerContextOptions
10
+ children: React.ReactNode
13
11
  }
14
12
 
15
- const MessengerProvider = ({ children, options }: Props) => {
16
-
17
- let defaultOptions: MessengerContextOptions = {
18
- reactions: ['😍', '😎', '😉', '😜', '👍']
19
- };
20
-
21
- let opts = { ...defaultOptions, ...options };
13
+ const MessengerProvider = ({ children }: Props) => {
22
14
 
23
15
  const [selectedConversationId, setSelectedConversation] = useState(null);
24
16
 
@@ -31,7 +23,7 @@ const MessengerProvider = ({ children, options }: Props) => {
31
23
 
32
24
  return (
33
25
  <>
34
- <MessengerContext.Provider value={{ options: opts, selectedConversationId, setSelectedConversationId }}>
26
+ <MessengerContext.Provider value={{ selectedConversationId, setSelectedConversationId }}>
35
27
  {children}
36
28
  </MessengerContext.Provider>
37
29
  </>