diff --git a/apps/mobile/app/common/filesystem/download-attachment.js b/apps/mobile/app/common/filesystem/download-attachment.js
index d35848a26..bb3dc5952 100644
--- a/apps/mobile/app/common/filesystem/download-attachment.js
+++ b/apps/mobile/app/common/filesystem/download-attachment.js
@@ -29,19 +29,29 @@ import { db } from "../database";
import Storage from "../database/storage";
import { cacheDir } from "./utils";
-export default async function downloadAttachment(hash, global = true) {
+export default async function downloadAttachment(
+ hash,
+ global = true,
+ options = {
+ silent: false,
+ cache: false
+ }
+) {
let attachment = db.attachments.attachment(hash);
+ console.log(attachment);
if (!attachment) {
console.log("attachment not found");
return;
}
let folder = {};
- if (Platform.OS === "android") {
- folder = await ScopedStorage.openDocumentTree();
- if (!folder) return;
- } else {
- folder.uri = await Storage.checkAndCreateDir("/downloads/");
+ if (!options.cache) {
+ if (Platform.OS === "android") {
+ folder = await ScopedStorage.openDocumentTree();
+ if (!folder) return;
+ } else {
+ folder.uri = await Storage.checkAndCreateDir("/downloads/");
+ }
}
try {
@@ -63,19 +73,29 @@ export default async function downloadAttachment(hash, global = true) {
hash: attachment.metadata.hash,
hashType: attachment.metadata.hashType,
mime: attachment.metadata.type,
- fileName: attachment.metadata.filename,
- uri: folder.uri,
+ fileName: options.cache ? undefined : attachment.metadata.filename,
+ uri: options.cache ? undefined : folder.uri,
chunkSize: attachment.chunkSize
};
- let fileUri = await Sodium.decryptFile(key, info, "file");
- ToastEvent.show({
- heading: "Download successful",
- message: attachment.metadata.filename + " downloaded",
- type: "success"
- });
+ let fileUri = await Sodium.decryptFile(
+ key,
+ info,
+ options.cache ? "cache" : "file"
+ );
- if (attachment.dateUploaded) {
+ if (!options.silent) {
+ ToastEvent.show({
+ heading: "Download successful",
+ message: attachment.metadata.filename + " downloaded",
+ type: "success"
+ });
+ }
+
+ if (
+ attachment.dateUploaded &&
+ !attachment.metadata?.type?.startsWith("image")
+ ) {
RNFetchBlob.fs
.unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.metadata.hash}`)
.catch(console.log);
@@ -85,24 +105,26 @@ export default async function downloadAttachment(hash, global = true) {
fileUri = folder.uri + `/${attachment.metadata.filename}`;
}
console.log("saved file uri: ", fileUri);
+ if (!options.silent) {
+ presentSheet({
+ title: "File downloaded",
+ paragraph: `${attachment.metadata.filename} saved to ${
+ Platform.OS === "android"
+ ? "selected path"
+ : "File Manager/Notesnook/downloads"
+ }`,
+ icon: "download",
+ context: global ? null : attachment.metadata.hash,
+ component: (
+
+ )
+ });
+ }
- presentSheet({
- title: "File downloaded",
- paragraph: `${attachment.metadata.filename} saved to ${
- Platform.OS === "android"
- ? "selected path"
- : "File Manager/Notesnook/downloads"
- }`,
- icon: "download",
- context: global ? null : attachment.metadata.hash,
- component: (
-
- )
- });
return fileUri;
} catch (e) {
console.log("download attachment error: ", e);
diff --git a/apps/mobile/app/components/image-preview/index.js b/apps/mobile/app/components/image-preview/index.js
index 72d632ac4..c350b36e5 100644
--- a/apps/mobile/app/components/image-preview/index.js
+++ b/apps/mobile/app/components/image-preview/index.js
@@ -20,16 +20,22 @@ along with this program. If not, see .
import React, { useEffect, useState } from "react";
import { View } from "react-native";
import ImageViewer from "react-native-image-zoom-viewer";
+import downloadAttachment from "../../common/filesystem/download-attachment";
+import { cacheDir } from "../../common/filesystem/utils";
import {
eSubscribeEvent,
eUnSubscribeEvent
} from "../../services/event-manager";
+import { useThemeStore } from "../../stores/use-theme-store";
import BaseDialog from "../dialog/base-dialog";
import { IconButton } from "../ui/icon-button";
+import { ProgressBarComponent } from "../ui/svg/lazy";
const ImagePreview = () => {
+ const colors = useThemeStore((state) => state.colors);
const [visible, setVisible] = useState(false);
const [image, setImage] = useState("");
+ const [loading, setLoading] = useState(false);
useEffect(() => {
eSubscribeEvent("ImagePreview", open);
@@ -39,9 +45,20 @@ const ImagePreview = () => {
};
}, []);
- const open = (image) => {
- setImage(image);
+ const open = async (image) => {
setVisible(true);
+ setLoading(true);
+ setTimeout(async () => {
+ const hash = image.hash;
+ const uri = await downloadAttachment(hash, false, {
+ silent: true,
+ cache: true
+ });
+ const path = `${cacheDir}/${uri}`;
+ console.log(path);
+ setImage("file://" + path);
+ setLoading(false);
+ }, 100);
};
const close = () => {
@@ -59,44 +76,60 @@ const ImagePreview = () => {
backgroundColor: "black"
}}
>
- <>>}
- enableSwipeDown
- useNativeDriver
- onSwipeDown={close}
- saveToLocalByLongPress={false}
- renderHeader={() => (
-
- {
- close();
+ {loading ? (
+
+
+
+ ) : (
+ <>>}
+ enableSwipeDown
+ useNativeDriver
+ onSwipeDown={close}
+ saveToLocalByLongPress={false}
+ renderHeader={() => (
+
-
- )}
- imageUrls={[
- {
- url: image
- }
- ]}
- />
+ >
+ {
+ close();
+ }}
+ />
+
+ )}
+ imageUrls={[
+ {
+ url: image
+ }
+ ]}
+ />
+ )}
)
diff --git a/apps/mobile/app/screens/editor/source.ts b/apps/mobile/app/screens/editor/source.ts
index 3f343edf2..c47ac529c 100644
--- a/apps/mobile/app/screens/editor/source.ts
+++ b/apps/mobile/app/screens/editor/source.ts
@@ -28,5 +28,5 @@ const EditorMobileSourceUrl =
* The url should be something like this: http://192.168.100.126:3000/index.html
*/
export const EDITOR_URI = __DEV__
- ? EditorMobileSourceUrl
+ ? "http://192.168.8.103:3000/index.html"
: EditorMobileSourceUrl;
diff --git a/apps/mobile/app/screens/editor/tiptap/editor-events.ts b/apps/mobile/app/screens/editor/tiptap/editor-events.ts
index eb7ce414a..8e2297e17 100644
--- a/apps/mobile/app/screens/editor/tiptap/editor-events.ts
+++ b/apps/mobile/app/screens/editor/tiptap/editor-events.ts
@@ -34,5 +34,6 @@ export const EventTypes = {
fullscreen: "editor-event:fullscreen",
link: "editor-event:link",
contentchange: "editor-event:content-change",
- reminders: "editor-event:reminders"
+ reminders: "editor-event:reminders",
+ previewAttachment: "editor-event:preview-attachment"
};
diff --git a/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts b/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts
index 66773168b..7be714a60 100644
--- a/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts
+++ b/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts
@@ -29,6 +29,7 @@ import {
} from "react-native";
import { WebViewMessageEvent } from "react-native-webview";
import { db } from "../../../common/database";
+import ImagePreview from "../../../components/image-preview";
import { RelationsList } from "../../../components/sheets/relations-list";
import ReminderSheet from "../../../components/sheets/reminder";
import useKeyboard from "../../../hooks/use-keyboard";
@@ -380,6 +381,10 @@ export const useEditorEvents = (
case EventTypes.link:
openLinkInBrowser(editorMessage.value as string);
break;
+
+ case EventTypes.previewAttachment:
+ eSendEvent("ImagePreview", editorMessage.value);
+ break;
default:
break;
}
diff --git a/packages/editor-mobile/src/components/editor.tsx b/packages/editor-mobile/src/components/editor.tsx
index b5a663c9b..fd990fdd5 100644
--- a/packages/editor-mobile/src/components/editor.tsx
+++ b/packages/editor-mobile/src/components/editor.tsx
@@ -92,6 +92,10 @@ const Tiptap = ({
global.editorController.downloadAttachment(attachment);
return true;
},
+ onPreviewAttachment(editor, attachment) {
+ global.editorController.previewAttachment(attachment);
+ return true;
+ },
theme: editorTheme,
element: !layout ? undefined : contentRef.current || undefined,
editable: !settings.readonly,
diff --git a/packages/editor-mobile/src/hooks/useEditorController.ts b/packages/editor-mobile/src/hooks/useEditorController.ts
index 6528d0147..86c2fd29a 100644
--- a/packages/editor-mobile/src/hooks/useEditorController.ts
+++ b/packages/editor-mobile/src/hooks/useEditorController.ts
@@ -58,6 +58,7 @@ export type EditorController = {
setTitle: React.Dispatch>;
openFilePicker: (type: "image" | "file" | "camera") => void;
downloadAttachment: (attachment: Attachment) => void;
+ previewAttachment: (attachment: Attachment) => void;
content: MutableRefObject;
onUpdate: () => void;
titlePlaceholder: string;
@@ -170,7 +171,9 @@ export function useEditorController(update: () => void): EditorController {
const downloadAttachment = useCallback((attachment: Attachment) => {
post(EventTypes.download, attachment);
}, []);
-
+ const previewAttachment = useCallback((attachment: Attachment) => {
+ post(EventTypes.previewAttachment, attachment);
+ }, []);
const openLink = useCallback((url: string) => {
post(EventTypes.link, url);
return true;
@@ -187,6 +190,7 @@ export function useEditorController(update: () => void): EditorController {
setTitlePlaceholder,
openFilePicker,
downloadAttachment,
+ previewAttachment,
content: htmlContentRef,
openLink,
onUpdate: onUpdate
diff --git a/packages/editor-mobile/src/utils/index.ts b/packages/editor-mobile/src/utils/index.ts
index 564b12462..286bc262d 100644
--- a/packages/editor-mobile/src/utils/index.ts
+++ b/packages/editor-mobile/src/utils/index.ts
@@ -145,7 +145,8 @@ export const EventTypes = {
fullscreen: "editor-event:fullscreen",
link: "editor-event:link",
contentchange: "editor-event:content-change",
- reminders: "editor-event:reminders"
+ reminders: "editor-event:reminders",
+ previewAttachment: "editor-event:preview-attachment"
} as const;
export function isReactNative(): boolean {
diff --git a/packages/editor/src/extensions/attachment/attachment.ts b/packages/editor/src/extensions/attachment/attachment.ts
index 5427a5dd6..fc50d3541 100644
--- a/packages/editor/src/extensions/attachment/attachment.ts
+++ b/packages/editor/src/extensions/attachment/attachment.ts
@@ -27,6 +27,7 @@ export interface AttachmentOptions {
HTMLAttributes: Record;
onDownloadAttachment: (editor: Editor, attachment: Attachment) => boolean;
onOpenAttachmentPicker: (editor: Editor, type: AttachmentType) => boolean;
+ onPreviewAttachment: (editor: Editor, attachment: Attachment) => boolean;
}
export type AttachmentWithProgress = AttachmentProgress & Attachment;
@@ -52,6 +53,7 @@ declare module "@tiptap/core" {
removeAttachment: () => ReturnType;
downloadAttachment: (attachment: Attachment) => ReturnType;
setAttachmentProgress: (progress: AttachmentProgress) => ReturnType;
+ previewAttachment: (options: Attachment) => ReturnType;
};
}
}
@@ -67,7 +69,8 @@ export const AttachmentNode = Node.create({
return {
HTMLAttributes: {},
onDownloadAttachment: () => false,
- onOpenAttachmentPicker: () => false
+ onOpenAttachmentPicker: () => false,
+ onPreviewAttachment: () => false
};
},
@@ -158,6 +161,12 @@ export const AttachmentNode = Node.create({
tr.setMeta("addToHistory", false);
if (dispatch) dispatch(tr);
return true;
+ },
+
+ previewAttachment:
+ (attachment) =>
+ ({ editor }) => {
+ return this.options.onPreviewAttachment(editor, attachment);
}
};
}
diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts
index f20e2ac1a..28c7681e0 100644
--- a/packages/editor/src/index.ts
+++ b/packages/editor/src/index.ts
@@ -17,64 +17,64 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import "./extensions";
-import CharacterCount from "@tiptap/extension-character-count";
-import Placeholder from "@tiptap/extension-placeholder";
-import Underline from "@tiptap/extension-underline";
-import StarterKit from "@tiptap/starter-kit";
-import { useEffect, useMemo } from "react";
-import Toolbar from "./toolbar";
-import TextAlign from "@tiptap/extension-text-align";
-import Subscript from "@tiptap/extension-subscript";
-import Superscript from "@tiptap/extension-superscript";
-import FontSize from "./extensions/font-size";
-import TextDirection from "./extensions/text-direction";
-import TextStyle from "@tiptap/extension-text-style";
-import FontFamily from "./extensions/font-family";
-import BulletList from "./extensions/bullet-list";
-import OrderedList from "./extensions/ordered-list";
-import Color from "@tiptap/extension-color";
-import TableRow from "@tiptap/extension-table-row";
-import TableCell from "./extensions/table-cell";
-import TableHeader from "@tiptap/extension-table-header";
-import { ImageNode } from "./extensions/image";
import { Theme } from "@notesnook/theme";
-import { AttachmentNode, AttachmentOptions } from "./extensions/attachment";
-import { TaskListNode } from "./extensions/task-list";
-import { TaskItemNode } from "./extensions/task-item";
-import { SearchReplace } from "./extensions/search-replace";
-import { EmbedNode } from "./extensions/embed";
-import { CodeBlock } from "./extensions/code-block";
-import { ListItem } from "./extensions/list-item";
-import { Link } from "@tiptap/extension-link";
-import { Codemark } from "./extensions/code-mark";
-import { MathInline, MathBlock } from "./extensions/math";
-import {
- NodeViewSelectionNotifier,
- usePortalProvider
-} from "./extensions/react";
-import { OutlineList } from "./extensions/outline-list";
-import { OutlineListItem } from "./extensions/outline-list-item";
-import { KeepInView } from "./extensions/keep-in-view";
-import { SelectionPersist } from "./extensions/selection-persist";
-import { Table } from "./extensions/table";
-import { useToolbarStore } from "./toolbar/stores/toolbar-store";
-import { useEditor } from "./hooks/use-editor";
import {
EditorOptions,
extensions as TiptapCoreExtensions,
getHTMLFromFragment
} from "@tiptap/core";
-import { usePermissionHandler } from "./hooks/use-permission-handler";
-import { Highlight } from "./extensions/highlight";
-import { Paragraph } from "./extensions/paragraph";
-import { ClipboardTextSerializer } from "./extensions/clipboard-text-serializer";
+import CharacterCount from "@tiptap/extension-character-count";
import { Code } from "@tiptap/extension-code";
-import { DateTime } from "./extensions/date-time";
-import { OpenLink, OpenLinkOptions } from "./extensions/open-link";
+import Color from "@tiptap/extension-color";
import HorizontalRule from "@tiptap/extension-horizontal-rule";
+import { Link } from "@tiptap/extension-link";
+import Placeholder from "@tiptap/extension-placeholder";
+import Subscript from "@tiptap/extension-subscript";
+import Superscript from "@tiptap/extension-superscript";
+import TableHeader from "@tiptap/extension-table-header";
+import TableRow from "@tiptap/extension-table-row";
+import TextAlign from "@tiptap/extension-text-align";
+import TextStyle from "@tiptap/extension-text-style";
+import Underline from "@tiptap/extension-underline";
+import StarterKit from "@tiptap/starter-kit";
+import { useEffect, useMemo } from "react";
+import "./extensions";
+import { AttachmentNode, AttachmentOptions } from "./extensions/attachment";
+import BulletList from "./extensions/bullet-list";
+import { ClipboardTextSerializer } from "./extensions/clipboard-text-serializer";
+import { CodeBlock } from "./extensions/code-block";
+import { Codemark } from "./extensions/code-mark";
+import { DateTime } from "./extensions/date-time";
+import { EmbedNode } from "./extensions/embed";
+import FontFamily from "./extensions/font-family";
+import FontSize from "./extensions/font-size";
+import { Highlight } from "./extensions/highlight";
+import { ImageNode, ImageOptions } from "./extensions/image";
+import { KeepInView } from "./extensions/keep-in-view";
import { KeyMap } from "./extensions/key-map";
+import { ListItem } from "./extensions/list-item";
+import { MathBlock, MathInline } from "./extensions/math";
+import { OpenLink, OpenLinkOptions } from "./extensions/open-link";
+import OrderedList from "./extensions/ordered-list";
+import { OutlineList } from "./extensions/outline-list";
+import { OutlineListItem } from "./extensions/outline-list-item";
+import { Paragraph } from "./extensions/paragraph";
+import {
+ NodeViewSelectionNotifier,
+ usePortalProvider
+} from "./extensions/react";
+import { SearchReplace } from "./extensions/search-replace";
+import { SelectionPersist } from "./extensions/selection-persist";
+import { Table } from "./extensions/table";
+import TableCell from "./extensions/table-cell";
+import { TaskItemNode } from "./extensions/task-item";
+import { TaskListNode } from "./extensions/task-list";
+import TextDirection from "./extensions/text-direction";
import { WebClipNode, WebClipOptions } from "./extensions/web-clip";
+import { useEditor } from "./hooks/use-editor";
+import { usePermissionHandler } from "./hooks/use-permission-handler";
+import Toolbar from "./toolbar";
+import { useToolbarStore } from "./toolbar/stores/toolbar-store";
import { DownloadOptions } from "./utils/downloader";
const CoreExtensions = Object.entries(TiptapCoreExtensions)
@@ -85,6 +85,7 @@ const CoreExtensions = Object.entries(TiptapCoreExtensions)
type TiptapOptions = EditorOptions &
Omit &
Omit &
+ Omit &
OpenLinkOptions & {
downloadOptions?: DownloadOptions;
theme: Theme;
@@ -104,6 +105,7 @@ const useTiptap = (
isKeyboardOpen,
onDownloadAttachment,
onOpenAttachmentPicker,
+ onPreviewAttachment,
onOpenLink,
onBeforeCreate,
downloadOptions,
@@ -219,7 +221,8 @@ const useTiptap = (
EmbedNode,
AttachmentNode.configure({
onDownloadAttachment,
- onOpenAttachmentPicker
+ onOpenAttachmentPicker,
+ onPreviewAttachment
}),
OutlineListItem,
OutlineList,
@@ -241,6 +244,7 @@ const useTiptap = (
injectCSS: false
}),
[
+ onPreviewAttachment,
onDownloadAttachment,
onOpenAttachmentPicker,
PortalProviderAPI,
@@ -260,6 +264,12 @@ const useTiptap = (
return editor;
};
+export { type Fragment } from "prosemirror-model";
+export { type Attachment, type AttachmentType } from "./extensions/attachment";
+export * from "./extensions/react";
+export * from "./toolbar";
+export * from "./types";
+export * from "./utils/word-counter";
export {
useTiptap,
Toolbar,
@@ -267,9 +277,3 @@ export {
getHTMLFromFragment,
type DownloadOptions
};
-export * from "./types";
-export * from "./extensions/react";
-export * from "./toolbar";
-export { type AttachmentType, type Attachment } from "./extensions/attachment";
-export { type Fragment } from "prosemirror-model";
-export * from "./utils/word-counter";
diff --git a/packages/editor/src/toolbar/icons.ts b/packages/editor/src/toolbar/icons.ts
index 17146c0a5..7614ca104 100644
--- a/packages/editor/src/toolbar/icons.ts
+++ b/packages/editor/src/toolbar/icons.ts
@@ -113,7 +113,8 @@ import {
mdiWeb,
mdiPageNextOutline,
mdiSortBoolAscendingVariant,
- mdiApplicationCogOutline
+ mdiApplicationCogOutline,
+ mdiArrowExpand
} from "@mdi/js";
export const Icons = {
@@ -156,6 +157,7 @@ export const Icons = {
fullscreen: mdiFullscreen,
url: mdiLink,
image: mdiImageOutline,
+ previewAttachment: mdiArrowExpand,
imageDownload: mdiProgressDownload,
imageFailed: mdiProgressAlert,
imageSettings: mdiImageEditOutline,
diff --git a/packages/editor/src/toolbar/tool-definitions.ts b/packages/editor/src/toolbar/tool-definitions.ts
index 0b56b02a0..e30ad2382 100644
--- a/packages/editor/src/toolbar/tool-definitions.ts
+++ b/packages/editor/src/toolbar/tool-definitions.ts
@@ -237,7 +237,6 @@ const tools: Record = {
title: "Cell border width",
conditional: true
},
-
imageSettings: {
icon: "imageSettings",
title: "Image settings",
@@ -263,6 +262,11 @@ const tools: Record = {
title: "Image properties",
conditional: true
},
+ previewAttachment: {
+ icon: "previewAttachment",
+ title: "Preview attachment",
+ conditional: true
+ },
attachmentSettings: {
icon: "attachmentSettings",
title: "Attachment settings",
@@ -354,6 +358,7 @@ export const STATIC_TOOLBAR_GROUPS: ToolbarDefinition = [
"cellProperties",
"imageSettings",
"embedSettings",
+ "previewAttachment",
"attachmentSettings",
"linkSettings",
"codeRemove",
diff --git a/packages/editor/src/toolbar/tools/attachment.tsx b/packages/editor/src/toolbar/tools/attachment.tsx
index 1d056e8ca..0d4abb437 100644
--- a/packages/editor/src/toolbar/tools/attachment.tsx
+++ b/packages/editor/src/toolbar/tools/attachment.tsx
@@ -27,7 +27,11 @@ import { Attachment } from "../../extensions/attachment";
export function AttachmentSettings(props: ToolProps) {
const { editor } = props;
const isBottom = useToolbarLocation() === "bottom";
- if (!editor.isActive("attachment") || !isBottom) return null;
+ if (
+ (!editor.isActive("attachment") && !editor.isActive("image")) ||
+ !isBottom
+ )
+ return null;
return (
{
+ const attachmentNode =
+ findSelectedNode(editor, "attachment") ||
+ findSelectedNode(editor, "image");
+
+ const attachment = (attachmentNode?.attrs || {}) as Attachment;
+ editor.current?.chain().focus().previewAttachment(attachment).run();
+ }}
+ />
+ );
+}
+
export function RemoveAttachment(props: ToolProps) {
const { editor } = props;
return (
diff --git a/packages/editor/src/toolbar/tools/index.ts b/packages/editor/src/toolbar/tools/index.ts
index 98611ba1a..6be37cdea 100644
--- a/packages/editor/src/toolbar/tools/index.ts
+++ b/packages/editor/src/toolbar/tools/index.ts
@@ -71,6 +71,7 @@ import {
import {
AttachmentSettings,
DownloadAttachment,
+ PreviewAttachment,
RemoveAttachment
} from "./attachment";
import {
@@ -133,6 +134,7 @@ const tools = {
webclipOpenSource: WebClipOpenSource,
webclipSettings: WebClipSettings,
+ previewAttachment: PreviewAttachment,
attachmentSettings: AttachmentSettings,
downloadAttachment: DownloadAttachment,
removeAttachment: RemoveAttachment,