@weavy/uikit-react 11.0.1 → 12.1.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/.github/workflows/publish.yml +1 -1
- package/README.md +3 -4
- package/changelog.md +30 -0
- package/dist/cjs/index.js +6 -6
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/client/WeavyClient.d.ts +1 -1
- package/dist/cjs/types/components/Image.d.ts +2 -0
- package/dist/cjs/types/components/PdfViewer.d.ts +6 -0
- package/dist/cjs/types/components/Preview.d.ts +58 -0
- package/dist/cjs/types/contexts/MessengerContext.d.ts +1 -2
- package/dist/cjs/types/hooks/useUser.d.ts +2 -1
- package/dist/cjs/types/types/Messenger.d.ts +0 -1
- package/dist/cjs/types/types/types.d.ts +11 -6
- package/dist/cjs/types/utils/fileUtilities.d.ts +16 -1
- package/dist/css/weavy-chat.css +2684 -0
- package/dist/css/weavy-messenger.css +3039 -0
- package/dist/css/weavy.css +3039 -0
- package/dist/esm/index.js +6 -6
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/client/WeavyClient.d.ts +1 -1
- package/dist/esm/types/components/Image.d.ts +2 -0
- package/dist/esm/types/components/PdfViewer.d.ts +6 -0
- package/dist/esm/types/components/Preview.d.ts +58 -0
- package/dist/esm/types/contexts/MessengerContext.d.ts +1 -2
- package/dist/esm/types/hooks/useUser.d.ts +2 -1
- package/dist/esm/types/types/Messenger.d.ts +0 -1
- package/dist/esm/types/types/types.d.ts +11 -6
- package/dist/esm/types/utils/fileUtilities.d.ts +16 -1
- package/dist/index.d.ts +2 -3
- package/package.json +14 -4
- package/rollup.config.js +0 -2
- package/src/client/WeavyClient.ts +3 -3
- package/src/components/Attachment.tsx +5 -6
- package/src/components/Avatar.tsx +2 -3
- package/src/components/Chat.tsx +10 -14
- package/src/components/Conversation.tsx +21 -30
- package/src/components/ConversationBadge.tsx +1 -2
- package/src/components/ConversationForm.tsx +11 -18
- package/src/components/ConversationList.tsx +4 -5
- package/src/components/ConversationListItem.tsx +13 -15
- package/src/components/FileBrowser.tsx +2 -3
- package/src/components/Image.tsx +39 -7
- package/src/components/MeetingCard.tsx +7 -8
- package/src/components/Message.tsx +12 -13
- package/src/components/Messages.tsx +19 -19
- package/src/components/Messenger.tsx +5 -6
- package/src/components/NewConversation.tsx +5 -7
- package/src/components/PdfViewer.tsx +271 -0
- package/src/components/Presence.tsx +2 -2
- package/src/components/Preview.tsx +294 -0
- package/src/components/Reactions.tsx +8 -8
- package/src/components/SearchUsers.tsx +18 -17
- package/src/components/SeenBy.tsx +1 -2
- package/src/components/Typing.tsx +4 -4
- package/src/contexts/MessengerContext.tsx +4 -12
- package/src/contexts/PreviewContext.tsx +4 -6
- package/src/contexts/WeavyContext.tsx +8 -1
- package/src/hooks/useBadge.ts +1 -1
- package/src/hooks/useChat.ts +1 -1
- package/src/hooks/useConversation.ts +1 -2
- package/src/hooks/useConversations.ts +1 -1
- package/src/hooks/useFileUploader.ts +5 -1
- package/src/hooks/useMembers.ts +1 -1
- package/src/hooks/useMessages.ts +1 -1
- package/src/hooks/useMutateChat.ts +1 -1
- package/src/hooks/useMutateConversation.ts +1 -1
- package/src/hooks/useMutateConversationName.ts +1 -1
- package/src/hooks/useMutateDeleteReaction.ts +1 -1
- package/src/hooks/useMutateExternalBlobs.ts +1 -1
- package/src/hooks/useMutateMeeting.ts +1 -1
- package/src/hooks/useMutateMembers.ts +1 -1
- package/src/hooks/useMutateMessage.ts +2 -1
- package/src/hooks/useMutatePinned.ts +1 -1
- package/src/hooks/useMutateReaction.ts +1 -1
- package/src/hooks/useMutateRead.ts +1 -1
- package/src/hooks/useMutateRemoveMembers.ts +1 -1
- package/src/hooks/useMutateTyping.ts +1 -1
- package/src/hooks/usePresence.ts +8 -5
- package/src/hooks/useReactions.ts +0 -1
- package/src/hooks/useSearchUsers.ts +1 -1
- package/src/hooks/useUser.ts +4 -3
- package/src/index.ts +2 -2
- package/src/scss/theme/_alert.scss +73 -0
- package/src/scss/theme/_appbar.scss +112 -0
- package/src/scss/theme/_attachments.scss +74 -0
- package/src/scss/theme/_avatar.scss +54 -0
- package/src/scss/theme/_badge.scss +47 -0
- package/src/scss/theme/_buttons.scss +96 -0
- package/src/scss/theme/_card.scss +7 -0
- package/src/scss/theme/_checkbox.scss +56 -0
- package/src/scss/theme/_cm-editor.scss +42 -0
- package/src/scss/theme/_code.scss +115 -0
- package/src/scss/theme/_colors.scss +520 -0
- package/src/scss/theme/_config.scss +6 -0
- package/src/scss/theme/_content.scss +15 -0
- package/src/scss/theme/_conversations.scss +91 -0
- package/src/scss/theme/_dropdown.scss +86 -0
- package/src/scss/theme/_emoji.scss +5 -0
- package/src/scss/theme/_filebrowser.scss +26 -0
- package/src/scss/theme/_files.scss +140 -0
- package/src/scss/theme/_icons.scss +62 -0
- package/src/scss/theme/_image-grid.scss +63 -0
- package/src/scss/theme/_inputs.scss +28 -0
- package/src/scss/theme/_message-editor.scss +90 -0
- package/src/scss/theme/_messages.scss +238 -0
- package/src/scss/theme/_nav.scss +52 -0
- package/src/scss/theme/_overlays.scss +157 -0
- package/src/scss/theme/_pager.scss +19 -0
- package/src/scss/theme/_palette.scss +165 -0
- package/src/scss/theme/_pane.scss +16 -0
- package/src/scss/theme/_panels.scss +137 -0
- package/src/scss/theme/_picker-list.scss +37 -0
- package/src/scss/theme/_preview-embed.scss +38 -0
- package/src/scss/theme/_preview-html.scss +7 -0
- package/src/scss/theme/_preview-icon.scss +41 -0
- package/src/scss/theme/_preview-image.scss +86 -0
- package/src/scss/theme/_preview-media.scss +28 -0
- package/src/scss/theme/_preview-pdf.scss +838 -0
- package/src/scss/theme/_preview-text.scss +5 -0
- package/src/scss/theme/_preview.scss +105 -0
- package/src/scss/theme/_reactions.scss +58 -0
- package/src/scss/theme/_reboot.scss +41 -0
- package/src/scss/theme/_root.scss +2 -0
- package/src/scss/theme/_scroll.scss +55 -0
- package/src/scss/theme/_search.scss +68 -0
- package/src/scss/theme/_spinner.scss +63 -0
- package/src/scss/theme/_tables.scss +53 -0
- package/src/scss/theme/_toasts.scss +47 -0
- package/src/scss/theme/_turbo.scss +17 -0
- package/src/scss/theme/_typing.scss +26 -0
- package/src/scss/theme/_variables.scss +139 -0
- package/src/scss/theme/bootstrap/_accordion.scss +146 -0
- package/src/scss/theme/bootstrap/_alert.scss +71 -0
- package/src/scss/theme/bootstrap/_badge.scss +38 -0
- package/src/scss/theme/bootstrap/_breadcrumb.scss +40 -0
- package/src/scss/theme/bootstrap/_button-group.scss +142 -0
- package/src/scss/theme/bootstrap/_buttons.scss +186 -0
- package/src/scss/theme/bootstrap/_card.scss +234 -0
- package/src/scss/theme/bootstrap/_carousel.scss +229 -0
- package/src/scss/theme/bootstrap/_close.scss +40 -0
- package/src/scss/theme/bootstrap/_containers.scss +41 -0
- package/src/scss/theme/bootstrap/_dropdown.scss +248 -0
- package/src/scss/theme/bootstrap/_forms.scss +9 -0
- package/src/scss/theme/bootstrap/_functions.scss +302 -0
- package/src/scss/theme/bootstrap/_grid.scss +33 -0
- package/src/scss/theme/bootstrap/_helpers.scss +10 -0
- package/src/scss/theme/bootstrap/_images.scss +42 -0
- package/src/scss/theme/bootstrap/_list-group.scss +191 -0
- package/src/scss/theme/bootstrap/_maps.scss +54 -0
- package/src/scss/theme/bootstrap/_mixins.scss +43 -0
- package/src/scss/theme/bootstrap/_modal.scss +237 -0
- package/src/scss/theme/bootstrap/_nav.scss +172 -0
- package/src/scss/theme/bootstrap/_navbar.scss +276 -0
- package/src/scss/theme/bootstrap/_offcanvas.scss +143 -0
- package/src/scss/theme/bootstrap/_pagination.scss +109 -0
- package/src/scss/theme/bootstrap/_placeholders.scss +51 -0
- package/src/scss/theme/bootstrap/_popover.scss +196 -0
- package/src/scss/theme/bootstrap/_progress.scss +59 -0
- package/src/scss/theme/bootstrap/_reboot.scss +610 -0
- package/src/scss/theme/bootstrap/_root.scss +73 -0
- package/src/scss/theme/bootstrap/_spinners.scss +85 -0
- package/src/scss/theme/bootstrap/_tables.scss +164 -0
- package/src/scss/theme/bootstrap/_toasts.scss +70 -0
- package/src/scss/theme/bootstrap/_tooltip.scss +120 -0
- package/src/scss/theme/bootstrap/_transitions.scss +27 -0
- package/src/scss/theme/bootstrap/_type.scss +106 -0
- package/src/scss/theme/bootstrap/_utilities.scss +647 -0
- package/src/scss/theme/bootstrap/_variables.scss +1633 -0
- package/src/scss/theme/bootstrap/forms/_floating-labels.scss +74 -0
- package/src/scss/theme/bootstrap/forms/_form-check.scss +175 -0
- package/src/scss/theme/bootstrap/forms/_form-control.scss +194 -0
- package/src/scss/theme/bootstrap/forms/_form-range.scss +91 -0
- package/src/scss/theme/bootstrap/forms/_form-select.scss +71 -0
- package/src/scss/theme/bootstrap/forms/_form-text.scss +11 -0
- package/src/scss/theme/bootstrap/forms/_input-group.scss +129 -0
- package/src/scss/theme/bootstrap/forms/_labels.scss +36 -0
- package/src/scss/theme/bootstrap/forms/_validation.scss +12 -0
- package/src/scss/theme/bootstrap/helpers/_clearfix.scss +3 -0
- package/src/scss/theme/bootstrap/helpers/_color-bg.scss +10 -0
- package/src/scss/theme/bootstrap/helpers/_colored-links.scss +12 -0
- package/src/scss/theme/bootstrap/helpers/_position.scss +36 -0
- package/src/scss/theme/bootstrap/helpers/_ratio.scss +26 -0
- package/src/scss/theme/bootstrap/helpers/_stacks.scss +15 -0
- package/src/scss/theme/bootstrap/helpers/_stretched-link.scss +15 -0
- package/src/scss/theme/bootstrap/helpers/_text-truncation.scss +7 -0
- package/src/scss/theme/bootstrap/helpers/_visually-hidden.scss +8 -0
- package/src/scss/theme/bootstrap/helpers/_vr.scss +8 -0
- package/src/scss/theme/bootstrap/mixins/_alert.scss +15 -0
- package/src/scss/theme/bootstrap/mixins/_backdrop.scss +14 -0
- package/src/scss/theme/bootstrap/mixins/_banner.scss +9 -0
- package/src/scss/theme/bootstrap/mixins/_border-radius.scss +78 -0
- package/src/scss/theme/bootstrap/mixins/_box-shadow.scss +18 -0
- package/src/scss/theme/bootstrap/mixins/_breakpoints.scss +127 -0
- package/src/scss/theme/bootstrap/mixins/_buttons.scss +70 -0
- package/src/scss/theme/bootstrap/mixins/_caret.scss +64 -0
- package/src/scss/theme/bootstrap/mixins/_clearfix.scss +9 -0
- package/src/scss/theme/bootstrap/mixins/_color-scheme.scss +7 -0
- package/src/scss/theme/bootstrap/mixins/_container.scss +11 -0
- package/src/scss/theme/bootstrap/mixins/_deprecate.scss +10 -0
- package/src/scss/theme/bootstrap/mixins/_forms.scss +152 -0
- package/src/scss/theme/bootstrap/mixins/_gradients.scss +47 -0
- package/src/scss/theme/bootstrap/mixins/_grid.scss +151 -0
- package/src/scss/theme/bootstrap/mixins/_image.scss +16 -0
- package/src/scss/theme/bootstrap/mixins/_list-group.scss +24 -0
- package/src/scss/theme/bootstrap/mixins/_lists.scss +7 -0
- package/src/scss/theme/bootstrap/mixins/_pagination.scss +10 -0
- package/src/scss/theme/bootstrap/mixins/_reset-text.scss +17 -0
- package/src/scss/theme/bootstrap/mixins/_resize.scss +6 -0
- package/src/scss/theme/bootstrap/mixins/_table-variants.scss +24 -0
- package/src/scss/theme/bootstrap/mixins/_text-truncate.scss +8 -0
- package/src/scss/theme/bootstrap/mixins/_transition.scss +26 -0
- package/src/scss/theme/bootstrap/mixins/_utilities.scss +97 -0
- package/src/scss/theme/bootstrap/mixins/_visually-hidden.scss +29 -0
- package/src/scss/theme/bootstrap/utilities/_api.scss +47 -0
- package/src/scss/theme/bootstrap/vendor/_rfs.scss +354 -0
- package/src/scss/theme/bs/_badge.scss +20 -0
- package/src/scss/theme/bs/_buttons.scss +185 -0
- package/src/scss/theme/bs/_dropdown.scss +86 -0
- package/src/scss/theme/bs/_forms.scss +161 -0
- package/src/scss/theme/bs/_list-group.scss +73 -0
- package/src/scss/theme/bs/_tables.scss +46 -0
- package/src/scss/theme/fonts/_fontmapping-roboto.scss +129 -0
- package/src/scss/theme/fonts/_fontmapping-segoe-ui.scss +127 -0
- package/src/scss/theme/fonts/_index.scss +2 -0
- package/src/scss/theme/mixins/_backdrop.scss +13 -0
- package/src/scss/theme/mixins/_palette.scss +165 -0
- package/src/scss/theme/mixins/_position.scss +33 -0
- package/src/scss/theme/mixins/_scrollbar.scss +110 -0
- package/src/scss/weavy-chat.scss +31 -0
- package/src/scss/weavy-messenger.scss +60 -0
- package/src/scss/weavy.scss +2 -0
- package/src/types/Messenger.ts +1 -1
- package/src/types/types.ts +14 -9
- package/src/ui/Button.tsx +3 -4
- package/src/ui/Dropdown.tsx +4 -5
- package/src/ui/Icon.tsx +75 -39
- package/src/ui/Overlay.tsx +2 -3
- package/src/utils/fileUtilities.ts +280 -72
- package/src/utils/scrollbarDetection.js +48 -0
- package/dist/cjs/types/utils/styles.d.ts +0 -17
- package/dist/esm/types/utils/styles.d.ts +0 -17
- package/src/utils/styles.ts +0 -42
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useCallback, useState } from "react";
|
|
2
|
+
import pdfjsLib from 'pdfjs-dist';
|
|
3
|
+
import pdfjsViewer from 'pdfjs-dist/web/pdf_viewer';
|
|
4
|
+
import Icon from "../ui/Icon";
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
src: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const PdfViewer = ({ src }: Props) => {
|
|
11
|
+
//const viewerRef = useRef(null);
|
|
12
|
+
const pageNumberRef = useRef<HTMLInputElement>(null);
|
|
13
|
+
const totalPagesRef = useRef<HTMLSpanElement>(null);
|
|
14
|
+
const zoomLevelRef = useRef<HTMLInputElement>(null);
|
|
15
|
+
|
|
16
|
+
const [pdfEventBus, setPdfEventBus] = useState<pdfjsViewer.EventBus>();
|
|
17
|
+
|
|
18
|
+
const [pdfViewer, setPdfViewer] = useState<pdfjsViewer.PDFViewer>();
|
|
19
|
+
const [pdfLinkService, setPdfLinkService] = useState<pdfjsViewer.PDFLinkService>();
|
|
20
|
+
const [pdfFindController, setPdfFindController] = useState<pdfjsViewer.PDFFindController>();
|
|
21
|
+
|
|
22
|
+
const DEFAULT_SCALE_DELTA = 1.1;
|
|
23
|
+
const MAX_SCALE = 3.0;
|
|
24
|
+
const MIN_SCALE = 0.2;
|
|
25
|
+
|
|
26
|
+
const SEARCH_FOR = "";
|
|
27
|
+
const ENABLE_XFA = true;
|
|
28
|
+
|
|
29
|
+
// Some PDFs need external cmaps.
|
|
30
|
+
const CMAP_URL = "/cmaps/";
|
|
31
|
+
const CMAP_PACKED = true;
|
|
32
|
+
|
|
33
|
+
// Setting worker path to worker bundle.
|
|
34
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = "/js/pdf.worker.min.js";
|
|
35
|
+
|
|
36
|
+
const viewerRef = useCallback((container: any) => {
|
|
37
|
+
if (container !== null) {
|
|
38
|
+
|
|
39
|
+
let linkService = pdfLinkService;
|
|
40
|
+
let findController = pdfFindController;
|
|
41
|
+
let viewer = pdfViewer;
|
|
42
|
+
let eventBus = pdfEventBus;
|
|
43
|
+
|
|
44
|
+
if (!pdfEventBus) {
|
|
45
|
+
eventBus = new pdfjsViewer.EventBus();
|
|
46
|
+
setPdfEventBus(eventBus);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// (Optionally) enable hyperlinks within PDF files.
|
|
50
|
+
if (!pdfLinkService) {
|
|
51
|
+
linkService = new pdfjsViewer.PDFLinkService({
|
|
52
|
+
eventBus: eventBus!,
|
|
53
|
+
})
|
|
54
|
+
setPdfLinkService(linkService);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// (Optionally) enable find controller.
|
|
58
|
+
if (!pdfFindController) {
|
|
59
|
+
findController = new pdfjsViewer.PDFFindController({
|
|
60
|
+
eventBus: eventBus!,
|
|
61
|
+
linkService: linkService!,
|
|
62
|
+
})
|
|
63
|
+
setPdfFindController(findController);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!pdfViewer && eventBus) {
|
|
67
|
+
// @ts-ignore due to incorrect type def
|
|
68
|
+
viewer = new pdfjsViewer.PDFViewer({
|
|
69
|
+
container: container,
|
|
70
|
+
eventBus: eventBus,
|
|
71
|
+
linkService: linkService!,
|
|
72
|
+
findController: findController,
|
|
73
|
+
//defaultZoomValue: 1.0,
|
|
74
|
+
//scriptingManager: pdfScriptingManager,
|
|
75
|
+
//enableScripting: true, // Only necessary in PDF.js version 2.10.377 and below.
|
|
76
|
+
})
|
|
77
|
+
//pdfViewer!.MAX_AUTO_SCALE = 1.0;
|
|
78
|
+
linkService!.setViewer(viewer);
|
|
79
|
+
setPdfViewer(viewer);
|
|
80
|
+
|
|
81
|
+
eventBus.on("scalechanging", function () {
|
|
82
|
+
console.debug("scalechanging")
|
|
83
|
+
zoomLevelRef.current!.value = (Math.round(viewer!.currentScale * 100)).toFixed(0) + "%";
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
eventBus.on("pagechanging", function () {
|
|
87
|
+
console.debug("pagechanging")
|
|
88
|
+
if (pageNumberRef.current) {
|
|
89
|
+
pageNumberRef.current.value = viewer!.currentPageNumber.toFixed(0);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
eventBus.on("pagesinit", function () {
|
|
94
|
+
// We can use pdfViewer now, e.g. let's change default scale.
|
|
95
|
+
viewer!.currentScaleValue = "auto";
|
|
96
|
+
pageNumberRef.current!.value = "1";
|
|
97
|
+
totalPagesRef.current!.innerText = viewer!.pagesCount.toFixed(0);
|
|
98
|
+
|
|
99
|
+
// We can try searching for things.
|
|
100
|
+
if (SEARCH_FOR) {
|
|
101
|
+
if (findController && !("_onFind" in findController)) {
|
|
102
|
+
// @ts-ignore due to missing type def
|
|
103
|
+
findController.executeCommand("find", { query: SEARCH_FOR });
|
|
104
|
+
} else {
|
|
105
|
+
eventBus!.dispatch("find", { type: "", query: SEARCH_FOR });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
} else {
|
|
112
|
+
console.warn("pdf viewer dismounted")
|
|
113
|
+
}
|
|
114
|
+
}, []);
|
|
115
|
+
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (src && pdfViewer && pdfLinkService) {
|
|
118
|
+
let loadingTask: pdfjsLib.PDFDocumentLoadingTask;
|
|
119
|
+
let loadedDoc: pdfjsLib.PDFDocumentProxy;
|
|
120
|
+
|
|
121
|
+
loadingTask = pdfjsLib.getDocument({
|
|
122
|
+
url: src,
|
|
123
|
+
enableXfa: ENABLE_XFA,
|
|
124
|
+
cMapUrl: CMAP_URL,
|
|
125
|
+
cMapPacked: CMAP_PACKED,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Re-use the worker?
|
|
129
|
+
//pdfjsLib.GlobalWorkerOptions.workerPort = loadingTask._worker.port
|
|
130
|
+
|
|
131
|
+
loadingTask.promise.then(function (doc) {
|
|
132
|
+
loadedDoc = doc;
|
|
133
|
+
//console.log("LOADED PDF");
|
|
134
|
+
pdfViewer.setDocument(doc);
|
|
135
|
+
pdfLinkService.setDocument(doc, null);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return () => {
|
|
139
|
+
if (loadingTask) {
|
|
140
|
+
console.log("resetting pdf viewer");
|
|
141
|
+
if(loadingTask._transport){
|
|
142
|
+
loadingTask._transport.destroy();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (loadedDoc) {
|
|
146
|
+
loadedDoc.destroy();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
//loadingTask.destroy();
|
|
150
|
+
pdfViewer.cleanup();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}, [src, pdfViewer])
|
|
155
|
+
|
|
156
|
+
function setScale(scale: number | string) {
|
|
157
|
+
console.debug("setScale:", scale)
|
|
158
|
+
if (pdfViewer) {
|
|
159
|
+
pdfViewer.currentScaleValue = typeof scale === "number" ? scale.toFixed(2) : scale;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function setPage(pageNumber: number) {
|
|
164
|
+
console.debug("setPage:", pageNumber)
|
|
165
|
+
if (pdfViewer) {
|
|
166
|
+
pdfViewer.currentPageNumber = pageNumber;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function updatePage() {
|
|
171
|
+
console.debug("updatePage");
|
|
172
|
+
if (pdfViewer && pageNumberRef.current) {
|
|
173
|
+
let pageNumber = parseInt(pageNumberRef.current.value);
|
|
174
|
+
|
|
175
|
+
if (isNaN(pageNumber)) {
|
|
176
|
+
setPage(pdfViewer.currentPageNumber);
|
|
177
|
+
} else if (pageNumber > pdfViewer.pagesCount) {
|
|
178
|
+
setPage(pdfViewer.pagesCount);
|
|
179
|
+
} else if (pageNumber <= 0) {
|
|
180
|
+
setPage(1);
|
|
181
|
+
} else {
|
|
182
|
+
setPage(pageNumber);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function select(e: any) {
|
|
189
|
+
console.debug("select");
|
|
190
|
+
e.target.setSelectionRange(0, e.target.value.length);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function fitToPage() {
|
|
194
|
+
setScale("page-fit");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function fitToWidth() {
|
|
198
|
+
setScale("page-width");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function zoomIn() {
|
|
202
|
+
console.debug("zoomIn");
|
|
203
|
+
|
|
204
|
+
if (pdfViewer) {
|
|
205
|
+
let newScale = pdfViewer.currentScale;
|
|
206
|
+
let steps = 1;
|
|
207
|
+
do {
|
|
208
|
+
newScale = newScale * DEFAULT_SCALE_DELTA;
|
|
209
|
+
newScale = Math.floor(newScale * 10) / 10;
|
|
210
|
+
newScale = Math.min(MAX_SCALE, newScale);
|
|
211
|
+
} while (--steps > 0 && newScale < MAX_SCALE);
|
|
212
|
+
|
|
213
|
+
setScale(newScale);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function zoomOut() {
|
|
218
|
+
console.debug("zoomOut");
|
|
219
|
+
if (pdfViewer) {
|
|
220
|
+
let newScale = pdfViewer.currentScale;
|
|
221
|
+
let steps = 1;
|
|
222
|
+
do {
|
|
223
|
+
newScale = newScale / DEFAULT_SCALE_DELTA;
|
|
224
|
+
newScale = Math.floor(newScale * 10) / 10;
|
|
225
|
+
newScale = Math.max(MIN_SCALE, newScale);
|
|
226
|
+
} while (--steps > 0 && newScale > MIN_SCALE);
|
|
227
|
+
|
|
228
|
+
setScale(newScale);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function updateZoom() {
|
|
233
|
+
console.debug("updateZoom");
|
|
234
|
+
if (pdfViewer && zoomLevelRef.current) {
|
|
235
|
+
let zoomValue = parseFloat(zoomLevelRef.current.value.replace("%", ""));
|
|
236
|
+
if (isNaN(zoomValue)) {
|
|
237
|
+
setScale(pdfViewer.currentScale + 0.0001);
|
|
238
|
+
} else {
|
|
239
|
+
setScale(zoomValue / 100);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
<div className="wy-content-pdf" data-controller="pdf" data-pdf-url-value="">
|
|
246
|
+
<div className="wy-toolbar">
|
|
247
|
+
<nav className="wy-tools">
|
|
248
|
+
<div className="wy-tool">
|
|
249
|
+
<input type="text" className="wy-input" ref={pageNumberRef} onChange={updatePage} onClick={select} data-pdf-target="pageNumber"/>
|
|
250
|
+
<span>/</span>
|
|
251
|
+
<span ref={totalPagesRef}>1</span>
|
|
252
|
+
</div>
|
|
253
|
+
<div className="wy-tool">
|
|
254
|
+
<button className="wy-button wy-button-icon btn-zoom-out" onClick={zoomOut} title="Zoom out"><Icon.UI name="minus" /></button>
|
|
255
|
+
<input type="text" className="wy-input" ref={zoomLevelRef} onChange={updateZoom} onClick={select} value="100%" data-pdf-target="zoomLevel"/>
|
|
256
|
+
<button className="wy-button wy-button-icon btn-zoom-in" onClick={zoomIn} title="Zoom in"><Icon.UI name="plus" /></button>
|
|
257
|
+
</div>
|
|
258
|
+
<div className="wy-tool">
|
|
259
|
+
<button className="wy-button wy-button-icon btn-fit-page" onClick={fitToWidth} title="Fit to width"><Icon.UI name="fit-width" /></button>
|
|
260
|
+
<button className="wy-button wy-button-icon" onClick={fitToPage} title="Fit to screen"><Icon.UI name="fit-screen" /></button>
|
|
261
|
+
</div>
|
|
262
|
+
</nav>
|
|
263
|
+
</div>
|
|
264
|
+
<div ref={viewerRef} className="wy-pdf-container">
|
|
265
|
+
<div id="viewer" className="pdfViewer"></div>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export default PdfViewer;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import
|
|
2
|
+
import classNames from "classnames";
|
|
3
3
|
|
|
4
4
|
type Props = {
|
|
5
5
|
id: number,
|
|
@@ -8,7 +8,7 @@ type Props = {
|
|
|
8
8
|
|
|
9
9
|
const Presence = ({ id, status }: Props) => {
|
|
10
10
|
return (
|
|
11
|
-
<span className={wy
|
|
11
|
+
<span className={classNames("wy-presence", { "wy-presence-active": status === "active" })} data-presence-id={id}></span>
|
|
12
12
|
)
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import React, { useEffect, useState, useCallback } from "react";
|
|
2
|
+
import Icon from "../ui/Icon";
|
|
3
|
+
import classNames from "classnames";
|
|
4
|
+
import PdfViewer from "./PdfViewer";
|
|
5
|
+
import { checkImageLoad, imageLoaded } from "./Image";
|
|
6
|
+
|
|
7
|
+
const getStream = (response: Response) => {
|
|
8
|
+
if (response && response.ok && response.body) {
|
|
9
|
+
const reader = response.body.getReader();
|
|
10
|
+
return new ReadableStream({
|
|
11
|
+
start(controller) {
|
|
12
|
+
let pump: any = () => {
|
|
13
|
+
return reader.read().then(({ done, value }) => {
|
|
14
|
+
// When no more data needs to be consumed, close the stream
|
|
15
|
+
if (done) {
|
|
16
|
+
controller.close();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// Enqueue the next data chunk into our target stream
|
|
20
|
+
controller.enqueue(value);
|
|
21
|
+
return pump();
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return pump();
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type ImageProps = {
|
|
32
|
+
src: string,
|
|
33
|
+
width?: number,
|
|
34
|
+
height?: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const PreviewImage = ({ src, width, height }: ImageProps) => {
|
|
38
|
+
const imageRef = useCallback((element: HTMLImageElement) => {
|
|
39
|
+
if (element) {
|
|
40
|
+
checkImageLoad(element);
|
|
41
|
+
}
|
|
42
|
+
}, [])
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<>
|
|
46
|
+
{width && height ?
|
|
47
|
+
<div className="wy-content-image wy-responsive-image" style={{ ["--width" as any]: width, ["--height" as any]: height }}>
|
|
48
|
+
<img ref={imageRef} src={src} onLoad={imageLoaded} width={width} height={height} decoding="async" />
|
|
49
|
+
{/* TODO: spinner here */}
|
|
50
|
+
</div>
|
|
51
|
+
:
|
|
52
|
+
<div className="wy-content-image wy-responsive-image wy-intrinsic-image">
|
|
53
|
+
<img ref={imageRef} src={src} onLoad={imageLoaded} decoding="async" />
|
|
54
|
+
</div>
|
|
55
|
+
}
|
|
56
|
+
</>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type DocumentProps = {
|
|
61
|
+
src: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const PreviewDocument = ({ src }: DocumentProps) => {
|
|
65
|
+
return (
|
|
66
|
+
<PdfViewer src={src} />
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function mediaFallback(media: HTMLVideoElement | HTMLAudioElement) {
|
|
71
|
+
if (media.classList.contains("wy-loading")) {
|
|
72
|
+
media.classList.add("wy-loaded");
|
|
73
|
+
}
|
|
74
|
+
media.classList.add("wy-error");
|
|
75
|
+
// TODO: replace with react way
|
|
76
|
+
media.outerHTML = media.outerHTML.replace(/<(video|audio)/, "<div").replace(/(video|audio)>/, "div>");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function mediaLoaded(event: any) {
|
|
80
|
+
var src = event.target;
|
|
81
|
+
if (src.tagName === 'VIDEO' || src.tagName === 'AUDIO') {
|
|
82
|
+
if (src.classList.contains("wy-loading")) {
|
|
83
|
+
console.log("loaded")
|
|
84
|
+
src.classList.add("wy-loaded");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function mediaError(event: any) {
|
|
90
|
+
var src = event.target;
|
|
91
|
+
var media;
|
|
92
|
+
if (src.tagName === 'SOURCE') {
|
|
93
|
+
media = src.parentNode;
|
|
94
|
+
media.dataset.errors = (media.dataset.errors || 0) + 1;
|
|
95
|
+
|
|
96
|
+
if (media.querySelectorAll("source").length >= media.dataset.errors) {
|
|
97
|
+
console.warn(media.tagName.toLowerCase() + " source error, switching to fallback");
|
|
98
|
+
mediaFallback(media);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function codecError(event: any) {
|
|
104
|
+
var src = event.target;
|
|
105
|
+
if (src.tagName === 'VIDEO' || src.tagName === 'AUDIO') {
|
|
106
|
+
// Capture codec-error for video in firefox
|
|
107
|
+
if (src.tagName === 'VIDEO' && !src.videoWidth || src.tagName === 'AUDIO' && !src.duration) {
|
|
108
|
+
console.warn(src.tagName.toLowerCase() + " track not available, switching to fallback");
|
|
109
|
+
mediaFallback(src);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
type VideoProps = {
|
|
115
|
+
src: string,
|
|
116
|
+
name: string,
|
|
117
|
+
mediaType?: string
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const PreviewVideo = ({ src, name, mediaType }: VideoProps) => {
|
|
121
|
+
/* TODO: loading, error handling and stopping */
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<>
|
|
125
|
+
<video className="wy-content-video" controls crossOrigin="use-credentials" autoPlay>
|
|
126
|
+
<source src={src} type={mediaType} />
|
|
127
|
+
<PreviewIcon src={src} name={name} icon="file-video" download />
|
|
128
|
+
</video>
|
|
129
|
+
{/* TODO: spinner here */}
|
|
130
|
+
</>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
type AudioProps = {
|
|
135
|
+
src: string,
|
|
136
|
+
name: string,
|
|
137
|
+
mediaType?: string
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export const PreviewAudio = ({ src, name, mediaType }: AudioProps) => {
|
|
141
|
+
/* TODO: loading, error handling and stopping */
|
|
142
|
+
return (
|
|
143
|
+
<>
|
|
144
|
+
<PreviewIcon src={src} name={name} icon="file-music" download>
|
|
145
|
+
<audio className="wy-content-audio" controls crossOrigin="use-credentials" autoPlay>
|
|
146
|
+
<source src={src} type={mediaType} />
|
|
147
|
+
</audio>
|
|
148
|
+
</PreviewIcon>
|
|
149
|
+
</>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
type TextProps = {
|
|
154
|
+
src: string,
|
|
155
|
+
html?: boolean,
|
|
156
|
+
code?: boolean
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const PreviewText = ({ src, html = false, code = false }: TextProps) => {
|
|
160
|
+
const [textContent, setTextContent] = useState("");
|
|
161
|
+
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
fetch(src)
|
|
164
|
+
.then(getStream)
|
|
165
|
+
// Create a new response out of the stream
|
|
166
|
+
.then(stream => new Response(stream))
|
|
167
|
+
// Create an object URL for the response
|
|
168
|
+
.then(response => response.text())
|
|
169
|
+
.then(text => {
|
|
170
|
+
setTextContent(text)
|
|
171
|
+
})
|
|
172
|
+
}, [src]);
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<>
|
|
176
|
+
{html ?
|
|
177
|
+
code ?
|
|
178
|
+
<div className="wy-content-code" dangerouslySetInnerHTML={{ __html: textContent }}></div>
|
|
179
|
+
:
|
|
180
|
+
<div className="wy-content-text">
|
|
181
|
+
<pre className="wy-document" dangerouslySetInnerHTML={{ __html: textContent }}></pre>
|
|
182
|
+
</div>
|
|
183
|
+
:
|
|
184
|
+
code ?
|
|
185
|
+
<div className="wy-content-code">{textContent}</div>
|
|
186
|
+
:
|
|
187
|
+
<div className="wy-content-text">
|
|
188
|
+
<pre className="wy-document">{textContent}</pre>
|
|
189
|
+
</div>
|
|
190
|
+
}
|
|
191
|
+
</>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
type EmbedProps = {
|
|
196
|
+
src: string,
|
|
197
|
+
name: string,
|
|
198
|
+
icon: string,
|
|
199
|
+
provider?: string
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export const PreviewEmbed = ({ src, name, icon, provider }: EmbedProps) => {
|
|
203
|
+
/* TODO: add loading and error handling */
|
|
204
|
+
return (
|
|
205
|
+
<>
|
|
206
|
+
{/* iframe needs to be object to not render error pages when content is blocked */}
|
|
207
|
+
<object className="wy-content-iframe" data={src}></object>
|
|
208
|
+
|
|
209
|
+
{/* TODO: add spinner here */}
|
|
210
|
+
|
|
211
|
+
<PreviewIcon src={src} name={name} icon={icon} provider={provider} className="wy-content-iframe-fallback" />
|
|
212
|
+
</>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
type IconProps = {
|
|
217
|
+
children?: React.ReactNode,
|
|
218
|
+
src: string,
|
|
219
|
+
icon: string,
|
|
220
|
+
name: string,
|
|
221
|
+
provider?: string,
|
|
222
|
+
download?: boolean,
|
|
223
|
+
className?: string
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export const PreviewIcon = ({ children, src, icon, name, provider, download = false, className }: IconProps) => {
|
|
227
|
+
return (
|
|
228
|
+
<div className={classNames("wy-content-media", className)}>
|
|
229
|
+
<div className="wy-content-icon">
|
|
230
|
+
<Icon.UI name={icon} />
|
|
231
|
+
</div>
|
|
232
|
+
<div className="wy-content-name">
|
|
233
|
+
{provider ?
|
|
234
|
+
<a href={src} target="_blank" title={`Open in ${provider}`}>{name} <Icon.UI name="open-in-new" size={1} /></a>
|
|
235
|
+
: download ?
|
|
236
|
+
<a href={src} target="_top" download>{name}</a>
|
|
237
|
+
:
|
|
238
|
+
<a href={src} target="_blank">{name} <Icon.UI name="open-in-new" size={1} /></a>
|
|
239
|
+
}
|
|
240
|
+
</div>
|
|
241
|
+
{children}
|
|
242
|
+
</div>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
type PreviewProps = {
|
|
247
|
+
src: string,
|
|
248
|
+
format: PreviewFormatType,
|
|
249
|
+
name: string,
|
|
250
|
+
icon: string,
|
|
251
|
+
width?: number,
|
|
252
|
+
height?: number,
|
|
253
|
+
mediaType?: string,
|
|
254
|
+
provider?: string
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export const Preview = ({ src, format, name, icon, width, height, mediaType, provider }: PreviewProps) => {
|
|
258
|
+
return (
|
|
259
|
+
<>
|
|
260
|
+
{format === "image" &&
|
|
261
|
+
<PreviewImage src={src} width={width} height={height} />
|
|
262
|
+
}
|
|
263
|
+
{format === "document" &&
|
|
264
|
+
<PreviewDocument src={src} />
|
|
265
|
+
}
|
|
266
|
+
{format === "video" &&
|
|
267
|
+
<PreviewVideo src={src} name={name} mediaType={mediaType} />
|
|
268
|
+
}
|
|
269
|
+
{format === "audio" &&
|
|
270
|
+
<PreviewAudio src={src} name={name} mediaType={mediaType} />
|
|
271
|
+
}
|
|
272
|
+
{format === "text" &&
|
|
273
|
+
<PreviewText src={src} />
|
|
274
|
+
}
|
|
275
|
+
{format === "code" &&
|
|
276
|
+
<PreviewText src={src} html code />
|
|
277
|
+
}
|
|
278
|
+
{format === "markup" &&
|
|
279
|
+
<PreviewText src={src} html />
|
|
280
|
+
}
|
|
281
|
+
{format === "embed" &&
|
|
282
|
+
<PreviewEmbed src={src} name={name} icon={icon} provider={provider} />
|
|
283
|
+
}
|
|
284
|
+
{format === "link" &&
|
|
285
|
+
<PreviewIcon src={src} name={name} icon={icon} provider={provider} />
|
|
286
|
+
}
|
|
287
|
+
{format === "download" &&
|
|
288
|
+
<PreviewIcon src={src} name={name} icon={icon} download />
|
|
289
|
+
}
|
|
290
|
+
</>
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
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(
|
|
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={
|
|
72
|
-
<Button.UI onClick={toggleReactionMenu}><Icon.UI name="emoticon-
|
|
73
|
-
<div className=
|
|
74
|
-
<div className=
|
|
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={
|
|
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=
|
|
91
|
+
return <span key={i} className="wy-reaction" title={r.count.toString()}>{r.content}</span> //r.has_reacted
|
|
92
92
|
})}
|
|
93
93
|
</>
|
|
94
94
|
)
|