Merge branch 'master' into beta

This commit is contained in:
Abdullah Atta
2025-10-30 13:31:01 +05:00
31 changed files with 482 additions and 83 deletions

View File

@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import "./overrides";
import { app, BrowserWindow, nativeTheme, shell } from "electron";
import { app, BrowserWindow, nativeTheme, shell, dialog } from "electron";
import { isDevelopment } from "./utils";
import { registerProtocol, PROTOCOL_URL } from "./utils/protocol";
import { configureAutoUpdater } from "./utils/autoupdater";
@@ -182,6 +182,17 @@ async function createWindow() {
app.once("ready", async () => {
console.info("App ready. Opening window.");
if (app.runningUnderARM64Translation) {
console.log("App is running under ARM64 translation");
dialog.showMessageBoxSync({
message:
"Notesnook detected that it is running under ARM64 translation. For the best performance, please download the ARM64 build of Notesnook from our website.",
type: "warning",
buttons: ["Okay"],
title: "Degraded Performance Warning"
});
}
if (config.customDns) enableCustomDns();
else disableCustomDns();

View File

@@ -59,6 +59,7 @@ export type Settings = {
fontScale: number;
markdownShortcuts: boolean;
features: Record<any, any>;
loggedIn: boolean;
};
export type EditorProps = {

View File

@@ -165,6 +165,7 @@ export const useEditorEvents = (
state.timeFormat
]);
const handleBack = useRef<NativeEventSubscription>();
const loggedIn = useUserStore((state) => !!state.user);
const { fontScale } = useWindowDimensions();
const doubleSpacedLines = useSettingStore(
@@ -224,7 +225,8 @@ export const useEditorEvents = (
timeFormat: db.settings?.getTimeFormat(),
fontScale,
markdownShortcuts,
features
features,
loggedIn
});
}, [
fullscreen,
@@ -242,7 +244,8 @@ export const useEditorEvents = (
timeFormat,
loading,
fontScale,
markdownShortcuts
markdownShortcuts,
loggedIn
]);
const onBackPress = useCallback(async () => {
@@ -556,9 +559,17 @@ export const useEditorEvents = (
if (editor.state.current?.isFocused) {
editor.state.current.isFocused = true;
}
PaywallSheet.present(
await isFeatureAvailable(editorMessage.value.feature)
);
if (editorMessage.value.feature === "insertAttachment") {
ToastManager.show({
type: "info",
message: strings.loginRequired()
});
} else {
PaywallSheet.present(
await isFeatureAvailable(editorMessage.value.feature)
);
}
break;
case EditorEvents.monograph:
publishNote();

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

@@ -82,6 +82,7 @@ import { mdToHtml } from "../../utils/md";
import { InboxSettings } from "./inbox-settings";
import { withFeatureCheck } from "../../common";
import { NotesnookCircleSettings } from "./notesnook-circle-settings";
import { hashNavigate } from "../../navigation";
type SettingsDialogProps = BaseDialogProps<false> & {
activeSection?: SectionKeys;
@@ -271,6 +272,12 @@ function SettingsSideBar(props: SettingsSideBarProps) {
const [route, setRoute] = useState<SectionKeys>(activeSection || "profile");
useUserStore((store) => store.isLoggedIn);
useEffect(() => {
hashNavigate(`/settings/${route}`, {
notify: false
});
}, [route, activeSection]);
return (
<FlexScrollContainer
id="settings-side-menu"

View File

@@ -20,29 +20,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { FeatureId } from "@notesnook/common";
import { Icon } from "../../components/icons";
export type SectionKeys =
| "profile"
| "auth"
| "subscription"
| "sync"
| "appearance"
| "behaviour"
| "desktop"
| "notifications"
| "servers"
| "editor"
| "backup-export"
| "export"
| "importer"
| "vault"
| "app-lock"
| "privacy"
| "support"
| "legal"
| "developer"
| "about"
| "inbox"
| "circle";
const SectionKeys = [
"profile",
"auth",
"subscription",
"sync",
"appearance",
"behaviour",
"desktop",
"notifications",
"servers",
"editor",
"backup-export",
"export",
"importer",
"vault",
"app-lock",
"privacy",
"support",
"legal",
"developer",
"about",
"inbox",
"circle"
] as const;
export type SectionKeys = (typeof SectionKeys)[number];
export type SectionGroupKeys =
| "account"
@@ -153,3 +156,7 @@ export type TextInputSettingComponent = BaseSettingComponent<"input"> & {
export type CustomSettingComponent = BaseSettingComponent<"custom"> & {
component: () => JSX.Element | null;
};
export function isSectionKey(key: string): key is SectionKeys {
return SectionKeys.includes(key as SectionKeys);
}

View File

@@ -33,6 +33,7 @@ import {
import { FeatureDialog } from "../dialogs/feature-dialog";
import { CreateTagDialog } from "../dialogs/item-dialog";
import { OnboardingDialog } from "../dialogs/onboarding-dialog";
import { isSectionKey, SectionKeys } from "../dialogs/settings/types";
const hashroutes = defineHashRoutes({
"/": () => {},
@@ -68,6 +69,11 @@ const hashroutes = defineHashRoutes({
},
"/settings": () => {
SettingsDialog.show({}).then(afterAction);
},
"/settings/:section": ({ section }) => {
SettingsDialog.show(
isSectionKey(section) ? { activeSection: section as SectionKeys } : {}
).then(afterAction);
}
});

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

@@ -19,13 +19,51 @@
"slugify": "1.6.6"
}
},
"node_modules/@emnapi/core": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.6.0.tgz",
"integrity": "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.1.0",
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.6.0.tgz",
"integrity": "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/wasi-threads": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
"integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@module-federation/error-codes": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.18.0.tgz",
"integrity": "sha512-Woonm8ehyVIUPXChmbu80Zj6uJkC0dD9SJUZ/wOPtO8iiz/m+dkrOugAuKgoiR6qH4F+yorWila954tBz4uKsQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@module-federation/runtime": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.18.0.tgz",
"integrity": "sha512-+C4YtoSztM7nHwNyZl6dQKGUVJdsPrUdaf3HIKReg/GQbrt9uvOlUWo2NXMZ8vDAnf/QRrpSYAwXHmWDn9Obaw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -36,6 +74,8 @@
},
"node_modules/@module-federation/runtime-core": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.18.0.tgz",
"integrity": "sha512-ZyYhrDyVAhUzriOsVfgL6vwd+5ebYm595Y13KeMf6TKDRoUHBMTLGQ8WM4TDj8JNsy7LigncK8C03fn97of0QQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -45,6 +85,8 @@
},
"node_modules/@module-federation/runtime-tools": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.18.0.tgz",
"integrity": "sha512-fSga9o4t1UfXNV/Kh6qFvRyZpPp3EHSPRISNeyT8ZoTpzDNiYzhtw0BPUSSD8m6C6XQh2s/11rI4g80UY+d+hA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -54,11 +96,15 @@
},
"node_modules/@module-federation/sdk": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.18.0.tgz",
"integrity": "sha512-Lo/Feq73tO2unjmpRfyyoUkTVoejhItXOk/h5C+4cistnHbTV8XHrW/13fD5e1Iu60heVdAhhelJd6F898Ve9A==",
"dev": true,
"license": "MIT"
},
"node_modules/@module-federation/webpack-bundler-runtime": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.18.0.tgz",
"integrity": "sha512-TEvErbF+YQ+6IFimhUYKK3a5wapD90d90sLsNpcu2kB3QGT7t4nIluE25duXuZDVUKLz86tEPrza/oaaCWTpvQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -67,14 +113,31 @@
}
},
"node_modules/@mozilla/readability": {
"version": "0.4.2",
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.4.4.tgz",
"integrity": "sha512-MCgZyANpJ6msfvVMi6+A0UAsvZj//4OHREYUB9f2087uXHVoU+H+SWhuihvb1beKpM323bReQPRio0WNk2+V6g==",
"license": "Apache-2.0",
"engines": {
"node": ">=10.0.0"
"node": ">=14.0.0"
}
},
"node_modules/@napi-rs/wasm-runtime": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz",
"integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.5.0",
"@emnapi/runtime": "^1.5.0",
"@tybys/wasm-util": "^0.10.1"
}
},
"node_modules/@playwright/test": {
"version": "1.48.2",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.2.tgz",
"integrity": "sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -89,6 +152,8 @@
},
"node_modules/@rsbuild/core": {
"version": "1.5.17",
"resolved": "https://registry.npmjs.org/@rsbuild/core/-/core-1.5.17.tgz",
"integrity": "sha512-tHa4puv+pEooQvSewu/K5sm270nkVPcP07Ioz1c+fbFCrFpiZWV5XumgznilS80097glUrieN+9xTbIHGXjThQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -107,6 +172,8 @@
},
"node_modules/@rspack/binding": {
"version": "1.5.8",
"resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.5.8.tgz",
"integrity": "sha512-/91CzhRl9r5BIQCgGsS7jA6MDbw1I2BQpbfcUUdkdKl2P79K3Zo/Mw/TvKzS86catwLaUQEgkGRmYawOfPg7ow==",
"dev": true,
"license": "MIT",
"optionalDependencies": {
@@ -122,8 +189,136 @@
"@rspack/binding-win32-x64-msvc": "1.5.8"
}
},
"node_modules/@rspack/binding-darwin-arm64": {
"version": "1.5.8",
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.5.8.tgz",
"integrity": "sha512-spJfpOSN3f7V90ic45/ET2NKB2ujAViCNmqb0iGurMNQtFRq+7Kd+jvVKKGXKBHBbsQrFhidSWbbqy2PBPGK8g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rspack/binding-darwin-x64": {
"version": "1.5.8",
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.5.8.tgz",
"integrity": "sha512-YFOzeL1IBknBcri8vjUp43dfUBylCeQnD+9O9p0wZmLAw7DtpN5JEOe2AkGo8kdTqJjYKI+cczJPKIw6lu1LWw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rspack/binding-linux-arm64-gnu": {
"version": "1.5.8",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.5.8.tgz",
"integrity": "sha512-UAWCsOnpkvy8eAVRo0uipbHXDhnoDq5zmqWTMhpga0/a3yzCp2e+fnjZb/qnFNYb5MeL0O1mwMOYgn1M3oHILQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rspack/binding-linux-arm64-musl": {
"version": "1.5.8",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.5.8.tgz",
"integrity": "sha512-GnSvGT4GjokPSD45cTtE+g7LgghuxSP1MRmvd+Vp/I8pnxTVSTsebRod4TAqyiv+l11nuS8yqNveK9qiOkBLWw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rspack/binding-linux-x64-gnu": {
"version": "1.5.8",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.5.8.tgz",
"integrity": "sha512-XLxh5n/pzUfxsugz/8rVBv+Tx2nqEM+9rharK69kfooDsQNKyz7PANllBQ/v4svJ+W0BRHnDL4qXSGdteZeEjA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rspack/binding-linux-x64-musl": {
"version": "1.5.8",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.5.8.tgz",
"integrity": "sha512-gE0+MZmwF+01p9/svpEESkzkLpBkVUG2o03YMpwXYC/maeRRhWvF8BJ7R3i/Ls/jFGSE87dKX5NbRLVzqksq/w==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rspack/binding-wasm32-wasi": {
"version": "1.5.8",
"resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-1.5.8.tgz",
"integrity": "sha512-cfg3niNHeJuxuml1Vy9VvaJrI/5TakzoaZvKX2g5S24wfzR50Eyy4JAsZ+L2voWQQp1yMJbmPYPmnTCTxdJQBQ==",
"cpu": [
"wasm32"
],
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@napi-rs/wasm-runtime": "^1.0.5"
}
},
"node_modules/@rspack/binding-win32-arm64-msvc": {
"version": "1.5.8",
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.5.8.tgz",
"integrity": "sha512-7i3ZTHFXKfU/9Jm9XhpMkrdkxO7lfeYMNVEGkuU5dyBfRMQj69dRgPL7zJwc2plXiqu9LUOl+TwDNTjap7Q36g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rspack/binding-win32-ia32-msvc": {
"version": "1.5.8",
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.5.8.tgz",
"integrity": "sha512-7ZPPWO11J+soea1+mnfaPpQt7GIodBM7A86dx6PbXgVEoZmetcWPrCF2NBfXxQWOKJ9L3RYltC4z+ZyXRgMOrw==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rspack/binding-win32-x64-msvc": {
"version": "1.5.8",
"resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.5.8.tgz",
"integrity": "sha512-N/zXQgzIxME3YUzXT8qnyzxjqcnXudWOeDh8CAG9zqTCnCiy16SFfQ/cQgEoLlD9geQntV6jx2GbDDI5kpDGMQ==",
"cpu": [
"x64"
],
@@ -136,6 +331,8 @@
},
"node_modules/@rspack/core": {
"version": "1.5.8",
"resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.5.8.tgz",
"integrity": "sha512-sUd2LfiDhqYVfvknuoz0+/c+wSpn693xotnG5g1CSWKZArbtwiYzBIVnNlcHGmuoBRsnj/TkSq8dTQ7gwfBroQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -157,6 +354,8 @@
},
"node_modules/@rspack/lite-tapable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.0.1.tgz",
"integrity": "sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==",
"dev": true,
"license": "MIT",
"engines": {
@@ -165,14 +364,30 @@
},
"node_modules/@swc/helpers": {
"version": "0.5.17",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "^2.8.0"
}
},
"node_modules/@tybys/wasm-util": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/core-js": {
"version": "3.46.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.46.0.tgz",
"integrity": "sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -181,12 +396,31 @@
"url": "https://opencollective.com/core-js"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/hyperapp": {
"version": "2.0.22",
"resolved": "https://registry.npmjs.org/hyperapp/-/hyperapp-2.0.22.tgz",
"integrity": "sha512-3uf9HjnjrhbfykowFNEObZewBEo4DXJIM+9FnGkiR9E4H2eh2f921SzMCMS69X5nN3A7KFfmZc9KCKh/7TQBFA==",
"license": "MIT"
},
"node_modules/jiti": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
"dev": true,
"license": "MIT",
"bin": {
@@ -195,6 +429,8 @@
},
"node_modules/playwright": {
"version": "1.48.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz",
"integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -212,6 +448,8 @@
},
"node_modules/playwright-core": {
"version": "1.48.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz",
"integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -223,6 +461,8 @@
},
"node_modules/slugify": {
"version": "1.6.6",
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz",
"integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -231,6 +471,8 @@
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
}

View File

@@ -113,7 +113,8 @@ const Tiptap = ({
claims: {
callout: !!settings.features?.callout?.isAllowed,
outlineList: !!settings.features?.outlineList?.isAllowed,
taskList: !!settings.features?.taskList?.isAllowed
taskList: !!settings.features?.taskList?.isAllowed,
insertAttachment: settings.loggedIn
},
onPermissionDenied: (claim) => {
post(

View File

@@ -73,9 +73,10 @@ function Title({
useEffect(() => {
if (!loading) {
resizeTextarea();
setTimeout(() => {
resizeTextarea();
}, 300);
}, 100);
}
}, [loading, resizeTextarea]);
@@ -154,6 +155,9 @@ function Title({
onPaste={() => {
resizeTextarea();
}}
onCut={() => {
resizeTextarea();
}}
placeholder={titlePlaceholder}
/>
</>

View File

@@ -34,7 +34,8 @@ const initialState = {
fontFamily: "sans-serif",
fontSize: 16,
timeFormat: "12-hour",
dateFormat: "DD-MM-YYYY"
dateFormat: "DD-MM-YYYY",
loggedIn: false
};
global.settingsController = {

View File

@@ -53,6 +53,7 @@ export type Settings = {
fontScale: number;
markdownShortcuts: boolean;
features: Record<any, any>;
loggedIn: boolean;
};
/* eslint-disable no-var */

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

@@ -258,10 +258,10 @@ export const Callout = Node.create({
container.onmousedown = onClick;
container.ontouchstart = onClick;
if (node.attrs.hiddenUnder) {
container.dataset.hiddenUnder = node.attrs.hiddenUnder;
if (node.attrs.hidden) {
container.dataset.hidden = node.attrs.hidden;
} else {
delete container.dataset.hiddenUnder;
delete container.dataset.hidden;
}
return {
@@ -275,9 +275,9 @@ export const Callout = Node.create({
if (updatedNode.attrs.collapsed) container.classList.add("collapsed");
else container.classList.remove("collapsed");
if (updatedNode.attrs.hiddenUnder)
container.dataset.hiddenUnder = updatedNode.attrs.hiddenUnder;
else delete container.dataset.hiddenUnder;
if (updatedNode.attrs.hidden)
container.dataset.hidden = updatedNode.attrs.hidden;
else delete container.dataset.hidden;
return true;
}

View File

@@ -555,7 +555,7 @@ export const CodeBlock = Node.create<CodeBlockOptions>({
compareCaretPosition(prev.caretPosition, next.caretPosition) ||
prev.language !== next.language ||
prev.indentType !== next.indentType ||
prev.hiddenUnder !== next.hiddenUnder
prev.hidden !== next.hidden
);
}
});

View File

@@ -89,14 +89,14 @@ export const Heading = TiptapHeading.extend({
{
types: COLLAPSIBLE_BLOCK_TYPES,
attributes: {
hiddenUnder: {
default: null,
hidden: {
default: false,
keepOnSplit: false,
parseHTML: (element) => element.dataset.hiddenUnder || null,
parseHTML: (element) => element.dataset.hidden === "true",
renderHTML: (attributes) => {
if (!attributes.hiddenUnder) return {};
if (!attributes.hidden) return {};
return {
"data-hidden-under": attributes.hiddenUnder
"data-hidden": attributes.hidden === true
};
}
}
@@ -200,16 +200,9 @@ export const Heading = TiptapHeading.extend({
if (currentNode && currentNode.type.name === "heading") {
const shouldCollapse = !currentNode.attrs.collapsed;
const headingLevel = currentNode.attrs.level;
const headingId = currentNode.attrs.blockId;
tr.setNodeAttribute(pos, "collapsed", shouldCollapse);
toggleNodesUnderHeading(
tr,
pos,
headingLevel,
shouldCollapse,
headingId
);
toggleNodesUnderHeading(tr, pos, headingLevel, shouldCollapse);
}
return true;
});
@@ -234,9 +227,9 @@ export const Heading = TiptapHeading.extend({
if (updatedNode.attrs.collapsed) heading.dataset.collapsed = "true";
else delete heading.dataset.collapsed;
if (updatedNode.attrs.hiddenUnder)
heading.dataset.hiddenUnder = updatedNode.attrs.hiddenUnder;
else delete heading.dataset.hiddenUnder;
if (updatedNode.attrs.hidden)
heading.dataset.hidden = updatedNode.attrs.hidden;
else delete heading.dataset.hidden;
if (updatedNode.attrs.textAlign)
heading.style.textAlign =
@@ -259,8 +252,7 @@ function toggleNodesUnderHeading(
tr: Transaction,
headingPos: number,
headingLevel: number,
isCollapsing: boolean,
headingId: string
isCollapsing: boolean
) {
const { doc } = tr;
const headingNode = doc.nodeAt(headingPos);
@@ -269,6 +261,8 @@ function toggleNodesUnderHeading(
let nextPos = headingPos + headingNode.nodeSize;
const cursorPos = tr.selection.from;
let shouldMoveCursor = false;
let insideCollapsedHeading = false;
let nestedHeadingLevel: number | null = null;
while (nextPos < doc.content.size) {
const nextNode = doc.nodeAt(nextPos);
@@ -289,15 +283,33 @@ function toggleNodesUnderHeading(
shouldMoveCursor = true;
}
const currentPos = nextPos;
nextPos += nextNode.nodeSize;
if (COLLAPSIBLE_BLOCK_TYPES.includes(nextNode.type.name)) {
if (isCollapsing && typeof nextNode.attrs.hiddenUnder !== "string") {
tr.setNodeAttribute(nextPos, "hiddenUnder", headingId);
} else if (!isCollapsing && nextNode.attrs.hiddenUnder === headingId) {
tr.setNodeAttribute(nextPos, "hiddenUnder", null);
if (isCollapsing) {
tr.setNodeAttribute(currentPos, "hidden", true);
} else {
if (insideCollapsedHeading) {
if (
nextNode.type.name === "heading" &&
nestedHeadingLevel !== null &&
nextNode.attrs.level <= nestedHeadingLevel
) {
insideCollapsedHeading = false;
nestedHeadingLevel = null;
} else {
continue;
}
}
tr.setNodeAttribute(currentPos, "hidden", false);
if (nextNode.type.name === "heading" && nextNode.attrs.collapsed) {
insideCollapsedHeading = true;
nestedHeadingLevel = nextNode.attrs.level;
}
}
}
nextPos += nextNode.nodeSize;
}
if (shouldMoveCursor) {

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

@@ -125,10 +125,10 @@ export class MathView implements NodeView, ICursorPosObserver {
if (options.className) this.dom.classList.add(options.className);
this.dom.classList.add("math-node");
if (node.attrs.hiddenUnder) {
this.dom.dataset.hiddenUnder = node.attrs.hiddenUnder;
if (node.attrs.hidden) {
this.dom.dataset.hidden = node.attrs.hidden;
} else {
delete this.dom.dataset.hiddenUnder;
delete this.dom.dataset.hidden;
}
this._mathRenderElt = document.createElement("span");

View File

@@ -128,3 +128,5 @@ exports[`outline list item > code block in outline list item 1`] = `
"type": "doc",
}
`;
exports[`outline list item > inline image as first child in the old outline list item 1`] = `"<ul data-type="outlineList"><li data-type="outlineListItem"><p data-spacing="double">item 1</p></li><li data-type="outlineListItem"><p data-spacing="double"></p><img src="image.png" data-aspect-ratio="1"></li></ul>"`;

View File

@@ -28,6 +28,8 @@ import { test, expect, describe, beforeAll, vi } from "vitest";
import { OutlineList } from "../../outline-list/outline-list.js";
import { OutlineListItem } from "../outline-list-item.js";
import { CodeBlock } from "../../code-block/code-block.js";
import { Paragraph } from "../../paragraph/paragraph.js";
import { ImageNode } from "../../image/image.js";
describe("outline list item", () => {
beforeAll(() => {
@@ -70,4 +72,30 @@ describe("outline list item", () => {
expect(editor.getJSON()).toMatchSnapshot();
});
/**
* Two changes happened:
* 1. Images were converted from inline nodes to block nodes (https://github.com/streetwriters/notesnook/pull/8563)
* 2. Outline list item's `content` schema was changed from `paragraph + list?` to `block+` to `paragraph block*` (https://github.com/streetwriters/notesnook/pull/8772 and https://github.com/streetwriters/notesnook/commit/0b943d8ecdf04fd7d996fd0a4b1d62ec9569f071)
*
* In the old editor, it was possible to have an inline image as the first item in the outline list item, but based on the new schema it is not possible anymore. So the editor should insert an empty paragraph before the image.
*/
test("inline image as first child in the old outline list item", async () => {
const el = outlineList(
outlineListItem(["item 1"]),
outlineListItem([h("img", [], { src: "image.png" })])
);
const { editor } = createEditor({
initialContent: el.outerHTML,
extensions: {
outlineList: OutlineList,
outlineListItem: OutlineListItem,
paragraph: Paragraph,
image: ImageNode
}
});
expect(editor.getHTML()).toMatchSnapshot();
});
});

View File

@@ -29,6 +29,7 @@ import {
import { OutlineList } from "../outline-list/outline-list.js";
import { keybindings, tiptapKeys } from "@notesnook/common";
import { Paragraph } from "../paragraph/paragraph.js";
import { DOMParser } from "@tiptap/pm/model";
export interface ListItemOptions {
HTMLAttributes: Record<string, unknown>;
@@ -64,7 +65,18 @@ export const OutlineListItem = Node.create<ListItemOptions>({
return [
{
priority: 100,
tag: `li[data-type="${this.name}"]`
tag: `li[data-type="${this.name}"]`,
getContent: (node, schema) => {
const parser = DOMParser.fromSchema(schema);
const fragment = parser.parse(node).content;
const firstNode = fragment.firstChild;
if (firstNode && firstNode.type.name !== "paragraph") {
const emptyParagraph = schema.nodes.paragraph.create();
return fragment.addToStart(emptyParagraph);
}
return fragment;
}
}
];
},
@@ -154,6 +166,7 @@ export const OutlineListItem = Node.create<ListItemOptions>({
const resolvedPos = editor.state.doc.resolve(pos);
if (isClickWithinBounds(e, resolvedPos, "left")) {
e.preventDefault();
e.stopImmediatePropagation();
editor.commands.command(({ tr }) => {
tr.setNodeAttribute(
pos,

View File

@@ -112,10 +112,10 @@ export class ReactNodeView<P extends ReactNodeViewProps> implements NodeView {
return;
}
if (this.node.attrs.hiddenUnder) {
this.domRef.dataset.hiddenUnder = this.node.attrs.hiddenUnder;
if (this.node.attrs.hidden) {
this.domRef.dataset.hidden = this.node.attrs.hidden;
} else {
delete this.domRef.dataset.hiddenUnder;
delete this.domRef.dataset.hidden;
}
portalProviderAPI.render(this.Component, this.domRef);

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

@@ -707,11 +707,6 @@ p > *::selection {
mask-size: cover;
}
.simple-checklist > li.checked > p {
opacity: 0.8;
text-decoration-line: line-through;
}
/* Callout */
.ProseMirror div.callout {
padding: 15px;
@@ -983,7 +978,7 @@ del.diffdel {
display: none;
}
[data-hidden-under] {
[data-hidden="true"] {
display: none !important;
}

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)`
};