Compare commits

...

66 Commits

Author SHA1 Message Date
Abdullah Atta
91b0de0580 web: bump version to 2.6.8 2023-11-02 13:23:00 +05:00
Abdullah Atta
a5704ad75c desktop: do not show auto update toggle for flatpak users 2023-11-02 13:20:51 +05:00
Abdullah Atta
ba8c09a7d9 desktop: update electron to 25.9.3 2023-11-02 13:07:15 +05:00
Abdullah Atta
dbe4015655 web: preserve note edited time when exporting 2023-11-02 12:59:47 +05:00
Abdullah Atta
76237ad7dd web: fix sync progress in account recovery 2023-11-02 10:35:06 +05:00
Abdullah Atta
dbfb1168fb core: fix restored notes not syncing (closes #3472) 2023-11-02 10:34:21 +05:00
Abdullah Atta
830d28fbe5 editor: fix pasting link replaces selected text
fixes #3520
2023-10-31 12:33:49 +05:00
Abdullah Atta
c6def2a8fe web: fix crash on opening editor properties
(fixes #3585)
2023-10-31 12:03:45 +05:00
Abdullah Atta
ebc95fcea7 web: make duplicate filenames in attachments unique on download (#3678)
* web: make duplicate filenames in attachments unique

* Update apps/web/src/utils/streams/attachment-stream.ts

Signed-off-by: Abdullah Atta <thecodrr@protonmail.com>

* web: make attachment filename case insensitive

---------

Signed-off-by: Abdullah Atta <thecodrr@protonmail.com>
2023-10-31 11:35:01 +05:00
Abdullah Atta
f78cfe59e8 web: hide note title immediately on setting change 2023-10-31 11:12:11 +05:00
Johnny Avila
a2cbc93d34 web: add setting to allow hiding note title from window title (#3480)
Signed-off-by: Johnny Avila <avila.johnny11@gmail.com>
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-10-31 11:08:14 +05:00
Muhammad Ali
4728f14e8c core: add year when grouping by month (#3630) 2023-10-31 10:57:52 +05:00
Abdullah Atta
e94b431d58 web: change pwa theme color according to current theme (#3621)
* web: change pwa theme color according to current theme

* web: use background color as pwa theme-color
2023-10-31 10:56:37 +05:00
Muhammad Ali
0ac5e2b995 web: hide unnecessary items from status bar in focus mode (#3577)
* web: used app-store to hide sync-status suring focus mode

* web: hide useless items from status bar in focus mode

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-10-31 10:54:57 +05:00
Muhammad Ali
365764e3e3 web: update vault setting immediately after vault creation (#3542)
* web: added onStateChange function to vault-settings.

* web: fix font style of checkbox in password dialog

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-10-31 10:50:38 +05:00
Ammar Ahmed
8d2b5aa3de mobile: release v2.6.8 2023-10-30 15:24:30 +05:00
Ammar Ahmed
9a031a4a43 mobile: fix selection state causing delete to fail 2023-10-30 14:25:14 +05:00
Ammar Ahmed
d412679fd7 mobile: fix relogin in session expired mode 2023-10-30 11:56:52 +05:00
Ammar Ahmed
22c52d4432 core: reset lastSynced if new login 2023-10-30 11:55:43 +05:00
Ammar Ahmed
9e5bdf9e33 core: fix access token not saved 2023-10-30 11:55:14 +05:00
Abdullah Atta
7253eca5c0 core: do not allow saving empty token response 2023-10-30 10:55:39 +05:00
Abdullah Atta
b1f3dc29c5 core: use single sse command for logging the user out 2023-10-30 10:55:39 +05:00
Abdullah Atta
8e3df8d41b editor: disallow links in inline code 2023-10-30 10:41:04 +05:00
Ammar Ahmed
efad22c00a mobile: fix 24 hour reminder time 2023-10-30 10:26:15 +05:00
Ammar Ahmed
b7690f13b1 mobile: hide pinned notification subtitle 2023-10-30 09:58:52 +05:00
Ammar Ahmed
6e1739da2e mobile: fix note not added to default notebook in widget 2023-10-24 17:35:53 +05:00
Ammar Ahmed
77ab423041 mobile: fix keep in view in editor (#3612) 2023-10-24 16:54:00 +05:00
Ammar Ahmed
96a8e885d3 Fix session expiry dialog not presented when user encryption key is not found (#3625)
* core: fix session expired dialog not shown when
user encryption key does not
exist

* core: add watch command
2023-10-24 15:58:26 +05:00
Ammar Ahmed
2b917c7eca mobile: fix pin input hide behind keyboard on session expiry sheet (#3624) 2023-10-24 15:57:20 +05:00
Ammar Ahmed
942d2f7a4e mobile: fix system bar colors (#3611) 2023-10-24 12:44:41 +05:00
Ammar Ahmed
75142ff1b2 mobile: fix session id mismatch causing editor to get stuck.(#3613) 2023-10-24 09:18:55 +05:00
Abdullah Atta
74172dc3fa docs: update organizing notes using notebooks doc 2023-10-03 14:42:54 +05:00
Abdullah Atta
848fdda8d7 docs: update outdated documentation 2023-10-03 13:44:21 +05:00
Abdullah Atta
3895569a0c setup: make flatpak source generation more robust 2023-10-03 12:02:27 +05:00
Abdullah Atta
0aac29ca76 web: bump version to 2.6.7 2023-10-02 21:06:08 +05:00
Abdullah Atta
25f9eece8d web: remove blur from dialogs 2023-10-02 21:06:08 +05:00
Ammar Ahmed
0e22b8ab2c mobile: release v2.6.7 2023-10-02 19:01:16 +05:00
Abdullah Atta
577c2ce6cd core: auto create vault if non exists & user opens a locked note 2023-10-02 18:00:36 +05:00
Abdullah Atta
5b9cb8ca4b desktop: fix close to system tray only works once 2023-10-02 17:56:41 +05:00
Ammar Ahmed
2eda93c0c2 mobile: fix add to notebook sheet hangs with many notebooks (#3505)
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2023-10-02 17:06:28 +05:00
Ammar Ahmed
d916adf2e3 mobile: fix dialog show above app lock 2023-10-02 17:05:41 +05:00
Abdullah Atta
1f065bcbb2 web: do not show save dialog when Ctrl+S is pressed 2023-10-02 16:37:09 +05:00
Abdullah Atta
3efdd9505e editor: do not open keyboard on task item drop 2023-10-02 16:22:20 +05:00
Abdullah Atta
ab3f5f6a6c editor: hide scrollbar in math nodes 2023-10-02 16:17:39 +05:00
Muhammad Ali
9027be7bbb web: add menu item to allow copying a note as text or markdown (#3193)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-10-02 16:09:35 +05:00
Abdullah Atta
7b08b5c035 core: fix vault key not getting synced 2023-10-02 16:07:52 +05:00
Abdullah Atta
4a557c67b7 core: fix require prismjs on desktop build 2023-10-02 15:28:47 +05:00
Abdullah Atta
a6ebe092e8 core: fix undefined is not an object when publishing empty note 2023-10-02 15:15:21 +05:00
Midhun Sudhir
9759e1b396 mobile: fix typo in pricing plans (#3477)
Signed-off-by: Midhun Sudhir <60651970+midhun3301@users.noreply.github.com>
2023-09-28 23:19:15 +05:00
Ammar Ahmed
b6147ca5a1 mobile: release v2.6.6 2023-09-25 13:26:40 +05:00
Ammar Ahmed
83887db77a mobile: pass e2e tests 2023-09-25 13:26:40 +05:00
Abdullah Atta
1facfbec0e web: bump version to 2.6.6 2023-09-25 12:38:49 +05:00
Abdullah Atta
d6608325b4 web: skip education plan in tests 2023-09-25 12:27:09 +05:00
Ammar Ahmed
439b090470 mobile: fix learn more button is empty 2023-09-25 12:23:37 +05:00
Ammar Ahmed
ed98319a21 mobile: fix topic sheet size 2023-09-25 12:23:37 +05:00
Ammar Ahmed
54f94f44c0 mobile: remove icon animation 2023-09-25 12:23:37 +05:00
Ammar Ahmed
57cf0e23cf core: fix sorting of debug logs 2023-09-25 12:23:37 +05:00
Abdullah Atta
9e66269aca core: fix core tests 2023-09-25 12:20:54 +05:00
Abdullah Atta
a4f50fa2c3 web: fix backup tests 2023-09-25 11:47:29 +05:00
Abdullah Atta
7a0bafb02c editor: improve drag/drop support in task lists 2023-09-25 11:06:05 +05:00
Abdullah Atta
a24658ac0e core: fix "Cannot read property type of undefined" 2023-09-25 10:43:54 +05:00
Abdullah Atta
bb77385b66 editor: update tiptap to 2.1.11 2023-09-25 10:36:36 +05:00
Ammar Ahmed
e8b1bb649c editor: focus editor on press enter on mobile 2023-09-22 17:32:01 +05:00
Ammar Ahmed
023347da3f editor: fix link color in tool 2023-09-22 17:31:36 +05:00
Ammar Ahmed
f10146be03 mobile: fix image preview not loading image 2023-09-22 17:17:25 +05:00
Ammar Ahmed
7bac3c1326 mobile: fix selection is not reset when switching topics 2023-09-22 16:35:40 +05:00
124 changed files with 1671 additions and 1304 deletions

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/desktop",
"version": "2.6.5",
"version": "2.6.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/desktop",
"version": "2.6.5",
"version": "2.6.8",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {
@@ -24,7 +24,7 @@
"@types/node": "18.16.1",
"@types/yargs": "^17.0.24",
"chokidar": "^3.5.3",
"electron": "25.8.1",
"electron": "25.9.3",
"electron-builder": "^24.6.4",
"electron-builder-notarize": "^1.5.1",
"esbuild": "^0.17.19",
@@ -1751,9 +1751,9 @@
}
},
"node_modules/electron": {
"version": "25.8.1",
"resolved": "https://registry.npmjs.org/electron/-/electron-25.8.1.tgz",
"integrity": "sha512-GtcP1nMrROZfFg0+mhyj1hamrHvukfF6of2B/pcWxmWkd5FVY1NJib0tlhiorFZRzQN5Z+APLPr7aMolt7i2AQ==",
"version": "25.9.3",
"resolved": "https://registry.npmjs.org/electron/-/electron-25.9.3.tgz",
"integrity": "sha512-dacaHg/PuwVcFRgPDCM5j7UDzqGJWOsbBRdS5wPKLNS/ejPeccIjuNUT1cqcrpvCJKAFW8swHWg9kdizNSEDHQ==",
"hasInstallScript": true,
"dependencies": {
"@electron/get": "^2.0.0",

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "2.6.5",
"version": "2.6.8",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/index.js",
@@ -25,7 +25,7 @@
"@types/node": "18.16.1",
"@types/yargs": "^17.0.24",
"chokidar": "^3.5.3",
"electron": "25.8.1",
"electron": "25.9.3",
"electron-builder": "^24.6.4",
"electron-builder-notarize": "^1.5.1",
"esbuild": "^0.17.19",

View File

@@ -29,6 +29,7 @@ import { dirname } from "path";
import { resolvePath } from "../utils/resolve-path";
import { observable } from "@trpc/server/observable";
import { AssetManager } from "../utils/asset-manager";
import { isFlatpak } from "../utils";
const t = initTRPC.create();
@@ -44,6 +45,8 @@ const NotificationOptions = z.object({
});
export const osIntegrationRouter = t.router({
isFlatpak: t.procedure.query(() => isFlatpak()),
zoomFactor: t.procedure.query(() => config.zoomFactor),
setZoomFactor: t.procedure.input(z.number()).mutation(({ input: factor }) => {
globalThis.window?.webContents.setZoomFactor(factor);

View File

@@ -193,7 +193,7 @@ function setupDesktopIntegration() {
app.on("before-quit", () => app.exit(0));
}
globalThis.window?.once("close", (e) => {
globalThis.window?.on("close", (e) => {
if (config.desktopSettings.closeToSystemTray) {
e.preventDefault();
if (process.platform == "darwin") {

View File

@@ -34,6 +34,7 @@ import { useAttachmentStore } from "../../stores/use-attachment-store";
import { db } from "../database";
import Storage from "../database/storage";
import { cacheDir, copyFileAsync, releasePermissions } from "./utils";
import { createCacheDir } from "./io";
export const FileDownloadStatus = {
Success: 1,
@@ -184,6 +185,7 @@ export default async function downloadAttachment(
}
) {
await createCacheDir();
let attachment = db.attachments.attachment(hash);
if (!attachment) {
console.log("attachment not found");

View File

@@ -27,16 +27,12 @@ import { IOS_APPGROUPID } from "../../utils/constants";
export async function readEncrypted(filename, key, cipherData) {
await migrateFilesFromCache();
console.log("read encrypted file...");
console.log("Read encrypted file...");
let path = `${cacheDir}/${filename}`;
try {
if (!(await exists(filename))) {
return false;
} else {
RNFetchBlob.fs.stat(path).then((r) => {
console.log(r);
});
}
const attachment = db.attachments.attachment(filename);
@@ -153,7 +149,10 @@ export async function migrateFilesFromCache() {
await createCacheDir();
const migratedFilesPath = cacheDir + "/.migrated_1";
const migrated = await RNFetchBlob.fs.exists(migratedFilesPath);
if (migrated) return;
if (migrated) {
console.log("Files migrated already");
return;
}
let files = await RNFetchBlob.fs.ls(cacheDir);
console.log("Files to migrate:", files.join(","));

View File

@@ -85,11 +85,13 @@ const AuthModal = () => {
background={colors.primary.background}
transparent={false}
animated={false}
centered={false}
>
<KeyboardAwareScrollView
style={{
width: "100%"
}}
enableAutomaticScroll={false}
keyboardShouldPersistTaps="handled"
>
{currentAuthMode !== AuthMode.login ? (

View File

@@ -46,6 +46,7 @@ import Input from "../ui/input";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { LoginSteps, useLogin } from "./use-login";
import BaseDialog from "../dialog/base-dialog";
function getObfuscatedEmail(email) {
if (!email) return "";
@@ -68,7 +69,8 @@ export const SessionExpired = () => {
eSendEvent(eUserLoggedIn, true);
setVisible(false);
setFocused(false);
}
},
true
);
const logout = async () => {
@@ -130,15 +132,20 @@ export const SessionExpired = () => {
return (
visible && (
<Modal
<BaseDialog
transparent={false}
background={colors.primary.background}
bounce={false}
animated={false}
centered={false}
onShow={async () => {
await sleep(300);
passwordInputRef.current?.focus();
setFocused(true);
}}
enableSheetKeyboardHandler={true}
visible={true}
>
<SheetProvider context="two_factor_verify" />
<View
style={{
width: focused ? "100%" : "99.9%",
@@ -231,7 +238,9 @@ export const SessionExpired = () => {
</View>
<Toast context="local" />
<Dialog context="session_expiry" />
</Modal>
<SheetProvider context="two_factor_verify" />
</BaseDialog>
)
);
};

View File

@@ -33,7 +33,7 @@ export const LoginSteps = {
passwordAuth: 3
};
export const useLogin = (onFinishLogin) => {
export const useLogin = (onFinishLogin, sessionExpired = false) => {
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const setUser = useUserStore((state) => state.setUser);
@@ -69,6 +69,7 @@ export const useLogin = (onFinishLogin) => {
switch (step) {
case LoginSteps.emailAuth: {
const mfaInfo = await db.user.authenticateEmail(email.current);
console.log("email auth", mfaInfo);
if (mfaInfo) {
TwoFactorVerification.present(async (mfa, callback) => {
try {
@@ -101,7 +102,12 @@ export const useLogin = (onFinishLogin) => {
break;
}
case LoginSteps.passwordAuth: {
await db.user.authenticatePassword(email.current, password.current);
await db.user.authenticatePassword(
email.current,
password.current,
null,
sessionExpired
);
finishLogin();
break;
}

View File

@@ -17,7 +17,7 @@ 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, { useEffect } from "react";
import React, { useEffect, useRef, useState } from "react";
import {
KeyboardAvoidingView,
Modal,
@@ -31,6 +31,9 @@ import useIsFloatingKeyboard from "../../hooks/use-is-floating-keyboard";
import { useSettingStore } from "../../stores/use-setting-store";
import { BouncingView } from "../ui/transitions/bouncing-view";
import { ScopedThemeProvider } from "@notesnook/theme";
import SettingsService from "../../services/settings";
import { useUserStore } from "../../stores/use-user-store";
import { useAppState } from "../../hooks/use-app-state";
const BaseDialog = ({
visible,
@@ -48,9 +51,13 @@ const BaseDialog = ({
bounce = true,
closeOnTouch = true,
useSafeArea = true,
avoidKeyboardResize = false
avoidKeyboardResize = false,
enableSheetKeyboardHandler = false
}) => {
const floating = useIsFloatingKeyboard();
const appState = useAppState();
const lockEvents = useRef(false);
const [internalVisible, setIntervalVisible] = useState(true);
useEffect(() => {
return () => {
@@ -58,14 +65,33 @@ const BaseDialog = ({
};
}, []);
useEffect(() => {
if (SettingsService.get().appLockMode === "background") {
if (appState === "background") {
setIntervalVisible(false);
if (useUserStore.getState().appLocked) {
lockEvents.current = true;
const unsub = useUserStore.subscribe((state) => {
if (!state.appLocked) {
setIntervalVisible(true);
unsub();
setTimeout(() => {
lockEvents.current = false;
});
}
});
}
}
}
}, [appState]);
const Wrapper = useSafeArea ? SafeAreaView : View;
return (
<ScopedThemeProvider value="dialog">
<Modal
visible={visible}
visible={visible && internalVisible}
transparent={true}
animated
statusBarTranslucent={statusBarTranslucent}
supportedOrientations={[
"portrait",
@@ -75,13 +101,17 @@ const BaseDialog = ({
"landscape-right"
]}
onShow={() => {
if (lockEvents.current) return;
if (onShow) {
onShow();
useSettingStore.getState().setSheetKeyboardHandler(false);
if (!enableSheetKeyboardHandler) {
useSettingStore.getState().setSheetKeyboardHandler(false);
}
}
}}
animationType={animation}
onRequestClose={() => {
if (lockEvents.current) return;
if (!closeOnTouch) return null;
useSettingStore.getState().setSheetKeyboardHandler(true);
onRequestClose && onRequestClose();
@@ -107,6 +137,7 @@ const BaseDialog = ({
style={[
styles.backdrop,
{
alignItems: centered ? "center" : undefined,
justifyContent: centered
? "center"
: bottom
@@ -133,8 +164,7 @@ const styles = StyleSheet.create({
backdrop: {
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center"
justifyContent: "center"
},
overlayButton: {
width: "100%",

View File

@@ -442,6 +442,7 @@ export class VaultDialog extends Component {
if (this.state.biometricUnlock && !this.state.isBiometryEnrolled) {
await this._enrollFingerprint(this.password);
}
if (this.state.goToEditor) {
this._openInEditor(note);
} else if (this.state.share) {
@@ -452,7 +453,6 @@ export class VaultDialog extends Component {
await this._copyNote(note);
}
} catch (e) {
console.log(e);
this._takeErrorAction(e);
}
};
@@ -467,44 +467,40 @@ export class VaultDialog extends Component {
}
async _enrollFingerprint(password) {
try {
this.setState(
{
loading: true
},
async () => {
try {
await db.vault.unlock(password);
await BiometricService.storeCredentials(password);
this.setState({
loading: false
});
eSendEvent("vaultUpdated");
ToastEvent.show({
heading: "Biometric unlocking enabled!",
message: "Now you can unlock notes in vault with biometrics.",
type: "success",
context: "global"
});
this.close();
} catch (e) {
ToastEvent.show({
heading: "Incorrect password",
message:
"Please enter the correct vault password to enable biometrics.",
type: "error",
context: "local"
});
this.setState({
loading: false
});
return;
}
this.setState(
{
loading: true
},
async () => {
try {
await db.vault.unlock(password);
await BiometricService.storeCredentials(password);
this.setState({
loading: false
});
eSendEvent("vaultUpdated");
ToastEvent.show({
heading: "Biometric unlocking enabled!",
message: "Now you can unlock notes in vault with biometrics.",
type: "success",
context: "global"
});
this.close();
} catch (e) {
this.close();
ToastEvent.show({
heading: "Incorrect password",
message:
"Please enter the correct vault password to enable biometrics.",
type: "error",
context: "local"
});
this.setState({
loading: false
});
}
);
} catch (e) {
this._takeErrorAction(e);
}
}
);
}
async _createVault() {

View File

@@ -43,6 +43,9 @@ const SelectionWrapper = ({
const onLongPress = () => {
if (!useSelectionStore.getState().selectionMode) {
useSelectionStore.setState({
selectedItemsList: []
});
useSelectionStore.getState().setSelectionMode(true);
}
useSelectionStore.getState().setSelectedItem(item);

View File

@@ -93,7 +93,7 @@ export const Card = ({ color, warning }) => {
</Paragraph>
<Paragraph
style={{
flexWrap: "no-wrap",
flexWrap: "nowrap",
flexShrink: 1
}}
color={colors.primary.heading}

View File

@@ -245,13 +245,17 @@ const MergeConflicts = () => {
};
return !visible ? null : (
<Modal
<BaseDialog
statusBarTranslucent
transparent={false}
animationType="slide"
animated={false}
bounce={false}
onRequestClose={() => {
close();
}}
centered={false}
background={colors?.primary.background}
supportedOrientations={[
"portrait",
"portrait-upside-down",
@@ -363,7 +367,7 @@ const MergeConflicts = () => {
</Animated.View>
</View>
</SafeAreaView>
</Modal>
</BaseDialog>
);
};

View File

@@ -500,7 +500,7 @@ export const PricingPlans = ({
textAlign: "center"
}}
>
By subscribing, your will be charged on your Google Account, and
By subscribing, you will be charged on your Google Account, and
your subscription will automatically renew until you cancel prior
to the end of the then current period.
</Paragraph>

View File

@@ -54,7 +54,7 @@ export const Synced = ({ item, close }) => {
<Icon
name="shield-key-outline"
color={colors.primary.accent}
size={SIZE.xxxl}
size={shouldShrink ? SIZE.xxl : SIZE.xxxl}
/>
<View
@@ -99,6 +99,7 @@ export const Synced = ({ item, close }) => {
console.error(e);
}
}}
title="Learn more"
fontSize={SIZE.xs}
height={30}
type="grayAccent"

View File

@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useCallback, useEffect, useRef, useState } from "react";
import { FlatList } from "react-native-actions-sheet";
import { FlashList } from "react-native-actions-sheet";
import { db } from "../../../common/database";
import { ListHeaderInputItem } from "./list-header-item.js";
@@ -54,7 +54,7 @@ export const FilteredList = ({
}, [data, onChangeText]);
return (
<FlatList
<FlashList
{...restProps}
data={filtered}
ref={listRef}

View File

@@ -161,9 +161,6 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
: "deselected";
if (itemState[notebook.id] === "selected") {
count++;
contextValue.select(notebook);
} else {
contextValue.deselect(notebook);
}
for (let topic of notebook.topics) {
itemState[topic.id] = state
@@ -176,9 +173,6 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
: "deselected";
if (itemState[topic.id] === "selected") {
count++;
contextValue.select(topic);
} else {
contextValue.deselect(topic);
}
}
}
@@ -189,7 +183,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
}
useItemSelectionStore.getState().setItemState(itemState);
},
[contextValue, getSelectedNotesCountInItem, selectedItemsList]
[getSelectedNotesCountInItem, selectedItemsList]
);
const getItemsForItem = (item) => {
@@ -214,7 +208,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
}
const updateItemState = useCallback(function (item, state) {
const itemState = useItemSelectionStore.getState().itemState;
const itemState = { ...useItemSelectionStore.getState().itemState };
const mergeState = {
[item.id]: state
};
@@ -370,56 +364,93 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
</View>
<SelectionProvider value={contextValue}>
<FilteredList
<View
style={{
paddingHorizontal: 12,
maxHeight: dimensions.height * 0.85
maxHeight: dimensions.height * 0.85,
height: 50 * (notebooks.length + 2)
}}
ListEmptyComponent={
notebooks.length > 0 ? null : (
<View
style={{
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center"
>
<FilteredList
ListEmptyComponent={
notebooks.length > 0 ? null : (
<View
style={{
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center"
}}
>
<Icon
name="book-outline"
color={colors.primary.icon}
size={100}
/>
<Paragraph style={{ marginBottom: 10 }}>
You do not have any notebooks.
</Paragraph>
</View>
)
}
estimatedItemSize={50}
data={notebooks}
hasHeaderSearch={true}
renderItem={({ item, index }) => (
<ListItem
item={item}
key={item.id}
index={index}
hasNotes={getSelectedNotesCountInItem(item) > 0}
sheetRef={actionSheetRef}
infoText={
<>
{item.topics.length === 1
? item.topics.length + " topic"
: item.topics.length + " topics"}
</>
}
getListItems={getItemsForItem}
getSublistItemProps={(topic) => ({
hasNotes: getSelectedNotesCountInItem(topic) > 0,
style: {
marginBottom: 0,
height: 40
},
onPress: (item) => {
const itemState =
useItemSelectionStore.getState().itemState;
const currentState = itemState[item.id];
if (currentState !== "selected") {
resetItemState("deselected");
contextValue.select(item);
} else {
contextValue.deselect(item);
}
},
key: item.id,
type: "transparent"
})}
icon={(expanded) => ({
name: expanded ? "chevron-up" : "chevron-down",
color: expanded
? colors.primary.accent
: colors.primary.paragraph
})}
onScrollEnd={() => {
actionSheetRef.current?.handleChildScrollEnd();
}}
>
<Icon
name="book-outline"
color={colors.primary.icon}
size={100}
/>
<Paragraph style={{ marginBottom: 10 }}>
You do not have any notebooks.
</Paragraph>
</View>
)
}
data={notebooks}
hasHeaderSearch={true}
renderItem={({ item, index }) => (
<ListItem
item={item}
key={item.id}
index={index}
hasNotes={getSelectedNotesCountInItem(item) > 0}
sheetRef={actionSheetRef}
infoText={
<>
{item.topics.length === 1
? item.topics.length + " topic"
: item.topics.length + " topics"}
</>
}
getListItems={getItemsForItem}
getSublistItemProps={(topic) => ({
hasNotes: getSelectedNotesCountInItem(topic) > 0,
style: {
marginBottom: 0,
height: 40
},
onPress: (item) => {
hasSubList={true}
hasHeaderSearch={false}
type="grayBg"
sublistItemType="topic"
onAddItem={(title) => {
return onAddTopic(title, item);
}}
onAddSublistItem={(item) => {
openAddTopicDialog(item);
}}
onPress={(item) => {
const itemState =
useItemSelectionStore.getState().itemState;
const currentState = itemState[item.id];
@@ -429,47 +460,16 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
} else {
contextValue.deselect(item);
}
},
key: item.id,
type: "transparent"
})}
icon={(expanded) => ({
name: expanded ? "chevron-up" : "chevron-down",
color: expanded
? colors.primary.accent
: colors.primary.paragraph
})}
onScrollEnd={() => {
actionSheetRef.current?.handleChildScrollEnd();
}}
hasSubList={true}
hasHeaderSearch={false}
type="grayBg"
sublistItemType="topic"
onAddItem={(title) => {
return onAddTopic(title, item);
}}
onAddSublistItem={(item) => {
openAddTopicDialog(item);
}}
onPress={(item) => {
const itemState = useItemSelectionStore.getState().itemState;
const currentState = itemState[item.id];
if (currentState !== "selected") {
resetItemState("deselected");
contextValue.select(item);
} else {
contextValue.deselect(item);
}
}}
/>
)}
itemType="notebook"
onAddItem={async (title) => {
return await onAddNotebook(title);
}}
ListFooterComponent={<View style={{ height: 20 }} />}
/>
}}
/>
)}
itemType="notebook"
onAddItem={async (title) => {
return await onAddNotebook(title);
}}
ListFooterComponent={<View style={{ height: 20 }} />}
/>
</View>
</SelectionProvider>
</View>
</>

View File

@@ -273,6 +273,7 @@ export const ListItem = ({
alignSelf: "flex-end",
maxHeight: 250
}}
estimatedItemSize={40}
itemType={sublistItemType}
hasHeaderSearch={hasHeaderSearch}
renderItem={({ item, index }) => (

View File

@@ -531,9 +531,7 @@ export default function ReminderSheet({
androidVariant="nativeAndroid"
is24hourSource="locale"
locale={
db.settings?.getTimeFormat() === "24-hour"
? "en_GB.UTF8"
: "en_US.UTF8"
db.settings?.getTimeFormat() === "24-hour" ? "en_GB" : "en_US"
}
mode={reminderMode === ReminderModes.Repeat ? "time" : "datetime"}
/>

View File

@@ -16,6 +16,7 @@ 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 { useThemeColors } from "@notesnook/theme";
import qclone from "qclone";
import React, {
createContext,
@@ -26,7 +27,7 @@ import React, {
useRef,
useState
} from "react";
import { RefreshControl, View } from "react-native";
import { RefreshControl, useWindowDimensions, View } from "react-native";
import ActionSheet, {
ActionSheetRef,
FlatList
@@ -45,7 +46,6 @@ import {
import useNavigationStore, {
NotebookScreenParams
} from "../../../stores/use-navigation-store";
import { useThemeColors } from "@notesnook/theme";
import {
eOnNewTopicAdded,
eOnTopicSheetUpdate,
@@ -54,18 +54,17 @@ import {
import { normalize, SIZE } from "../../../utils/size";
import { GroupHeader, NotebookType, TopicType } from "../../../utils/types";
import { getTotalNotes } from "@notesnook/common";
import { groupArray } from "@notesnook/core/dist/utils/grouping";
import Config from "react-native-config";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { notesnook } from "../../../../e2e/test.ids";
import { MMKV } from "../../../common/database/mmkv";
import { openEditor } from "../../../screens/notes/common";
import { getTotalNotes } from "@notesnook/common";
import { useSelectionStore } from "../../../stores/use-selection-store";
import { deleteItems } from "../../../utils/functions";
import { presentDialog } from "../../dialog/functions";
import { Properties } from "../../properties";
import Sort from "../sort";
import { useSelectionStore } from "../../../stores/use-selection-store";
type ConfigItem = { id: string; type: string };
class TopicSheetConfig {
@@ -108,7 +107,8 @@ export const TopicsSheet = () => {
)
: []
);
const currentItem = useRef<string>();
const { fontScale } = useWindowDimensions();
const [groupOptions, setGroupOptions] = useState(
db.settings?.getGroupOptions("topics")
);
@@ -206,6 +206,11 @@ export const TopicsSheet = () => {
if (canShow) {
setTimeout(() => {
const id = isTopic ? currentScreen?.notebookId : currentScreen?.id;
if (currentItem.current !== id) {
setSelection([]);
setEnabled(false);
}
currentItem.current = id;
const notebook = db.notebooks?.notebook(id as string)?.data;
const snapPoint = isTopic
? 0
@@ -226,6 +231,8 @@ export const TopicsSheet = () => {
}
}, 300);
} else {
setSelection([]);
setEnabled(false);
ref.current?.hide();
}
}, [
@@ -271,7 +278,7 @@ export const TopicsSheet = () => {
backgroundColor: colors.secondary.background
}}
keyboardHandlerEnabled={false}
snapPoints={Config.isTesting === "true" ? [100] : [25, 100]}
snapPoints={Config.isTesting === "true" ? [100] : [20, 100]}
initialSnapIndex={1}
backgroundInteractionEnabled
gestureEnabled
@@ -305,8 +312,8 @@ export const TopicsSheet = () => {
</View>
<View
style={{
maxHeight: 300,
height: 300,
maxHeight: 450,
height: 450,
width: "100%"
}}
>
@@ -329,7 +336,9 @@ export const TopicsSheet = () => {
{enabled ? (
<IconButton
customStyle={{
marginLeft: 10
marginLeft: 10,
width: 40 * fontScale,
height: 40 * fontScale
}}
onPress={async () => {
//@ts-ignore
@@ -365,8 +374,8 @@ export const TopicsSheet = () => {
color={colors.primary.icon}
size={22}
customStyle={{
width: 40,
height: 40
width: 40 * fontScale,
height: 40 * fontScale
}}
/>
<IconButton
@@ -376,8 +385,8 @@ export const TopicsSheet = () => {
color={colors.primary.icon}
size={22}
customStyle={{
width: 40,
height: 40
width: 40 * fontScale,
height: 40 * fontScale
}}
/>
@@ -395,8 +404,8 @@ export const TopicsSheet = () => {
color={colors.primary.icon}
size={22}
customStyle={{
width: 40,
height: 40
width: 40 * fontScale,
height: 40 * fontScale
}}
/>
</>
@@ -470,6 +479,7 @@ const TopicItem = ({
selection.selection.findIndex((selected) => selected.id === item.id) > -1;
const isFocused = screen.id === item.id;
const notesCount = getTotalNotes(item);
const { fontScale } = useWindowDimensions();
return (
<PressableButton
@@ -506,6 +516,10 @@ const TopicItem = ({
<IconButton
size={SIZE.lg}
color={isSelected ? colors.selected.icon : colors.primary.icon}
top={0}
left={0}
bottom={0}
right={0}
name={
isSelected
? "check-circle-outline"
@@ -525,8 +539,8 @@ const TopicItem = ({
<IconButton
name="dots-horizontal"
customStyle={{
width: 40,
height: 40
width: 40 * fontScale,
height: 40 * fontScale
}}
testID={notesnook.ids.notebook.menu}
onPress={() => {

View File

@@ -40,7 +40,6 @@ interface IconButtonProps extends PressableButtonProps {
iconStyle?: TextStyle;
}
const AnimatedIcon = Animated.createAnimatedComponent(Icon);
export const IconButton = ({
onPress,
name,
@@ -88,8 +87,7 @@ export const IconButton = ({
...customStyle
}}
>
<AnimatedIcon
layout={Layout}
<Icon
name={name}
style={iconStyle as any}
allowFontScaling

View File

@@ -17,15 +17,17 @@ 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 from "react";
import { ScopedThemeProvider, useThemeColors } from "@notesnook/theme";
import React, { useEffect, useRef } from "react";
import { Platform, View } from "react-native";
import ActionSheet from "react-native-actions-sheet";
import useGlobalSafeAreaInsets from "../../../hooks/use-global-safe-area-insets";
import { useSettingStore } from "../../../stores/use-setting-store";
import { ScopedThemeProvider, useThemeColors } from "@notesnook/theme";
import { PremiumToast } from "../../premium/premium-toast";
import { Toast } from "../../toast";
import { BouncingView } from "../transitions/bouncing-view";
import { useAppState } from "../../../hooks/use-app-state";
import SettingsService from "../../../services/settings";
import { useUserStore } from "../../../stores/use-user-store";
const SheetWrapper = ({
children,
@@ -41,6 +43,7 @@ const SheetWrapper = ({
enableGesturesInScrollView = false,
bottomPadding = true
}) => {
const localRef = useRef(null);
const { colors } = useThemeColors("sheet");
const deviceMode = useSettingStore((state) => state.deviceMode);
const sheetKeyboardHandler = useSettingStore(
@@ -50,6 +53,8 @@ const SheetWrapper = ({
const smallTablet = deviceMode === "smallTablet";
const dimensions = useSettingStore((state) => state.dimensions);
const insets = useGlobalSafeAreaInsets();
const appState = useAppState();
const lockEvents = useRef(false);
let width = dimensions.width > 600 ? 600 : 500;
const style = React.useMemo(() => {
@@ -68,19 +73,40 @@ const SheetWrapper = ({
}, [colors.primary.background, largeTablet, smallTablet, width]);
const _onOpen = () => {
if (lockEvents.current) return;
onOpen && onOpen();
};
const _onClose = async () => {
if (lockEvents.current) return;
if (onClose) {
onClose();
}
};
useEffect(() => {
if (SettingsService.get().appLockMode === "background") {
if (appState === "background") {
const ref = fwdRef || localRef;
ref?.current?.hide();
if (useUserStore.getState().appLocked) {
lockEvents.current = true;
const unsub = useUserStore.subscribe((state) => {
if (!state.appLocked) {
ref?.current?.show();
unsub();
lockEvents.current = false;
}
});
}
}
}
}, [appState, fwdRef]);
return (
<ScopedThemeProvider value="sheet">
<ActionSheet
ref={fwdRef}
ref={fwdRef || localRef}
testIDs={{
backdrop: "sheet-backdrop"
}}

View File

@@ -188,7 +188,7 @@ export const useActions = ({ close = () => null, item }) => {
Notifications.displayNotification({
title: item.title,
message: item.headline || text,
subtitle: item.headline || text,
subtitle: "",
bigText: html,
ongoing: true,
actions: ["UNPIN"],

View File

@@ -384,7 +384,16 @@ const _TabsHolder = () => {
}, []);
useEffect(() => {
changeNavigationBarColor(colors.primary.background, isDark, true);
function updateSystemBars() {
changeNavigationBarColor(colors.primary.background, isDark, true);
StatusBar.setBackgroundColor("transparent");
StatusBar.setTranslucent(true);
StatusBar.setBarStyle(isDark ? "light-content" : "dark-content");
}
updateSystemBars();
setTimeout(() => {
updateSystemBars();
}, 1000);
}, [colors.primary.background, isDark]);
return (
@@ -406,11 +415,7 @@ const _TabsHolder = () => {
: 0
}}
>
<StatusBar
barStyle={isDark ? "light-content" : "dark-content"}
translucent={true}
backgroundColor="transparent"
/>
<StatusBar translucent={true} backgroundColor="transparent" />
{!introCompleted ? (
<NavigationStack />

View File

@@ -16,7 +16,7 @@
"html-to-text": "9.0.5",
"phone": "^3.1.14",
"qclone": "^1.2.0",
"react-native-actions-sheet": "0.9.0-alpha.23",
"react-native-actions-sheet": "0.9.0-alpha.24",
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
"react-native-drax": "^0.10.2",
"react-native-image-zoom-viewer": "^3.0.1",

View File

@@ -167,7 +167,7 @@ const Editor = React.memo(
globalThis.noToolbar=${noToolbar};
globalThis.noHeader=${noHeader};
`}
injectedJavaScript={`globalThis.sessionId="${editor.sessionId}";`}
useSharedProcessPool={false}
javaScriptEnabled={true}
focusable={true}
onContentProcessDidTerminate={onError}

View File

@@ -113,7 +113,7 @@ typeof globalThis.statusBar !== "undefined" && statusBar.current.set({date:"",sa
};
setSessionId = async (id: string | null) =>
await this.doAsync(`globalThis.sessionId = "${id}"`);
await this.doAsync(`globalThis.sessionId = "${id}";`);
setStatus = async (date: string | undefined, saved: string) =>
await this.doAsync(

View File

@@ -205,7 +205,6 @@ export const useEditorEvents = (
fullscreen,
isPremium,
readonly,
editor.sessionId,
editor.loading,
deviceMode,
tools,
@@ -229,6 +228,8 @@ export const useEditorEvents = (
logger.info("editor handled back event", editorHandledBack);
return;
}
editorState().currentlyEditing = false;
editor.reset();
setTimeout(async () => {
if (deviceMode !== "mobile" && fullscreen) {
if (fullscreen) {
@@ -241,14 +242,10 @@ export const useEditorEvents = (
editorState().movedAway = true;
tabBarRef.current?.goToPage(0);
}
setImmediate(() => {
useEditorStore.getState().setCurrentlyEditingNote(null);
setTimeout(() => {
Navigation.queueRoutesForUpdate();
}, 500);
});
editorState().currentlyEditing = false;
editor.reset();
setTimeout(() => {
Navigation.queueRoutesForUpdate();
}, 500);
}, 1);
}, [editor, deviceMode, fullscreen]);
@@ -344,8 +341,12 @@ export const useEditorEvents = (
});
}
if (editorMessage.type === EventTypes.back) {
return onBackPress();
}
if (
editorMessage.sessionId !== editor.sessionId &&
editorMessage.sessionId !== editor.sessionId.current &&
editorMessage.type !== EditorEvents.status
) {
return;
@@ -431,9 +432,6 @@ export const useEditorEvents = (
editorState().isFullscreen = true;
eSendEvent(eOpenFullscreenEditor);
break;
case EventTypes.back:
onBackPress();
break;
case EventTypes.link:
openLinkInBrowser(editorMessage.value as string);
break;

View File

@@ -63,8 +63,7 @@ export const useEditor = (
const theme = useThemeEngineStore((state) => state.theme);
const [loading, setLoading] = useState(false);
const [sessionId, setSessionId] = useState<string>(makeSessionId());
const sessionIdRef = useRef(sessionId);
const sessionIdRef = useRef(makeSessionId());
const editorRef = useRef<WebView>(null);
const currentNote = useRef<NoteType | null>();
const currentContent = useRef<Content | null>();
@@ -99,10 +98,6 @@ export const useEditor = (
postMessage(EditorEvents.theme, theme);
}, [theme, postMessage]);
useEffect(() => {
sessionIdRef.current = sessionId;
}, [sessionId]);
useEffect(() => {
commands.setTags(currentNote.current);
}, [commands, tags]);
@@ -142,6 +137,7 @@ export const useEditor = (
clearTimeout(timers.current["loading-images"]);
sessionHistoryId.current = undefined;
saveCount.current = 0;
loadingState.current = undefined;
lock.current = false;
useEditorStore.getState().setReadonly(false);
resetContent && postMessage(EditorEvents.title, "");
@@ -151,8 +147,8 @@ export const useEditor = (
if (resetState) {
const newSessionId = makeSessionId();
sessionIdRef.current = newSessionId;
await commands.setSessionId(newSessionId);
setSessionId(newSessionId);
isDefaultEditor &&
useEditorStore.getState().setCurrentlyEditingNote(null);
@@ -363,7 +359,6 @@ export const useEditor = (
if (item && item.type === "new") {
currentNote.current && (await reset());
const nextSessionId = makeSessionId(item as NoteType);
setSessionId(nextSessionId);
sessionIdRef.current = nextSessionId;
sessionHistoryId.current = Date.now();
await commands.setSessionId(nextSessionId);
@@ -405,12 +400,10 @@ export const useEditor = (
}
lastContentChangeTime.current = item.dateEdited;
const nextSessionId = makeSessionId(item as NoteType);
sessionIdRef.current = nextSessionId;
lockedSessionId.current = nextSessionId;
sessionHistoryId.current = Date.now();
setSessionId(nextSessionId);
commands.setSessionId(nextSessionId);
sessionIdRef.current = nextSessionId;
await commands.setSessionId(nextSessionId);
currentNote.current = item as NoteType;
await commands.setStatus(getFormattedDate(item.dateEdited), "Saved");
await postMessage(EditorEvents.title, item.title);
@@ -638,7 +631,7 @@ export const useEditor = (
useEffect(() => {
state.current.saveCount = 0;
}, [sessionId, loading]);
}, [loading]);
const onReady = useCallback(async () => {
if (!(await isEditorLoaded(editorRef, sessionIdRef.current))) {
@@ -690,8 +683,7 @@ export const useEditor = (
loading,
setLoading,
state,
sessionId,
setSessionId,
sessionId: sessionIdRef,
note: currentNote,
onReady,
saveContent,

View File

@@ -56,7 +56,7 @@ export const useSelectionStore = create<SelectionStore>((set, get) => ({
set({
selectedItemsList: selectedItems,
selectionMode: get().selectionMode
selectionMode: selectedItems.length === 0 ? false : get().selectionMode
});
},
clearSelection: () => {

View File

@@ -67,15 +67,10 @@ export const deleteItems = async (item, context) => {
});
return;
}
if (
item &&
item.id &&
useSelectionStore.getState().selectedItemsList.length === 0
) {
useSelectionStore.getState().setSelectedItem(item);
}
const { selectedItemsList } = useSelectionStore.getState();
const selectedItemsList = item
? [item]
: useSelectionStore.getState().selectedItemsList;
let notes = selectedItemsList.filter((i) => i.type === "note");
let notebooks = selectedItemsList.filter((i) => i.type === "notebook");
@@ -179,7 +174,9 @@ export const deleteItems = async (item, context) => {
});
}
Navigation.queueRoutesForUpdate();
useSelectionStore.getState().clearSelection();
if (!item) {
useSelectionStore.getState().clearSelection();
}
useMenuStore.getState().setMenuPins();
useMenuStore.getState().setColorNotes();
SearchService.updateAndSearch();

View File

@@ -66,17 +66,37 @@ async function createNotes(bundle) {
const sessionId = bundle.note.sessionId;
const id = await db.notes.add(bundle.note);
for (const item of bundle.notebooks) {
if (item.type === "notebook") {
db.relations.add(item, { id, type: "note" });
} else {
db.notes.addToNotebook(
{
id: item.notebookId,
topic: item.id
},
id
);
if (!bundle.notebooks || !bundle.notebooks.length) {
const defaultNotebook = db.settings?.getDefaultNotebook();
if (defaultNotebook) {
if (!defaultNotebook.topic) {
db.relations.add(
{ type: "notebook", id: defaultNotebook.id },
{ id, type: "note" }
);
} else {
db.notes.addToNotebook(
{
id: defaultNotebook?.id,
topic: defaultNotebook.topic
},
id
);
}
}
} else {
for (const item of bundle.notebooks) {
if (item.type === "notebook") {
db.relations.add(item, { id, type: "note" });
} else {
db.notes.addToNotebook(
{
id: item.notebookId,
topic: item.id
},
id
);
}
}
}

View File

@@ -30,36 +30,10 @@ import {
elementByText,
sleep,
notVisibleByText,
visibleById
visibleById,
createNotebook
} from "./utils";
export async function createNotebook(
title = "Notebook 1",
description = true,
topic = true,
topicCount = 1
) {
await tapByText("Add your first notebook");
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
title
);
if (description) {
await elementById(
notesnook.ids.dialogs.notebook.inputs.description
).typeText(`Description of ${title}`);
}
if (topic) {
for (let i = 1; i <= topicCount; i++) {
await elementById(notesnook.ids.dialogs.notebook.inputs.topic).typeText(
i === 1 ? "Topic" : "Topic " + i
);
await tapById("topic-add-button");
}
}
await tapByText("Save");
await sleep(500);
}
// async function addTopic(title = "Topic") {
// await tapById(notesnook.buttons.add);
// await elementById("input-title").typeText(title);

View File

@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { notesnook } from "../test.ids";
import { createNotebook } from "./notebook.e2e";
import {
tapById,
visibleByText,
@@ -27,7 +26,8 @@ import {
tapByText,
notVisibleByText,
sleep,
navigate
navigate,
createNotebook
} from "./utils";
async function sortBy(sorting, elementText = "Default") {

View File

@@ -129,8 +129,36 @@ async function matchSnapshot(element, name) {
});
}
async function createNotebook(
title = "Notebook 1",
description = true,
topic = true,
topicCount = 1
) {
await tapByText("Add your first notebook");
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
title
);
if (description) {
await elementById(
notesnook.ids.dialogs.notebook.inputs.description
).typeText(`Description of ${title}`);
}
if (topic) {
for (let i = 1; i <= topicCount; i++) {
await elementById(notesnook.ids.dialogs.notebook.inputs.topic).typeText(
i === 1 ? "Topic" : "Topic " + i
);
await tapById("topic-add-button");
}
}
await tapByText("Save");
await sleep(500);
}
export {
matchSnapshot,
createNotebook,
prepare,
LaunchApp,
createNote,

View File

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

View File

@@ -1,3 +1,7 @@
- New backup format nnbackupz with upto 90% smaller backups
- Improve UI of code blocks & language selection popup
- Fixed some bugs in sync v2
- Fix 24 hour reminder time in date picker
- Fix pin input hide behind keyboard on session expiry
- Disallow links in inline code
- Fix default notebook not set in widget
- Bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -997,7 +997,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2056;
CURRENT_PROJECT_VERSION = 2059;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1071,7 +1071,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.6.5;
MARKETING_VERSION = 2.6.8;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1102,7 +1102,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2056;
CURRENT_PROJECT_VERSION = 2059;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1176,7 +1176,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.6.5;
MARKETING_VERSION = 2.6.8;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1335,7 +1335,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2056;
CURRENT_PROJECT_VERSION = 2059;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1347,7 +1347,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.6.5;
MARKETING_VERSION = 2.6.8;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1378,7 +1378,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2056;
CURRENT_PROJECT_VERSION = 2059;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1391,7 +1391,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.6.5;
MARKETING_VERSION = 2.6.8;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1421,7 +1421,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2056;
CURRENT_PROJECT_VERSION = 2059;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1495,7 +1495,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.6.5;
MARKETING_VERSION = 2.6.8;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1526,7 +1526,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2056;
CURRENT_PROJECT_VERSION = 2059;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1601,12 +1601,12 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.6.5;
MARKETING_VERSION = 2.6.8;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Notesnook Share Distribution 2023";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Notesnook Share Distrubution 2023";
SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = "Make Note/Make Note-Bridging-Header.h";
SWIFT_VERSION = 5.0;

View File

@@ -347,7 +347,7 @@ PODS:
- ReactCommon/turbomodule/core
- react-native-share-extension (2.5.6):
- React
- react-native-sodium (1.5.2):
- react-native-sodium (1.5.4):
- React
- react-native-webview (11.26.1):
- React-Core
@@ -882,7 +882,7 @@ SPEC CHECKSUMS:
react-native-pdf: 33c622cbdf776a649929e8b9d1ce2d313347c4fa
react-native-safe-area-context: 36cc67648134e89465663b8172336a19eeda493d
react-native-share-extension: df66a2ee48a62277d79898375e2142bde0782063
react-native-sodium: 7d2bbb9ca840dc58d63f661b2ff733a38170fec5
react-native-sodium: 955bb0dc3ea05f8ea06d5e96cb89d1be7b5d7681
react-native-webview: 9f111dfbcfc826084d6c507f569e5e03342ee1c1
React-NativeModulesApple: 1d81d927ef1a67a3545a01e14c2e98500bf9b199
React-perflogger: 684a11499a0589cc42135d6d5cc04d0e4e0e261a

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/mobile",
"version": "2.6.5",
"version": "2.6.8",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "2.6.5",
"version": "2.6.8",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -110,28 +110,28 @@
"@social-embed/lib": "^0.0.2-next.1",
"@theme-ui/components": ">=0.14.0",
"@theme-ui/core": ">=0.14.0",
"@tiptap/core": "2.1.7",
"@tiptap/extension-character-count": "2.1.7",
"@tiptap/extension-color": "2.1.7",
"@tiptap/extension-font-family": "2.1.7",
"@tiptap/extension-history": "2.1.7",
"@tiptap/extension-horizontal-rule": "2.1.7",
"@tiptap/extension-link": "2.1.7",
"@tiptap/extension-list-keymap": "2.1.7",
"@tiptap/extension-placeholder": "2.1.7",
"@tiptap/extension-subscript": "2.1.7",
"@tiptap/extension-superscript": "2.1.7",
"@tiptap/extension-table": "2.1.7",
"@tiptap/extension-table-cell": "2.1.7",
"@tiptap/extension-table-header": "2.1.7",
"@tiptap/extension-table-row": "2.1.7",
"@tiptap/extension-task-item": "2.1.7",
"@tiptap/extension-task-list": "2.1.7",
"@tiptap/extension-text-align": "2.1.7",
"@tiptap/extension-text-style": "2.1.7",
"@tiptap/extension-underline": "2.1.7",
"@tiptap/pm": "2.1.7",
"@tiptap/starter-kit": "2.1.7",
"@tiptap/core": "2.1.11",
"@tiptap/extension-character-count": "2.1.11",
"@tiptap/extension-color": "2.1.11",
"@tiptap/extension-font-family": "2.1.11",
"@tiptap/extension-history": "2.1.11",
"@tiptap/extension-horizontal-rule": "2.1.11",
"@tiptap/extension-link": "2.1.11",
"@tiptap/extension-list-keymap": "2.1.11",
"@tiptap/extension-placeholder": "2.1.11",
"@tiptap/extension-subscript": "2.1.11",
"@tiptap/extension-superscript": "2.1.11",
"@tiptap/extension-table": "2.1.11",
"@tiptap/extension-table-cell": "2.1.11",
"@tiptap/extension-table-header": "2.1.11",
"@tiptap/extension-table-row": "2.1.11",
"@tiptap/extension-task-item": "2.1.11",
"@tiptap/extension-task-list": "2.1.11",
"@tiptap/extension-text-align": "2.1.11",
"@tiptap/extension-text-style": "2.1.11",
"@tiptap/extension-underline": "2.1.11",
"@tiptap/pm": "2.1.11",
"@tiptap/starter-kit": "2.1.11",
"clipboard-polyfill": "4.0.0",
"detect-indent": "^7.0.0",
"entities": "^4.5.0",
@@ -139,7 +139,7 @@
"nanoid": "^4.0.1",
"prism-themes": "^1.9.0",
"prosemirror-codemark": "^0.4.2",
"prosemirror-view": "^1.31.7",
"prosemirror-view": "^1.31.8",
"re-resizable": "^6.9.9",
"react-colorful": "^5.5.1",
"react-modal": "3.13.1",
@@ -256,7 +256,7 @@
"katex": "0.16.2",
"phone": "^3.1.14",
"qclone": "^1.2.0",
"react-native-actions-sheet": "0.9.0-alpha.23",
"react-native-actions-sheet": "0.9.0-alpha.24",
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
"react-native-drax": "^0.10.2",
"react-native-image-zoom-viewer": "^3.0.1",
@@ -17921,9 +17921,9 @@
}
},
"node_modules/react-native-actions-sheet": {
"version": "0.9.0-alpha.23",
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.23.tgz",
"integrity": "sha512-d8sZJHKjme33i34zQ8+oIsGs8wY+GCDtxDboExgpO1OoB9np1y4tGOf3ZHyfIpClITpuy+LT7r9Uz+KfAJTMHA==",
"version": "0.9.0-alpha.24",
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.24.tgz",
"integrity": "sha512-NF0oO0vf3DrFsqI+pbQ+HTG2q+VEotkVwhtFr8aLiUFJT876icVOSurCaeqD1t4zHtH2U5qa8vKQxbA/j5Mu8w==",
"peerDependencies": {
"react-native": "*",
"react-native-gesture-handler": "*"
@@ -26473,28 +26473,28 @@
"@social-embed/lib": "^0.0.2-next.1",
"@theme-ui/components": ">=0.14.0",
"@theme-ui/core": ">=0.14.0",
"@tiptap/core": "2.1.7",
"@tiptap/extension-character-count": "2.1.7",
"@tiptap/extension-color": "2.1.7",
"@tiptap/extension-font-family": "2.1.7",
"@tiptap/extension-history": "2.1.7",
"@tiptap/extension-horizontal-rule": "2.1.7",
"@tiptap/extension-link": "2.1.7",
"@tiptap/extension-list-keymap": "2.1.7",
"@tiptap/extension-placeholder": "2.1.7",
"@tiptap/extension-subscript": "2.1.7",
"@tiptap/extension-superscript": "2.1.7",
"@tiptap/extension-table": "2.1.7",
"@tiptap/extension-table-cell": "2.1.7",
"@tiptap/extension-table-header": "2.1.7",
"@tiptap/extension-table-row": "2.1.7",
"@tiptap/extension-task-item": "2.1.7",
"@tiptap/extension-task-list": "2.1.7",
"@tiptap/extension-text-align": "2.1.7",
"@tiptap/extension-text-style": "2.1.7",
"@tiptap/extension-underline": "2.1.7",
"@tiptap/pm": "2.1.7",
"@tiptap/starter-kit": "2.1.7",
"@tiptap/core": "2.1.11",
"@tiptap/extension-character-count": "2.1.11",
"@tiptap/extension-color": "2.1.11",
"@tiptap/extension-font-family": "2.1.11",
"@tiptap/extension-history": "2.1.11",
"@tiptap/extension-horizontal-rule": "2.1.11",
"@tiptap/extension-link": "2.1.11",
"@tiptap/extension-list-keymap": "2.1.11",
"@tiptap/extension-placeholder": "2.1.11",
"@tiptap/extension-subscript": "2.1.11",
"@tiptap/extension-superscript": "2.1.11",
"@tiptap/extension-table": "2.1.11",
"@tiptap/extension-table-cell": "2.1.11",
"@tiptap/extension-table-header": "2.1.11",
"@tiptap/extension-table-row": "2.1.11",
"@tiptap/extension-task-item": "2.1.11",
"@tiptap/extension-task-list": "2.1.11",
"@tiptap/extension-text-align": "2.1.11",
"@tiptap/extension-text-style": "2.1.11",
"@tiptap/extension-underline": "2.1.11",
"@tiptap/pm": "2.1.11",
"@tiptap/starter-kit": "2.1.11",
"@types/katex": "^0.14.0",
"@types/prismjs": "^1.26.0",
"@types/react": "17.0.2",
@@ -26513,7 +26513,7 @@
"prism-themes": "^1.9.0",
"prosemirror-codemark": "^0.4.2",
"prosemirror-test-builder": "^1.1.0",
"prosemirror-view": "^1.31.7",
"prosemirror-view": "^1.31.8",
"re-resizable": "^6.9.9",
"react": "17.0.2",
"react-colorful": "^5.5.1",
@@ -26577,7 +26577,7 @@
"katex": "0.16.2",
"phone": "^3.1.14",
"qclone": "^1.2.0",
"react-native-actions-sheet": "0.9.0-alpha.23",
"react-native-actions-sheet": "0.9.0-alpha.24",
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
"react-native-drax": "^0.10.2",
"react-native-image-zoom-viewer": "^3.0.1",
@@ -35563,9 +35563,9 @@
}
},
"react-native-actions-sheet": {
"version": "0.9.0-alpha.23",
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.23.tgz",
"integrity": "sha512-d8sZJHKjme33i34zQ8+oIsGs8wY+GCDtxDboExgpO1OoB9np1y4tGOf3ZHyfIpClITpuy+LT7r9Uz+KfAJTMHA==",
"version": "0.9.0-alpha.24",
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.24.tgz",
"integrity": "sha512-NF0oO0vf3DrFsqI+pbQ+HTG2q+VEotkVwhtFr8aLiUFJT876icVOSurCaeqD1t4zHtH2U5qa8vKQxbA/j5Mu8w==",
"requires": {}
},
"react-native-actions-shortcuts": {

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "2.6.5",
"version": "2.6.8",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -43,4 +43,4 @@
"react": "18.2.0",
"react-native": "0.72.0"
}
}
}

View File

@@ -7,7 +7,8 @@ index 0000000..0d259dd
+o/classes
diff --git a/node_modules/react-native-vector-icons/android/build/.transforms/f1314fc28eaff1170375cbb76465cb24/transformed/classes/classes.dex b/node_modules/react-native-vector-icons/android/build/.transforms/f1314fc28eaff1170375cbb76465cb24/transformed/classes/classes.dex
new file mode 100644
index 0000000..e69de29
index 0000000..d4bdd72
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/.transforms/f1314fc28eaff1170375cbb76465cb24/transformed/classes/classes.dex differ
diff --git a/node_modules/react-native-vector-icons/android/build/generated/source/buildConfig/debug/com/oblador/vectoricons/BuildConfig.java b/node_modules/react-native-vector-icons/android/build/generated/source/buildConfig/debug/com/oblador/vectoricons/BuildConfig.java
new file mode 100644
index 0000000..52a02e0
@@ -84,10 +85,12 @@ index 0000000..9e26dfe
\ No newline at end of file
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/compile_library_classes_jar/debug/classes.jar b/node_modules/react-native-vector-icons/android/build/intermediates/compile_library_classes_jar/debug/classes.jar
new file mode 100644
index 0000000..e69de29
index 0000000..77c2a69
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/intermediates/compile_library_classes_jar/debug/classes.jar differ
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/compile_r_class_jar/debug/R.jar b/node_modules/react-native-vector-icons/android/build/intermediates/compile_r_class_jar/debug/R.jar
new file mode 100644
index 0000000..e69de29
index 0000000..bd4434a
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/intermediates/compile_r_class_jar/debug/R.jar differ
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/compile_symbol_list/debug/R.txt b/node_modules/react-native-vector-icons/android/build/intermediates/compile_symbol_list/debug/R.txt
new file mode 100644
index 0000000..5347f17
@@ -2050,11 +2053,11 @@ index 0000000..5347f17
+int xml rn_dev_preferences 0x0
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties b/node_modules/react-native-vector-icons/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties
new file mode 100644
index 0000000..2dd0b63
index 0000000..c671bf3
--- /dev/null
+++ b/node_modules/react-native-vector-icons/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties
@@ -0,0 +1 @@
+#Wed Aug 02 11:28:45 PKT 2023
+#Mon Oct 02 09:47:12 PKT 2023
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml b/node_modules/react-native-vector-icons/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml
new file mode 100644
index 0000000..08d62cc
@@ -2093,16 +2096,20 @@ index 0000000..ef96fe3
\ No newline at end of file
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/BuildConfig.class b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/BuildConfig.class
new file mode 100644
index 0000000..e69de29
index 0000000..1276d6a
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/BuildConfig.class differ
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsModule$Errors.class b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsModule$Errors.class
new file mode 100644
index 0000000..e69de29
index 0000000..3348b06
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsModule$Errors.class differ
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsModule.class b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsModule.class
new file mode 100644
index 0000000..e69de29
index 0000000..de0d974
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsModule.class differ
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsPackage.class b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsPackage.class
new file mode 100644
index 0000000..e69de29
index 0000000..07a935e
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsPackage.class differ
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/local_only_symbol_list/debug/R-def.txt b/node_modules/react-native-vector-icons/android/build/intermediates/local_only_symbol_list/debug/R-def.txt
new file mode 100644
index 0000000..78ac5b8
@@ -2174,7 +2181,8 @@ index 0000000..0be2a09
\ No newline at end of file
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/runtime_library_classes_jar/debug/classes.jar b/node_modules/react-native-vector-icons/android/build/intermediates/runtime_library_classes_jar/debug/classes.jar
new file mode 100644
index 0000000..e69de29
index 0000000..f0a7660
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/intermediates/runtime_library_classes_jar/debug/classes.jar differ
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/symbol_list_with_package_name/debug/package-aware-r.txt b/node_modules/react-native-vector-icons/android/build/intermediates/symbol_list_with_package_name/debug/package-aware-r.txt
new file mode 100644
index 0000000..873e0a9
@@ -3661,7 +3669,8 @@ index 0000000..f4f75c1
+ INJECTED from /Users/ammarahmed/Repos/apps/notesnook/apps/mobile/node_modules/react-native-vector-icons/android/src/main/AndroidManifest.xml
diff --git a/node_modules/react-native-vector-icons/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin b/node_modules/react-native-vector-icons/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin
new file mode 100644
index 0000000..e69de29
index 0000000..26da0e3
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin differ
diff --git a/node_modules/react-native-vector-icons/fonts.gradle b/node_modules/react-native-vector-icons/fonts.gradle
index 5485fac..ef3ccb3 100644
--- a/node_modules/react-native-vector-icons/fonts.gradle
@@ -3678,7 +3687,7 @@ index 5485fac..ef3ccb3 100644
generateAssetsTask.dependsOn(fontCopyTask)
}
diff --git a/node_modules/react-native-vector-icons/glyphmaps/MaterialCommunityIcons.json b/node_modules/react-native-vector-icons/glyphmaps/MaterialCommunityIcons.json
index c06a819..0962de1 100644
index c06a819..13c76cc 100644
--- a/node_modules/react-native-vector-icons/glyphmaps/MaterialCommunityIcons.json
+++ b/node_modules/react-native-vector-icons/glyphmaps/MaterialCommunityIcons.json
@@ -1,6598 +1 @@
@@ -10281,7 +10290,7 @@ index c06a819..0962de1 100644
- "blank": 63116
-}
\ No newline at end of file
+{"menu":983900,"lock-open-outline":983872,"key-outline":986582,"pin-off-outline":985392,"pin-outline":985393,"star-off":984273,"star-outline":984274,"link-variant-remove":987393,"link-variant":983865,"bell":983194,"bell-off-outline":985745,"check":983340,"magnify":983881,"plus":984085,"view-list-outline":988299,"view-list":984434,"play":984074,"pause":984036,"notebook-outline":986815,"text-short":985513,"radiobox-marked":984126,"radiobox-blank":984125,"sort-descending":984253,"information":983804,"sort-ascending":984252,"alert":983078,"arrow-right":983124,"bookmark-outline":983235,"checkbox-marked":983346,"checkbox-blank-outline":983345,"unfold-less-horizontal":984398,"minus-circle":983926,"vibrate":984422,"volume-high":984446,"checkbox-blank-circle-outline":983344,"check-circle-outline":984545,"chevron-up":983363,"chevron-down":983360,"calendar":983277,"book-outline":985956,"pound":984099,"clock-plus-outline":989282,"cloud-upload-outline":985982,"export":983559,"message-badge-outline":989506,"square-edit-outline":985356,"content-copy":983439,"delete-restore":985113,"share-variant":984215,"pencil-lock":984046,"sync-off":984296,"content-duplicate":983441,"theme-light-dark":984334,"pencil":984043,"clock-outline":983376,"attachment":983142,"history":983770,"notebook":985134,"bookmark":983232,"minus-circle-outline":983927,"delete-outline":985575,"delete":983476,"link":983863,"cloud-upload":983399,"star":984270,"key":983814,"account-outline":983059,"email":983534,"backup-restore":983151,"update":984752,"android":983090,"web":984479,"home-variant-outline":986023,"text-box-multiple-outline":985784,"download":983514,"circle-small":985567,"upload":984402,"file-check":983574,"form-textbox":984590,"fingerprint":983607,"file":983572,"alert-circle-outline":984534,"check-all":983341,"shield-key-outline":986053,"arrow-left":983117,"message-plus-outline":987323,"email-outline":983536,"cellphone-key":985422,"dots-vertical":983513,"close":983382,"chevron-right":983362,"emoticon-wink":986232,"sync":984294,"shield":984216,"billboard":987152,"cellphone":983324,"cellphone-lock":985423,"lock":983870,"file-lock":983585,"image-multiple":983801,"harddisk":983754,"file-cabinet":985782,"refresh":984144,"emoticon":986216,"palette":984024,"pin":984067,"format-bold":983652,"table":984299,"language-markdown":983892,"gesture-tap-button":987816,"send-lock":985069,"bomb":984721,"file-pdf-box":983590,"language-html5":983837,"clipboard-text-outline":985656,"invert-colors":983809,"crown":983461,"select-all":984198,"minus":983924,"information-outline":983805,"cog-outline":985275,"arrow-top-right-thick":985542,"checkbox-blank-circle":983343,"sync-alert":984295,"note":983962,"dots-horizontal":983512,"shield-lock":985501,"chevron-left":983361,"account-cog":988016,"two-factor-authentication":985519,"logout":983875,"autorenew":983146,"shape":985137,"circle-half":988053,"brightness-6":983263,"note-edit-outline":989058,"format-line-spacing":983672,"format-size":983679,"format-font":984790,"radar":984119,"arrow-decision-outline":985534,"eye-off-outline":984785,"folder":983627,"bell-outline":983196,"bell-ring":983198,"bug":983268,"mail":986811,"file-document":983577,"mastodon":985809,"twitter":984388,"discord":984687,"open-source-initiative":986030,"monitor":983929,"chart-timeline":984686,"cellphone-arrow-down":985557,"alpha-v":985859,"alert-circle":983080,"reload":984147,"card-text":985975,"open-in-new":984012,"eye":983560,"drag":983515,"menu-down":983901}
+{"menu":983900,"lock-open-outline":983872,"key-outline":986582,"pin-off-outline":985392,"pin-outline":985393,"star-off":984273,"star-outline":984274,"link-variant-remove":987393,"link-variant":983865,"bell":983194,"bell-off-outline":985745,"check":983340,"magnify":983881,"plus":984085,"view-list-outline":988299,"view-list":984434,"play":984074,"pause":984036,"notebook-outline":986815,"text-short":985513,"radiobox-marked":984126,"radiobox-blank":984125,"sort-descending":984253,"information":983804,"sort-ascending":984252,"alert":983078,"arrow-right":983124,"bookmark-outline":983235,"checkbox-marked":983346,"checkbox-blank-outline":983345,"unfold-less-horizontal":984398,"minus-circle":983926,"vibrate":984422,"volume-high":984446,"checkbox-blank-circle-outline":983344,"check-circle-outline":984545,"chevron-up":983363,"chevron-down":983360,"calendar":983277,"minus-circle-outline":983927,"close-circle-outline":983386,"qrcode":984114,"text":985512,"cloud":983391,"book-outline":985956,"pound":984099,"clock-plus-outline":989282,"cloud-upload-outline":985982,"export":983559,"message-badge-outline":989506,"square-edit-outline":985356,"content-copy":983439,"delete-restore":985113,"share-variant":984215,"pencil-lock":984046,"sync-off":984296,"content-duplicate":983441,"theme-light-dark":984334,"pencil":984043,"clock-outline":983376,"attachment":983142,"history":983770,"notebook":985134,"bookmark":983232,"delete-outline":985575,"delete":983476,"link":983863,"cloud-upload":983399,"star":984270,"key":983814,"account-outline":983059,"email":983534,"backup-restore":983151,"update":984752,"android":983090,"web":984479,"home-variant-outline":986023,"text-box-multiple-outline":985784,"download":983514,"arrow-left":983117,"dots-horizontal":983512,"chevron-right":983362,"close":983382,"cellphone-key":985422,"message-plus-outline":987323,"email-outline":983536,"shield-lock":985501,"chevron-left":983361,"minus":983924,"crown":983461,"account-cog":988016,"two-factor-authentication":985519,"information-outline":983805,"logout":983875,"autorenew":983146,"sync-alert":984295,"shape":985137,"circle-half":988053,"brightness-6":983263,"note-edit-outline":989058,"format-line-spacing":983672,"format-size":983679,"format-font":984790,"radar":984119,"arrow-decision-outline":985534,"fingerprint":983607,"eye-off-outline":984785,"folder":983627,"lock":983870,"form-textbox":984590,"bell-outline":983196,"bell-ring":983198,"bug":983268,"mail":986811,"file-document":983577,"mastodon":985809,"twitter":984388,"discord":984687,"open-source-initiative":986030,"monitor":983929,"chart-timeline":984686,"cellphone-arrow-down":985557,"alpha-v":985859,"circle-small":985567,"upload":984402,"file-check":983574,"file":983572,"alert-circle-outline":984534,"check-all":983341,"shield-key-outline":986053,"dots-vertical":983513,"emoticon-wink":986232,"sync":984294,"shield":984216,"billboard":987152,"cellphone":983324,"cellphone-lock":985423,"file-lock":983585,"image-multiple":983801,"harddisk":983754,"file-cabinet":985782,"refresh":984144,"emoticon":986216,"palette":984024,"pin":984067,"format-bold":983652,"table":984299,"language-markdown":983892,"gesture-tap-button":987816,"send-lock":985069,"bomb":984721,"file-pdf-box":983590,"language-html5":983837,"clipboard-text-outline":985656,"invert-colors":983809,"select-all":984198,"cog-outline":985275,"arrow-top-right-thick":985542,"checkbox-blank-circle":983343,"note":983962,"drag":983515,"menu-down":983901,"alert-circle":983080,"reload":984147,"card-text":985975,"open-in-new":984012,"eye":983560}
\ No newline at end of file
diff --git a/node_modules/react-native-vector-icons/glyphmaps/MaterialCommunityIcons.old.json b/node_modules/react-native-vector-icons/glyphmaps/MaterialCommunityIcons.old.json
new file mode 100644

View File

@@ -65,7 +65,12 @@ const EXTRA_ICON_NAMES = [
"check-circle-outline",
"chevron-up",
"chevron-down",
"calendar"
"calendar",
"minus-circle-outline",
"close-circle-outline",
"qrcode",
"text",
"cloud"
];
const __filename = fileURLToPath(import.meta.url);

View File

@@ -21,7 +21,7 @@ import ShareExtension from "@ammarahmed/react-native-share-extension";
import { getPreviewData } from "@flyerhq/react-native-link-preview";
import { formatBytes } from "@notesnook/common";
import { isImage } from "@notesnook/core/dist/utils/filename";
import { parseHTML } from "@notesnook/core/dist/utils/html-parser";
import { useThemeColors } from "@notesnook/theme";
import React, { useCallback, useEffect, useRef, useState } from "react";
import {
ActivityIndicator,
@@ -53,10 +53,9 @@ import { getElevationStyle } from "../app/utils/elevation";
import { eOnLoadNote } from "../app/utils/events";
import { NoteBundle } from "../app/utils/note-bundle";
import { Editor } from "./editor";
import { HtmlLoadingWebViewAgent, fetchHandle } from "./fetch-webview";
import { Search } from "./search";
import { initDatabase, useShareStore } from "./store";
import { useThemeColors } from "@notesnook/theme";
import { HtmlLoadingWebViewAgent, fetchHandle } from "./fetch-webview";
const getLinkPreview = (url) => {
return getPreviewData(url, 5000);

View File

@@ -18,21 +18,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { test, expect } from "@playwright/test";
import { AppModel } from "./models/app.model";
import { NOTE, USER } from "./utils";
import { USER } from "./utils";
test("create a backup", async ({ page }) => {
test("create an unencrypted backup", async ({ page }) => {
const app = new AppModel(page);
await app.goto();
const notes = await app.goToNotes();
await notes.createNote(NOTE);
await app.auth.goto();
await app.auth.login(USER.CURRENT);
const settings = await app.goToSettings();
await settings.toggleBackupEncryption(USER.CURRENT.password);
const backup = await settings.createBackup();
const backup = await settings.createBackup(USER.CURRENT.password);
expect(backup.length > 0).toBeTruthy();
});
test("restore a backup", async ({ page }) => {
test("restore an unencrypted backup", async ({ page }) => {
const app = new AppModel(page);
await app.goto();
const settings = await app.goToSettings();
@@ -56,7 +56,7 @@ test("create an encrypted backup", async ({ page }, info) => {
await app.auth.login(USER.CURRENT);
const settings = await app.goToSettings();
const backup = await settings.createBackup(USER.CURRENT.password);
const backup = await settings.createBackup();
expect(backup.length > 0).toBeTruthy();
});

View File

@@ -46,7 +46,10 @@ export class CheckoutModel {
for await (const item of iterateList(
this.page.locator(getTestId("checkout-plan"))
)) {
plans.push(new Plan(item));
const plan = new Plan(item);
const planTitle = await plan.getTitle();
if (planTitle?.startsWith("Education")) continue;
plans.push(plan);
}
return plans;
}

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/web",
"version": "2.6.5",
"version": "2.6.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/web",
"version": "2.6.5",
"version": "2.6.8",
"license": "GPL-3.0-or-later",
"dependencies": {
"@aws-sdk/util-base64-browser": "^3.208.0",

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -119,7 +119,11 @@ export async function exportNotes(
return await exportToPDF(note.title, exported);
}
files.push({ filename: note.title, content: exported });
files.push({
filename: note.title,
content: exported,
date: note.dateEdited
});
}
if (!files.length) return false;

View File

@@ -157,6 +157,7 @@ export async function restoreBackupFile(backupFile: File) {
} else {
await restoreWithProgress(backup);
}
await db.initCollections();
} else {
const error = await TaskManager.startTask<Error | void>({
title: "Restoring backup",

View File

@@ -154,6 +154,10 @@ function TipTap(props: TipTapProps) {
const tiptapOptions = useMemo<Partial<TiptapOptions>>(() => {
return {
editorProps: {
handleKeyDown(view, event) {
if ((event.ctrlKey || event.metaKey) && event.key === "s")
event.preventDefault();
},
handlePaste: (view, event) => {
const hasText = event.clipboardData?.types?.some((type) =>
type.startsWith("text/")

View File

@@ -49,7 +49,8 @@ import {
AddToNotebook,
RemoveShortcutLink,
Plus,
Tag
Tag,
Copy
} from "../icons";
import TimeAgo from "../time-ago";
import ListItem from "../list-item";
@@ -85,6 +86,7 @@ import {
ReferencesWithDateEdited
} from "../list-container/types";
import { SchemeColors } from "@notesnook/theme";
import Vault from "../../common/vault";
type NoteProps = {
tags: Item[];
@@ -454,6 +456,32 @@ const menuItems: (note: any, items?: any[]) => MenuItem[] = (
multiSelect: true,
isPro: true
},
{
type: "button",
key: "copy",
title: "Copy as",
icon: Copy.path,
menu: {
items: [
{
type: "button",
key: "copy-as-text",
tooltip: `Export as Text`,
title: "Text",
icon: Plaintext.path,
onClick: () => copyNote(note.id, "txt")
},
{
type: "button",
key: "copy-as-markdown",
tooltip: `Export as Markdown`,
title: "Markdown",
icon: Markdown.path,
onClick: () => copyNote(note.id, "md")
}
]
}
},
{
type: "button",
key: "duplicate",
@@ -667,3 +695,25 @@ function tagsMenuItems(items: any[]): MenuItem[] {
return menuItems;
}
async function copyNote(noteId: string, format: "md" | "txt") {
try {
const note = db.notes?.note(noteId);
if (!note) throw new Error("No note with this id exists.");
if (note?.data.locked && !(await Vault.unlockVault()))
throw new Error("Please unlock this note to copy it.");
const rawContent = await db.content?.raw(note.data.contentId);
const content = note?.data.locked
? await db.vault?.decryptContent(rawContent)
: rawContent;
const text = await note.export(format, content, false);
if (!text) throw new Error(`Could not convert note to ${format}.`);
await navigator.clipboard.writeText(text);
showToast("success", "Copied!");
} catch (e) {
if (e instanceof Error)
showToast("error", `Failed to copy note: ${e.message}.`);
}
}

View File

@@ -110,10 +110,11 @@ function Properties(props) {
"reminder"
);
const allNotebooks = useMemo(
() => [
...notebooks.map((ref) => db.notebooks.notebook(ref.id)?.data),
...db.relations.to({ id: sessionId, type: "note" }, "notebook")
],
() =>
[
...notebooks.map((ref) => db.notebooks.notebook(ref.id)?.data),
...db.relations.to({ id: sessionId, type: "note" }, "notebook")
].filter(Boolean),
[sessionId, notebooks]
);

View File

@@ -50,6 +50,7 @@ function StatusBar() {
const isLoggedIn = useUserStore((state) => state.isLoggedIn);
const statuses = useStatus();
const updateStatus = useAutoUpdater();
const isFocusMode = useAppStore((state) => state.isFocusMode);
return (
<ScopedThemeProvider
@@ -63,124 +64,130 @@ function StatusBar() {
}}
px={2}
>
<Flex>
{isLoggedIn ? (
<>
{isFocusMode ? (
<Flex />
) : (
<Flex>
{isLoggedIn ? (
<>
<Button
onClick={() =>
user?.isEmailConfirmed
? navigate("/settings")
: hashNavigate("/email/verify")
}
variant="statusitem"
sx={{
alignItems: "center",
justifyContent: "center",
display: "flex"
}}
>
<Circle
size={7}
color={
user?.isEmailConfirmed
? "var(--icon-success)"
: "var(--icon-error)"
}
/>
<Text
className="selectable"
variant="subBody"
ml={1}
sx={{ color: "paragraph" }}
>
{user?.email}
{user?.isEmailConfirmed ? "" : " (not verified)"}
</Text>
</Button>
<SyncStatus />
</>
) : isLoggedIn === false ? (
<Button
onClick={() =>
user?.isEmailConfirmed
? navigate("/settings")
: hashNavigate("/email/verify")
}
variant="statusitem"
onClick={() => hardNavigate("/login")}
sx={{
alignItems: "center",
justifyContent: "center",
display: "flex"
}}
data-test-id="not-logged-in"
>
<Circle
size={7}
color={
user?.isEmailConfirmed
? "var(--icon-success)"
: "var(--icon-error)"
}
/>
<Text
className="selectable"
variant="subBody"
ml={1}
sx={{ color: "paragraph" }}
>
{user?.email}
{user?.isEmailConfirmed ? "" : " (not verified)"}
<Circle size={7} color="var(--icon-error)" />
<Text variant="subBody" ml={1} sx={{ color: "paragraph" }}>
Not logged in
</Text>
</Button>
<SyncStatus />
</>
) : isLoggedIn === false ? (
) : null}
<Button
variant="statusitem"
onClick={() => hardNavigate("/login")}
onClick={() => showIssueDialog()}
sx={{
alignItems: "center",
justifyContent: "center",
display: "flex"
}}
data-test-id="not-logged-in"
title="Facing an issue? Click here to create a bug report."
>
<Circle size={7} color="var(--icon-error)" />
<Issue size={12} />
<Text variant="subBody" ml={1} sx={{ color: "paragraph" }}>
Not logged in
Report an issue
</Text>
</Button>
) : null}
<Button
variant="statusitem"
onClick={() => showIssueDialog()}
sx={{
alignItems: "center",
justifyContent: "center",
display: "flex"
}}
title="Facing an issue? Click here to create a bug report."
>
<Issue size={12} />
<Text variant="subBody" ml={1} sx={{ color: "paragraph" }}>
Report an issue
</Text>
</Button>
{statuses?.map((status) => {
const { key, icon: Icon } = status;
return (
<Flex
key={key}
ml={1}
sx={{ alignItems: "center", justifyContent: "center" }}
{statuses?.map((status) => {
const { key, icon: Icon } = status;
return (
<Flex
key={key}
ml={1}
sx={{ alignItems: "center", justifyContent: "center" }}
>
{Icon ? <Icon size={12} /> : <Loading size={12} />}
<Text variant="subBody" ml={1} sx={{ color: "paragraph" }}>
{statusToString(status)}
</Text>
</Flex>
);
})}
{updateStatus && updateStatus.type !== "updated" && (
<Button
variant="statusitem"
onClick={async () => {
if (updateStatus.type === "available") {
await showUpdateAvailableNotice(updateStatus);
} else if (updateStatus.type === "completed") {
installUpdate();
} else {
checkForUpdate();
}
}}
sx={{
ml: 1,
alignItems: "center",
justifyContent: "center",
display: "flex"
}}
>
{Icon ? <Icon size={12} /> : <Loading size={12} />}
<Update
rotate={
updateStatus.type !== "completed" &&
updateStatus.type !== "available"
}
color={
updateStatus.type === "available" ? "accent" : "paragraph"
}
size={12}
/>
<Text variant="subBody" ml={1} sx={{ color: "paragraph" }}>
{statusToString(status)}
{statusToInfoText(updateStatus)}
</Text>
</Flex>
);
})}
{updateStatus && updateStatus.type !== "updated" && (
<Button
variant="statusitem"
onClick={async () => {
if (updateStatus.type === "available") {
await showUpdateAvailableNotice(updateStatus);
} else if (updateStatus.type === "completed") {
installUpdate();
} else {
checkForUpdate();
}
}}
sx={{
ml: 1,
alignItems: "center",
justifyContent: "center",
display: "flex"
}}
>
<Update
rotate={
updateStatus.type !== "completed" &&
updateStatus.type !== "available"
}
color={updateStatus.type === "available" ? "accent" : "paragraph"}
size={12}
/>
<Text variant="subBody" ml={1} sx={{ color: "paragraph" }}>
{statusToInfoText(updateStatus)}
</Text>
</Button>
)}
</Flex>
</Button>
)}
</Flex>
)}
<EditorFooter />
</ScopedThemeProvider>
);

View File

@@ -59,6 +59,16 @@ export function BaseThemeProvider(
useEffect(() => {
useThemeEngineStore.getState().setTheme(theme);
const themeColorElement = document.head.querySelector(
"meta[name='theme-color']"
);
if (themeColorElement) {
themeColorElement.setAttribute(
"content",
theme.scopes.base.primary.background
);
}
}, [theme]);
return (

View File

@@ -400,12 +400,7 @@ const Sidebar = memo(
flexDirection: "column",
justifyContent: "space-between",
width: 240,
"@supports ((-webkit-backdrop-filter: none) or (backdrop-filter: none))":
{
backgroundColor: alpha("background", 0.6),
backdropFilter: "blur(8px)"
},
backgroundColor: "var(--background-secondary)"
backgroundColor: "background"
}}
>
<Flex sx={{ flexDirection: "column" }}>

View File

@@ -99,12 +99,7 @@ export function BuyDialog(props: BuyDialogProps) {
display: "flex",
overflow: ["hidden", "hidden", "auto"],
flexDirection: "column",
"@supports ((-webkit-backdrop-filter: none) or (backdrop-filter: none))":
{
backgroundColor: alpha("background", 0.8),
backdropFilter: "blur(8px)"
},
backgroundColor: "var(--background-secondary)",
backgroundColor: "background",
flexShrink: 0,
alignItems: "center",
justifyContent: "center",

View File

@@ -89,13 +89,7 @@ const features: Record<FeatureKeys, Feature> = {
)
}
]
: [
{
title: "Much faster & more reliable sync",
subtitle:
"Syncing should now work more reliably & also much, much faster. Like 10x faster than before."
}
],
: [],
cta: {
title: "Got it",
icon: Checkmark,

View File

@@ -105,7 +105,11 @@ function PasswordDialog(props) {
<Label
key={check.key}
mt={2}
sx={{ fontSize: "title", alignItems: "center" }}
sx={{
fontSize: "body",
fontFamily: "body",
alignItems: "center"
}}
>
<Checkbox id={check.key} name={check.key} />
{check.title}

View File

@@ -139,6 +139,7 @@ export const BehaviourSettings: SettingsGroup[] = [
key: "updates",
section: "behaviour",
header: "Updates",
isHidden: () => useSettingStore.getState().isFlatpak,
settings: [
{
key: "auto-updates",

View File

@@ -238,12 +238,7 @@ function SettingsSideBar(props: SettingsSideBarProps) {
flexDirection: "column",
display: "flex",
overflow: "hidden",
"@supports ((-webkit-backdrop-filter: none) or (backdrop-filter: none))":
{
backgroundColor: alpha("background", 0.6),
backdropFilter: "blur(8px)"
},
backgroundColor: "var(--background-secondary)"
backgroundColor: "background"
}}
>
<Input

View File

@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { SettingsGroup } from "./types";
import { useStore as useSettingStore } from "../../stores/setting-store";
import { useStore as useUserStore } from "../../stores/user-store";
import { getPlatform, isDesktop } from "../../utils/platform";
import { getPlatform } from "../../utils/platform";
import { db } from "../../common/db";
import { showPromptDialog } from "../../common/dialog-controller";
import Config from "../../utils/config";
@@ -74,6 +74,20 @@ What data is collected & when?`,
}
]
},
{
key: "hide-note-title",
title: "Hide note title",
description: "Prevent note title from appearing in tab/window title.",
onStateChange: (listener) =>
useSettingStore.subscribe((s) => s.hideNoteTitle, listener),
components: [
{
type: "toggle",
isToggled: () => useSettingStore.getState().hideNoteTitle,
toggle: () => useSettingStore.getState().toggleHideTitle()
}
]
},
{
key: "privacy-mode",
title: "Privacy mode",

View File

@@ -58,6 +58,8 @@ export const VaultSettings: SettingsGroup[] = [
title: "Change vault password",
description: "Set a new password for your vault",
isHidden: () => !useAppStore.getState().isVaultCreated,
onStateChange: (listener) =>
useAppStore.subscribe((s) => s.isVaultCreated, listener),
components: [
{
type: "button",
@@ -72,6 +74,8 @@ export const VaultSettings: SettingsGroup[] = [
title: "Clear vault",
description: "Unlock all locked notes and clear vault.",
isHidden: () => !useAppStore.getState().isVaultCreated,
onStateChange: (listener) =>
useAppStore.subscribe((s) => s.isVaultCreated, listener),
components: [
{
type: "button",
@@ -91,6 +95,8 @@ export const VaultSettings: SettingsGroup[] = [
title: "Delete vault",
description: "Delete vault including all locked notes.",
isHidden: () => !useAppStore.getState().isVaultCreated,
onStateChange: (listener) =>
useAppStore.subscribe((s) => s.isVaultCreated, listener),
components: [
{
type: "button",

View File

@@ -3,16 +3,9 @@
<head>
<meta charset="utf-8" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link
rel="icon"
type="image/png"
sizes="32x32"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAHUUExURUxpcf////////////////////////////////////////////////////////////////////////////////////39/f39/f////////////////////////////////z8/O3s7P7+/vTz8wAAAPz7++/v7/Py8u/u7vDv7+7t7dfX1/79/ezr66Kiovv6+vf29vX09OTj4w0NDfv7++Ti4vr6+vT09Ovq6vn5+f39/fb19dnX1/b29tvb29/f3xMTE/j4+Pn4+O7s7MnJyerq6uno6M/Pz8jIyN3d3dnZ2d7e3kZGRvj39/38/Pf39+zs7PDw8OPi4ubl5cvLy+rp6czMzPLy8vPz887Ozujo6MrKyuDf3+fn59PT0+bm5uXl5c3NzfX19evr6wsLC+/t7ePj4/Ty8u3r6/Lx8eXk5OPh4dDQ0Nza2tjX19vZ2fHw8Nva2tLR0djW1tbW1tra2tLS0uHh4evp6eLi4tXV1djY2L+/vysrK7q6utHR0aGhoa6urrm5uQICAigoKCEhIZCQkFxcXBgYGFRUVBISEm1tbQ4ODklJSbu7uyAgID09PZqamkVFRXh4eLGxsTg4OOTk5FFRUQEBAQoKCnx8fBcXF7/+rP8AAAAfdFJOUwBOqO2h6KT9Mtyr5+w3Str+6TTZOjXb31M24O8BOd0PapSsAAABxElEQVQ4y2NgYOBgYWWXxwLYBdmEGICAjx9Z1NJJVQfB42QE6hdG0RaoaaCqhKSCl4EF1VxNVAXyTAzcaArUUBXwMLBjKDBBViDAII+uwBZFgTy6AjU1awIKNKz1aa0g1VBfB58CDS08CuIb40AKzLAqsJKXN2tXacChwKS+rTtB3tJHRU9ewxmbFZ6tKiqTrSAKdIO1LDEUaPeoqMzsUwAp0NIt8Uj0ijJXQFVgnDJVZcK0XrACXcXY0sjoHM8AfyUkBbOn9KvMmgRSYFEW4hUVqJagHeaShqTAwX7ijJQusAkW5VXRHu6eruFqaigKkqarqAAVKFbYxWiYq1XGedijuMHBWz6xo7m2Rl7RwtvdJaw6NjzCFl2BVVNdshJQgbqBsqtdpIu7nasjXIFXS2cyRL2CW6i6JTCgFCLitUPs4QrM7cDmKZi7FRYVm6o5QeLbDGICLNEqJAVk59rk2djY5Me4FThawQOKFSzrrxmaZWpqBAamQUGmwRaakNTNzMAm7+ekrKmlqOjsrIgAGcAA1ci0NVPiYhAV8zVURgbp6nBg7isuwsDAKKfkp4AEVOHASklKApS9JZmY5bECaS5ZGQYA3vqpy6NoYh0AAABXelRYdFJhdyBwcm9maWxlIHR5cGUgaXB0YwAAeJzj8gwIcVYoKMpPy8xJ5VIAAyMLLmMLEyMTS5MUAxMgRIA0w2QDI7NUIMvY1MjEzMQcxAfLgEigSi4A6hcRdPJCNZUAAAAASUVORK5CYII="
/>
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<!-- <link rel="manifest" href="/site.webmanifest" /> -->
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#01c352" />
<meta name="msapplication-TileColor" content="#01c352" />
<meta name="theme-color" content="#01c352" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

View File

@@ -22,6 +22,7 @@ import { store as noteStore } from "./note-store";
import { store as attachmentStore } from "./attachment-store";
import { store as appStore } from "./app-store";
import { store as tagStore } from "./tag-store";
import { store as settingStore } from "./setting-store";
import { db } from "../common/db";
import BaseStore from ".";
import { EV, EVENTS } from "@notesnook/core/dist/common";
@@ -151,7 +152,7 @@ class EditorStore extends BaseStore {
if (!note) return;
noteStore.setSelectedNote(note.id);
setDocumentTitle(note.title);
setDocumentTitle(settingStore.get().hideNoteTitle ? undefined : note.title);
if (note.locked)
return hashNavigate(`/notes/${noteId}/unlock`, { replace: true });
@@ -247,7 +248,9 @@ class EditorStore extends BaseStore {
state.session.dateEdited = note.dateEdited;
state.session.attachmentsLength = attachments.length;
});
setDocumentTitle(note.title);
setDocumentTitle(
settingStore.get().hideNoteTitle ? undefined : note.title
);
this.setSaveState(1);
} catch (err) {

View File

@@ -23,7 +23,9 @@ import { desktop } from "../common/desktop-bridge";
import createStore from "../common/store";
import Config from "../utils/config";
import BaseStore from "./index";
import { store as editorStore } from "./editor-store";
import { isTelemetryEnabled, setTelemetry } from "../utils/telemetry";
import { setDocumentTitle } from "../utils/dom";
/**
* @extends {BaseStore<SettingStore>}
@@ -40,6 +42,7 @@ class SettingStore extends BaseStore {
zoomFactor = 1.0;
privacyMode = false;
hideNoteTitle = Config.get("hideNoteTitle", false);
telemetry = isTelemetryEnabled();
/** @type {string} */
dateFormat = null;
@@ -54,6 +57,7 @@ class SettingStore extends BaseStore {
*/
desktopIntegrationSettings = undefined;
autoUpdates = true;
isFlatpak = false;
refresh = async () => {
this.set({
@@ -61,6 +65,7 @@ class SettingStore extends BaseStore {
timeFormat: db.settings.getTimeFormat(),
titleFormat: db.settings.getTitleFormat(),
trashCleanupInterval: db.settings.getTrashCleanupInterval(),
isFlatpak: await desktop?.integration.isFlatpak.query(),
desktopIntegrationSettings:
await desktop?.integration.desktopIntegration.query(),
privacyMode: await desktop?.integration.privacyMode.query(),
@@ -163,6 +168,15 @@ class SettingStore extends BaseStore {
await desktop?.integration.setPrivacyMode.mutate({ enabled: !privacyMode });
};
toggleHideTitle = async () => {
const { hideNoteTitle } = this.get();
this.set({ hideNoteTitle: !hideNoteTitle });
Config.set("hideNoteTitle", !hideNoteTitle);
setDocumentTitle(
!hideNoteTitle ? undefined : editorStore.get().session.title
);
};
toggleAutoUpdates = async () => {
const autoUpdates = this.get().autoUpdates;
this.set({ autoUpdates: !autoUpdates });

View File

@@ -29,12 +29,13 @@ export class AttachmentStream extends ReadableStream<ZipFile> {
signal?: AbortSignal,
onProgress?: (current: number) => void
) {
let index = 0;
const counters: Record<string, number> = {};
if (signal)
signal.onabort = async () => {
await db.fs?.cancel(GROUP_ID, "download");
};
let index = 0;
super({
start() {},
async pull(controller) {
@@ -63,9 +64,9 @@ export class AttachmentStream extends ReadableStream<ZipFile> {
});
if (file) {
const filePath = attachment.metadata.filename;
const filePath: string = attachment.metadata.filename;
controller.enqueue({
path: filePath,
path: makeUniqueFilename(filePath, counters),
data: new Uint8Array(await file.arrayBuffer())
});
} else {
@@ -79,3 +80,17 @@ export class AttachmentStream extends ReadableStream<ZipFile> {
});
}
}
function makeUniqueFilename(
filePath: string,
counters: Record<string, number>
) {
filePath = filePath.toLowerCase();
counters[filePath] = (counters[filePath] || 0) + 1;
if (counters[filePath] === 1) return filePath;
const parts = filePath.split(".");
return `${parts.slice(0, -1).join(".")}-${counters[filePath]}.${
parts[parts.length - 1]
}`;
}

View File

@@ -17,19 +17,22 @@ 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 { Unzipped } from "fflate";
import { AsyncZippable } from "fflate";
import { sanitizeFilename } from "@notesnook/common";
const textEncoder = new TextEncoder();
type File = { filename: string; content: string };
type File = { filename: string; content: string; date: number };
async function zip(files: File[], format: string): Promise<Uint8Array> {
const obj: Unzipped = Object.create(null);
const obj: AsyncZippable = Object.create(null);
files.forEach((file) => {
const name = sanitizeFilename(file.filename, { replacement: "-" });
let counter = 0;
while (obj[makeFilename(name, format, counter)]) ++counter;
obj[makeFilename(name, format, counter)] = textEncoder.encode(file.content);
obj[makeFilename(name, format, counter)] = [
textEncoder.encode(file.content),
{ mtime: file.date }
];
});
const { zip } = await import("fflate");
return new Promise((resolve, reject) =>

View File

@@ -329,23 +329,14 @@ function RecoveryMethods(props: BaseRecoveryComponentProps<"methods">) {
function RecoveryKeyMethod(props: BaseRecoveryComponentProps<"method:key">) {
const { navigate } = props;
const [progress, setProgress] = useState("0");
const [progress, setProgress] = useState(0);
useEffect(() => {
db.eventManager.subscribe(
EVENTS.syncProgress,
({
type,
total,
current
}: {
type: string;
total: number;
current: number;
}) => {
if (total === current) return;
({ type, current }: { type: string; current: number }) => {
if (type === "download") {
setProgress(((current / total) * 100).toFixed());
setProgress(current);
}
}
);
@@ -358,11 +349,11 @@ function RecoveryKeyMethod(props: BaseRecoveryComponentProps<"method:key">) {
title="Recover your account"
subtitle={"Use a data recovery key to reset your account password."}
loading={{
title: `Downloading your data (${progress}%)`,
title: `Downloading your data (${progress})`,
subtitle: "Please wait while your data is downloaded & decrypted."
}}
onSubmit={async (form) => {
setProgress("0");
setProgress(0);
const user = await db.user?.getUser();
if (!user) throw new Error("User not authenticated");
@@ -479,14 +470,13 @@ function BackupData(props: BaseRecoveryComponentProps<"backup">) {
function NewPassword(props: BaseRecoveryComponentProps<"new">) {
const { navigate, formData } = props;
const [progress, setProgress] = useState("0");
const [progress, setProgress] = useState(0);
useEffect(() => {
db.eventManager.subscribe(
EVENTS.syncProgress,
({ total, current }: { total: number; current: number }) => {
if (total === current) return;
setProgress(((current / total) * 100).toFixed());
({ current }: { current: number }) => {
setProgress(current);
}
);
}, []);
@@ -500,11 +490,11 @@ function NewPassword(props: BaseRecoveryComponentProps<"new">) {
"Notesnook is E2E encrypted — your password never leaves this device."
}
loading={{
title: `Resetting account password (${progress}%)`,
title: `Resetting account password (${progress})`,
subtitle: "Please wait while we reset your account password."
}}
onSubmit={async (form) => {
setProgress("0");
setProgress(0);
if (form.password !== form.confirmPassword)
throw new Error("Passwords do not match.");

View File

@@ -8,6 +8,6 @@ Unfortunately, we cannot find what you are looking for.
Here are some other pages you might be interested in:
1. [Installing Notesnook](installation)
1. [Installing Notesnook](organizing-notes/organize-notes-using-notebooks)
2. [Creating your first note](create-a-note-in-notesnook)
3. [How is my data encrypted?](how-is-my-data-encrypted)

View File

@@ -11,4 +11,6 @@ Notesnook is our **proof** that privacy does _not_ (always) have to come at the
And with that convenience in mind, we believe that it is equally important to put together a simple & useful help website about everything Notesnook. Our help is a complete walkthrough of the Notesnook app and it's features.
So let's get started with [installing Notesnook](/installation) on all your devices.
Before we get started, let's [download & install Notesnook](https://notesnook.com/downloads) on all your devices.
Once that's done, let's learn how to [create your first note](/create-a-note-in-notesnook) in Notesnook.

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@@ -163,6 +163,10 @@ nav li {
line-height: 24px;
}
li.nested {
min-height: 38px !important;
}
nav li li:not(li.nested):hover {
background-color: var(--hover-bg);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@@ -5,41 +5,58 @@ description: Notesnook allows you to backup all your notes data to a single back
# Backup your notes
It is always a good practice to take a backup of your data so in case you lose access to your account, you don't lose your notes. Currently backups are local only.
It is always a good practice to take regular backups of your data so you can easily recover your data in case of data corruption or losing access to your account. All backups are stored locally encrypted (unless you turn off backup encryption) on your device.
> warning
> error Store your password & recovery key safely
>
> Since all your data is end-to-end encrypted, we do not have the access to restore your account if you forgot your account password and recovery key. Therefore we recommend that you store your password safely and make sure that you have saved your account recovery.
> Since all your data is end-to-end encrypted, we have no way to restore your account data if you forget your account password and lose your account recovery key. That's why we recommend that you store your password & recovery key in a password manager or some other safe place.
# [Desktop/Web](#/tab/web)
1. Go to Settings from Sidebar
2. Scroll down to `Backup & Restore` section
3. Click on `Backup Data` to download the `.nnbackup` file
1. Go to Settings
2. Scroll down in the Settings navigation menu and click on `Backup & export` section
3. Click on `Create backup` under `Backup now` heading to create a new `.nnbackupz` file
![](/create-backup-web.png)
# [Mobile](#/tab/mobile)
1. Go to Settings from Sidebar
2. Scroll down to `Backup and Restore`
3. Go to `Backups`
4. Press on `Backup now` to save `.nnbackup` file
3. Tap on `Backups`
4. Press on `Backup now` to create a new `.nnbackupz` file
> info
>
> On **Android** when you take a backup first time, you will be asked to select a folder on your phone where you would like to store backup files. You can always change your backup files location from `Backups > Change backup location`.
> On **Android** when you take a backup for the first time, you will be asked to select a folder where you want to store all your backup files. You can always change your backup files location from `Backups > Select backup directory`.
>
> Regardless of the folder you select, Notesnook will create a folder "Notesnook/backups" inside it and store backup files there.
> Regardless of the folder you select, Notesnook will create a folder "Notesnook/backups" inside it and store all backup files there.
---
## Automatic Backups
On mobile and desktop apps you can enable automatic daily, weekly or monthly backups of your notes. Your data will backup automatically and backup file will be stored locally.
For maximum safety against potential data loss, you can enable daily, weekly or monthly backups of your notes. Enabling automatic backups will ensure that all your data is safely backed up locally after a regular interval.
# [Desktop/Web](#/tab/web)
# [Desktop](#/tab/desktop)
1. Go to Settings > Backup & Restore
2. Select automatic backup frequency to enable automatic backups
1. Go to Settings
2. Scroll down in the Settings navigation menu and click on `Backup & export` section
3. Select the Automatic backups interval from the dropdown
![](/auto-backups-desktop.png)
# [Web](#/tab/web)
> info
>
> On the **web** app there is no way to automatically save backups to a folder, that is why Notesnook only reminds the users when it's time to create a new backup.
1. Go to Settings
2. Scroll down in the Settings navigation menu and click on `Backup & export` section
3. Select the Backup reminders interval from the dropdown
![](/auto-backups-web.png)
# [Mobile](#/tab/mobile)
@@ -51,41 +68,44 @@ On mobile and desktop apps you can enable automatic daily, weekly or monthly bac
## Encrypted Backups (Recommended)
To keep your backups secure & private, it is recommended that you enable encryption on your backup files instead of storing them as plain json data.
To keep your backups secure & private, it is recommended that you enable encryption on your backup files instead of storing them as plaintext data. **Starting from v2.6.0, encrypted backups are enabled by default for all users, free or Pro.**
# [Desktop/Web](#/tab/web)
1. Go to Settings > Backup & Restore
2. Enable encrypted backups option
1. Go to Settings
2. Scroll down in the Settings navigation menu and click on `Backup & export` section
3. Click on the toggle next to `Backup encryption` to enable/disable encrypted backups
# [Mobile](#/tab/mobile)
1. Go to Settings > Backup & Restore
2. Press Backups
3. Enable encrypted backups option
1. Go to `Settings` > `Backup & Restore`
2. Tap on `Backups`
3. Tap on the toggle next to `Backup encryption` to enable/disable encrypted backups
---
> info
>
> Backups are encrypted with your account password.
> Backups are always encrypted with your account password.
# Restore a backup
To restore a backup, you must be logged in to your Notesnook account.
At any point in time, you can restore a backup to recover lost data. However, **to restore a backup, you must be logged in to your Notesnook account.** Backups created on one account can be restored on another Notesnook account.
# [Desktop/Web](#/tab/web)
1. Go to Settings from Sidebar
2. Scroll down to `Backup & Restore` section
3. Click on `Restore backup`
4. Select the `.nnbackup` file from your PC that you want to restore.
1. Go to Settings
2. Scroll down in the Settings navigation menu and click on `Backup & export` section
3. Click on `Restore` button next to `Restore backup` heading
4. Select the `.nnbackupz` or `.nnbackup` file from your PC that you want to restore.
# [Mobile](#/tab/mobile)
1. Go to Settings from Sidebar
2. Scroll down to `Backup & Restore` section
3. Press on `Restore backup`
4. From `Restore backup` sheet, select the backup you want to restore. If your backup file is located in some other location, click on `Open file manager` on top right corner of the sheet then select the backup file.
3. Tap on `Restore backup`
4. From `Restore backup` sheet, select the backup you want to restore. If your backup file is located in some other location, click on `Restore from files` on top right corner of the sheet then select the backup file.
<img src="/restore-backup-mobile.png" height="700px">
---

View File

@@ -5,33 +5,34 @@ description: Notesnook let's you create unlimited notes for free. Learn how you
# Creating your first note
You are in a notes app, the first thing you want to do is make a note.
You are in a note taking app, the first thing you'd want to do is create a note.
# [Desktop/Web](#/tab/web)
1. Click on the `+` button on top right corner or click anywhere inside the editor to focus it.
2. Start typing in the editor and your note will be saved.
2. Start typing in the editor and a new note will be automatically created.
3. As you type, your note is saved automatically whenever you stop for a few seconds.
![First note web](/first-note-desktop.png)
> info
>
> The bottom right corner of the app will show the no. of words of current note & the last saved time.
> The bottom right corner of the app will show the number of words of current note & the last saved time.
>
> ![Status bar desktop](/editor-status-bar-desktop.png)
# [Mobile](#/tab/mobile)
1. On mobile, just like desktop, regardless of what screen you have opened, you can always swipe from right to left and open the editor.
2. You can also press the `+` button on bottom right corner to open the editor when you are in notes, a topic or tag.
3. As you type, your note is saved automatically whenever you stop for a few seconds.
1. On mobile regardless of what screen you are on, you can swipe from right to left to open the editor.
2. You can also press the `+` button on bottom right corner to open the editor (note: the `+` button is visible only on some screens).
3. Start typing in the editor and a new note will be automatically created.
4. As you type, your note is saved automatically whenever you stop for a few seconds.
![First note mobile](/first-note-mobile.png)
> info
>
> Below the note title is the editor status bar. It shows you the no. of words in the note and last saved time.
> Below the note title is the editor status bar. It shows you the number of words in the note and last saved time.
---

View File

@@ -12,7 +12,7 @@ Notesnook allows you to delete your accounts and your data without any questions
1. Open Notesnook web or desktop app
2. Make sure you are logged in
3. Go to Settings
4. Scroll to the very end & click on `Delete account`
4. Click on `Delete account` button next to **Account removal** heading
## [Mobile](#/tab/mobile)
@@ -20,7 +20,7 @@ Notesnook allows you to delete your accounts and your data without any questions
2. Make sure you are logged in
3. Go to Settings
4. Go to `Account Settings`
5. Click on `Delete account`
5. Tap on `Delete account`
---
@@ -32,4 +32,4 @@ All your notes, notebooks, attachments, and any other data that you created with
### Is there any way to recover deleted data?
No. Once you delete your account, it's gone for good and there is no way for us or anyone else to recover it. If your data is important to you, make sure to take a [backup](/backup-and-restore-notes-in-notesnook) before deleting your account or [exporting your notes](/export-notes-from-notesnook).
No. Once you delete your account, it's gone for good and there is no way for us or anyone else to recover it. If your data is important to you, make sure to take a [backup](/backup-and-restore-notes-in-notesnook) or [export your notes](/export-notes-from-notesnook) before deleting your account.

View File

@@ -4,6 +4,8 @@ Notesnook supports quick actions from the "jumplist" menu (dock menu on macOS):
## [Windows](#/tab/windows)
![](/static/desktop-integration/jumplist-menu-windows.png)
## [Linux](#/tab/linux)
![](/static/desktop-integration/jumplist-menu-linux.png)
@@ -16,9 +18,8 @@ Notesnook supports quick actions from the "jumplist" menu (dock menu on macOS):
## How it works?
The jumplist menu doesn't work if you already have Notesnook opened. They are responsible for opening Notesnook at the specified page. For example:
Jumplist menu items are responsible for opening Notesnook at the specified page. For example:
1. Close Notesnook if you already have it opened
2. Right click on the Notesnook icon in your taskbar/dock
3. Click on "New notebook"
4. Notice how the new notebook dialog is already opened after the app starts.
1. Right click on the Notesnook icon in your taskbar/dock
2. Click on "New notebook"
3. Notice how the new notebook dialog is opened after the Notesnook app is focused

View File

@@ -5,23 +5,23 @@ description: Notesnook is zero lock-in. You can always export and backup your no
# Exporting notes
You can export your notes as PDF, HTML, Markdown and Plain text files on desktop and mobile apps.
You can export some or all your notes as PDF, HTML, Markdown and Plain text files on all Notesnook apps.
## Exporting a single note
# [Desktop/Web](#/tab/web)
1. Right click on a note
2. Click on 'Export'
3. Select the desired format to export your note
2. Click on `Export as`
3. Select the desired format
4. Wait a few moments while your note is exported
5. Save the note in desired location
5. Save the note at your desired location
# [Mobile](#/tab/mobile)
1. Press the ![Three dot button](/three-dot-button.png) button on a note
2. Press on 'Export'
3. Select the desired format to export your note
1. Tap the ![Three dot button](/three-dot-button.png) button on a note
2. Tap on `Export`
3. Select the desired format
4. Wait a few moments while your note is exported
5. Exported notes are stored in `Notesnook/exported` folder.
@@ -31,20 +31,19 @@ You can export your notes as PDF, HTML, Markdown and Plain text files on desktop
# [Desktop/Web](#/tab/web)
1. Hold `Ctrl` & Click on a note to enter multi-selection mode
2. Select the notes you want to export then right click
3. Click on "Export as" & select your desired export format
1. Hold `Ctrl` & click on all the notes you want to export
2. Right click & click on `Export as`
3. Select your desired export format
4. Wait a few moments while your notes are exported
5. Save the note in desired location
5. Save the `.zip` file at your desired location
# [Mobile](#/tab/mobile)
1. Long press on a note item
2. Press on "Select" on the action bar to enter selection mode
3. Select the notes you want to export
4. Press on the export icon on top right corner
5. Select the desired format to export your note
6. Exported notes are stored in `Notesnook/exported` folder
1. Long press on a note to enter multi selection mode
2. Tap on all the notes you want to export
3. Press on the Export button on top right corner
4. Select the desired format
5. Exported notes are stored in `Notesnook/exported` folder
---
@@ -52,14 +51,19 @@ You can export your notes as PDF, HTML, Markdown and Plain text files on desktop
# [Desktop/Web](#/tab/web)
1. Go to Settings from Sidebar
2. Scroll down to `Backup & Restore` section
3. Click on `Backup Data` to download the `.nnbackup` file
1. Go to Settings
2. Scroll down to `Import & export` section
3. Click on `Backup & export`
4. Click on `Select format` dropdown next to `Export all notes` heading
5. Select the desired format
6. Enter account password for authentication
7. Save the `.zip` file at your desired location
# [Mobile](#/tab/mobile)
1. Go to Settings from Sidebar
2. Scroll down to `Backup and Restore`
3. Select "Export all notes"
4. Select the desired format to export your notes
5. Exported notes are stored in `Notesnook/exported` folder as a single .zip file
3. Tap on `Export all notes`
4. Select the desired format
5. Enter account password for authentication
6. Exported notes are stored in `Notesnook/exported` folder as a single .zip file

View File

@@ -1,130 +0,0 @@
---
title: Installation
description: Be productive and keep your notes private with Notesnook. Download Notesnook on Windows, Mac, Linux, iOS, android or even access your notes on the web directly.
---
# Installation
Notesnook is fully cross platform & downloadable on all your devices.
## Mac OS
# [Installing via dmg](#/tab/dmg)
Download the latest `.dmg` file from the links below.
- [For Intel Mac users](https://notesnook.com/releases/macos/notesnook_mac_x64.dmg)
- [For M1 Macs users](https://notesnook.com/releases/macos/notesnook_mac_arm64.dmg)
Once you have the dmg downloaded,
1. Double click to open the .dmg file
2. Move the Notesnook.app to `Applications` folder.
![installing notesnook on mac os](/install-macos.png)
Notesnook is now installed on your Mac. Start notesnook from the Launchpad.
# [Installing via terminal](#/tab/terminal)
If you have [homebrew](https://brew.sh/) installed, you can quickly install Notesnook via terminal:
```bash
brew install notesnook
```
Once installation completes, you can start Notesnook app from the Launchpad.
---
## Windows
# [Installing via .exe files](#/tab/exe)
Download the latest **.exe** file from the links below:
- [Notesnook x64 (Setup)](https://notesnook.com/releases/windows/notesnook_win_x64.exe)
- [Notesnook x64 (Portable)](https://notesnook.com/releases/windows/notesnook_win_x64_portable.exe)
Once you have the **.exe** downloaded:
1. Double click to open the **.exe** file
2. Follow the installation instructions to install the app.
# [Installing via PowerShell](#/tab/terminal)
To install Notesnook, run the following command from the command line or from PowerShell:
#### [Chocolatey](https://community.chocolatey.org/packages/notesnook)
```bash
choco install notesnook
```
#### [Winget](https://winget.run/pkg/Streetwriters/Notesnook)
```bash
winget install -e --id Streetwriters.Notesnook
```
#### [Scoop](https://scoop.sh/#/apps?q=notesnook&s=0&d=1&o=true)
```bash
scoop bucket add extras && scoop install notesnook
```
Start Notesnook from Windows Start Menu.
---
## Linux
# [AppImage (Recommended)](#/tab/appimage)
You can install Notesnook on any linux distro with AppImage.
[Download AppImage](https://notesnook.com/releases/linux/notesnook_linux_x86_64.AppImage)
# [.deb](#/tab/deb)
If you are on Ubuntu or debian based distro, you can install Notesnook via .deb package.
[Download .deb package](https://notesnook.com/releases/linux/notesnook_linux_amd64.deb)
# [.rpm](#/tab/rpm)
If you are on Fedora based distro, you can install Notesnook via .rpm package.
[Download .rpm package](https://notesnook.com/releases/linux/notesnook_linux_x86_64.rpm)
# [Snapcraft](#/tab/snap)
Notesnook can be downloaded on any linux distro via Snapcraft.
[Go to Snapcraft](https://snapcraft.io/notesnook)
---
## Android
# [Google Playstore](#/tab/android)
If you have Google play services installed on your Android phone, you can get Notesnook from Google Play store
[Download from playstore](https://play.google.com/store/apps/details?id=com.streetwriters.notesnook)
# [Github](#/tab/android-github)
You you want to download Notesnook app that is free of any google play services bloat or if you use a custom android rom, you can download Notesnook via Github.
- [Arm64-v8a.apk](https://notesnook.com/releases/android/notesnook-arm64-v8a.apk)
- [Armeabi v7a.apk](https://notesnook.com/releases/android/notesnook-armeabi-v7a.apk)
- [x86.apk](https://notesnook.com/releases/android/notesnook-x86.apk)
- [x86_64.apk](https://notesnook.com/releases/android/notesnook-x86_64.apk)
---
## iOS
iOS users can get Notesnook from the AppStore.
[AppStore](https://apps.apple.com/us/app/notesnook-take-private-notes/id1544027013)

View File

@@ -13,20 +13,19 @@ Adding notes to private vault is useful when you do not want anyone to read your
# [Desktop/Web](#/tab/web)
1. Go to Settings from Sidebar.
2. Click on `Create a vault` in `Vault` section.
3. Enter the password for the vault in dialog.
4. Click on create to create the vault.
5. Now you will be able to lock your notes with the password for vault.
1. Go to Settings
2. Go to `Vault` in `Security & privacy` section
3. Click on `Create` button
4. Enter the password for your vault (this password will be used to open all locked notes)
5. Click on `Create` in the dialog to create the vault.
# [Mobile](#/tab/mobile)
1. Go to Settings from Sidebar
2. Scroll down to `Privacy and Security` section
3. Press on `Create vault`
4. Enter password for the vault and reenter to confirm.
5. Press on `Create` button to create the vault.
6. Now you will be able to lock your notes with the password for vault.
3. Tap on `Create vault`
4. Enter password for the vault (this password will be used to open all locked notes)
5. Tap on `Create` button to create the vault.
---
@@ -35,14 +34,14 @@ Adding notes to private vault is useful when you do not want anyone to read your
# [Desktop/Web](#/tab/web)
1. Right click on any note
2. Select `Lock` from the `Note properties` menu
3. Enter the password for the vault in dialog.
4. Click on Lock to lock the note.
2. Select `Lock` from the context menu
3. Enter the password for the vault
4. Press `Enter` key to lock the note
# [Mobile](#/tab/mobile)
1. Press on the `three-dot menu` button
2. Press on `Add to vault` button in `Note properties` menu
1. Tap the ![Three dot button](/three-dot-button.png) button on a note
2. Tap on `Lock` button in the note properties
3. Enter password for the vault
4. Press on `Lock` to add note to vault.
@@ -57,16 +56,16 @@ To open, edit or delete a locked note, you must provide the password for the vau
# [Desktop/Web](#/tab/web)
1. Right click on any note
2. Select `Unlock` from the `Note properties` menu
2. Select `Unlock` from the context menu
3. Enter the password for the vault in dialog.
4. Click on Unlock to remove note from vault
# [Mobile](#/tab/mobile)
1. Press on the `three-dot menu` button
2. Press on `Remove from vault` button in `Note properties` menu
1. Tap the ![Three dot button](/three-dot-button.png) button on a note
2. Tap on `Unlock` button in the note properties
3. Enter password for the vault
4. Press on `Unlock` to remove note from vault.
4. Tap on `Unlock` to remove note from vault.
---
@@ -74,19 +73,20 @@ To open, edit or delete a locked note, you must provide the password for the vau
# [Desktop/Web](#/tab/web)
1. Go to Settings from Sidebar.
2. Click on `Change vault password` in `Vault` section.
3. Enter the old and new password for the vault in dialog.
4. Click on Change to update password
5. All your locked notes will be encrypted with the new password.
1. Go to Settings
2. Go to `Vault` in `Security & privacy` section
3. Click on `Change` button next to `Change vault password` heading
4. Enter the old and new password for the vault
5. Click on `Change password` to update the password
# [Mobile](#/tab/mobile)
1. Go to Settings from Sidebar.
2. Scroll down to `Privacy and Security` section
3. Press on `Change vault password`
4. Click on Change to update password
5. All your locked notes will be encrypted with the new password.
3. Tap on `Vault`
4. Press on `Change vault password`
5. Enter the old and new password for the vault
6. Click on Change to update password
---
@@ -94,34 +94,38 @@ To open, edit or delete a locked note, you must provide the password for the vau
# [Desktop/Web](#/tab/web)
1. Go to Settings from Sidebar.
2. Go to `Privacy and Security` section and Click on `Clear vault`.
3. Enter your vault password and click on clear vault. All notes in the vault will be deleted.
1. Go to Settings
2. Go to `Vault` in `Security & privacy` section
3. Click on `Clear` button next to `Clear vault` heading
4. Enter your vault password and click on clear vault. All notes in the vault will be deleted.
# [Mobile](#/tab/mobile)
1. Go to Settings from Sidebar.
2. Scroll down to `Privacy and Security` and go to `Vault`
3. Tap on `Clear vault`
4. Enter your vault password and tap on `Clear`. All notes in the vault will be deleted.
2. Scroll down to `Privacy and Security` section
3. Tap on `Vault`
4. Tap on `Clear vault`
5. Enter your vault password and tap on `Clear`. All notes in the vault will be deleted.
---
## Delete vault
In the event that you have forgotten your vault password, you can delete the vault and optionally delete all the notes in it.
In the event that you have forgotten your vault password, you can delete the vault and (optionally) delete all the notes in it.
# [Desktop/Web](#/tab/web)
1. Go to Settings from Sidebar.
2. Go to `Privacy and Security` section and Click on `Delete vault`.
3. Enter your account password and click on clear vault. All notes in the vault will be deleted.
1. Go to Settings
2. Go to `Vault` in `Security & privacy` section
3. Click on `Delete` button next to `Delete vault` heading
4. Enter your account password and click on `Delete vault` to delete the vault
# [Mobile](#/tab/mobile)
1. Go to Settings from Sidebar.
2. Scroll down to `Privacy and Security` and go to `Vault`
3. Tap on `Clear vault`
4. Enter your account password and tap on `Delete`. The vault will be deleted.
2. Scroll down to `Privacy and Security` section
3. Tap on `Vault`
4. Tap on `Delete vault`
5. Enter your account password and tap on `Delete` to delete the vault
---

View File

@@ -4,7 +4,13 @@ title: Notebooks
# Organizing notes with notebooks
Notebooks consist of topics where you can add/create and edit your notes.
Notebooks are a quick and easy way for multi-level notes organization. Starting from v2.4.7, topics are now optional in Notebooks and notes can be put directly under a notebook.
If you are coming from Evernote, you can think of Notebooks as "Evernote Stacks" & topics as "Evernote Notebooks". However, keep in mind, that there is a key difference:
> "Evernote Stacks" do not allow putting notes directly under them i.e., a Stack is just a collection of Notebooks and nothing else.
In Notesnook, one note can belong to multiple notebooks & topics. This allows for very flexible organization structures. For example, an author can "link" the character list to all the chapter topics instead of duplicating it for each chapter. Similarly, a user can link common notes between multiple topics without the need for any duplication.
## Creating a notebook
@@ -98,87 +104,78 @@ Notebooks consist of topics where you can add/create and edit your notes.
# [Desktop/Web](#/tab/web)
1. Go to `Notebooks` and open a notebook
2. Open the topic in which you want to add a note
3. Click on the ![Three dot button](/plus-button-desktop.png) button on top right corner
4. Start writing in editor to create a note in the topic
2. Click on the ![Three dot button](/plus-button-desktop.png) button on top right corner
3. Start writing in editor to create a note in the notebook
# [Mobile](#/tab/mobile)
1. Go to `Notebooks` and open a notebook
2. Open the topic in which you want to add a note
3. Click on the ![Three dot button](/plus-button-desktop.png) button on bottom right corner to open the editor or just **swipe from right to left** to open editor
4. Start writing in editor to create a note in the topic
2. Click on the ![Three dot button](/plus-button-desktop.png) button on bottom right corner to open the editor or just **swipe from right to left** to open editor
3. Start writing in editor to create a note in the notebook
---
> info
>
> Once a note is added to a notebook, it's references will show on top of the note in the list. Clicking on it will take you to the topic.
> Once a note is added to a notebook, you will see its path on top of the note in the list. Clicking on it will take you to the respective notebook/topic.
>
> ![Notebook reference on a note](/notebook-ref.png)
## Adding a note to a topic
## Linking an existing note to a notebook/topic
# [Desktop/Web](#/tab/web)
1. Right click on a note
2. Select `Add to Notebook(s)`
3. Select the notebook and topic in which you want to add the note
4. Click "Finish" to add the notes to the topic.
5. You should now see the notebook/topic below the note title. You can click on it to navigate to the topic directly.
2. Click on `Notebooks` > `Link to...`
3. Select the notebook or topic you want to link the note to (you can select multiple)
4. Click `Done` to save your changes.
# [Mobile](#/tab/mobile)
1. Press the ![Three dot button](/three-dot-button.png) button
2. Press on `Add to notebook` button.
3. Select the notebook and topic in which you want to add the note.
4. Once done, swipe down to close the sheet.
5. You should now see the notebook/topic on the top left side of the note. You can click on it to navigate to the topic directly.
1. Tap the ![Three dot button](/three-dot-button.png) button
2. Tap on `Link Notebooks` button.
3. Select the notebook or topic you want to link the note to (you can select multiple)
4. Click the `Save` button at the top right corner to save your changes.
---
> info
>
> In Notesnook a single note can exist in multiple Notebooks and topics. However a note shows at most only one reference on top.
> In Notesnook a single note can exist in multiple Notebooks and topics. However, a note will show only one reference on top.
## Adding multiple notes to a topic
## Linking multiple notes to a notebook/topic
# [Desktop/Web](#/tab/web)
1. Hold `Ctrl` key and click on the notes you want to select
1. Hold `Ctrl` key and click on all the notes you want to link
2. Right click on selected notes
3. Click on `Add to notebook(s)`.
4. Select the notebook and topic in which you want to add the notes
5. Click "Finish" to add the notes to the topic.
6. You should now see the notebook/topic below the note title. You can click on it to navigate to the topic directly.
3. Click on `Notebooks` > `Link to...`
4. Select the notebook or topic you want to link the note to (you can select multiple)
5. Click `Done` to save your changes.
# [Mobile](#/tab/mobile)
1. Long press on a note.
2. Click on `Select` from the `Action bar`.
3. Select notes & press the ![Three dot button](/plus-button-desktop.png) button in top header
4. Select the notebook and topic in which you want to add the notes
5. Once done, swipe down to close the sheet.
1. Long press on a note to enter multi selection mode.
2. Tap on all the notes you want to link to select them
3. Tap the `+` button in top header
4. Select the notebook or topic you want to link the note to (you can select multiple)
5. Click the `Save` button at the top right corner to save your changes.
---
## Remove note from a topic
## Remove note from a notebook/topic
# [Desktop/Web](#/tab/web)
1. Right click on a note
2. Select `Add to Notebook(s)`
3. The notebooks in which the note exists will appear green. Click on the notebook to expand topics
4. Click on the topics in notebook from which you want to remove th note
5. Click "Finish" to save changes
2. Click on `Notebooks` in the context menu
3. Click on the notebook/topic you want to remove the note from
# [Mobile](#/tab/mobile)
1. Press the ![Three dot button](/three-dot-button.png) button
2. Press on `Add to notebooks`
3. The notebook(s) in which the note exists will appear green. Press on a notebook to expand topics
4. Tap on topics you want to remove the note from
5. Once done, swipe down to close `Add to notebook(s)` sheet
2. Click on `See all linked notebooks`
3. Long press on the notebook or topic you want to removve the note from
---
@@ -189,14 +186,14 @@ Notebooks consist of topics where you can add/create and edit your notes.
1. Go to `Notebooks` and open the notebook from which you want to delete a topic.
2. Right click on the topic
3. Click on `Delete` to delete the topic.
4. Select whether you also want to delete the notes inside this topic.
4. (Optional) Select whether you also want to delete the notes inside this topic.
# [Mobile](#/tab/mobile)
1. Go to `Notebooks` from side menu and open the notebook from which you want to delete a topic.
2. Press the ![Three dot button](/three-dot-button.png) button to open `Topic properties` sheet
3. Press `Delete topic`
4. Select whether you also want to delete the notes inside this topic.
4. (Optional) Select whether you also want to delete the notes inside this topic.
---
@@ -208,7 +205,6 @@ Notebooks consist of topics where you can add/create and edit your notes.
2. Right click on the notebook
3. Click on `Move to trash`.
4. Select whether you also want to delete the notes inside this notebook.
5. Notebook will be kept in trash for 7 days after which it will be permanently removed.
# [Mobile](#/tab/mobile)
@@ -216,6 +212,5 @@ Notebooks consist of topics where you can add/create and edit your notes.
2. Press the ![Three dot button](/three-dot-button.png) button.
3. Press `Move to trash` to delete the topic.
4. Select whether you also want to delete the notes inside this notebook.
5. Notebook will be kept in trash for 7 days after which it will be permanently removed.
---

View File

@@ -16,13 +16,14 @@ Privacy mode enables some OS specific settings to enhance your privacy while wor
## [Desktop](#/tab/desktop)
1. Open `Settings`
2. Click on `Privacy & security`
3. Click on `Privacy mode` to enable/disable it.
1. Go to `Settings`
2. Scroll down to `Security & privacy` section
3. Click on `Privacy`
4. Click on toggle next to `Privacy mode` to enable/disable privacy mode.
## [Mobile](#/tab/mobile)
1. Open `Settings`
1. Go to `Settings`
2. Scroll down to `Privacy and Security`
3. Tap on `Privacy mode` to enable/disable it

View File

@@ -13,7 +13,6 @@ footer:
copyright: Copyright © 2023 Streetwriters (Private) Limited
navigation:
- path: installation.md
- path: create-a-note-in-notesnook.md
- path: organizing-notes
children:
@@ -78,4 +77,4 @@ navigation:
- path: custom-themes/introduction.md
- path: custom-themes/create-a-theme-with-theme-builder.md
- path: custom-themes/install-a-theme-from-file.md
- path: custom-themes/publish-a-theme.md
- path: custom-themes/publish-a-theme.md

View File

@@ -0,0 +1,8 @@
- Large titles in editor now wrap into multiple lines
- Added support for font scaling in mobile app
- Clipper in share extension now supports clipping dynamically loaded websites
- Cache files are now stored in app's data directory
- Improved drag/drop support in task lists
- Bug fixes
Thank you for using Notesnook!

View File

@@ -0,0 +1,3 @@
- Bug fixes and performance improvements
Thank you for using Notesnook!

View File

@@ -0,0 +1,7 @@
- Fix 24 hour reminder time in date picker
- Fix pin input hide behind keyboard on session expiry
- Disallow links in inline code
- Fix default notebook not set in widget
- Bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -51,6 +51,7 @@ test(
expect(types.every((t) => t === "download")).toBe(true);
console.log("Case 1 log out");
await cleanup(deviceA, deviceB, deviceC);
},
TEST_TIMEOUT
@@ -78,7 +79,8 @@ test(
expect(deviceA.notes.note(note1Id)).toBeTruthy();
expect(deviceB.notes.note(note2Id)).toBeTruthy();
await cleanup(deviceA, deviceA);
console.log("Case 3 log out");
await cleanup(deviceA, deviceB);
},
TEST_TIMEOUT
);
@@ -225,7 +227,8 @@ test(
expect(handler).not.toHaveBeenCalled();
await cleanup(deviceB);
console.log("issue force sync log out");
await cleanup(deviceA, deviceB);
},
TEST_TIMEOUT
);
@@ -264,6 +267,7 @@ test(
true
);
console.log("issue colors log out");
await cleanup(deviceA, deviceB);
},
TEST_TIMEOUT
@@ -310,6 +314,7 @@ test(
expect(deviceA.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
expect(deviceB.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
console.log("issue new topic log out");
await cleanup(deviceA, deviceB);
},
TEST_TIMEOUT
@@ -375,6 +380,7 @@ test(
deviceB.notebooks.notebook(id).topics.topic(topic.id).totalNotes
).toBe(2);
console.log("issue assigning 2 notes log out");
await cleanup(deviceA, deviceB);
},
TEST_TIMEOUT
@@ -420,13 +426,12 @@ async function initializeDevice(id, capabilities = []) {
* @param {...Database} devices
*/
async function cleanup(...devices) {
await Promise.all([
devices.map(async (device) => {
await device.syncer.stop();
await device.user.logout();
device.eventManager.unsubscribeAll();
})
]);
for (const device of devices) {
device.disconnectSSE();
await device.syncer.stop();
await device.user.logout();
device.eventManager.unsubscribeAll();
}
EV.unsubscribeAll();
}

View File

@@ -18,11 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import "isomorphic-fetch";
import crypto from "crypto";
import dotenv from "dotenv";
import { DOMParser } from "linkedom";
globalThis.DOMParser = DOMParser;
require("abortcontroller-polyfill/dist/polyfill-patch-fetch");
globalThis.crypto = crypto;
dotenv.config();

View File

@@ -33,6 +33,7 @@
"pretest": "node scripts/prebuild.mjs",
"pretest:e2e": "node scripts/prebuild.mjs",
"build": "tsc",
"watch": "tsc --watch",
"test:e2e": "cross-env IS_E2E=true vitest run",
"test": "vitest run"
},

View File

@@ -32,7 +32,7 @@ const languagesList = await langen(
path.join(ROOT_DIR, "src", "utils", "templates", "html", "languages")
);
const languageIndex = `function hasRequire() {
return typeof require === "function" && !("IS_DESKTOP_APP" in globalThis);
return typeof require === "function" && !IS_DESKTOP_APP;
}
export async function loadLanguage(language) {

View File

@@ -256,16 +256,8 @@ class Database {
EV.publish(EVENTS.userSubscriptionUpdated, data);
break;
}
case "userDeleted": {
await this.user.logout(false, "Account Deleted");
break;
}
case "userEmailChanged": {
await this.user.logout(true, "Email changed");
break;
}
case "userPasswordChanged": {
await this.user.logout(true, "Password changed");
case "logout": {
await this.user.logout(true, data.reason || "Unknown.");
break;
}
case "emailConfirmed": {

Some files were not shown because too many files have changed in this diff Show More