mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 03:29:21 +02:00
Compare commits
1 Commits
3.4.10-and
...
editor/upd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78837454ba |
@@ -181,6 +181,24 @@ const Actions = ({
|
||||
setFilename(value);
|
||||
setAttachments();
|
||||
eSendEvent(eDBItemUpdate, attachment.id);
|
||||
|
||||
const relations = await db.relations
|
||||
.to(attachment, "note")
|
||||
.get();
|
||||
relations
|
||||
.map((relation) => relation.fromId)
|
||||
.forEach(async (id) => {
|
||||
useTabStore.getState().forEachNoteTab(id, async (tab) => {
|
||||
editorController.current?.commands.updateAttachment(
|
||||
{
|
||||
hash: attachment.hash,
|
||||
filename: value
|
||||
},
|
||||
tab.id
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
ToastManager.show({
|
||||
message: `Attachment renamed to ${value}`,
|
||||
type: "success"
|
||||
|
||||
@@ -183,11 +183,11 @@ class Commands {
|
||||
await this.sendCommand("insertAttachment", attachment, tabId);
|
||||
};
|
||||
|
||||
setAttachmentProgress = async (
|
||||
attachmentProgress: Partial<Attachment>,
|
||||
updateAttachment = async (
|
||||
attachmentAttributes: Partial<Attachment>,
|
||||
tabId: string
|
||||
) => {
|
||||
await this.sendCommand("setAttachmentProgress", attachmentProgress, tabId);
|
||||
await this.sendCommand("updateAttachment", attachmentAttributes, tabId);
|
||||
};
|
||||
|
||||
insertImage = async (
|
||||
|
||||
@@ -578,6 +578,21 @@ export const useEditorEvents = (
|
||||
break;
|
||||
}
|
||||
|
||||
case EditorEvents.getAttachmentMetaData: {
|
||||
const { hash } = editorMessage.value as Pick<Attachment, "hash">;
|
||||
const attachment = await db
|
||||
.sql()
|
||||
.selectFrom("attachments")
|
||||
.where("hash", "=", hash)
|
||||
.select("filename")
|
||||
.executeTakeFirst();
|
||||
editor.postMessage(NativeEvents.resolve, {
|
||||
resolverId: editorMessage.resolverId,
|
||||
data: attachment ? { filename: attachment.filename } : undefined
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case EditorEvents.pro:
|
||||
if (editor.state.current?.isFocused) {
|
||||
editor.state.current.isFocused = true;
|
||||
|
||||
@@ -68,7 +68,7 @@ export const useAttachmentStore = create<AttachmentStore>((set, get) => ({
|
||||
remove: (hash) => {
|
||||
const progress = get().progress;
|
||||
if (!progress) return;
|
||||
editorController.current?.commands.setAttachmentProgress(
|
||||
editorController.current?.commands.updateAttachment(
|
||||
{
|
||||
hash: hash,
|
||||
progress: 100
|
||||
@@ -85,7 +85,7 @@ export const useAttachmentStore = create<AttachmentStore>((set, get) => ({
|
||||
const progressPercentage =
|
||||
type === "upload" ? sent / total : recieved / total;
|
||||
|
||||
editorController.current?.commands.setAttachmentProgress(
|
||||
editorController.current?.commands.updateAttachment(
|
||||
{
|
||||
hash: hash,
|
||||
//@ts-ignore
|
||||
|
||||
@@ -519,10 +519,9 @@ export function Editor(props: EditorProps) {
|
||||
AppEvents.UPDATE_ATTACHMENT_PROGRESS,
|
||||
({ hash, loaded, total }: AttachmentProgress) => {
|
||||
const editor = useEditorManager.getState().getEditor(id)?.editor;
|
||||
editor?.sendAttachmentProgress(
|
||||
hash,
|
||||
Math.round((loaded / total) * 100)
|
||||
);
|
||||
editor?.updateAttachment(hash, {
|
||||
progress: Math.round((loaded / total) * 100)
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -644,6 +643,17 @@ export function Editor(props: EditorProps) {
|
||||
|
||||
return result;
|
||||
}}
|
||||
onGetAttachmentMetaData={async (hash) => {
|
||||
const result = await db
|
||||
.sql()
|
||||
.selectFrom("attachments")
|
||||
.where("hash", "=", hash)
|
||||
.select("filename")
|
||||
.executeTakeFirst();
|
||||
if (!result || !result.filename) return undefined;
|
||||
|
||||
return { filename: result.filename };
|
||||
}}
|
||||
onAttachFiles={async (files) => {
|
||||
await AttachFilesDialog.show({
|
||||
files,
|
||||
|
||||
@@ -90,6 +90,9 @@ type TipTapProps = {
|
||||
attachment: Pick<Attachment, "hash" | "type">
|
||||
) => Promise<string | undefined>)
|
||||
| undefined;
|
||||
onGetAttachmentMetaData?: (
|
||||
hash: string
|
||||
) => Promise<Pick<Attachment, "filename"> | undefined>;
|
||||
onAttachFiles?: (files: File[]) => void;
|
||||
onInsertInternalLink?: (
|
||||
attributes?: LinkAttributes
|
||||
@@ -170,6 +173,7 @@ function TipTap(props: TipTapProps) {
|
||||
onDownloadAttachment,
|
||||
onPreviewAttachment,
|
||||
onGetAttachmentData,
|
||||
onGetAttachmentMetaData,
|
||||
onAttachFiles,
|
||||
onInsertInternalLink,
|
||||
onContentChange,
|
||||
@@ -423,6 +427,7 @@ function TipTap(props: TipTapProps) {
|
||||
previewAttachment: onPreviewAttachment,
|
||||
createInternalLink: onInsertInternalLink,
|
||||
getAttachmentData: onGetAttachmentData,
|
||||
getAttachmentMetaData: onGetAttachmentMetaData,
|
||||
openLink: async (url, openInNewTab) => {
|
||||
const link = parseInternalLink(url);
|
||||
if (link && link.type === "note") {
|
||||
@@ -750,13 +755,11 @@ function toIEditor(editor: Editor): IEditor {
|
||||
file.type === "image"
|
||||
? editor.commands.insertImage(file)
|
||||
: editor.commands.insertAttachment(file),
|
||||
sendAttachmentProgress: (hash, progress) =>
|
||||
editor.commands.updateAttachment(
|
||||
{
|
||||
progress
|
||||
},
|
||||
{ query: (a) => a.hash === hash, preventUpdate: true }
|
||||
),
|
||||
updateAttachment: (hash, attributes) =>
|
||||
editor.commands.updateAttachment(attributes, {
|
||||
query: (a) => a.hash === hash,
|
||||
preventUpdate: true
|
||||
}),
|
||||
startSearch: () => editor.commands.startSearch(),
|
||||
getContent: () =>
|
||||
getHTMLFromFragment(editor.state.doc.content, editor.schema),
|
||||
|
||||
@@ -49,7 +49,7 @@ export interface IEditor {
|
||||
redo: () => void;
|
||||
updateContent: (content: string) => void;
|
||||
attachFile: (file: Attachment) => void;
|
||||
sendAttachmentProgress: (hash: string, progress: number) => void;
|
||||
updateAttachment: (hash: string, attributes: Partial<Attachment>) => void;
|
||||
startSearch: () => void;
|
||||
getContent: () => string;
|
||||
getSelection: () => { from: number; to: number };
|
||||
|
||||
@@ -29,6 +29,7 @@ import { createWriteStream } from "../utils/stream-saver";
|
||||
import { Attachment } from "@notesnook/core";
|
||||
import { TaskManager } from "../common/task-manager";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useEditorManager } from "../components/editor/manager";
|
||||
|
||||
let abortController: AbortController | undefined = undefined;
|
||||
class AttachmentStore extends BaseStore<AttachmentStore> {
|
||||
@@ -116,11 +117,31 @@ class AttachmentStore extends BaseStore<AttachmentStore> {
|
||||
};
|
||||
|
||||
rename = async (hash: string, newName: string) => {
|
||||
await db.attachments.add({
|
||||
const attachmentId = await db.attachments.add({
|
||||
hash,
|
||||
filename: newName
|
||||
});
|
||||
await this.get().refresh();
|
||||
|
||||
if (!attachmentId) return;
|
||||
|
||||
const linkedNotes = (
|
||||
await db.relations
|
||||
.to({ id: attachmentId, type: "attachment" }, "note")
|
||||
.get()
|
||||
).map((l) => l.fromId);
|
||||
const sessions = useEditorStore.getState().sessions;
|
||||
for (const session of sessions) {
|
||||
if (!("note" in session) || !session.note.id || !session.note.contentId) {
|
||||
continue;
|
||||
}
|
||||
if (!linkedNotes.includes(session.note.id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const editor = useEditorManager.getState().getEditor(session.id)?.editor;
|
||||
editor?.updateAttachment(hash, { filename: newName });
|
||||
}
|
||||
};
|
||||
|
||||
permanentDelete = async (attachment: Attachment) => {
|
||||
|
||||
@@ -162,6 +162,11 @@ const Tiptap = ({
|
||||
attachment
|
||||
) as Promise<string | undefined>;
|
||||
},
|
||||
async getAttachmentMetaData(hash) {
|
||||
return globalThis.editorControllers[tab.id]?.getAttachmentMetaData(
|
||||
hash
|
||||
);
|
||||
},
|
||||
createInternalLink(attributes) {
|
||||
return postAsyncWithTimeout(EditorEvents.createInternalLink, {
|
||||
attributes
|
||||
|
||||
@@ -102,6 +102,21 @@ const Tiptap = ({
|
||||
});
|
||||
});
|
||||
},
|
||||
getAttachmentMetaData(hash) {
|
||||
return new Promise<{ filename: string } | undefined>(
|
||||
(resolve, reject) => {
|
||||
const resolverId = randId("get_attachment_metadata");
|
||||
pendingResolvers[resolverId] = (data) => {
|
||||
delete pendingResolvers[resolverId];
|
||||
resolve(data);
|
||||
};
|
||||
post(EditorEvents.getAttachmentMetaData, {
|
||||
hash,
|
||||
resolverId: resolverId
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
element: getContentDiv(),
|
||||
editable: false,
|
||||
editorProps: {
|
||||
|
||||
@@ -93,6 +93,9 @@ export type EditorController = {
|
||||
countWords: (ms: number) => void;
|
||||
copyToClipboard: (text: string) => void;
|
||||
getAttachmentData: (attachment: Partial<Attachment>) => Promise<string>;
|
||||
getAttachmentMetaData: (
|
||||
hash: string
|
||||
) => Promise<{ filename: string } | undefined>;
|
||||
updateTab: () => void;
|
||||
loading: boolean;
|
||||
setLoading: (value: boolean) => void;
|
||||
@@ -454,6 +457,15 @@ export function useEditorController({
|
||||
});
|
||||
};
|
||||
|
||||
const getAttachmentMetaData = (hash: string) => {
|
||||
return postAsyncWithTimeout<{ filename: string } | undefined>(
|
||||
EditorEvents.getAttachmentMetaData,
|
||||
{
|
||||
hash
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const scrollToSearchResult = useCallback((index: number) => {
|
||||
const marks = document.getElementsByTagName("nn-search-result");
|
||||
if (marks.length > index) {
|
||||
@@ -490,6 +502,7 @@ export function useEditorController({
|
||||
countWords,
|
||||
copyToClipboard,
|
||||
getAttachmentData,
|
||||
getAttachmentMetaData,
|
||||
getContentDiv,
|
||||
updateTab: () => {
|
||||
// When the tab is focused, we apply any updates to content that were recieved when
|
||||
|
||||
@@ -135,15 +135,15 @@ globalThis.commands = {
|
||||
}
|
||||
},
|
||||
|
||||
setAttachmentProgress: (
|
||||
attachmentProgress: Partial<Attachment>,
|
||||
updateAttachment: (
|
||||
attachmentAttributes: Partial<Attachment>,
|
||||
tabId: number
|
||||
) => {
|
||||
const editor = editors[tabId];
|
||||
if (editor) {
|
||||
editor.commands.updateAttachment(attachmentProgress, {
|
||||
editor.commands.updateAttachment(attachmentAttributes, {
|
||||
preventUpdate: true,
|
||||
query: (attachment) => attachment.hash === attachmentProgress.hash
|
||||
query: (attachment) => attachment.hash === attachmentAttributes.hash
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -39,6 +39,7 @@ export const EditorEvents = {
|
||||
previewAttachment: "editor-event:preview-attachment",
|
||||
copyToClipboard: "editor-events:copy-to-clipboard",
|
||||
getAttachmentData: "editor-events:get-attachment-data",
|
||||
getAttachmentMetaData: "editor-events:get-attachment-metadata",
|
||||
tabsChanged: "editor-events:tabs-changed",
|
||||
showTabs: "editor-events:show-tabs",
|
||||
tabFocused: "editor-events:tab-focused",
|
||||
|
||||
@@ -101,7 +101,9 @@ export const AttachmentNode = Node.create<AttachmentOptions>({
|
||||
addNodeView() {
|
||||
return createNodeView(AttachmentComponent, {
|
||||
shouldUpdate: ({ attrs: prev }, { attrs: next }) => {
|
||||
return prev.progress !== next.progress;
|
||||
return (
|
||||
prev.progress !== next.progress || prev.filename !== next.filename
|
||||
);
|
||||
},
|
||||
forceEnableSelection: true
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { Box, Text } from "@theme-ui/components";
|
||||
import { FileAttachment } from "./types.js";
|
||||
import { useRef, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Icon } from "@notesnook/ui";
|
||||
import { Icons } from "../../toolbar/icons.js";
|
||||
import { ReactNodeViewProps } from "../react/index.js";
|
||||
@@ -28,11 +28,21 @@ import { DesktopOnly } from "../../components/responsive/index.js";
|
||||
import { formatBytes } from "@notesnook/common";
|
||||
|
||||
export function AttachmentComponent(props: ReactNodeViewProps<FileAttachment>) {
|
||||
const { editor, node, selected } = props;
|
||||
const { filename, size, progress } = node.attrs;
|
||||
const { editor, node, selected, updateAttributes } = props;
|
||||
const { filename, size, progress, hash } = node.attrs;
|
||||
const elementRef = useRef<HTMLSpanElement>();
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hash) return;
|
||||
|
||||
editor.storage.getAttachmentMetaData?.(hash).then((meta) => {
|
||||
if (meta?.filename && meta.filename !== filename) {
|
||||
updateAttributes({ filename: meta.filename });
|
||||
}
|
||||
});
|
||||
}, [hash, filename]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
ref={elementRef}
|
||||
|
||||
@@ -36,13 +36,23 @@ const SAMPLE_AUDIO = toBlobURL(
|
||||
);
|
||||
|
||||
export function AudioComponent(props: ReactNodeViewProps<AudioAttachment>) {
|
||||
const { editor, node, selected } = props;
|
||||
const { editor, node, selected, updateAttributes } = props;
|
||||
const { filename, size, progress, mime, hash } = node.attrs;
|
||||
const elementRef = useRef<HTMLDivElement>();
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const isLoading = useRef(false);
|
||||
const [error, setError] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!hash) return;
|
||||
|
||||
editor.storage.getAttachmentMetaData?.(hash).then((meta) => {
|
||||
if (meta?.filename && meta.filename !== filename) {
|
||||
updateAttributes({ filename: meta.filename });
|
||||
}
|
||||
});
|
||||
}, [hash, filename]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (hash) {
|
||||
|
||||
@@ -107,6 +107,9 @@ interface TiptapStorage {
|
||||
attachment: Pick<Attachment, "hash" | "type">
|
||||
) => Promise<string | undefined>)
|
||||
| undefined;
|
||||
getAttachmentMetaData?: (
|
||||
hash: string
|
||||
) => Promise<Pick<Attachment, "filename"> | undefined>;
|
||||
}
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
@@ -144,6 +147,7 @@ const useTiptap = (
|
||||
) => {
|
||||
const {
|
||||
getAttachmentData,
|
||||
getAttachmentMetaData,
|
||||
downloadAttachment,
|
||||
openAttachmentPicker,
|
||||
previewAttachment,
|
||||
@@ -395,6 +399,7 @@ const useTiptap = (
|
||||
editor.storage.createInternalLink = createInternalLink;
|
||||
editor.storage.getAttachmentData = getAttachmentData;
|
||||
editor.storage.downloadCsvTable = downloadCsvTable;
|
||||
editor.storage.getAttachmentMetaData = getAttachmentMetaData;
|
||||
|
||||
if (onBeforeCreate) onBeforeCreate({ editor });
|
||||
},
|
||||
@@ -413,7 +418,8 @@ const useTiptap = (
|
||||
timeFormat,
|
||||
editorProps,
|
||||
copyToClipboard,
|
||||
createInternalLink
|
||||
createInternalLink,
|
||||
getAttachmentMetaData
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user