Compare commits

..

18 Commits

Author SHA1 Message Date
Ammar Ahmed
10b495cdbe mobile: convert sheets with input to screens or simple modals 2025-06-25 13:45:01 +05:00
Ammar Ahmed
d456132dd6 mobile: release v3.2.2 2025-06-21 13:24:24 +05:00
Abdullah Atta
4ac7941b6c web: bump version to v3.2.2 2025-06-21 13:03:04 +05:00
Abdullah Atta
e7f5463917 web: make trash actions multi selectable 2025-06-21 13:00:46 +05:00
Abdullah Atta
6e41ce459d common: bulk resolve notebooks & tags 2025-06-21 13:00:46 +05:00
Abdullah Atta
f5095fa643 core: fix hang when resolving recursive nested notebooks 2025-06-21 13:00:46 +05:00
Abdullah Atta
e6a5b85011 web: register fts5 extensions after db decryption 2025-06-21 13:00:46 +05:00
Abdullah Atta
409c5a2517 web: update sqlite to v3.49.2 2025-06-21 13:00:46 +05:00
Abdullah Atta
35cd6b101d desktop: update better-sqlite3-multiple-ciphers to v11.10.0 2025-06-21 13:00:46 +05:00
Ammar Ahmed
2440e28168 mobile: fix android toolbar hidden behind keyboard on android 15 2025-06-20 14:49:15 +05:00
Ammar Ahmed
0078d509c5 mobile: release v3.2.1 2025-06-18 11:25:33 +05:00
Ammar Ahmed
0c2d57716d mobile: fix app crash on android 15 & 16 2025-06-18 11:16:07 +05:00
Abdullah Atta
678aeb98c3 ci: release snap to stable track (instead of release track) 2025-06-18 10:35:56 +05:00
Abdullah Atta
5f422f4eb7 web: bump version to v3.2.1 2025-06-18 10:27:50 +05:00
Abdullah Atta
2b26524fcd web: bump version to v3.2.1-beta.0 2025-06-18 10:27:13 +05:00
Abdullah Atta
7326533cc1 ci: publish snap beta 2025-06-18 10:27:13 +05:00
Abdullah Atta
c8aae5be61 desktop: change snap base to core22 2025-06-18 10:27:13 +05:00
Abdullah Atta
be1a8bf849 docs: remove mention of importer.notesnook.com (#8218) 2025-06-17 13:59:38 +05:00
73 changed files with 551 additions and 1790 deletions

View File

@@ -251,7 +251,7 @@ jobs:
run: echo "SNAPCRAFT_STORE_CREDENTIALS=${{ secrets.snapcraft_token }}" >> $GITHUB_ENV
- name: Install Snapcraft
uses: samuelmeuli/action-snapcraft@v1
uses: samuelmeuli/action-snapcraft@v3
if: inputs.publish-snap
- name: Download build
@@ -294,7 +294,11 @@ jobs:
- name: Publish on Snapcraft
if: inputs.publish-snap
run: |
snapcraft upload --release=stable ./output/notesnook_linux_amd64.snap
if [ ${{ inputs.release-track }} == 'beta' ]; then
snapcraft upload --release=beta ./output/notesnook_linux_amd64.snap
else
snapcraft upload --release=stable ./output/notesnook_linux_amd64.snap
fi
working-directory: ./apps/desktop
build-linux-arm64:

View File

@@ -66,7 +66,7 @@ We take all queries, issues and bug reports that you might have. Feel free to as
## Additional Resources
- [Migrating & Importing your data from other apps — Importer](https://importer.notesnook.com/)
- [Migrating & Importing your data from other apps — Importer](https://help.notesnook.com/importing-notes)
- [Privacy policy](https://notesnook.com/privacy) & [Terms of service](https://notesnook.com/terms)
- [Verify Notesnook encryption claims yourself — Vericrypt](https://vericrypt.notesnook.com/)
- [Why Notesnook requires an email address?](https://blog.notesnook.com/why-notesnook-requires-an-email-address/)

View File

@@ -38,6 +38,9 @@ const linuxExecutableName = process.env.NN_PRODUCT_NAME
const year = new Date().getFullYear();
const isBeta = pkg.version.includes("-beta");
/**
* @type {import("app-builder-lib").Configuration}
*/
module.exports = {
appId: appId,
productName: productName,
@@ -194,7 +197,8 @@ module.exports = {
snap: {
autoStart: false,
confinement: "strict",
allowNativeWayland: true
allowNativeWayland: true,
base: "core22"
},
extraResources: ["app-update.yml", "./assets/**"],
extraMetadata: {

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/desktop",
"version": "3.2.0",
"version": "3.2.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/desktop",
"version": "3.2.0",
"version": "3.2.2",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {
@@ -15,7 +15,7 @@
"@notesnook/ui": "file:../../packages/ui",
"@trpc/client": "10.45.2",
"@trpc/server": "10.45.2",
"better-sqlite3-multiple-ciphers": "11.3.0",
"better-sqlite3-multiple-ciphers": "^11.10.0",
"electron-trpc": "0.7.1",
"electron-updater": "^6.6.2",
"icojs": "^0.19.5",
@@ -2282,9 +2282,9 @@
"license": "MIT"
},
"node_modules/better-sqlite3-multiple-ciphers": {
"version": "11.3.0",
"resolved": "https://registry.npmjs.org/better-sqlite3-multiple-ciphers/-/better-sqlite3-multiple-ciphers-11.3.0.tgz",
"integrity": "sha512-F0+gYaT8drCyHpujgMjS4RRAElVdAtif0uH/v4rA5cLTR5v25zDXTkj2/ie8A26Www+vT60pCF0MD+MGzIcUzw==",
"version": "11.10.0",
"resolved": "https://registry.npmjs.org/better-sqlite3-multiple-ciphers/-/better-sqlite3-multiple-ciphers-11.10.0.tgz",
"integrity": "sha512-/dKO3lKuJFbmuzh80uN2cmMsz8iyTskGB2l/fd9X6rt1P3EPIOvRUIxD7Qim8gLygUPB/u+db8byZGumOOdp3g==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "3.2.0",
"version": "3.2.2",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/cjs/index.js",
@@ -30,7 +30,7 @@
"@notesnook/ui": "file:../../packages/ui",
"@trpc/client": "10.45.2",
"@trpc/server": "10.45.2",
"better-sqlite3-multiple-ciphers": "11.3.0",
"better-sqlite3-multiple-ciphers": "^11.10.0",
"electron-trpc": "0.7.1",
"electron-updater": "^6.6.2",
"icojs": "^0.19.5",

View File

@@ -1,5 +1,5 @@
diff --git a/node_modules/better-sqlite3-multiple-ciphers/deps/defines.gypi b/node_modules/better-sqlite3-multiple-ciphers/deps/defines.gypi
index 1a14ecd..dabcce2 100644
index 1a14ecd..ff938f1 100644
--- a/node_modules/better-sqlite3-multiple-ciphers/deps/defines.gypi
+++ b/node_modules/better-sqlite3-multiple-ciphers/deps/defines.gypi
@@ -38,5 +38,6 @@

View File

@@ -33,6 +33,8 @@ export class SQLite {
initialized = false;
preparedStatements: Map<string, Statement<unknown[]>> = new Map();
retryCounter: Record<string, number> = {};
extensionsLoaded = false;
constructor() {
console.log("new sqlite worker");
}
@@ -46,10 +48,6 @@ export class SQLite {
this.sqlite = require("better-sqlite3-multiple-ciphers")(
filePath
).unsafeMode(true);
const betterTrigram = require("sqlite-better-trigram");
const fts5Html = require("sqlite3-fts5-html");
betterTrigram.load(this.sqlite);
fts5Html.load(this.sqlite);
}
/**
@@ -117,6 +115,20 @@ export class SQLite {
} catch (e) {
if (e instanceof Error) e.message += ` (query: ${sql})`;
throw e;
} finally {
// Since SQLite 3.48.0 (SQLite3MC v2.0.2) it's not possible to load fts5
// extensions before database has been decrypting. This is because
// executing a `SELECT` now accesses the underlying databases resulting in
// an error. Since FTS5 extensions depend on `SELECT fts5` to load the
// fts5 API, we must wait decrypt the database before we can load
// the extensions.
if (!this.extensionsLoaded && (await this.isDatabaseReady())) {
const betterTrigram = require("sqlite-better-trigram");
const fts5Html = require("sqlite3-fts5-html");
betterTrigram.load(this.sqlite);
fts5Html.load(this.sqlite);
this.extensionsLoaded = true;
}
}
}
@@ -144,4 +156,22 @@ export class SQLite {
retryDelay: 500
});
}
/**
* This just executes `SELECT 1` on the database to make sure its ready.
* On an encrypted database, this will fail until `PRAGMA key` has been
* called.
*/
private async isDatabaseReady() {
// return this.exec(`SELECT 1;`)
// .then(() => true)
// .catch(() => false);
if (!this.sqlite) return false;
try {
this.sqlite.prepare(`SELECT 1;`).run();
return true;
} catch {
return false;
}
}
}

View File

@@ -17,26 +17,20 @@ 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 { strings } from "@notesnook/intl";
import React, { useRef, useState } from "react";
import { View } from "react-native";
import { db } from "../../common/database";
import {
eSendEvent,
presentSheet,
ToastManager
} from "../../services/event-manager";
import BackupService from "../../services/backup";
import { eSendEvent, ToastManager } from "../../services/event-manager";
import Navigation from "../../services/navigation";
import { useUserStore } from "../../stores/use-user-store";
import { eCloseSheet, eOpenRecoveryKeyDialog } from "../../utils/events";
import DialogHeader from "../dialog/dialog-header";
import { eOpenRecoveryKeyDialog } from "../../utils/events";
import { DefaultAppStyles } from "../../utils/styles";
import { Dialog } from "../dialog";
import { Button } from "../ui/button";
import Input from "../ui/input";
import { Notice } from "../ui/notice";
import Seperator from "../ui/seperator";
import { Dialog } from "../dialog";
import BackupService from "../../services/backup";
import { sleep } from "../../utils/time";
import { strings } from "@notesnook/intl";
import { DefaultAppStyles } from "../../utils/styles";
export const ChangePassword = () => {
const passwordInputRef = useRef();
@@ -85,8 +79,7 @@ export const ChangePassword = () => {
context: "global"
});
setLoading(false);
eSendEvent(eCloseSheet);
await sleep(300);
Navigation.goBack();
eSendEvent(eOpenRecoveryKeyDialog);
} catch (e) {
setLoading(false);
@@ -108,9 +101,6 @@ export const ChangePassword = () => {
}}
>
<Dialog context="change-password-dialog" />
<DialogHeader title={strings.changePassword()} />
<Seperator />
<Input
fwdRef={oldPasswordInputRef}
onChangeText={(value) => {
@@ -160,9 +150,3 @@ export const ChangePassword = () => {
</View>
);
};
ChangePassword.present = () => {
presentSheet({
component: <ChangePassword />
});
};

View File

@@ -39,6 +39,7 @@ import { hideAuth } from "./common";
import { ForgotPassword } from "./forgot-password";
import { useLogin } from "./use-login";
import { DefaultAppStyles } from "../../utils/styles";
import { Dialog } from "../dialog";
const LoginSteps = {
emailAuth: 1,
@@ -89,7 +90,7 @@ export const Login = ({ changeMode }) => {
return (
<>
<ForgotPassword />
<SheetProvider context="two_factor_verify" />
<Dialog context="two_factor_verify" />
<View
style={{
borderRadius: DDS.isTab ? 5 : 0,

View File

@@ -24,12 +24,8 @@ import { View } from "react-native";
import { ScrollView } from "react-native-actions-sheet";
import { db } from "../../common/database/index";
import useTimer from "../../hooks/use-timer";
import {
eSendEvent,
presentSheet,
ToastManager
} from "../../services/event-manager";
import { eCloseSheet } from "../../utils/events";
import { eSendEvent, ToastManager } from "../../services/event-manager";
import { eCloseSimpleDialog } from "../../utils/events";
import { AppFontSize } from "../../utils/size";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
@@ -38,6 +34,7 @@ import { Pressable } from "../ui/pressable";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { DefaultAppStyles } from "../../utils/styles";
import { presentDialog } from "../dialog/functions";
const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
const { colors } = useThemeColors();
@@ -46,7 +43,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
method: mfaInfo?.primaryMethod,
isPrimary: true
});
const { seconds, start } = useTimer(currentMethod.method);
const { seconds, start, reset } = useTimer(currentMethod.method);
const [loading, setLoading] = useState(false);
const inputRef = useRef();
const [sending, setSending] = useState(false);
@@ -62,7 +59,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
},
(result) => {
if (result) {
eSendEvent(eCloseSheet, "two_factor_verify");
eSendEvent(eCloseSimpleDialog, "two_factor_verify");
}
setLoading(false);
}
@@ -133,6 +130,12 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
<ScrollView
keyboardShouldPersistTaps="handled"
keyboardDismissMode="interactive"
style={{
width: "100%",
height: "100%",
backgroundColor: colors.primary.background,
paddingTop: 60
}}
>
<View
style={{
@@ -236,7 +239,10 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
<Button
title={strings.cancel()}
type="secondaryAccented"
onPress={onCancel}
onPress={() => {
reset();
onCancel();
}}
width={250}
/>
@@ -297,7 +303,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo, onCancel }) => {
};
TwoFactorVerification.present = (onMfaLogin, data, onCancel, context) => {
presentSheet({
presentDialog({
component: () => (
<TwoFactorVerification
onMfaLogin={onMfaLogin}
@@ -306,7 +312,9 @@ TwoFactorVerification.present = (onMfaLogin, data, onCancel, context) => {
/>
),
context: context || "two_factor_verify",
disableClosing: true
disableClosing: true,
transparent: false,
statusBarTranslucent: true
});
};

View File

@@ -24,7 +24,7 @@ import { clearMessage } from "../../services/message";
import PremiumService from "../../services/premium";
import SettingsService from "../../services/settings";
import { useUserStore } from "../../stores/use-user-store";
import { eCloseSheet } from "../../utils/events";
import { eCloseSimpleDialog } from "../../utils/events";
import TwoFactorVerification from "./two-factor";
import { strings } from "@notesnook/intl";
@@ -92,7 +92,7 @@ export const useLogin = (onFinishLogin, sessionExpired = false) => {
} catch (e) {
callback && callback(false);
if (e.message === "invalid_grant") {
eSendEvent(eCloseSheet, "two_factor_verify");
eSendEvent(eCloseSimpleDialog, "two_factor_verify");
setLoading(false);
setStep(LoginSteps.emailAuth);
}
@@ -100,7 +100,7 @@ export const useLogin = (onFinishLogin, sessionExpired = false) => {
},
mfaInfo,
() => {
eSendEvent(eCloseSheet, "two_factor_verify");
eSendEvent(eCloseSimpleDialog, "two_factor_verify");
setLoading(false);
setStep(LoginSteps.emailAuth);
}

View File

@@ -27,6 +27,9 @@ export type DialogInfo = {
paragraph?: string;
positiveText: string;
negativeText: string;
background?: string;
transparent?: boolean;
statusBarTranslucent?: boolean;
positivePress?: (...args: any[]) => Promise<any>;
onClose?: () => void;
positiveType?:

View File

@@ -130,9 +130,17 @@ export const Dialog = ({ context = "global" }: { context?: string }) => {
return visible && dialogInfo ? (
<BaseDialog
statusBarTranslucent={false}
statusBarTranslucent={
dialogInfo.statusBarTranslucent === undefined
? false
: dialogInfo.statusBarTranslucent
}
bounce={!dialogInfo.input}
closeOnTouch={!dialogInfo.disableBackdropClosing}
background={dialogInfo.background}
transparent={
dialogInfo.transparent === undefined ? true : dialogInfo.transparent
}
onShow={async () => {
if (dialogInfo.input) {
inputRef.current?.setNativeProps({

View File

@@ -97,7 +97,6 @@ export const Header = ({
style={{
flexDirection: "row",
justifyContent: "space-between",
marginTop: DefaultAppStyles.GAP_SMALL,
borderRadius: 10,
paddingVertical: 3,
borderWidth: hasSearch ? 1 : 0,

View File

@@ -52,7 +52,14 @@ const useTimer = (initialId?: string) => {
};
}, [seconds, id]);
return { seconds, setId, start };
const reset = () => {
if (id) {
timers[id] = 0;
setSeconds(0);
}
};
return { seconds, setId, start, reset };
};
export default useTimer;

View File

@@ -29,6 +29,7 @@ import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { useSettingStore } from "../../stores/use-setting-store";
import { NotesnookModule } from "../../utils/notesnook-module";
import { DefaultAppStyles } from "../../utils/styles";
import { SafeAreaView } from "react-native-safe-area-context";
const NoteItem = (props: {
id: string | number;
@@ -111,7 +112,7 @@ export const NotePreviewConfigure = () => {
);
return (
<View
<SafeAreaView
style={{
backgroundColor: colors.primary.background,
flex: 1
@@ -165,6 +166,6 @@ export const NotePreviewConfigure = () => {
ListFooterComponent={<View style={{ height: 200 }} />}
/>
</View>
</View>
</SafeAreaView>
);
};

View File

@@ -16,35 +16,22 @@ GNU General Public License for more details.
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 React, { RefObject, useRef, useState } from "react";
import React, { useRef, useState } from "react";
import { TextInput, View } from "react-native";
import { ActionSheetRef } from "react-native-actions-sheet";
import { db } from "../../../common/database";
import {
eSendEvent,
presentSheet,
PresentSheetOptions,
ToastManager
} from "../../../services/event-manager";
import DialogHeader from "../../dialog/dialog-header";
import { Button } from "../../ui/button";
import Input from "../../ui/input";
import { eSendEvent, ToastManager } from "../../../services/event-manager";
import { eUserLoggedIn } from "../../../utils/events";
import { strings } from "@notesnook/intl";
import { DefaultAppStyles } from "../../../utils/styles";
type ChangeEmailProps = {
actionSheetRef: RefObject<ActionSheetRef>;
close?: () => void;
update?: (options: PresentSheetOptions) => void;
};
import Input from "../../../components/ui/input";
import { Button } from "../../../components/ui/button";
enum EmailChangeSteps {
verify,
changeEmail
}
export const ChangeEmail = ({ close }: ChangeEmailProps) => {
export const ChangeEmail = () => {
const [step, setStep] = useState(EmailChangeSteps.verify);
const emailChangeData = useRef<{
email?: string;
@@ -103,10 +90,6 @@ export const ChangeEmail = ({ close }: ChangeEmailProps) => {
return (
<View style={{ paddingHorizontal: DefaultAppStyles.GAP }}>
<DialogHeader
title={strings.changeEmail()}
paragraph={strings.changeEmailDesc()}
/>
<View
style={{
marginTop: DefaultAppStyles.GAP_VERTICAL
@@ -156,23 +139,12 @@ export const ChangeEmail = ({ close }: ChangeEmailProps) => {
: strings.changeEmail()
}
type="accent"
width={250}
style={{
width: "100%"
}}
loading={loading}
onPress={onSubmit}
style={{
borderRadius: 100,
height: 45,
marginTop: 2
}}
/>
</View>
);
};
ChangeEmail.present = () => {
presentSheet({
component: (ref, close, update) => (
<ChangeEmail actionSheetRef={ref} close={close} update={update} />
)
});
};

View File

@@ -43,6 +43,8 @@ import ThemeSelector from "./theme-selector";
import { TitleFormat } from "./title-format";
import { View } from "react-native";
import { DefaultAppStyles } from "../../utils/styles";
import { ChangePassword } from "../../components/auth/change-password";
import { ChangeEmail } from "./change-email";
export const components: { [name: string]: ReactElement } = {
colorpicker: <AccentColorPicker />,
@@ -69,5 +71,7 @@ export const components: { [name: string]: ReactElement } = {
<AttachmentGroupProgress groupId="offline-mode" />
</View>
),
"sidebar-tab-selector": <SidebarTabPicker />
"sidebar-tab-selector": <SidebarTabPicker />,
"change-password": <ChangePassword />,
"change-email": <ChangeEmail />
};

View File

@@ -32,11 +32,9 @@ import { enabled } from "react-native-privacy-snapshot";
import ScreenGuardModule from "react-native-screenguard";
import { db } from "../../common/database";
import filesystem from "../../common/filesystem";
import { ChangePassword } from "../../components/auth/change-password";
import { presentDialog } from "../../components/dialog/functions";
import { AppLockPassword } from "../../components/dialogs/applock-password";
import { endProgress, startProgress } from "../../components/dialogs/progress";
import { ChangeEmail } from "../../components/sheets/change-email";
import ExportNotesSheet from "../../components/sheets/export-notes";
import { Issue } from "../../components/sheets/github/issue";
import { Progress } from "../../components/sheets/progress";
@@ -243,17 +241,15 @@ export const settingsGroups: SettingSection[] = [
{
id: "change-password",
name: strings.changePassword(),
modifer: async () => {
ChangePassword.present();
},
description: strings.changePasswordDesc()
type: "screen",
description: strings.changePasswordDesc(),
component: "change-password"
},
{
id: "change-email",
name: strings.changeEmail(),
modifer: async () => {
ChangeEmail.present();
},
type: "screen",
component: "change-email",
description: strings.changeEmailDesc()
},
{

View File

@@ -124,7 +124,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled true
versionCode 3056
versionCode 3058
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

View File

@@ -60,15 +60,6 @@
android:largeHeap="true"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/network_security_config">
<receiver android:exported="false" android:label="@string/reminders_title" android:name=".ReminderWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_reminders_info" />
</receiver>
<receiver android:exported="false" android:label="@string/quick_note" android:name=".NoteWidget">
<intent-filter>
@@ -90,6 +81,15 @@
android:resource="@xml/note_widget_info" />
</receiver>
<receiver android:exported="false" android:label="@string/reminders_title" android:name=".ReminderWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_reminders_info" />
</receiver>
<activity android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:label="NotePreviewConfigure"
android:launchMode="singleTask"

View File

@@ -48,7 +48,8 @@ public class NotePreviewWidget extends AppWidgetProvider {
private static Bundle getActivityOptionsBundle() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
ActivityOptions activityOptions = ActivityOptions.makeBasic();
activityOptions.setPendingIntentBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
activityOptions.setPendingIntentCreatorBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
return activityOptions.toBundle();
} else
return null;

View File

@@ -31,7 +31,8 @@ public class NoteWidget extends AppWidgetProvider {
private static Bundle getActivityOptionsBundle() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
ActivityOptions activityOptions = ActivityOptions.makeBasic();
activityOptions.setPendingIntentBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
activityOptions.setPendingIntentCreatorBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
return activityOptions.toBundle();
} else
return null;

View File

@@ -26,7 +26,8 @@ public class ReminderWidgetProvider extends AppWidgetProvider {
private static Bundle getActivityOptionsBundle() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
ActivityOptions activityOptions = ActivityOptions.makeBasic();
activityOptions.setPendingIntentBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
activityOptions.setPendingIntentCreatorBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
return activityOptions.toBundle();
} else
return null;

View File

@@ -1091,7 +1091,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2134;
CURRENT_PROJECT_VERSION = 2136;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1165,7 +1165,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.2.0;
MARKETING_VERSION = 3.2.2;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1196,7 +1196,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2134;
CURRENT_PROJECT_VERSION = 2136;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1270,7 +1270,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.2.0;
MARKETING_VERSION = 3.2.2;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1429,7 +1429,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2134;
CURRENT_PROJECT_VERSION = 2136;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1441,7 +1441,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.2.0;
MARKETING_VERSION = 3.2.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1472,7 +1472,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2134;
CURRENT_PROJECT_VERSION = 2136;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1485,7 +1485,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.2.0;
MARKETING_VERSION = 3.2.2;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1515,7 +1515,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2134;
CURRENT_PROJECT_VERSION = 2136;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1589,7 +1589,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.2.0;
MARKETING_VERSION = 3.2.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1620,7 +1620,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2134;
CURRENT_PROJECT_VERSION = 2136;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1695,7 +1695,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.2.0;
MARKETING_VERSION = 3.2.2;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -66,7 +66,7 @@
"react-native-screenguard": "^1.0.0",
"@formatjs/intl-locale": "4.0.0",
"@formatjs/intl-pluralrules": "5.2.14",
"@ammarahmed/react-native-sodium": "^1.6.3",
"@ammarahmed/react-native-sodium": "^1.6.4",
"@react-native-community/datetimepicker": "^8.2.0",
"react-native-exit-app": "github:ammarahm-ed/react-native-exit-app",
"react-native-begin-background-task": "github:blockfirm/react-native-begin-background-task",

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/mobile",
"version": "3.2.0",
"version": "3.2.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "3.2.0",
"version": "3.2.2",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -14,7 +14,6 @@
"app/"
],
"dependencies": {
"@ammarahmed/react-native-sodium": "^1.6.3",
"@notesnook/common": "file:../../packages/common",
"@notesnook/core": "file:../../packages/core",
"@notesnook/crypto": "file:../../packages/crypto",
@@ -28325,7 +28324,7 @@
"@ammarahmed/react-native-eventsource": "1.1.0",
"@ammarahmed/react-native-fingerprint-scanner": "^5.0.0",
"@ammarahmed/react-native-share-extension": "^2.9.0",
"@ammarahmed/react-native-sodium": "^1.6.3",
"@ammarahmed/react-native-sodium": "^1.6.4",
"@bam.tech/react-native-image-resizer": "3.0.11",
"@callstack/repack": "~5.1.2",
"@formatjs/intl-locale": "4.0.0",
@@ -28479,9 +28478,9 @@
}
},
"node_modules/@ammarahmed/react-native-sodium": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-sodium/-/react-native-sodium-1.6.3.tgz",
"integrity": "sha512-TnJqAa/c3MmWouD4q32VR03JI4TgXqUarzBOrRk3tcX91JVsG2UnuPUHK3iRzyLI7/mBiLdMR4XBdzoy+oXmjw==",
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-sodium/-/react-native-sodium-1.6.4.tgz",
"integrity": "sha512-+OIBABV/8IPXVQPRxiub0WW86X/bs14GHnQ1VqtxWGv7phwfyOVzi9c1/LDeRqjCDKBAYHOv2a5PLHZb2RzeGw==",
"license": "ISC"
},
"node_modules/@ampproject/remapping": {

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "3.2.0",
"version": "3.2.2",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -54,7 +54,6 @@
"@types/validator": "^13.12.2",
"diffblazer": "^1.0.1",
"react": "18.2.0",
"react-native": "0.77.2",
"@ammarahmed/react-native-sodium": "^1.6.3"
"react-native": "0.77.2"
}
}
}

View File

@@ -0,0 +1,38 @@
diff --git a/node_modules/react-native-pdf/android/.gradle/5.6.1/fileChanges/last-build.bin b/node_modules/react-native-pdf/android/.gradle/5.6.1/fileChanges/last-build.bin
deleted file mode 100644
index f76dd23..0000000
Binary files a/node_modules/react-native-pdf/android/.gradle/5.6.1/fileChanges/last-build.bin and /dev/null differ
diff --git a/node_modules/react-native-pdf/android/.gradle/5.6.1/fileHashes/fileHashes.lock b/node_modules/react-native-pdf/android/.gradle/5.6.1/fileHashes/fileHashes.lock
deleted file mode 100644
index 52d8866..0000000
Binary files a/node_modules/react-native-pdf/android/.gradle/5.6.1/fileHashes/fileHashes.lock and /dev/null differ
diff --git a/node_modules/react-native-pdf/android/.gradle/5.6.1/gc.properties b/node_modules/react-native-pdf/android/.gradle/5.6.1/gc.properties
deleted file mode 100644
index e69de29..0000000
diff --git a/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock
deleted file mode 100644
index 1508dc9..0000000
Binary files a/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock and /dev/null differ
diff --git a/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/cache.properties b/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/cache.properties
deleted file mode 100644
index f4a32a7..0000000
--- a/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/cache.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-#Sat Oct 12 21:44:37 CST 2019
-gradle.version=5.6.1
diff --git a/node_modules/react-native-pdf/android/.gradle/vcs-1/gc.properties b/node_modules/react-native-pdf/android/.gradle/vcs-1/gc.properties
deleted file mode 100644
index e69de29..0000000
diff --git a/node_modules/react-native-pdf/android/build.gradle b/node_modules/react-native-pdf/android/build.gradle
index 3dfe1dc..70ef9da 100644
--- a/node_modules/react-native-pdf/android/build.gradle
+++ b/node_modules/react-native-pdf/android/build.gradle
@@ -126,6 +126,6 @@ dependencies {
// The repo from zacharee is based on PdfiumAndroidKt, a much newer fork of PdfiumAndroid, with better maintenance and updated native libraries.
implementation 'com.github.zacharee:AndroidPdfViewer:4.0.1'
// Depend on PdfiumAndroidKt directly so this can be updated independently of AndroidPdfViewer as updates are provided.
- implementation 'io.legere:pdfiumandroid:1.0.24'
+ implementation 'io.legere:pdfiumandroid:1.0.32'
implementation 'com.google.code.gson:gson:2.8.5'
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "@notesnook/web",
"description": "Your private note taking space",
"version": "3.2.0",
"version": "3.2.2",
"private": true,
"main": "./src/app.js",
"homepage": "https://notesnook.com/",

View File

@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { store as noteStore } from "../stores/note-store";
import { store as trashStore } from "../stores/trash-store";
import { store as notebookStore } from "../stores/notebook-store";
import { store as attachmentStore } from "../stores/attachment-store";
import { store as reminderStore } from "../stores/reminder-store";
@@ -27,7 +28,11 @@ import { db } from "./db";
import { showToast } from "../utils/toast";
import Vault from "./vault";
import { TaskManager } from "./task-manager";
import { ConfirmDialog, showMultiDeleteConfirmation } from "../dialogs/confirm";
import {
ConfirmDialog,
showMultiDeleteConfirmation,
showMultiPermanentDeleteConfirmation
} from "../dialogs/confirm";
import { strings } from "@notesnook/intl";
async function moveNotesToTrash(ids: string[], confirm = true) {
@@ -77,14 +82,7 @@ async function moveNotebooksToTrash(ids: string[]) {
if (!result) return;
if (result.deleteContainingNotes) {
await Multiselect.moveNotesToTrash(
Array.from(
new Set(
(await Promise.all(ids.map((id) => db.notebooks.notes(id)))).flat()
)
),
false
);
await Multiselect.moveNotesToTrash(await db.notebooks.notes(...ids), false);
}
await TaskManager.startTask({
@@ -182,10 +180,50 @@ async function deleteTags(ids: string[]) {
showToast("success", strings.actions.deleted.tag(ids.length));
}
async function restoreItemsFromTrash(ids: string[]) {
if (!ids.length) return;
await TaskManager.startTask({
type: "status",
id: "restoreItems",
title: strings.inProgressActions.restoring.item(ids.length),
action: async (report) => {
report({
text: strings.inProgressActions.restoring.item(ids.length)
});
await trashStore.restore(...ids);
}
});
showToast("success", strings.actions.restored.item(ids.length));
}
async function deleteItemsFromTrash(ids: string[]) {
if (!ids.length) return;
if (!(await showMultiPermanentDeleteConfirmation(ids.length))) return;
await TaskManager.startTask({
type: "status",
id: "restoreItems",
title: strings.inProgressActions.permanentlyDeleting.item(ids.length),
action: async (report) => {
report({
text: strings.inProgressActions.permanentlyDeleting.item(ids.length)
});
await trashStore.delete(...ids);
}
});
showToast("success", strings.actions.permanentlyDeleted.item(ids.length));
}
export const Multiselect = {
moveRemindersToTrash,
moveNotebooksToTrash,
moveNotesToTrash,
deleteAttachments,
deleteTags
deleteTags,
restoreItemsFromTrash,
deleteItemsFromTrash
};

View File

@@ -561,11 +561,16 @@ export function Factory(Module) {
databases.add(db);
Module._sqlite3_free(zVfs);
check(fname, result);
return db;
};
})();
sqlite3.register_extensions = (function () {
return async function (db) {
Module.ccall("RegisterExtensionFunctions", "void", ["number"], [db]);
Module.ccall("sqlite3Fts5BetterTrigramInit", "void", ["number"], [db]);
Module.ccall("sqlite3Fts5HtmlInit", "void", ["number"], [db]);
check(fname, result);
return db;
};
})();

View File

@@ -724,6 +724,12 @@ export interface SQLiteAPI {
*/
open_v2(zFilename: string, iFlags?: number, zVfs?: string): Promise<number>;
/**
* Register and init extensions.
* @param db database pointer
*/
register_extensions(db: number): Promise<void>;
/**
* Compile an SQL statement
*

View File

@@ -227,6 +227,9 @@ class _SQLiteWorker {
}
async initialize() {
if (typeof this.db === "number")
await this.sqlite.register_extensions(this.db);
self.dispatchEvent(
new MessageEvent("message", {
data: { type: "databaseInitialized", dbName: this.name }

View File

@@ -142,12 +142,12 @@ a._sqlite3_table_column_metadata=function(){return(a._sqlite3_table_column_metad
a._sqlite3_test_control=function(){return(a._sqlite3_test_control=a.asm.Oe).apply(null,arguments)};a._sqlite3_create_filename=function(){return(a._sqlite3_create_filename=a.asm.Pe).apply(null,arguments)};a._sqlite3_free_filename=function(){return(a._sqlite3_free_filename=a.asm.Qe).apply(null,arguments)};a._sqlite3_uri_parameter=function(){return(a._sqlite3_uri_parameter=a.asm.Re).apply(null,arguments)};a._sqlite3_uri_key=function(){return(a._sqlite3_uri_key=a.asm.Se).apply(null,arguments)};
a._sqlite3_uri_boolean=function(){return(a._sqlite3_uri_boolean=a.asm.Te).apply(null,arguments)};a._sqlite3_uri_int64=function(){return(a._sqlite3_uri_int64=a.asm.Ue).apply(null,arguments)};a._sqlite3_filename_database=function(){return(a._sqlite3_filename_database=a.asm.Ve).apply(null,arguments)};a._sqlite3_filename_journal=function(){return(a._sqlite3_filename_journal=a.asm.We).apply(null,arguments)};a._sqlite3_filename_wal=function(){return(a._sqlite3_filename_wal=a.asm.Xe).apply(null,arguments)};
a._sqlite3_db_name=function(){return(a._sqlite3_db_name=a.asm.Ye).apply(null,arguments)};a._sqlite3_db_filename=function(){return(a._sqlite3_db_filename=a.asm.Ze).apply(null,arguments)};a._sqlite3_db_readonly=function(){return(a._sqlite3_db_readonly=a.asm._e).apply(null,arguments)};a._sqlite3_compileoption_used=function(){return(a._sqlite3_compileoption_used=a.asm.$e).apply(null,arguments)};a._sqlite3_compileoption_get=function(){return(a._sqlite3_compileoption_get=a.asm.af).apply(null,arguments)};
a._sqlite3_sourceid=function(){return(a._sqlite3_sourceid=a.asm.bf).apply(null,arguments)};a._sqlite3mc_config=function(){return(a._sqlite3mc_config=a.asm.cf).apply(null,arguments)};a._sqlite3mc_cipher_count=function(){return(a._sqlite3mc_cipher_count=a.asm.df).apply(null,arguments)};a._sqlite3mc_cipher_index=function(){return(a._sqlite3mc_cipher_index=a.asm.ef).apply(null,arguments)};a._sqlite3mc_cipher_name=function(){return(a._sqlite3mc_cipher_name=a.asm.ff).apply(null,arguments)};
a._sqlite3mc_config_cipher=function(){return(a._sqlite3mc_config_cipher=a.asm.gf).apply(null,arguments)};a._sqlite3mc_codec_data=function(){return(a._sqlite3mc_codec_data=a.asm.hf).apply(null,arguments)};a._sqlite3_key=function(){return(a._sqlite3_key=a.asm.jf).apply(null,arguments)};a._sqlite3_key_v2=function(){return(a._sqlite3_key_v2=a.asm.kf).apply(null,arguments)};a._sqlite3_rekey_v2=function(){return(a._sqlite3_rekey_v2=a.asm.lf).apply(null,arguments)};
a._sqlite3_rekey=function(){return(a._sqlite3_rekey=a.asm.mf).apply(null,arguments)};a._sqlite3_regexp_init=function(){return(a._sqlite3_regexp_init=a.asm.nf).apply(null,arguments)};a._sqlite3mc_register_cipher=function(){return(a._sqlite3mc_register_cipher=a.asm.of).apply(null,arguments)};
var kd=a.___errno_location=function(){return(kd=a.___errno_location=a.asm.pf).apply(null,arguments)},Rb=a._malloc=function(){return(Rb=a._malloc=a.asm.qf).apply(null,arguments)},$c=a._free=function(){return($c=a._free=a.asm.rf).apply(null,arguments)};a._RegisterExtensionFunctions=function(){return(a._RegisterExtensionFunctions=a.asm.sf).apply(null,arguments)};a._sqlite3Fts5BetterTrigramInit=function(){return(a._sqlite3Fts5BetterTrigramInit=a.asm.tf).apply(null,arguments)};
a._sqlite3Fts5HtmlInit=function(){return(a._sqlite3Fts5HtmlInit=a.asm.uf).apply(null,arguments)};a._set_authorizer=function(){return(a._set_authorizer=a.asm.vf).apply(null,arguments)};a._create_function=function(){return(a._create_function=a.asm.wf).apply(null,arguments)};a._create_module=function(){return(a._create_module=a.asm.xf).apply(null,arguments)};a._progress_handler=function(){return(a._progress_handler=a.asm.yf).apply(null,arguments)};
a._register_vfs=function(){return(a._register_vfs=a.asm.zf).apply(null,arguments)};a._getSqliteFree=function(){return(a._getSqliteFree=a.asm.Af).apply(null,arguments)};a._main=function(){return(a._main=a.asm.Bf).apply(null,arguments)};
a._sqlite3_sourceid=function(){return(a._sqlite3_sourceid=a.asm.bf).apply(null,arguments)};var kd=a.___errno_location=function(){return(kd=a.___errno_location=a.asm.cf).apply(null,arguments)};a._sqlite3mc_config=function(){return(a._sqlite3mc_config=a.asm.df).apply(null,arguments)};a._sqlite3mc_cipher_count=function(){return(a._sqlite3mc_cipher_count=a.asm.ef).apply(null,arguments)};a._sqlite3mc_cipher_index=function(){return(a._sqlite3mc_cipher_index=a.asm.ff).apply(null,arguments)};
a._sqlite3mc_cipher_name=function(){return(a._sqlite3mc_cipher_name=a.asm.gf).apply(null,arguments)};a._sqlite3mc_config_cipher=function(){return(a._sqlite3mc_config_cipher=a.asm.hf).apply(null,arguments)};a._sqlite3mc_codec_data=function(){return(a._sqlite3mc_codec_data=a.asm.jf).apply(null,arguments)};a._sqlite3_key=function(){return(a._sqlite3_key=a.asm.kf).apply(null,arguments)};a._sqlite3_key_v2=function(){return(a._sqlite3_key_v2=a.asm.lf).apply(null,arguments)};
a._sqlite3_rekey_v2=function(){return(a._sqlite3_rekey_v2=a.asm.mf).apply(null,arguments)};a._sqlite3_rekey=function(){return(a._sqlite3_rekey=a.asm.nf).apply(null,arguments)};a._sqlite3_regexp_init=function(){return(a._sqlite3_regexp_init=a.asm.of).apply(null,arguments)};a._sqlite3mc_register_cipher=function(){return(a._sqlite3mc_register_cipher=a.asm.pf).apply(null,arguments)};
var Rb=a._malloc=function(){return(Rb=a._malloc=a.asm.qf).apply(null,arguments)},$c=a._free=function(){return($c=a._free=a.asm.rf).apply(null,arguments)};a._RegisterExtensionFunctions=function(){return(a._RegisterExtensionFunctions=a.asm.sf).apply(null,arguments)};a._sqlite3Fts5BetterTrigramInit=function(){return(a._sqlite3Fts5BetterTrigramInit=a.asm.tf).apply(null,arguments)};a._sqlite3Fts5HtmlInit=function(){return(a._sqlite3Fts5HtmlInit=a.asm.uf).apply(null,arguments)};
a._set_authorizer=function(){return(a._set_authorizer=a.asm.vf).apply(null,arguments)};a._create_function=function(){return(a._create_function=a.asm.wf).apply(null,arguments)};a._create_module=function(){return(a._create_module=a.asm.xf).apply(null,arguments)};a._progress_handler=function(){return(a._progress_handler=a.asm.yf).apply(null,arguments)};a._register_vfs=function(){return(a._register_vfs=a.asm.zf).apply(null,arguments)};
a._getSqliteFree=function(){return(a._getSqliteFree=a.asm.Af).apply(null,arguments)};a._main=function(){return(a._main=a.asm.Bf).apply(null,arguments)};
var ab=a._emscripten_builtin_memalign=function(){return(ab=a._emscripten_builtin_memalign=a.asm.Cf).apply(null,arguments)},md=a.getTempRet0=function(){return(md=a.getTempRet0=a.asm.Ef).apply(null,arguments)},hd=a.stackSave=function(){return(hd=a.stackSave=a.asm.Ff).apply(null,arguments)},fd=a.stackRestore=function(){return(fd=a.stackRestore=a.asm.Gf).apply(null,arguments)},gd=a.stackAlloc=function(){return(gd=a.stackAlloc=a.asm.Hf).apply(null,arguments)},Yc=a._asyncify_start_unwind=function(){return(Yc=
a._asyncify_start_unwind=a.asm.If).apply(null,arguments)},Mc=a._asyncify_stop_unwind=function(){return(Mc=a._asyncify_stop_unwind=a.asm.Jf).apply(null,arguments)},Wc=a._asyncify_start_rewind=function(){return(Wc=a._asyncify_start_rewind=a.asm.Kf).apply(null,arguments)},Zc=a._asyncify_stop_rewind=function(){return(Zc=a._asyncify_stop_rewind=a.asm.Lf).apply(null,arguments)};a._sqlite3_version=3232;a.UTF8ToString=x;a.stringToUTF8=qa;a.lengthBytesUTF8=ra;a.getTempRet0=md;a.ccall=Z;
a.cwrap=function(b,c,d,e){d=d||[];var f=d.every(g=>"number"===g||"boolean"===g);return"string"!==c&&f&&!e?a["_"+b]:function(){return Z(b,c,d,arguments,e)}};

View File

@@ -137,9 +137,9 @@ a._sqlite3_table_column_metadata=function(){return(a._sqlite3_table_column_metad
a._sqlite3_test_control=function(){return(a._sqlite3_test_control=a.asm.Oe).apply(null,arguments)};a._sqlite3_create_filename=function(){return(a._sqlite3_create_filename=a.asm.Pe).apply(null,arguments)};a._sqlite3_free_filename=function(){return(a._sqlite3_free_filename=a.asm.Qe).apply(null,arguments)};a._sqlite3_uri_parameter=function(){return(a._sqlite3_uri_parameter=a.asm.Re).apply(null,arguments)};a._sqlite3_uri_key=function(){return(a._sqlite3_uri_key=a.asm.Se).apply(null,arguments)};
a._sqlite3_uri_boolean=function(){return(a._sqlite3_uri_boolean=a.asm.Te).apply(null,arguments)};a._sqlite3_uri_int64=function(){return(a._sqlite3_uri_int64=a.asm.Ue).apply(null,arguments)};a._sqlite3_filename_database=function(){return(a._sqlite3_filename_database=a.asm.Ve).apply(null,arguments)};a._sqlite3_filename_journal=function(){return(a._sqlite3_filename_journal=a.asm.We).apply(null,arguments)};a._sqlite3_filename_wal=function(){return(a._sqlite3_filename_wal=a.asm.Xe).apply(null,arguments)};
a._sqlite3_db_name=function(){return(a._sqlite3_db_name=a.asm.Ye).apply(null,arguments)};a._sqlite3_db_filename=function(){return(a._sqlite3_db_filename=a.asm.Ze).apply(null,arguments)};a._sqlite3_db_readonly=function(){return(a._sqlite3_db_readonly=a.asm._e).apply(null,arguments)};a._sqlite3_compileoption_used=function(){return(a._sqlite3_compileoption_used=a.asm.$e).apply(null,arguments)};a._sqlite3_compileoption_get=function(){return(a._sqlite3_compileoption_get=a.asm.af).apply(null,arguments)};
a._sqlite3_sourceid=function(){return(a._sqlite3_sourceid=a.asm.bf).apply(null,arguments)};a._sqlite3mc_config=function(){return(a._sqlite3mc_config=a.asm.cf).apply(null,arguments)};a._sqlite3mc_cipher_count=function(){return(a._sqlite3mc_cipher_count=a.asm.df).apply(null,arguments)};a._sqlite3mc_cipher_index=function(){return(a._sqlite3mc_cipher_index=a.asm.ef).apply(null,arguments)};a._sqlite3mc_cipher_name=function(){return(a._sqlite3mc_cipher_name=a.asm.ff).apply(null,arguments)};
a._sqlite3mc_config_cipher=function(){return(a._sqlite3mc_config_cipher=a.asm.gf).apply(null,arguments)};a._sqlite3mc_codec_data=function(){return(a._sqlite3mc_codec_data=a.asm.hf).apply(null,arguments)};a._sqlite3_key=function(){return(a._sqlite3_key=a.asm.jf).apply(null,arguments)};a._sqlite3_key_v2=function(){return(a._sqlite3_key_v2=a.asm.kf).apply(null,arguments)};a._sqlite3_rekey_v2=function(){return(a._sqlite3_rekey_v2=a.asm.lf).apply(null,arguments)};
a._sqlite3_rekey=function(){return(a._sqlite3_rekey=a.asm.mf).apply(null,arguments)};a._sqlite3_regexp_init=function(){return(a._sqlite3_regexp_init=a.asm.nf).apply(null,arguments)};a._sqlite3mc_register_cipher=function(){return(a._sqlite3mc_register_cipher=a.asm.of).apply(null,arguments)};var Nc=a.___errno_location=function(){return(Nc=a.___errno_location=a.asm.pf).apply(null,arguments)},Pb=a._malloc=function(){return(Pb=a._malloc=a.asm.qf).apply(null,arguments)};
a._sqlite3_sourceid=function(){return(a._sqlite3_sourceid=a.asm.bf).apply(null,arguments)};var Nc=a.___errno_location=function(){return(Nc=a.___errno_location=a.asm.cf).apply(null,arguments)};a._sqlite3mc_config=function(){return(a._sqlite3mc_config=a.asm.df).apply(null,arguments)};a._sqlite3mc_cipher_count=function(){return(a._sqlite3mc_cipher_count=a.asm.ef).apply(null,arguments)};a._sqlite3mc_cipher_index=function(){return(a._sqlite3mc_cipher_index=a.asm.ff).apply(null,arguments)};
a._sqlite3mc_cipher_name=function(){return(a._sqlite3mc_cipher_name=a.asm.gf).apply(null,arguments)};a._sqlite3mc_config_cipher=function(){return(a._sqlite3mc_config_cipher=a.asm.hf).apply(null,arguments)};a._sqlite3mc_codec_data=function(){return(a._sqlite3mc_codec_data=a.asm.jf).apply(null,arguments)};a._sqlite3_key=function(){return(a._sqlite3_key=a.asm.kf).apply(null,arguments)};a._sqlite3_key_v2=function(){return(a._sqlite3_key_v2=a.asm.lf).apply(null,arguments)};
a._sqlite3_rekey_v2=function(){return(a._sqlite3_rekey_v2=a.asm.mf).apply(null,arguments)};a._sqlite3_rekey=function(){return(a._sqlite3_rekey=a.asm.nf).apply(null,arguments)};a._sqlite3_regexp_init=function(){return(a._sqlite3_regexp_init=a.asm.of).apply(null,arguments)};a._sqlite3mc_register_cipher=function(){return(a._sqlite3mc_register_cipher=a.asm.pf).apply(null,arguments)};var Pb=a._malloc=function(){return(Pb=a._malloc=a.asm.qf).apply(null,arguments)};
a._free=function(){return(a._free=a.asm.rf).apply(null,arguments)};a._RegisterExtensionFunctions=function(){return(a._RegisterExtensionFunctions=a.asm.sf).apply(null,arguments)};a._sqlite3Fts5BetterTrigramInit=function(){return(a._sqlite3Fts5BetterTrigramInit=a.asm.tf).apply(null,arguments)};a._sqlite3Fts5HtmlInit=function(){return(a._sqlite3Fts5HtmlInit=a.asm.uf).apply(null,arguments)};a._set_authorizer=function(){return(a._set_authorizer=a.asm.vf).apply(null,arguments)};
a._create_function=function(){return(a._create_function=a.asm.wf).apply(null,arguments)};a._create_module=function(){return(a._create_module=a.asm.xf).apply(null,arguments)};a._progress_handler=function(){return(a._progress_handler=a.asm.yf).apply(null,arguments)};a._register_vfs=function(){return(a._register_vfs=a.asm.zf).apply(null,arguments)};a._getSqliteFree=function(){return(a._getSqliteFree=a.asm.Af).apply(null,arguments)};a._main=function(){return(a._main=a.asm.Bf).apply(null,arguments)};
var Za=a._emscripten_builtin_memalign=function(){return(Za=a._emscripten_builtin_memalign=a.asm.Cf).apply(null,arguments)},Pc=a.getTempRet0=function(){return(Pc=a.getTempRet0=a.asm.Ef).apply(null,arguments)},Kc=a.stackSave=function(){return(Kc=a.stackSave=a.asm.Ff).apply(null,arguments)},Lc=a.stackRestore=function(){return(Lc=a.stackRestore=a.asm.Gf).apply(null,arguments)},Jc=a.stackAlloc=function(){return(Jc=a.stackAlloc=a.asm.Hf).apply(null,arguments)};a._sqlite3_version=3232;a.UTF8ToString=v;

View File

@@ -19,17 +19,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import ListItem from "../list-item";
import { Restore, DeleteForver } from "../icons";
import { store } from "../../stores/trash-store";
import { Flex, Text } from "@theme-ui/components";
import TimeAgo from "../time-ago";
import { pluralize, toTitleCase } from "@notesnook/common";
import { showToast } from "../../utils/toast";
import { toTitleCase } from "@notesnook/common";
import { MenuItem } from "@notesnook/ui";
import { TrashItem as TrashItemType } from "@notesnook/core";
import { useEditorStore } from "../../stores/editor-store";
import { showMultiPermanentDeleteConfirmation } from "../../dialogs/confirm";
import { useStore as useSelectionStore } from "../../stores/selection-store";
import { strings } from "@notesnook/intl";
import { Multiselect } from "../../common/multi-select";
type TrashItemProps = { item: TrashItemType; date: number };
function TrashItem(props: TrashItemProps) {
@@ -44,7 +42,9 @@ function TrashItem(props: TrashItemProps) {
body={item.itemType === "note" ? item.headline : item.description}
onKeyPress={async (e) => {
if (e.key === "Delete") {
await deleteTrash(useSelectionStore.getState().selectedItems);
await Multiselect.deleteItemsFromTrash(
useSelectionStore.getState().selectedItems
);
}
}}
footer={
@@ -81,9 +81,7 @@ export const trashMenuItems: (
key: "restore",
title: strings.restore(),
icon: Restore.path,
onClick: async () => {
await store.restore(...ids);
},
onClick: () => Multiselect.restoreItemsFromTrash(ids),
multiSelect: true
},
{
@@ -92,16 +90,8 @@ export const trashMenuItems: (
title: strings.delete(),
icon: DeleteForver.path,
variant: "dangerous",
onClick: async () => {
await deleteTrash(ids);
},
onClick: () => Multiselect.deleteItemsFromTrash(ids),
multiSelect: true
}
];
};
export async function deleteTrash(ids: string[]) {
if (!(await showMultiPermanentDeleteConfirmation(ids.length))) return;
await store.delete(...ids);
showToast("success", `${pluralize(ids.length, "item")} permanently deleted`);
}

View File

@@ -188,3 +188,7 @@ pre {
.docgen-content {
box-shadow: none;
}
nav details[open] {
max-height: max-content;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -4,24 +4,24 @@ title: Importing notes
# Import notes from any notes app
Notesnook importer is your one stop location to import all your notes from any notes app. We have added support for most of the popular note apps and common export formats such as markdown, html and text files.
Notesnook supports importing from most of the popular note apps and common export formats such as markdown, html and text files.
## Try it out
You can try out the importer by going to [https://importer.notesnook.com](https://importer.notesnook.com) to import your notes from any notes app.
You can try out the importer by opening the web or desktop app and going to `Settings > Notesnook Importer`.
## Supported note apps and formats
1. Plain text files
2. HTML files
3. Markdown (.md) files
4. OneNote
5. EverNote
6. Standard Notes
7. Simplenote
8. Google Keep
9. Joplin
10. Zoho Notebook
4. EverNote
5. Simplenote
6. Google Keep
7. Joplin
8. Zoho Notebook
9. Obsidian
10. Skiff Pages
**Don't see your notes app?** No worries, create an issue on [Github](https://github.com/streetwriters/notesnook/issues)

View File

@@ -23,19 +23,16 @@ The following steps will help you quickly import your notes from Evernote into N
Once you have all the .ENEX files containing your Evernote notes, its time to import them into Notesnook.
1. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) and select Evernote from the list of apps.
1. Open the Notesnook app (web or desktop)
2. Go to `Settings > Notesnook Importer` and select Evernote from the list of apps.
![](/static/evernote-importer/4.png)
2. Drop (or select) the `.enex` files you exported earlier from Evernote, and click the "Start processing" button.
3. Drop (or select) the `.enex` files you exported earlier from Evernote, and click the "Start processing" button.
![](/static/evernote-importer/5.png)
3. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
4. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
5. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
4. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
## Supported formats
Notesnook Importer is one of the most robust Evernote importers around supporting almost 100% of Evernote formats. Here's a list of everything that can (or can't be) imported into Notesnook:
Notesnook Importer is one of the most robust Evernote importers, supporting almost 100% of Evernote formats. Here's a list of everything that can (or can't be) imported into Notesnook:
- [x] Attachments
- [x] Web clips (full page, screenshot, bookmark, article, & simplified article are all supported)
@@ -44,6 +41,6 @@ Notesnook Importer is one of the most robust Evernote importers around supportin
- [x] Images
- [x] Rich text (bold, italic, lists etc.)
- [ ] Reminders
- [ ] Internal note links
- [x] Internal note links
- [x] Notebooks
- [x] Tags

View File

@@ -20,15 +20,12 @@ The following steps will help you quickly import your notes from Google Keep int
Once you have the Google Takeout containing your Google Keep notes, its time to import them into Notesnook.
1. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) and select `Google Keep` from list of apps.
1. Open the Notesnook app (web or desktop)
2. Go to `Settings > Notesnook Importer` and select `Google Keep` from list of apps.
![](/static/google-keep-importer/4.png)
2. Drop the .zip backup file(s) you exported earlier from Google Takeout in the box or click anywhere to open system file picker to select the backup.
3. Drop the .zip backup file(s) you exported earlier from Google Takeout in the box or click anywhere to open system file picker to select the backup.
![](/static/google-keep-importer/5.png)
3. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
4. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
5. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
4. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
## Supported formats

View File

@@ -4,13 +4,9 @@ title: HTML files
# How do I import notes from HTML files?
1. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) on your PC/Laptop.
2. From the list of note apps to import from, select "Text".
1. Open the Notesnook app (web or desktop)
2. Go to `Settings > Notesnook Importer` and select `HTML` from list of apps.
![](/static/html/1.png)
3. Drop your .html files, or click anywhere inside the box to browse and select your .html files. You can also provide a .zip file containing all your .html files. Then click "Start processing".
![](/static/html/2.png)
4. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
5. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
6. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
4. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).

View File

@@ -9,15 +9,12 @@ The following steps will help you import your notes from Joplin easily.
1. Open the Joplin Desktop app.
2. Click on `File > Export All -> JEX - Joplin Export File` and save the .JEX file at your desired location.
![](/static/joplin-importer/1.png)
3. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) and select `Joplin` from list of apps.
3. Open the Notesnook app (web or desktop)
4. Go to `Settings > Notesnook Importer` and select `Joplin` from list of apps.
![](/static/joplin-importer/2.png)
4. Drop (or select) the .jex backup file you exported earlier from Joplin:
5. Drop (or select) the .jex backup file you exported earlier from Joplin:
![](/static/joplin-importer/3.png)
5. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
6. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
7. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
6. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
## Supported formats

View File

@@ -4,16 +4,12 @@ title: Markdown files
# How do I import notes from Markdown files?
1. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) on your PC/Laptop.
2. From the list of note apps to import from, select "Markdown".
1. Open the Notesnook app (web or desktop)
2. Go to `Settings > Notesnook Importer` and select "Markdown".
![](/static/markdown-importer/1.png)
3. Drop your .md files, or click anywhere inside the box to browse and select your .md files. You can also provide a .zip file containing all your .md files. Then click "Start processing".
![](/static/markdown-importer/2.png)
4. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
5. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
6. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
4. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
## Supported formats

View File

@@ -4,16 +4,12 @@ title: Obsidian
# How do I import notes from Obsidian?
1. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) on your PC/Laptop.
2. From the list of note apps to import from, select "Obsidian".
![](/static/markdown-importer/1.png)
1. Open the Notesnook app (web or desktop)
2. Go to `Settings > Notesnook Importer` and select "Obsidian".
![](/static/obsidian-importer/1.png)
3. Drop your .md files from your Obsidian Vault, or click anywhere inside the box to browse and select your .md files. You can also provide a .zip file containing all your Obsidian .md files. Then click "Start processing".
![](/static/markdown-importer/2.png)
4. Once the Importer finishes processing your files, download the .zip file.
<img src="/static/import-ready.png" alt="Download importer .zip file"/>
5. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
<img src="/static/import-zip-app.png" alt="Import .zip in Notesnook app"/>
6. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
![](/static/obsidian-importer/2.png)
4. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
## Supported formats

View File

@@ -4,13 +4,9 @@ title: Plaintext files
# How do I import notes from Plaintext files?
1. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) on your PC/Laptop.
2. From the list of note apps to import from, select "Text".
1. Open the Notesnook app (web or desktop)
2. Go to `Settings > Notesnook Importer` and select "Text".
![](/static/plaintext-importer/1.png)
3. Drop your .txt files, or click anywhere inside the box to browse and select your .txt files. You can also provide a .zip file containing all your .txt files. Then click "Start processing".
![](/static/plaintext-importer/2.png)
4. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
5. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
6. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
4. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).

View File

@@ -12,15 +12,12 @@ The following steps will help you import your notes from Simplenote easily.
3. Go to `Tools` tab in Settings
![](/static/simplenote-importer/2.png)
4. Click on `Export notes` to download your notes as a .zip file.
5. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) and select `Simplenote` from list of apps.
5. Open the Notesnook app (web or desktop)
6. Go to `Settings > Notesnook Importer` and select `Simplenote` from list of apps.
![](/static/simplenote-importer/3.png)
6. Drop the .zip backup file you exported earlier from Simplenote in the box or click anywhere to open system file picker to select the backup and click "Start processing".
7. Drop the .zip backup file you exported earlier from Simplenote in the box or click anywhere to open system file picker to select the backup and click "Start processing".
![](/static/simplenote-importer/4.png)
7. Once importer completes processing, download the .zip file.
![](/static/simplenote-importer/5.png)
8. After you have downloaded the `.zip` file, go to Notesnook app > Settings > Notesnook Importer and click on "Import from ZIP file" & select the .zip you downloaded earlier.
![](/static/import-zip-app.png)
9. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, [report it on github](https://github.com/streetwriters/notesnook).
8. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, [report it on github](https://github.com/streetwriters/notesnook).
## Supported formats

View File

@@ -19,16 +19,12 @@ The following steps will help you quickly import your notes from Skiff Pages int
Once you have the `Skiff.zip` file containing your Skiff pages, its time to import them into Notesnook.
1. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) on your PC/Laptop.
2. From the list of note apps to import from, select "Markdown".
1. Open the Notesnook app (web or desktop)
2. Go to `Settings > Notesnook Importer` and select "Skiff Pages".
![](/static/skiff-importer/3.png)
3. Drop your Skiff.zip file, or click anywhere inside the box to browse and select your Skiff.zip file. Then click "Start processing".
![](/static/skiff-importer/4.png)
4. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
5. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
6. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
4. Once the importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
## Supported formats

View File

@@ -12,12 +12,9 @@ The following steps will help you import your notes from Standard notes easily.
3. Scroll down to `Data backups` section and choose backup type `Decrypted`.
![](/static/standard-notes-importer/2.png)
4. Go to `Backups` from the Sidebar & select "Data backup" to download backup .zip file.
5. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) and select `Standard Notes` from list of apps.
5. Open the Notesnook app (web or desktop)
6. Go to `Settings > Notesnook Importer` and select `Standard Notes` from list of apps.
![](/static/standard-notes-importer/3.png)
6. Drop the .zip backup file you exported earlier from Standard Notes in the box or click anywhere to open system file picker to select the backup.
7. Drop the .zip backup file you exported earlier from Standard Notes in the box or click anywhere to open system file picker to select the backup.
![](/static/standard-notes-importer/4.png)
7. Once importer completes processing, download the .zip file.
![](/static/standard-notes-importer/5.png)
8. After you have downloaded the `.zip` file, go to Notesnook app > Settings > Notesnook Importer and click on "Import from ZIP file" & select the .zip you downloaded earlier.
![](/static/import-zip-app.png)
9. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, [report it on github](https://github.com/streetwriters/notesnook).
8. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, [report it on github](https://github.com/streetwriters/notesnook).

View File

@@ -14,12 +14,9 @@ The following steps will help you import your notes from Zoho notebook easily.
![](/static/zoho-importer/3.png)
4. Wait while your notes are exported. Once export completes, download exported .zip file
![](/static/zoho-importer/4.png)
5. Go to [https://importer.notesnook.com/](https://importer.notesnook.com/) and select `Zoho notebook` from the list of apps.
5. Open the Notesnook app (web or desktop)
6. Go to `Settings > Notesnook Importer` and select `Zoho notebook` from the list of apps.
![](/static/zoho-importer/5.png)
6. Drop the .zip backup file(s) you exported earlier in the box or click anywhere to open system file picker to select the backup then click start processing.
7. Drop the .zip backup file(s) you exported earlier in the box or click anywhere to open system file picker to select the backup then click start processing.
![](/static/zoho-importer/6.png)
7. Once the Importer finishes processing your files, download the .zip file.
![](/static/import-ready.png)
8. After you have downloaded the `.zip` file, [go to the Notesnook Web App](https://app.notesnook.com/) > Settings > Notesnook Importer. Select the .zip you downloaded earlier and click "Start import" button.
![](/static/import-zip-app.png)
9. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).
8. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, feel free to [report them on GitHub](https://github.com/streetwriters/notesnook-importer).

View File

@@ -34,9 +34,7 @@ navigation:
- path: importing-notes/import-notes-from-evernote.md
- path: importing-notes/import-notes-from-googlekeep.md
- path: importing-notes/import-notes-from-joplin.md
- path: importing-notes/import-notes-from-onenote.md
- path: importing-notes/import-notes-from-simplenote.md
- path: importing-notes/import-notes-from-standardnotes.md
- path: importing-notes/import-notes-from-zoho-notebook.md
- path: importing-notes/import-notes-from-skiff-pages.md
- path: importing-notes/import-notes-from-obsidian.md

View File

@@ -1,5 +1,5 @@
- You can now share multiple files to Notesnook
- Fix file and image sharing not working
- Many other bug fixes and small improvements
- New and improved search with highlighting
- Fixed app crashing on android 16 devices
- Many bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -0,0 +1,5 @@
- New and improved search with highlighting
- Fixed app crashing on android 16 devices
- Many bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -48,11 +48,12 @@ export async function resolveItems(ids: string[], items: Item[]) {
const { type } = items[0];
if (type === "note") return resolveNotes(ids);
else if (type === "notebook") {
return Promise.all(ids.map((id) => db.notebooks.totalNotes(id)));
return await db.notebooks.totalNotes(...ids);
} else if (type === "tag") {
return Promise.all(
ids.map((id) => db.relations.from({ id, type: "tag" }, "note").count())
);
const relations = await db.relations
.from({ type: "tag", ids }, "note")
.get();
return ids.map((id) => relations.filter((r) => r.fromId === id).length);
}
return [];
}

View File

@@ -99,8 +99,8 @@ test("delete note", () =>
await db.notes.moveToTrash(id);
expect(await db.notes.note(id)).toBeUndefined();
expect(await db.notebooks.totalNotes(notebookId)).toBe(0);
expect(await db.notebooks.totalNotes(subNotebookId)).toBe(0);
expect(await db.notebooks.totalNotes(notebookId)).toStrictEqual([0]);
expect(await db.notebooks.totalNotes(subNotebookId)).toStrictEqual([0]);
}));
test("get all notes", () =>
@@ -323,8 +323,8 @@ test("add note to subnotebook", () =>
.from({ type: "notebook", id: notebookId }, "notebook")
.count()
).toBe(1);
expect(await db.notebooks.totalNotes(subNotebookId)).toBe(1);
expect(await db.notebooks.totalNotes(notebookId)).toBe(1);
expect(await db.notebooks.totalNotes(subNotebookId)).toStrictEqual([1]);
expect(await db.notebooks.totalNotes(notebookId)).toStrictEqual([1]);
}));
test("duplicate note to topic should not be added", () =>
@@ -333,7 +333,7 @@ test("duplicate note to topic should not be added", () =>
notebookTitle: "Hello",
subNotebookTitle: "Home"
});
expect(await db.notebooks.totalNotes(subNotebookId)).toBe(1);
expect(await db.notebooks.totalNotes(subNotebookId)).toStrictEqual([1]);
}));
test("add the same note to 2 notebooks", () =>

View File

@@ -22,8 +22,8 @@ import Database from "../api/index.js";
import { Notebook, TrashOrItem, isTrashItem } from "../types.js";
import { ICollection } from "./collection.js";
import { SQLCollection } from "../database/sql-collection.js";
import { isFalse } from "../database/index.js";
import { sql } from "@streetwriters/kysely";
import { DatabaseSchema, isFalse } from "../database/index.js";
import { Kysely, sql, Transaction } from "@streetwriters/kysely";
import { deleteItems } from "../utils/array.js";
import {
CHECK_IDS,
@@ -125,65 +125,45 @@ export class Notebooks implements ICollection {
await this.collection.update(ids, { pinned: state });
}
async totalNotes(id: string) {
const result = await this.db
.sql()
.withRecursive(`subNotebooks(id)`, (eb) =>
eb
.selectNoFrom((eb) => eb.val(id).as("id"))
.unionAll((eb) =>
eb
.selectFrom(["relations", "subNotebooks"])
.select("relations.toId as id")
.where("toType", "==", "notebook")
.where("fromType", "==", "notebook")
.whereRef("fromId", "==", "subNotebooks.id")
.where("toId", "not in", this.db.trash.cache.notebooks)
.$narrowType<{ id: string }>()
)
)
async totalNotes(...ids: string[]) {
const result = await withSubNotebooks(
this.db.sql(),
ids,
this.db.trash.cache.notebooks
)
.selectFrom("relations")
.innerJoin("subNotebooks", "subNotebooks.id", "relations.fromId")
.where("toType", "==", "note")
.where("fromType", "==", "notebook")
.where("fromId", "in", (eb) =>
eb.selectFrom("subNotebooks").select("subNotebooks.id")
)
.where("toId", "not in", this.db.trash.cache.notes)
.select((eb) => eb.fn.count<number>("relations.toId").as("totalNotes"))
.executeTakeFirst();
if (!result) return 0;
return result.totalNotes;
}
async notes(id: string) {
const result = await this.db
.sql()
.withRecursive(`subNotebooks(id)`, (eb) =>
eb
.selectNoFrom((eb) => eb.val(id).as("id"))
.unionAll((eb) =>
eb
.selectFrom(["relations", "subNotebooks"])
.select("relations.toId as id")
.where("toType", "==", "notebook")
.where("fromType", "==", "notebook")
.whereRef("fromId", "==", "subNotebooks.id")
.where("toId", "not in", this.db.trash.cache.notebooks)
.$narrowType<{ id: string }>()
)
)
.selectFrom("relations")
.where("toType", "==", "note")
.where("fromType", "==", "notebook")
.where("fromId", "in", (eb) =>
eb.selectFrom("subNotebooks").select("subNotebooks.id")
)
.where("toId", "not in", this.db.trash.cache.notes)
.select("relations.toId as id")
.$narrowType<{ id: string }>()
.select((eb) => [
"subNotebooks.rootId as id",
eb.fn.count<number>("relations.toId").distinct().as("totalNotes")
])
.groupBy("subNotebooks.rootId")
.execute();
return ids.map((id) => {
const item = result.find((i) => i.id === id);
return item ? item.totalNotes : 0;
});
}
async notes(...ids: string[]) {
const result = await withSubNotebooks(
this.db.sql(),
ids,
this.db.trash.cache.notebooks
)
.selectFrom("relations")
.innerJoin("subNotebooks", "subNotebooks.id", "relations.fromId")
.where("toType", "==", "note")
.where("fromType", "==", "notebook")
.where("toId", "not in", this.db.trash.cache.notes)
.select("relations.toId as id")
.distinct()
.$narrowType<{ id: string }>()
.execute();
return result.map((i) => i.id);
}
@@ -254,26 +234,7 @@ export class Notebooks implements ICollection {
async moveToTrash(...ids: string[]) {
await this.db.transaction(async (tr) => {
const query = tr
.withRecursive(`subNotebooks(id)`, (eb) =>
eb
.selectFrom(() =>
sql<{ id: string }>`(VALUES ${sql.join(
ids.map((id) => sql.raw(`('${id}')`))
)})`.as("roots")
)
.selectAll()
.unionAll((eb) =>
eb
.selectFrom(["relations", "subNotebooks"])
.select("relations.toId as id")
.where("toType", "==", "notebook")
.where("fromType", "==", "notebook")
.whereRef("fromId", "==", "subNotebooks.id")
.where("toId", "not in", this.db.trash.cache.notebooks)
.$narrowType<{ id: string }>()
)
)
const query = withSubNotebooks(tr, ids, this.db.trash.cache.notebooks)
.selectFrom("subNotebooks")
.select("id");
@@ -305,3 +266,45 @@ export class Notebooks implements ICollection {
return relation[0]?.fromId;
}
}
export function withSubNotebooks(
db: Kysely<DatabaseSchema> | Transaction<DatabaseSchema>,
ids: string[],
excluded: string[]
) {
return db.withRecursive(`subNotebooks(id, path, rootId)`, (eb) =>
eb
.selectFrom(() =>
sql<{
id: string;
path: string;
rootId: string;
}>`(VALUES ${sql.join(
ids.map((id) => sql.raw(`('${id}', '${id}', '${id}')`))
)})`.as("roots")
)
.selectAll()
.unionAll((eb) =>
eb
.selectFrom(["relations", "subNotebooks"])
.select([
"relations.toId as id",
// Concatenate parent path with current id
sql<string>`subNotebooks.path || '/' || relations.toId`.as("path"),
// Preserve original root
"subNotebooks.rootId as rootId"
])
.where("toType", "==", "notebook")
.where("fromType", "==", "notebook")
.whereRef("fromId", "==", "subNotebooks.id")
.where("toId", "not in", excluded)
// Use path to prevent cycles
.where(
"subNotebooks.path",
"not like",
sql`'%' || relations.toId || '%'`
)
.$narrowType<{ id: string; path: string; rootId: string }>()
)
);
}

View File

@@ -34,6 +34,7 @@ import {
checkIsUserPremium,
FREE_NOTEBOOKS_LIMIT
} from "../common.js";
import { withSubNotebooks } from "./notebooks.js";
export default class Trash {
collections = ["notes", "notebooks"] as const;
@@ -324,27 +325,11 @@ export default class Trash {
}
private async subNotebooks(notebookIds: string[]) {
const ids = await this.db
.sql()
.withRecursive(`subNotebooks(id)`, (eb) =>
eb
.selectFrom((eb) =>
sql<{ id: string }>`(VALUES ${sql.join(
notebookIds.map((id) => eb.parens(sql`${id}`))
)})`.as("notebookIds")
)
.selectAll()
.unionAll((eb) =>
eb
.selectFrom(["relations", "subNotebooks"])
.select("relations.toId as id")
.where("toType", "==", "notebook")
.where("fromType", "==", "notebook")
.whereRef("fromId", "==", "subNotebooks.id")
.where("toId", "not in", this.userDeletedCache.notebooks)
.$narrowType<{ id: string }>()
)
)
const ids = await withSubNotebooks(
this.db.sql(),
notebookIds,
this.userDeletedCache.notebooks
)
.selectFrom("subNotebooks")
.select("id")
.where("id", "not in", notebookIds)

View File

@@ -44,10 +44,12 @@ export default function TiptapEditorWrapper(props: {
display: props.settings.noToolbar ? "none" : "flex",
overflowY: "hidden",
minHeight: "50px",
position: "absolute",
bottom: 0,
left: 0,
right: 0
...(globalThis.__PLATFORM__ === "ios" && {
position: "absolute",
bottom: 0,
left: 0,
right: 0
})
}}
editor={editor}
location="bottom"

View File

@@ -1,17 +1,11 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-06-05 11:43+0500\n"
"POT-Creation-Date: 2025-06-21 11:05+0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: en\n"
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"Plural-Forms: \n"
#: src/strings.ts:2410
msgid " \"Notebook > Notes\""
@@ -157,7 +151,7 @@ msgstr "{count, plural, one {Attachment deleted} other {# attachments deleted}}"
msgid "{count, plural, one {Attachment downloaded at {path}} other {#/{total} attachments downloaded as a zip file at {path}}}"
msgstr "{count, plural, one {Attachment downloaded at {path}} other {#/{total} attachments downloaded as a zip file at {path}}}"
#: generated/actions.ts:210
#: generated/actions.ts:216
msgid "{count, plural, one {Color renamed} other {# colors renamed}}"
msgstr "{count, plural, one {Color renamed} other {# colors renamed}}"
@@ -250,7 +244,7 @@ msgstr "{count, plural, one {Item could not be published} other {# items could n
msgid "{count, plural, one {Item could not be unpublished} other {# items could not be unpublished}}"
msgstr "{count, plural, one {Item could not be unpublished} other {# items could not be unpublished}}"
#: generated/actions.ts:202
#: generated/actions.ts:208
msgid "{count, plural, one {Item created} other {# items created}}"
msgstr "{count, plural, one {Item created} other {# items created}}"
@@ -258,7 +252,7 @@ msgstr "{count, plural, one {Item created} other {# items created}}"
msgid "{count, plural, one {Item deleted} other {# items deleted}}"
msgstr "{count, plural, one {Item deleted} other {# items deleted}}"
#: generated/actions.ts:173
#: generated/actions.ts:179
msgid "{count, plural, one {Item edited} other {# items edited}}"
msgstr "{count, plural, one {Item edited} other {# items edited}}"
@@ -266,24 +260,25 @@ msgstr "{count, plural, one {Item edited} other {# items edited}}"
msgid "{count, plural, one {Item moved to trash} other {# items moved to trash}}"
msgstr "{count, plural, one {Item moved to trash} other {# items moved to trash}}"
#: generated/actions.ts:87
#: generated/actions.ts:80
#: generated/actions.ts:93
msgid "{count, plural, one {Item permanently deleted} other {# items permanently deleted}}"
msgstr "{count, plural, one {Item permanently deleted} other {# items permanently deleted}}"
#: generated/actions.ts:104
#: generated/actions.ts:110
msgid "{count, plural, one {Item published} other {# items published}}"
msgstr "{count, plural, one {Item published} other {# items published}}"
#: generated/actions.ts:219
#: generated/actions.ts:225
msgid "{count, plural, one {Item renamed} other {# items renamed}}"
msgstr "{count, plural, one {Item renamed} other {# items renamed}}"
#: generated/actions.ts:137
#: generated/actions.ts:150
#: generated/actions.ts:143
#: generated/actions.ts:156
msgid "{count, plural, one {Item restored} other {# items restored}}"
msgstr "{count, plural, one {Item restored} other {# items restored}}"
#: generated/actions.ts:121
#: generated/actions.ts:127
msgid "{count, plural, one {Item unpublished} other {# items unpublished}}"
msgstr "{count, plural, one {Item unpublished} other {# items unpublished}}"
@@ -323,15 +318,15 @@ msgstr "{count, plural, one {Note moved to trash} other {# notes moved to trash}
msgid "{count, plural, one {Note permanently deleted} other {# notes permanently deleted}}"
msgstr "{count, plural, one {Note permanently deleted} other {# notes permanently deleted}}"
#: generated/actions.ts:95
#: generated/actions.ts:101
msgid "{count, plural, one {Note published} other {# notes published}}"
msgstr "{count, plural, one {Note published} other {# notes published}}"
#: generated/actions.ts:129
#: generated/actions.ts:135
msgid "{count, plural, one {Note restored} other {# notes restored}}"
msgstr "{count, plural, one {Note restored} other {# notes restored}}"
#: generated/actions.ts:112
#: generated/actions.ts:118
msgid "{count, plural, one {Note unpublished} other {# notes unpublished}}"
msgstr "{count, plural, one {Note unpublished} other {# notes unpublished}}"
@@ -339,7 +334,7 @@ msgstr "{count, plural, one {Note unpublished} other {# notes unpublished}}"
msgid "{count, plural, one {Note will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?} other {# notes will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?}}"
msgstr "{count, plural, one {Note will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?} other {# notes will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?}}"
#: generated/actions.ts:181
#: generated/actions.ts:187
msgid "{count, plural, one {Notebook created} other {# notebooks created}}"
msgstr "{count, plural, one {Notebook created} other {# notebooks created}}"
@@ -347,7 +342,7 @@ msgstr "{count, plural, one {Notebook created} other {# notebooks created}}"
msgid "{count, plural, one {Notebook deleted} other {# notebooks deleted}}"
msgstr "{count, plural, one {Notebook deleted} other {# notebooks deleted}}"
#: generated/actions.ts:162
#: generated/actions.ts:168
msgid "{count, plural, one {Notebook edited} other {# notebooks edited}}"
msgstr "{count, plural, one {Notebook edited} other {# notebooks edited}}"
@@ -359,7 +354,7 @@ msgstr "{count, plural, one {Notebook moved to trash} other {# notebooks moved t
msgid "{count, plural, one {Notebook permanently deleted} other {# notebooks permanently deleted}}"
msgstr "{count, plural, one {Notebook permanently deleted} other {# notebooks permanently deleted}}"
#: generated/actions.ts:133
#: generated/actions.ts:139
msgid "{count, plural, one {Notebook restored} other {# notebooks restored}}"
msgstr "{count, plural, one {Notebook restored} other {# notebooks restored}}"
@@ -372,6 +367,11 @@ msgstr "{count, plural, one {Permanently delete attachment} other {Permanently d
msgid "{count, plural, one {Permanently delete item} other {Permanently delete # items}}"
msgstr "{count, plural, one {Permanently delete item} other {Permanently delete # items}}"
#: generated/in-progress-actions.ts:66
#: generated/in-progress-actions.ts:75
msgid "{count, plural, one {permanently deleting item...} other {permanently deleting # items...}}"
msgstr "{count, plural, one {permanently deleting item...} other {permanently deleting # items...}}"
#: generated/do-actions.ts:99
msgid "{count, plural, one {Pin item} other {Pin # items}}"
msgstr "{count, plural, one {Pin item} other {Pin # items}}"
@@ -444,11 +444,16 @@ msgstr "{count, plural, one {Restore note} other {Restore # notes}}"
msgid "{count, plural, one {Restore notebook} other {Restore # notebooks}}"
msgstr "{count, plural, one {Restore notebook} other {Restore # notebooks}}"
#: generated/actions.ts:189
#: generated/in-progress-actions.ts:49
#: generated/in-progress-actions.ts:58
msgid "{count, plural, one {Restoring item...} other {Restoring # items...}}"
msgstr "{count, plural, one {Restoring item...} other {Restoring # items...}}"
#: generated/actions.ts:195
msgid "{count, plural, one {Shortcut created} other {# shortcuts created}}"
msgstr "{count, plural, one {Shortcut created} other {# shortcuts created}}"
#: generated/actions.ts:185
#: generated/actions.ts:191
msgid "{count, plural, one {Tag created} other {# tags created}}"
msgstr "{count, plural, one {Tag created} other {# tags created}}"
@@ -456,7 +461,7 @@ msgstr "{count, plural, one {Tag created} other {# tags created}}"
msgid "{count, plural, one {Tag deleted} other {# tags deleted}}"
msgstr "{count, plural, one {Tag deleted} other {# tags deleted}}"
#: generated/actions.ts:158
#: generated/actions.ts:164
msgid "{count, plural, one {Tag edited} other {# tags edited}}"
msgstr "{count, plural, one {Tag edited} other {# tags edited}}"
@@ -613,8 +618,8 @@ msgid "A to Z"
msgstr "A to Z"
#: src/strings.ts:447
msgid "A vault stores your notes in a encrypted storage."
msgstr "A vault stores your notes in a encrypted storage."
msgid "A vault stores your notes in an encrypted storage."
msgstr "A vault stores your notes in an encrypted storage."
#: src/strings.ts:661
msgid "Abc"

View File

@@ -1,17 +1,11 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-06-05 11:43+0500\n"
"POT-Creation-Date: 2025-06-21 11:05+0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: pseudo-LOCALE\n"
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"Plural-Forms: \n"
#: src/strings.ts:2410
msgid " \"Notebook > Notes\""
@@ -157,7 +151,7 @@ msgstr ""
msgid "{count, plural, one {Attachment downloaded at {path}} other {#/{total} attachments downloaded as a zip file at {path}}}"
msgstr ""
#: generated/actions.ts:210
#: generated/actions.ts:216
msgid "{count, plural, one {Color renamed} other {# colors renamed}}"
msgstr ""
@@ -250,7 +244,7 @@ msgstr ""
msgid "{count, plural, one {Item could not be unpublished} other {# items could not be unpublished}}"
msgstr ""
#: generated/actions.ts:202
#: generated/actions.ts:208
msgid "{count, plural, one {Item created} other {# items created}}"
msgstr ""
@@ -258,7 +252,7 @@ msgstr ""
msgid "{count, plural, one {Item deleted} other {# items deleted}}"
msgstr ""
#: generated/actions.ts:173
#: generated/actions.ts:179
msgid "{count, plural, one {Item edited} other {# items edited}}"
msgstr ""
@@ -266,24 +260,25 @@ msgstr ""
msgid "{count, plural, one {Item moved to trash} other {# items moved to trash}}"
msgstr ""
#: generated/actions.ts:87
#: generated/actions.ts:80
#: generated/actions.ts:93
msgid "{count, plural, one {Item permanently deleted} other {# items permanently deleted}}"
msgstr ""
#: generated/actions.ts:104
#: generated/actions.ts:110
msgid "{count, plural, one {Item published} other {# items published}}"
msgstr ""
#: generated/actions.ts:219
#: generated/actions.ts:225
msgid "{count, plural, one {Item renamed} other {# items renamed}}"
msgstr ""
#: generated/actions.ts:137
#: generated/actions.ts:150
#: generated/actions.ts:143
#: generated/actions.ts:156
msgid "{count, plural, one {Item restored} other {# items restored}}"
msgstr ""
#: generated/actions.ts:121
#: generated/actions.ts:127
msgid "{count, plural, one {Item unpublished} other {# items unpublished}}"
msgstr ""
@@ -323,15 +318,15 @@ msgstr ""
msgid "{count, plural, one {Note permanently deleted} other {# notes permanently deleted}}"
msgstr ""
#: generated/actions.ts:95
#: generated/actions.ts:101
msgid "{count, plural, one {Note published} other {# notes published}}"
msgstr ""
#: generated/actions.ts:129
#: generated/actions.ts:135
msgid "{count, plural, one {Note restored} other {# notes restored}}"
msgstr ""
#: generated/actions.ts:112
#: generated/actions.ts:118
msgid "{count, plural, one {Note unpublished} other {# notes unpublished}}"
msgstr ""
@@ -339,7 +334,7 @@ msgstr ""
msgid "{count, plural, one {Note will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?} other {# notes will be automatically deleted from all other devices & any future changes won't get synced. Are you sure you want to continue?}}"
msgstr ""
#: generated/actions.ts:181
#: generated/actions.ts:187
msgid "{count, plural, one {Notebook created} other {# notebooks created}}"
msgstr ""
@@ -347,7 +342,7 @@ msgstr ""
msgid "{count, plural, one {Notebook deleted} other {# notebooks deleted}}"
msgstr ""
#: generated/actions.ts:162
#: generated/actions.ts:168
msgid "{count, plural, one {Notebook edited} other {# notebooks edited}}"
msgstr ""
@@ -359,7 +354,7 @@ msgstr ""
msgid "{count, plural, one {Notebook permanently deleted} other {# notebooks permanently deleted}}"
msgstr ""
#: generated/actions.ts:133
#: generated/actions.ts:139
msgid "{count, plural, one {Notebook restored} other {# notebooks restored}}"
msgstr ""
@@ -372,6 +367,11 @@ msgstr ""
msgid "{count, plural, one {Permanently delete item} other {Permanently delete # items}}"
msgstr ""
#: generated/in-progress-actions.ts:66
#: generated/in-progress-actions.ts:75
msgid "{count, plural, one {permanently deleting item...} other {permanently deleting # items...}}"
msgstr ""
#: generated/do-actions.ts:99
msgid "{count, plural, one {Pin item} other {Pin # items}}"
msgstr ""
@@ -444,11 +444,16 @@ msgstr ""
msgid "{count, plural, one {Restore notebook} other {Restore # notebooks}}"
msgstr ""
#: generated/actions.ts:189
#: generated/in-progress-actions.ts:49
#: generated/in-progress-actions.ts:58
msgid "{count, plural, one {Restoring item...} other {Restoring # items...}}"
msgstr ""
#: generated/actions.ts:195
msgid "{count, plural, one {Shortcut created} other {# shortcuts created}}"
msgstr ""
#: generated/actions.ts:185
#: generated/actions.ts:191
msgid "{count, plural, one {Tag created} other {# tags created}}"
msgstr ""
@@ -456,7 +461,7 @@ msgstr ""
msgid "{count, plural, one {Tag deleted} other {# tags deleted}}"
msgstr ""
#: generated/actions.ts:158
#: generated/actions.ts:164
msgid "{count, plural, one {Tag edited} other {# tags edited}}"
msgstr ""
@@ -613,7 +618,7 @@ msgid "A to Z"
msgstr ""
#: src/strings.ts:447
msgid "A vault stores your notes in a encrypted storage."
msgid "A vault stores your notes in an encrypted storage."
msgstr ""
#: src/strings.ts:661

View File

@@ -83,7 +83,7 @@ const ACTIONS = [
{
action: "permanentlyDeleted",
label: "permanently deleted",
dataTypes: ["note", "notebook"]
dataTypes: ["note", "notebook", "item"]
},
{ action: "published", label: "published", dataTypes: ["note"] },
{ action: "unpublished", label: "unpublished", dataTypes: ["note"] },
@@ -121,7 +121,7 @@ const ACTION_CONFIRMATIONS = [
"tag",
"color",
"attachment"
]
],
},
{
action: "permanentlyDelete",
@@ -148,6 +148,16 @@ const IN_PROGRESS_ACTIONS = [
action: "deleting",
label: "Deleting",
dataTypes: ["note", "notebook", "attachment", "tag", "reminder"]
},
{
action: "restoring",
label: "Restoring",
dataTypes: ["item"]
},
{
action: "permanentlyDeleting",
label: "permanently deleting",
dataTypes: ["item"]
}
];