editor: restrict attachment upload for not logged-in users

Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
This commit is contained in:
01zulfi
2025-10-09 12:05:59 +05:00
parent 91ef98df4f
commit 351d412fdf
11 changed files with 62 additions and 5 deletions

View File

@@ -44,6 +44,7 @@ import {
useStore as useAppStore,
store as appstore
} from "../../stores/app-store";
import { useStore as useUserStore } from "../../stores/user-store";
import { useStore as useSearchStore } from "../../stores/search-store";
import { AppEventManager, AppEvents } from "../../common/app-events";
import { FlexScrollContainer } from "../scroll-container";
@@ -77,6 +78,7 @@ import { Pane, SplitPane } from "../split-pane";
import { TITLE_BAR_HEIGHT } from "../title-bar";
import { isMobile } from "../../hooks/use-mobile";
import { isTablet } from "../../hooks/use-tablet";
import { ConfirmDialog } from "../../dialogs/confirm";
const PDFPreview = React.lazy(() => import("../pdf-preview"));
@@ -604,6 +606,15 @@ export function Editor(props: EditorProps) {
}
}}
onInsertAttachment={async (type) => {
if (!useUserStore.getState().isLoggedIn) {
ConfirmDialog.show({
title: strings.notLoggedIn(),
message: strings.loginToUploadAttachments(),
positiveButtonText: strings.okay()
});
return;
}
const mime = type === "file" ? "*/*" : "image/*";
const attachments = await insertAttachments(mime);
const editor = useEditorManager.getState().getEditor(id)?.editor;

View File

@@ -51,6 +51,7 @@ import {
import { IEditor, MAX_AUTO_SAVEABLE_WORDS } from "./types";
import { useEditorConfig, useToolbarConfig, useEditorManager } from "./manager";
import { useStore as useSettingsStore } from "../../stores/setting-store";
import { useStore as useUserStore } from "../../stores/user-store";
import { debounce, useAreFeaturesAvailable } from "@notesnook/common";
import { ScopedThemeProvider } from "../theme-provider";
import { useStore as useThemeStore } from "../../stores/theme-store";
@@ -65,6 +66,8 @@ import { EDITOR_ZOOM } from "./common";
import { ScrollContainer } from "@notesnook/ui";
import { showFeatureNotAllowedToast } from "../../common/toasts";
import { UpgradeDialog } from "../../dialogs/buy-dialog/upgrade-dialog";
import { ConfirmDialog } from "../../dialogs/confirm";
import { strings } from "@notesnook/intl";
export type OnChangeHandler = (
content: () => string,
@@ -185,7 +188,6 @@ function TipTap(props: TipTapProps) {
const autoSave = useRef(true);
const { toolbarConfig } = useToolbarConfig();
const features = useAreFeaturesAvailable([
"callout",
"outlineList",
@@ -196,9 +198,19 @@ function TipTap(props: TipTapProps) {
claims: {
callout: !!features?.callout?.isAllowed,
outlineList: !!features?.outlineList?.isAllowed,
taskList: !!features?.taskList?.isAllowed
taskList: !!features?.taskList?.isAllowed,
insertAttachment: !!useUserStore.getState().isLoggedIn
},
onPermissionDenied: (claim, silent) => {
if (claim === "insertAttachment") {
ConfirmDialog.show({
title: strings.notLoggedIn(),
message: strings.loginToUploadAttachments(),
positiveButtonText: strings.okay()
});
return;
}
if (silent) {
console.log(features, features?.[claim]);
if (features?.[claim]) showFeatureNotAllowedToast(features[claim]);

View File

@@ -0,0 +1,8 @@
---
title: Login to upload attachments
description: We require users to be logged in to upload attachments.
---
# Login to upload attachments
We require users to be logged in to upload attachments. This is because attachments are encrypted using a sub-key derived from your database encryption key. Without a login, we cannot encrypt/upload/sync attachments.

View File

@@ -84,3 +84,4 @@ navigation:
children:
- path: faqs/what-are-merge-conflicts.md
- path: faqs/is-there-an-eta.md
- paht: faqs/login-to-upload-attachments.md

View File

@@ -23,6 +23,7 @@ import { createNodeView } from "../react/index.js";
import { AttachmentComponent } from "./component.js";
import { Attachment } from "./types.js";
import { tiptapKeys } from "@notesnook/common";
import { hasPermission } from "../../types.js";
export type AttachmentType = "image" | "file" | "camera";
export interface AttachmentOptions {
@@ -110,6 +111,10 @@ export const AttachmentNode = Node.create<AttachmentOptions>({
insertAttachment:
(attachment) =>
({ commands, state }) => {
if (!hasPermission("insertAttachment")) {
return false;
}
const { $from } = state.selection;
const maybeAttachmentNode = state.doc.nodeAt($from.pos);
if (maybeAttachmentNode?.type === this.type) {

View File

@@ -28,7 +28,7 @@ import { createNodeView } from "../react/index.js";
import { TextDirections } from "../text-direction/index.js";
import { ImageComponent } from "./component.js";
import { tiptapKeys } from "@notesnook/common";
import { DOMParser } from "@tiptap/pm/model";
import { hasPermission } from "../../types.js";
export interface ImageOptions {
inline: boolean;
@@ -159,6 +159,10 @@ export const ImageNode = Node.create<ImageOptions>({
insertImage:
(options) =>
({ commands, state }) => {
if (!hasPermission("insertAttachment")) {
return false;
}
const { $from } = state.selection;
const maybeImageNode = state.doc.nodeAt($from.pos);
if (maybeImageNode?.type === this.type) {

View File

@@ -30,7 +30,8 @@ export type PermissionHandlerOptions = {
const ClaimsMap = {
callout: ["setCallout"] as (keyof UnionCommands)[],
outlineList: ["toggleOutlineList"] as (keyof UnionCommands)[],
taskList: ["toggleTaskList"] as (keyof UnionCommands)[]
taskList: ["toggleTaskList"] as (keyof UnionCommands)[],
insertAttachment: ["insertAttachment"] as (keyof UnionCommands)[]
};
export function usePermissionHandler(options: PermissionHandlerOptions) {

View File

@@ -25,6 +25,7 @@ import { Popup } from "../components/popup.js";
import { downloadImage, toDataURL } from "../../utils/downloader.js";
import { useToolbarStore } from "../stores/toolbar-store.js";
import { strings } from "@notesnook/intl";
import { hasPermission } from "../../types.js";
export type ImageUploadPopupProps = {
onInsert: (image: Partial<ImageAttributes>) => void;
@@ -46,6 +47,10 @@ export function ImageUploadPopup(props: ImageUploadPopupProps) {
title: strings.insert(),
disabled: !url,
onClick: async () => {
if (!hasPermission("insertAttachment")) {
return false;
}
setIsDownloading(true);
setError(undefined);

View File

@@ -3699,6 +3699,10 @@ msgstr "Login successful"
msgid "Login to encrypt and sync notes"
msgstr "Login to encrypt and sync notes"
#: src/strings.ts:2608
msgid "Login to upload attachments. [Read more](https://help.notesnook.com/faqs/login-to-upload-attachments)"
msgstr "Login to upload attachments. [Read more](https://help.notesnook.com/faqs/login-to-upload-attachments)"
#: src/strings.ts:541
msgid "Login to your account"
msgstr "Login to your account"

View File

@@ -3679,6 +3679,10 @@ msgstr ""
msgid "Login to encrypt and sync notes"
msgstr ""
#: src/strings.ts:2608
msgid "Login to upload attachments. [Read more](https://help.notesnook.com/faqs/login-to-upload-attachments)"
msgstr ""
#: src/strings.ts:541
msgid "Login to your account"
msgstr ""

View File

@@ -2603,5 +2603,7 @@ Use this if changes from other devices are not appearing on this device. This wi
finishPurchaseInBrowser: () => t`Finish your purchase in the browser.`,
goBack: () => t`Go back`,
clickToDirectlyClaimPromo: () =>
t`Click here to directly claim the promotion.`
t`Click here to directly claim the promotion.`,
loginToUploadAttachments: () =>
t`Login to upload attachments. [Read more](https://help.notesnook.com/faqs/login-to-upload-attachments)`
};