diff --git a/apps/web/src/app.css b/apps/web/src/app.css
index c3ef75d27..1c132f0c5 100644
--- a/apps/web/src/app.css
+++ b/apps/web/src/app.css
@@ -115,13 +115,13 @@ textarea,
}
.rpv-core__text-layer-text::selection {
- background-color: var(--background-selected) !important;
+ background-color: var(--accent-selected) !important;
color: transparent;
}
.rpv-core__text-layer-text::-moz-selection {
/* Code for Firefox */
- background-color: var(--background-selected) !important;
+ background-color: var(--accent-selected) !important;
color: transparent;
}
diff --git a/apps/web/src/common/attachments.ts b/apps/web/src/common/attachments.tsx
similarity index 82%
rename from apps/web/src/common/attachments.ts
rename to apps/web/src/common/attachments.tsx
index 493c26b26..d27a66859 100644
--- a/apps/web/src/common/attachments.ts
+++ b/apps/web/src/common/attachments.tsx
@@ -22,6 +22,10 @@ import { logger } from "../utils/logger";
import { showToast } from "../utils/toast";
import { db } from "./db";
import { checkUpload, decryptFile, saveFile } from "../interfaces/fs";
+import { ScopedThemeProvider } from "../components/theme-provider";
+import { Lightbox } from "../components/lightbox";
+import ReactDOM from "react-dom";
+import { Attachment } from "@notesnook/core";
async function download(hash: string, groupId?: string) {
const attachment = await db.attachments.attachment(hash);
@@ -127,3 +131,29 @@ export async function checkAttachment(hash: string) {
}
return { success: true };
}
+
+export async function previewImageAttachment(attachment: Attachment) {
+ const container = document.getElementById("dialogContainer");
+ if (!(container instanceof HTMLElement)) return;
+
+ const dataurl = await downloadAttachment(
+ attachment.hash,
+ "base64",
+ attachment.id
+ );
+ if (!dataurl) {
+ return showToast("error", strings.imagePreviewFailed());
+ }
+
+ ReactDOM.render(
+
+ {
+ ReactDOM.unmountComponentAtNode(container);
+ }}
+ />
+ ,
+ container
+ );
+}
diff --git a/apps/web/src/components/attachment/index.tsx b/apps/web/src/components/attachment/index.tsx
index 72709ffd0..f7be946e8 100644
--- a/apps/web/src/components/attachment/index.tsx
+++ b/apps/web/src/components/attachment/index.tsx
@@ -33,6 +33,7 @@ import {
FileWebClip,
Icon,
Loading,
+ PasswordInvisible,
References,
Rename,
Reupload,
@@ -40,7 +41,11 @@ import {
} from "../icons";
import { store, useStore } from "../../stores/attachment-store";
import { db } from "../../common/db";
-import { saveAttachment } from "../../common/attachments";
+import {
+ downloadAttachment,
+ previewImageAttachment,
+ saveAttachment
+} from "../../common/attachments";
import { reuploadAttachment } from "../editor/picker";
import { Multiselect } from "../../common/multi-select";
import { Menu } from "../../hooks/use-menu";
@@ -59,6 +64,8 @@ import { PromptDialog } from "../../dialogs/prompt";
import { DialogManager } from "../../common/dialog-manager";
import { useStore as useSelectionStore } from "../../stores/selection-store";
import { strings } from "@notesnook/intl";
+import { showToast } from "../../utils/toast";
+import { PdfPreviewDialog } from "../../dialogs/pdf-preview-dialog";
const FILE_ICONS: Record = {
"image/": FileImage,
@@ -249,6 +256,34 @@ const AttachmentMenuItems: (
status?: AttachmentProgressStatus
) => MenuItem[] = (attachment, status) => {
return [
+ {
+ key: "preview-attachment",
+ type: "button",
+ title: strings.previewAttachment(),
+ icon: PasswordInvisible.path,
+ isHidden:
+ !attachment.mimeType.startsWith("image/") &&
+ attachment.mimeType !== PDFMimeType,
+ onClick: async () => {
+ if (attachment.mimeType.startsWith("image")) {
+ await previewImageAttachment(attachment);
+ } else if (attachment.mimeType === PDFMimeType) {
+ const blob = await downloadAttachment(
+ attachment.hash,
+ "blob",
+ attachment.id
+ );
+ if (!blob) {
+ return showToast("error", strings.attachmentPreviewFailed());
+ }
+
+ PdfPreviewDialog.show({
+ url: URL.createObjectURL(blob),
+ hash: attachment.hash
+ });
+ }
+ }
+ },
{
key: "notes",
type: "button",
diff --git a/apps/web/src/components/dialog/index.tsx b/apps/web/src/components/dialog/index.tsx
index e4396a038..2216f9b31 100644
--- a/apps/web/src/components/dialog/index.tsx
+++ b/apps/web/src/components/dialog/index.tsx
@@ -44,6 +44,7 @@ type DialogProps = SxProp & {
) => void;
onOpen?: () => void;
width?: number | string;
+ height?: number | string;
showCloseButton?: boolean;
textAlignment?: "left" | "right" | "center";
buttonsAlignment?: "start" | "center" | "end";
@@ -98,7 +99,7 @@ function BaseDialog(props: React.PropsWithChildren) {
display: "flex",
flexDirection: "column",
width: ["100%", "90%", props.width || "380px"],
- maxHeight: ["100%", "80%", "70%"],
+ maxHeight: ["100%", "80%", props.height || "70%"],
height: ["100%", "auto", "auto"],
bg: "background",
alignSelf: "center",
diff --git a/apps/web/src/components/editor/index.tsx b/apps/web/src/components/editor/index.tsx
index 20c2ac5eb..1dc6bf684 100644
--- a/apps/web/src/components/editor/index.tsx
+++ b/apps/web/src/components/editor/index.tsx
@@ -26,7 +26,6 @@ import React, {
useLayoutEffect,
useCallback
} from "react";
-import ReactDOM from "react-dom";
import { Box, Button, Flex, Progress, Text } from "@theme-ui/components";
import Properties from "../properties";
import {
@@ -53,13 +52,16 @@ import Header from "./header";
import { Attachment } from "../icons";
import { attachFiles, AttachmentProgress, insertAttachments } from "./picker";
import { useEditorManager } from "./manager";
-import { saveAttachment, downloadAttachment } from "../../common/attachments";
+import {
+ saveAttachment,
+ downloadAttachment,
+ previewImageAttachment
+} from "../../common/attachments";
import { EV, EVENTS } from "@notesnook/core";
import { db } from "../../common/db";
import Titlebox, { resizeTextarea } from "./title-box";
import Config from "../../utils/config";
import { ScopedThemeProvider } from "../theme-provider";
-import { Lightbox } from "../lightbox";
import { showToast } from "../../utils/toast";
import { Item, MaybeDeletedItem, isDeleted } from "@notesnook/core";
import { debounce, debounceWithId } from "@notesnook/common";
@@ -571,24 +573,7 @@ export function Editor(props: EditorProps) {
const { hash, type } = data;
const attachment = await db.attachments.attachment(hash);
if (attachment && type === "image") {
- const container = document.getElementById("dialogContainer");
- if (!(container instanceof HTMLElement)) return;
-
- const dataurl = await downloadAttachment(hash, "base64", id);
- if (!dataurl)
- return showToast("error", strings.imagePreviewFailed());
-
- ReactDOM.render(
-
- {
- ReactDOM.unmountComponentAtNode(container);
- }}
- />
- ,
- container
- );
+ await previewImageAttachment(attachment);
} else if (
attachment &&
onPreviewDocument &&
diff --git a/apps/web/src/dialogs/pdf-preview-dialog.tsx b/apps/web/src/dialogs/pdf-preview-dialog.tsx
new file mode 100644
index 000000000..688c95507
--- /dev/null
+++ b/apps/web/src/dialogs/pdf-preview-dialog.tsx
@@ -0,0 +1,55 @@
+/*
+This file is part of the Notesnook project (https://notesnook.com/)
+
+Copyright (C) 2023 Streetwriters (Private) Limited
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import React, { Suspense } from "react";
+import { BaseDialogProps, DialogManager } from "../common/dialog-manager";
+import Dialog from "../components/dialog";
+import { Loading } from "../components/icons";
+
+const PdfPreview = React.lazy(() => import("../components/pdf-preview"));
+
+type PdfPreviewDialogProps = BaseDialogProps & {
+ url: string;
+ hash: string;
+};
+
+export const PdfPreviewDialog = DialogManager.register(
+ function PdfPreviewDialog({ onClose, url, hash }: PdfPreviewDialogProps) {
+ return (
+
+ );
+ }
+);