mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 19:57:52 +01:00
editor: add support for attachment previews (#2123)
This adds support for basic attachment preview support. Currently only image previews are supported on mobile.
Fixed image downloading not working on mobile
Added image preview support on mobile with support for loading full quality images in full screen.
Added Attachment preview logic in editor, so in future we can support preview for other files such as audio & video
This commit is contained in:
@@ -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: (
|
||||
<ShareComponent
|
||||
uri={fileUri}
|
||||
name={attachment.metadata.filename}
|
||||
padding={12}
|
||||
/>
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
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: (
|
||||
<ShareComponent
|
||||
uri={fileUri}
|
||||
name={attachment.metadata.filename}
|
||||
padding={12}
|
||||
/>
|
||||
)
|
||||
});
|
||||
return fileUri;
|
||||
} catch (e) {
|
||||
console.log("download attachment error: ", e);
|
||||
|
||||
@@ -20,16 +20,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
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"
|
||||
}}
|
||||
>
|
||||
<ImageViewer
|
||||
enableImageZoom={true}
|
||||
renderIndicator={() => <></>}
|
||||
enableSwipeDown
|
||||
useNativeDriver
|
||||
onSwipeDown={close}
|
||||
saveToLocalByLongPress={false}
|
||||
renderHeader={() => (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
height: 80,
|
||||
marginTop: 0,
|
||||
paddingHorizontal: 12,
|
||||
position: "absolute",
|
||||
zIndex: 999,
|
||||
backgroundColor: "rgba(0,0,0,0.3)",
|
||||
paddingTop: 30
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
name="close"
|
||||
color="white"
|
||||
onPress={() => {
|
||||
close();
|
||||
{loading ? (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<ProgressBarComponent
|
||||
indeterminate
|
||||
color={colors.accent}
|
||||
borderColor="transparent"
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<ImageViewer
|
||||
enableImageZoom={true}
|
||||
renderIndicator={() => <></>}
|
||||
enableSwipeDown
|
||||
useNativeDriver
|
||||
onSwipeDown={close}
|
||||
saveToLocalByLongPress={false}
|
||||
renderHeader={() => (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
height: 80,
|
||||
marginTop: 0,
|
||||
paddingHorizontal: 12,
|
||||
position: "absolute",
|
||||
zIndex: 999,
|
||||
backgroundColor: "rgba(0,0,0,0.3)",
|
||||
paddingTop: 30
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
imageUrls={[
|
||||
{
|
||||
url: image
|
||||
}
|
||||
]}
|
||||
/>
|
||||
>
|
||||
<IconButton
|
||||
name="close"
|
||||
color="white"
|
||||
onPress={() => {
|
||||
close();
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
imageUrls={[
|
||||
{
|
||||
url: image
|
||||
}
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</BaseDialog>
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -58,6 +58,7 @@ export type EditorController = {
|
||||
setTitle: React.Dispatch<React.SetStateAction<string>>;
|
||||
openFilePicker: (type: "image" | "file" | "camera") => void;
|
||||
downloadAttachment: (attachment: Attachment) => void;
|
||||
previewAttachment: (attachment: Attachment) => void;
|
||||
content: MutableRefObject<string | null>;
|
||||
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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface AttachmentOptions {
|
||||
HTMLAttributes: Record<string, unknown>;
|
||||
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<AttachmentOptions>({
|
||||
return {
|
||||
HTMLAttributes: {},
|
||||
onDownloadAttachment: () => false,
|
||||
onOpenAttachmentPicker: () => false
|
||||
onOpenAttachmentPicker: () => false,
|
||||
onPreviewAttachment: () => false
|
||||
};
|
||||
},
|
||||
|
||||
@@ -158,6 +161,12 @@ export const AttachmentNode = Node.create<AttachmentOptions>({
|
||||
tr.setMeta("addToHistory", false);
|
||||
if (dispatch) dispatch(tr);
|
||||
return true;
|
||||
},
|
||||
|
||||
previewAttachment:
|
||||
(attachment) =>
|
||||
({ editor }) => {
|
||||
return this.options.onPreviewAttachment(editor, attachment);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,64 +17,64 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<AttachmentOptions, "HTMLAttributes"> &
|
||||
Omit<WebClipOptions, "HTMLAttributes"> &
|
||||
Omit<ImageOptions, "HTMLAttributes"> &
|
||||
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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -237,7 +237,6 @@ const tools: Record<ToolId, ToolDefinition> = {
|
||||
title: "Cell border width",
|
||||
conditional: true
|
||||
},
|
||||
|
||||
imageSettings: {
|
||||
icon: "imageSettings",
|
||||
title: "Image settings",
|
||||
@@ -263,6 +262,11 @@ const tools: Record<ToolId, ToolDefinition> = {
|
||||
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",
|
||||
|
||||
@@ -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 (
|
||||
<MoreTools
|
||||
@@ -58,6 +62,28 @@ export function DownloadAttachment(props: ToolProps) {
|
||||
);
|
||||
}
|
||||
|
||||
export function PreviewAttachment(props: ToolProps) {
|
||||
const { editor } = props;
|
||||
const isBottom = useToolbarLocation() === "bottom";
|
||||
|
||||
if (!editor.isActive("image") || !isBottom) return null;
|
||||
|
||||
return (
|
||||
<ToolButton
|
||||
{...props}
|
||||
toggled={false}
|
||||
onClick={() => {
|
||||
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 (
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user