Compare commits
18 Commits
v3.2.0
...
fix-input-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10b495cdbe | ||
|
|
d456132dd6 | ||
|
|
4ac7941b6c | ||
|
|
e7f5463917 | ||
|
|
6e41ce459d | ||
|
|
f5095fa643 | ||
|
|
e6a5b85011 | ||
|
|
409c5a2517 | ||
|
|
35cd6b101d | ||
|
|
2440e28168 | ||
|
|
0078d509c5 | ||
|
|
0c2d57716d | ||
|
|
678aeb98c3 | ||
|
|
5f422f4eb7 | ||
|
|
2b26524fcd | ||
|
|
7326533cc1 | ||
|
|
c8aae5be61 | ||
|
|
be1a8bf849 |
8
.github/workflows/desktop.publish.yml
vendored
@@ -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:
|
||||
|
||||
@@ -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/)
|
||||
|
||||
@@ -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: {
|
||||
|
||||
12
apps/desktop/package-lock.json
generated
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 @@
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 />
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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?:
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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} />
|
||||
)
|
||||
});
|
||||
};
|
||||
@@ -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 />
|
||||
};
|
||||
|
||||
@@ -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()
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)";
|
||||
|
||||
@@ -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",
|
||||
|
||||
13
apps/mobile/package-lock.json
generated
@@ -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": {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
38
apps/mobile/patches/react-native-pdf+6.7.7.patch
Normal 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'
|
||||
}
|
||||
1375
apps/web/package-lock.json
generated
@@ -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/",
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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)}};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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`);
|
||||
}
|
||||
|
||||
@@ -188,3 +188,7 @@ pre {
|
||||
.docgen-content {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
nav details[open] {
|
||||
max-height: max-content;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 20 KiB |
BIN
docs/help/contents/_include/static/obsidian-importer/1.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
docs/help/contents/_include/static/obsidian-importer/2.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 9.8 KiB |
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||

|
||||
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.
|
||||

|
||||
3. Once the Importer finishes processing your files, download the .zip file.
|
||||

|
||||
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.
|
||||

|
||||
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
|
||||
|
||||
@@ -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.
|
||||

|
||||
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.
|
||||

|
||||
3. Once the Importer finishes processing your files, download the .zip file.
|
||||

|
||||
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.
|
||||

|
||||
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
|
||||
|
||||
|
||||
@@ -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.
|
||||

|
||||
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".
|
||||

|
||||
4. Once the Importer finishes processing your files, download the .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.
|
||||

|
||||
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).
|
||||
|
||||
@@ -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.
|
||||

|
||||
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.
|
||||

|
||||
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:
|
||||

|
||||
5. Once the Importer finishes processing your files, download the .zip file.
|
||||

|
||||
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.
|
||||

|
||||
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
|
||||
|
||||
|
||||
@@ -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".
|
||||

|
||||
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".
|
||||

|
||||
4. Once the Importer finishes processing your files, download the .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.
|
||||

|
||||
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
|
||||
|
||||
|
||||
@@ -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".
|
||||

|
||||
1. Open the Notesnook app (web or desktop)
|
||||
2. Go to `Settings > Notesnook Importer` and select "Obsidian".
|
||||

|
||||
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".
|
||||

|
||||
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).
|
||||

|
||||
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
|
||||
|
||||
|
||||
@@ -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".
|
||||

|
||||
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".
|
||||

|
||||
4. Once the Importer finishes processing your files, download the .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.
|
||||

|
||||
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).
|
||||
|
||||
@@ -12,15 +12,12 @@ The following steps will help you import your notes from Simplenote easily.
|
||||
3. Go to `Tools` tab in Settings
|
||||

|
||||
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.
|
||||

|
||||
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".
|
||||

|
||||
7. Once importer completes processing, download the .zip file.
|
||||

|
||||
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.
|
||||

|
||||
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
|
||||
|
||||
|
||||
@@ -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".
|
||||

|
||||
3. Drop your Skiff.zip file, or click anywhere inside the box to browse and select your Skiff.zip file. Then click "Start processing".
|
||||

|
||||
4. Once the Importer finishes processing your files, download the .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.
|
||||

|
||||
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
|
||||
|
||||
|
||||
@@ -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`.
|
||||

|
||||
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.
|
||||

|
||||
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.
|
||||

|
||||
7. Once importer completes processing, download the .zip file.
|
||||

|
||||
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.
|
||||

|
||||
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).
|
||||
|
||||
@@ -14,12 +14,9 @@ The following steps will help you import your notes from Zoho notebook easily.
|
||||

|
||||
4. Wait while your notes are exported. Once export completes, download exported .zip file
|
||||

|
||||
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.
|
||||

|
||||
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.
|
||||

|
||||
7. Once the Importer finishes processing your files, download the .zip file.
|
||||

|
||||
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.
|
||||

|
||||
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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!
|
||||
5
fastlane/metadata/android/en-US/changelogs/15294.txt
Normal 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!
|
||||
@@ -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 [];
|
||||
}
|
||||
|
||||
@@ -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", () =>
|
||||
|
||||
@@ -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 }>()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
];
|
||||
|
||||
|
||||