Compare commits

...

58 Commits

Author SHA1 Message Date
Ammar Ahmed
1f691d55a7 mobile: allow saving empty string input 2024-12-13 15:44:08 +05:00
01zulfi
0921fef267 web: show account password label in delete vault dialog (#7085)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-12-11 11:30:42 +05:00
luis-411
60989359e2 theme: make link color align with theme (#7077)
Signed-off-by: Luis Kriner <luis@kriner.info>
2024-12-10 11:01:04 +05:00
luis-411
8fe381afa9 editor: prevent creation of multiple empty notes (#6922)
Signed-off-by: Luis Kriner <luis@kriner.info>
2024-12-10 10:30:19 +05:00
01zulfi
fea9c4e42b web: fix links in report issue dialog (#7071)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-12-10 10:28:17 +05:00
01zulfi
1d78402d5d web: fix tags not showing in locked note (#7061)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-12-10 09:58:13 +05:00
01zulfi
aa72c33aea webclipper: handle default case in listener (#7058)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-12-10 09:57:23 +05:00
01zulfi
69504a150e core: handle error in parseInternalLink (#7065)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-12-10 09:56:47 +05:00
dyw770
a78a2e4ae2 core: fix object-id is sometimes less than 24 chars in length (#6991) (#6994)
Signed-off-by: dyw770 <dyw770@qq.com>
2024-12-06 10:11:57 +05:00
luis-411
0ffbf83141 editor: hide edit link button when in read only mode (#7018)
Signed-off-by: Luis Kriner <luis@kriner.info>
2024-12-06 09:46:44 +05:00
01zulfi
75c346a460 docs: add README.md for web clipper (#7054)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-12-05 10:55:28 +05:00
01zulfi
93af79926d editor: disable spellcheck for inline code (#7036)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-12-05 10:49:40 +05:00
01zulfi
1b354e4bca web: hide spin buttons in mfa code input (#7048)
* they overlap with the resend button

Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-12-05 10:26:54 +05:00
01zulfi
6d53b78f6a web: round off zoom value
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-11-28 09:59:32 +05:00
01zulfi
2462eb1631 monograph: remove callout headings from toc (#6875) (#6993)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-11-26 16:27:32 +05:00
Abdullah Atta
986ff32293 monograph: bump version to 1.2.2 2024-11-26 16:23:00 +05:00
Abdullah Atta
8f7fd9d8ee monograph: use fallback fonts when rendering og image 2024-11-26 16:22:48 +05:00
01zulfi
54d6a5c307 monograph: fix PUBLIC_URL on client
* use loader to get PUBLIC_URL

Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-11-26 15:27:43 +05:00
Abdullah Atta
d672a43a83 monograph: bump version to 1.2.1 2024-11-26 11:58:54 +05:00
Abdullah Atta
48343e5d5c web: do not zoom if e.deltaY is 0 2024-11-25 16:26:02 +05:00
Levi Ob
0d3a8c88c8 web: fix inconsistent zoom amount when adjusting with scroll wheel. (#6990)
Zoom steps are always in increments of 10 when using a mouse wheel or trackpad.

Signed-off-by: Levi Ob <leviob.msrni@slmail.me>
2024-11-25 12:36:32 +05:00
Abdullah Atta
31367066f7 web: run prerequisite checks on app start
this gives useful errors to users if their browser doesn't support something
2024-11-25 12:33:05 +05:00
Abdullah Atta
f7c097988a web: fix cannot read property clear of undefined when pressing fix it 2024-11-25 12:31:38 +05:00
Abdullah Atta
b7f1f36090 web: fix async sqlite
async sqlite mode uses indexeddb and has some limitations like:
- running 1 query at a time
This commit uses a mutex to run all queries sequentially
2024-11-25 12:31:11 +05:00
01zulfi
8756299d80 web: always show desktop backup directory path (#6867)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2024-11-25 10:26:30 +05:00
dyw770
3525d60f8c monograph: add monograph response charset header
Signed-off-by: dyw770 <dyw770@qq.com>
2024-11-24 23:19:36 +05:00
Abdullah Atta
d08a485111 desktop: extract better-sqlite3 patching to its own module 2024-11-24 22:01:28 +05:00
Abdullah Atta
c203b035c4 monograph: bump version to 1.2.0 2024-11-24 15:38:20 +05:00
Abdullah Atta
0f4b2bdae7 monograph: fix Cannot find module '@remix-run/server-runtime' 2024-11-24 15:24:46 +05:00
Abdullah Atta
cb0bc39cd1 monograph: use bun in prod 2024-11-24 15:18:52 +05:00
Abdullah Atta
84abfa3478 monograph: speed up og image generation
this gets rid of satori and uses custom logic for drawing using @napi-rs/canvas
2024-11-24 15:18:52 +05:00
thecodrr
a40a48e2c1 server(theme): add installs.json to gitignore 2024-11-23 17:48:34 +00:00
Abdullah Atta
124499cabb monograph: use sharp output as buffer instead of stream 2024-11-23 22:23:42 +05:00
Ammar Ahmed
e874d1efd4 mobile: changelog 2024-11-23 22:20:05 +05:00
Ammar Ahmed
fb1a17566f mobile: release v3.0.23 2024-11-23 22:17:42 +05:00
Abdullah Atta
8dd8447212 monograph: cache satori output in lru cache
satori is sloooooow so we need to cache.
find another solution for og generation
2024-11-23 22:16:47 +05:00
Ammar Ahmed
eba7341d45 mobile: restore editor state only if not restored already 2024-11-23 22:15:57 +05:00
Ammar Ahmed
08bd36d518 mobile: fix deps 2024-11-23 22:15:57 +05:00
Ammar Ahmed
7a45522ff3 mobile: make attachment item pressable 2024-11-23 22:15:57 +05:00
Ammar Ahmed
4ee2923181 mobile: fix attachment downloads failing on mobile 2024-11-23 22:15:57 +05:00
Abdullah Atta
48459efdb1 monograph: mark sharp as external in ssr build 2024-11-23 21:45:44 +05:00
Abdullah Atta
c848f3c588 monograph: mark sharp as external dep in vite 2024-11-23 21:44:24 +05:00
Abdullah Atta
52a75cfdaf monograph: install sharp with optional deps during docker 2024-11-23 21:42:28 +05:00
Abdullah Atta
9331b5f919 monograph: bump version to 1.1.1 2024-11-23 21:33:37 +05:00
Abdullah Atta
2ff53d4650 monograph: use sharp for generating og images 2024-11-23 21:33:21 +05:00
Abdullah Atta
7f2c8ca05a web: fix checkout snapshots 2024-11-23 14:56:58 +05:00
Abdullah Atta
9e752e9406 web: bump version to 3.0.22 2024-11-23 14:53:31 +05:00
Abdullah Atta
b8c3e2098f web: fix one-time discount wrongly shown as recurring 2024-11-23 14:39:45 +05:00
Abdullah Atta
4308e3bf49 web: fix editor zoom on trackpads 2024-11-23 14:03:31 +05:00
Abdullah Atta
25956ccadb web: remove auto item reveal when opening items 2024-11-23 13:31:23 +05:00
Abdullah Atta
af3fba5176 core: lazily use db in config & kv stores 2024-11-23 13:28:55 +05:00
Ammar Ahmed
4afaa6a36e mobile: fix reminder visual issues 2024-11-23 13:00:57 +05:00
Ammar Ahmed
e98e1a5f36 mobile: fix pdf-preview crash 2024-11-23 12:24:17 +05:00
01zulfi
abfdad0fec web: fix setting item width (#6909) (#6935)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
Co-authored-by: Abdullah Atta <thecodrr@protonmail.com>
2024-11-23 12:16:26 +05:00
dyw770
99e50ddad6 monograph: fix monograph version endpoints cors
Signed-off-by: dyw770 <dyw770@qq.com>
2024-11-23 12:05:29 +05:00
Ammar Ahmed
67a61dd3f3 mobile: release v3.0.22 2024-11-20 12:40:37 +05:00
Ammar Ahmed
13df01d55a mobile: fix readonly editor not loading 2024-11-20 12:38:17 +05:00
Ammar Ahmed
af4ea036c8 mobile: fix sync on iOS 2024-11-20 12:37:38 +05:00
117 changed files with 3836 additions and 3268 deletions

View File

@@ -10,7 +10,7 @@ const authors = readFileSync("AUTHORS", "utf-8");
const isAuthor = authors.includes(`<${authorEmail}>`);
const SCOPES = [
// for full list of scopes + details see: https://github.com/streetwriters/notesnook-private/blob/master/CONTRIBUTING.md#commit-guidelines
// for full list of scopes + details see: https://github.com/streetwriters/notesnook/blob/master/CONTRIBUTING.md#commit-guidelines
"mobile",
"web",
@@ -36,7 +36,8 @@ const SCOPES = [
"global",
"docs",
"themebuilder",
"intl"
"intl",
"webclipper"
];
module.exports = {

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/desktop",
"version": "3.0.21",
"version": "3.0.22",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/desktop",
"version": "3.0.21",
"version": "3.0.22",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "3.0.21",
"version": "3.0.22",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/cjs/index.js",

View File

@@ -18,19 +18,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import path from "path";
import fs, { readFile, writeFile } from "fs/promises";
import { existsSync, readFileSync } from "fs";
import fs from "fs/promises";
import { existsSync } from "fs";
import yargs from "yargs-parser";
import os from "os";
import * as childProcess from "child_process";
import { fileURLToPath } from "url";
import { patchBetterSQLite3 } from "./patch-better-sqlite3.mjs";
const args = yargs(process.argv);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const packageJson = JSON.parse(
readFileSync(path.join(__dirname, "..", "package.json"), "utf-8")
);
const webAppPath = path.resolve(path.join(__dirname, "..", "..", "web"));
@@ -46,12 +44,6 @@ if (args.rebuild || !existsSync(path.join(webAppPath, "build"))) {
// temporary until there's support for prebuilt binaries for linux ARM
if (os.platform() === "linux") await patchBetterSQLite3();
// if (os.platform() === "win32")
// await exec(
// `npx prebuildify --arch=arm64 --strip -t electron@${packageJson.devDependencies.electron}`,
// path.join(__dirname, "..", "node_modules", "sodium-native")
// );
await fs.cp(path.join(webAppPath, "build"), "build", {
recursive: true,
force: true
@@ -83,21 +75,3 @@ async function exec(cmd, cwd) {
cwd: cwd || process.cwd()
});
}
async function patchBetterSQLite3() {
const jsonPath = path.join(
__dirname,
"..",
"node_modules",
"better-sqlite3-multiple-ciphers",
"package.json"
);
const json = JSON.parse(await readFile(jsonPath, "utf-8"));
json.version = "11.5.1";
json.homepage = "https://github.com/thecodrr/better-sqlite3-multiple-ciphers";
json.repository.url =
"git://github.com/thecodrr/better-sqlite3-multiple-ciphers.git";
await writeFile(jsonPath, JSON.stringify(json));
}

View File

@@ -0,0 +1,48 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 { readFile, writeFile } from "fs/promises";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export async function patchBetterSQLite3() {
const jsonPath = path.join(
__dirname,
"..",
"node_modules",
"better-sqlite3-multiple-ciphers",
"package.json"
);
const json = JSON.parse(await readFile(jsonPath, "utf-8"));
json.version = "11.5.1";
json.homepage = "https://github.com/thecodrr/better-sqlite3-multiple-ciphers";
json.repository.url =
"git://github.com/thecodrr/better-sqlite3-multiple-ciphers.git";
await writeFile(jsonPath, JSON.stringify(json));
}
if (process.argv[1] === __filename) {
console.log("Patching better-sqlite3");
patchBetterSQLite3();
}

View File

@@ -224,6 +224,13 @@ export default async function downloadAttachment(
}
try {
useAttachmentStore.getState().setDownloading({
groupId: options.groupId || attachment.hash,
current: 0,
total: 1,
filename: attachment.filename
});
await db
.fs()
.downloadFile(
@@ -231,6 +238,15 @@ export default async function downloadAttachment(
attachment.hash,
attachment.chunkSize
);
useAttachmentStore.getState().setDownloading({
groupId: options.groupId || attachment.hash,
current: 1,
total: 1,
filename: attachment.filename,
success: true
});
if (!(await exists(attachment.hash))) {
DatabaseLogger.log("Attachment does not exist after download.");
return;
@@ -301,6 +317,14 @@ export default async function downloadAttachment(
.unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.hash}_dcache`)
.catch(console.log);
}
useAttachmentStore.getState().setDownloading({
groupId: options.groupId || attachment.hash,
current: 0,
total: 0,
filename: attachment.filename,
success: false
});
DatabaseLogger.error(e);
useAttachmentStore.getState().remove(attachment.hash);
if (options.throwError) {

View File

@@ -63,9 +63,11 @@ const Actions = ({
attachment,
close,
setAttachments,
fwdRef
fwdRef,
context
}: {
attachment: Attachment;
context: string;
setAttachments: (attachments?: VirtualizedGrouping<Attachment>) => void;
close?: () => void;
fwdRef: RefObject<ActionSheetRef>;
@@ -79,7 +81,6 @@ const Actions = ({
const [loading, setLoading] = useState<{
name?: string;
}>({});
const actions = [
{
name: strings.network.download(),
@@ -88,7 +89,7 @@ const Actions = ({
await db.fs().cancel(attachment.hash);
useAttachmentStore.getState().remove(attachment.hash);
}
downloadAttachment(attachment.hash, false);
downloadAttachment(attachment.hash, context === "global");
fwdRef.current?.hide();
},
icon: "download"
@@ -375,6 +376,7 @@ Actions.present = (
setAttachments={set}
close={close}
attachment={attachment}
context={context || "global"}
/>
)
});

View File

@@ -31,6 +31,7 @@ import { ProgressCircleComponent } from "../ui/svg/lazy";
import Paragraph from "../ui/typography/paragraph";
import Actions from "./actions";
import { strings } from "@notesnook/intl";
import { Pressable } from "../ui/pressable";
function getFileExtension(filename: string) {
const ext = /^.+\.([^.]+)$/.exec(filename);
@@ -69,8 +70,7 @@ export const AttachmentItem = ({
};
return errorOnly && attachment && !attachment?.failed ? null : (
<TouchableOpacity
activeOpacity={0.9}
<Pressable
onPress={onPress}
style={{
flexDirection: "row",
@@ -186,6 +186,6 @@ export const AttachmentItem = ({
)}
</>
)}
</TouchableOpacity>
</Pressable>
);
};

View File

@@ -206,7 +206,7 @@ export const AttachmentDialog = ({
errorOnly={currentFilter === "errors"}
attachments={attachments}
id={index}
context="global"
context={!isSheet ? "global" : "attachments-list"}
/>
);

View File

@@ -188,7 +188,7 @@ const PDFPreview = () => {
}}
color={colors.static.white}
>
{strings.loadingWithProgress(progress.percent)}
{strings.loadingWithProgress(progress?.percent)}
</Paragraph>
</Animated.View>
) : (

View File

@@ -19,9 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useRef } from "react";
import { Platform, StyleSheet, View } from "react-native";
//@ts-ignore
import { useThemeColors } from "@notesnook/theme";
import Menu from "react-native-reanimated-material-menu";
import { Menu } from "react-native-material-menu";
import { notesnook } from "../../../e2e/test.ids";
import {
HeaderRightButton,
@@ -97,14 +96,16 @@ export const RightMenus = ({
style={{
borderRadius: 5,
backgroundColor: contextMenuColors.primary.background,
marginTop: -40
marginTop: 35
}}
onRequestClose={() => {
//@ts-ignore
menuRef.current?.hide();
}}
anchor={
<IconButton
onPress={() => {
//@ts-ignore
menuRef.current?.show();
}}
name="dots-vertical"
@@ -116,9 +117,10 @@ export const RightMenus = ({
{headerRightButtons.map((item) => (
<Button
style={{
width: 150,
justifyContent: "flex-start",
borderRadius: 0
borderRadius: 0,
alignSelf: "flex-start",
width: "100%"
}}
type="plain"
buttonType={{
@@ -127,6 +129,7 @@ export const RightMenus = ({
key={item.title}
title={item.title}
onPress={async () => {
//@ts-ignore
menuRef.current?.hide();
if (Platform.OS === "ios") await sleep(300);
item.onPress();

View File

@@ -81,7 +81,8 @@ const ReminderItem = React.memo(
<View
style={{
flexDirection: "row",
flexWrap: "wrap"
flexWrap: "wrap",
marginTop: 5
}}
>
{item.disabled ? (
@@ -149,10 +150,8 @@ const ReminderItem = React.memo(
fontSize={SIZE.xs}
style={{
justifyContent: "flex-start",
borderWidth: 0,
height: 30,
alignSelf: "flex-start",
marginTop: 5
height: 25,
alignSelf: "flex-start"
}}
/>
</View>

View File

@@ -70,6 +70,7 @@ const SelectionWrapper = ({
}
const onLongPress = () => {
if (isSheet) return;
if (useSelectionStore.getState().selectionMode !== item.type) {
useSelectionStore.getState().setSelectionMode(item.type);
}
@@ -78,7 +79,7 @@ const SelectionWrapper = ({
return (
<Pressable
customColor={isSheet ? colors.primary.hover : "transparent"}
customColor={isSheet ? colors.secondary.background : "transparent"}
testID={testID}
onLongPress={onLongPress}
onPress={onPress}

View File

@@ -26,7 +26,7 @@ import {
Platform,
View
} from "react-native";
import Menu from "react-native-reanimated-material-menu/src/Menu";
import { Menu } from "react-native-material-menu";
import { db } from "../../common/database";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { ToastManager } from "../../services/event-manager";
@@ -101,11 +101,14 @@ export const SelectionHeader = React.memo(
};
const deleteItem = async () => {
if (!type) return;
presentDialog({
title: strings.doActions.delete[type](selectedItemsList.length),
paragraph: strings.actionConfirmations.delete[type](
selectedItemsList.length
),
title: strings.doActions.delete[
type as keyof typeof strings.doActions.delete
](selectedItemsList.length),
paragraph: strings.actionConfirmations.delete[
type as keyof typeof strings.doActions.delete
](selectedItemsList.length),
positiveText: strings.delete(),
negativeText: strings.cancel(),
positivePress: async () => {
@@ -218,14 +221,16 @@ export const SelectionHeader = React.memo(
style={{
borderRadius: 5,
backgroundColor: contextMenuColors.primary.background,
marginTop: -20
marginTop: 35
}}
onRequestClose={() => {
//@ts-ignore
menuRef.current?.hide();
}}
anchor={
<IconButton
onPress={() => {
//@ts-ignore
menuRef.current?.show();
}}
name="dots-vertical"
@@ -319,9 +324,10 @@ export const SelectionHeader = React.memo(
!item.visible ? null : (
<Button
style={{
width: 150,
justifyContent: "flex-start",
borderRadius: 0
borderRadius: 0,
alignSelf: "flex-start",
width: "100%"
}}
type="plain"
buttonType={{
@@ -331,6 +337,7 @@ export const SelectionHeader = React.memo(
key={item.title}
title={item.title}
onPress={async () => {
//@ts-ignore
menuRef.current?.hide();
if (Platform.OS === "ios") await sleep(300);
item.onPress();

View File

@@ -88,14 +88,14 @@ export const RelationsList = ({
sortDirection: "desc"
})
.then((grouped) => {
setItems(grouped);
setTimeout(() => {
setItems(grouped);
}, 300);
});
}, [relationType, referenceType, item?.id, item?.type]);
}, [relationType, referenceType, item?.id, item?.type, updater]);
return (
<View
style={{ paddingHorizontal: 12, height: hasNoRelations ? 300 : "100%" }}
>
<View style={{ paddingHorizontal: 12, height: "100%" }}>
<SheetProvider context="local" />
<DialogHeader
title={title}

View File

@@ -124,9 +124,11 @@ export default function ReminderSheet({
const referencedItem = reference ? (reference as Note) : null;
const title = useRef<string | undefined>(
reminder?.title || referencedItem?.title
!reminder ? referencedItem?.title : reminder?.title
);
const details = useRef<string | undefined>(
!reminder ? referencedItem?.headline : reminder?.description
);
const details = useRef<string | undefined>(reminder?.description);
const titleRef = useRef<TextInput>(null);
const timer = useRef<NodeJS.Timeout>();
@@ -543,7 +545,7 @@ export default function ReminderSheet({
/>
{reminderMode === ReminderModes.Permanent ? null : (
<ScrollView
<RNScrollView
style={{
flexDirection: "row",
marginTop: 12,
@@ -588,7 +590,7 @@ export default function ReminderSheet({
}}
/>
))}
</ScrollView>
</RNScrollView>
)}
</ScrollView>

View File

@@ -51,7 +51,6 @@ export const ReminderTime = ({
<Button
title={time}
key={reminder.id}
height={20}
icon="bell"
fontSize={SIZE.xs}
iconSize={SIZE.sm}
@@ -67,12 +66,10 @@ export const ReminderTime = ({
marginRight: 0
}}
style={{
height: "auto",
borderRadius: 5,
marginRight: 5,
borderWidth: 0.5,
borderColor: colors.primary.border,
paddingHorizontal: 6,
marginBottom: 5,
...(style as ViewStyle)
}}
{...props}

View File

@@ -17,13 +17,11 @@
"phone": "^3.1.14",
"qclone": "^1.2.0",
"react-native-actions-sheet": "0.9.7",
"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",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-progress": "^5.0.0",
"react-native-qrcode-svg": "^6.0.6",
"react-native-reanimated-material-menu": "github:ammarahm-ed/react-native-reanimated-material-menu",
"react-native-reanimated-progress-bar": "1.0.1",
"react-native-swiper-flatlist": "3.2.2",
"timeago.js": "4.0.2",
@@ -35,15 +33,17 @@
"@readme/data-urls": "3.0.0",
"react-native-wheel-color-picker": "^1.3.1",
"@azure/core-asynciterator-polyfill": "^1.0.2",
"@tanstack/react-query": "^4.36.1",
"@trpc/client": "10.45.2",
"@trpc/react-query": "10.45.2",
"@trpc/server": "10.45.2",
"@streetwriters/kysely": "^0.27.4",
"pathe": "1.1.2",
"react-native-format-currency": "0.0.5",
"@lingui/react": "4.11.2",
"@lingui/core": "4.11.2"
"@lingui/core": "4.11.2",
"react-native-check-version": "^1.3.0",
"react-native-material-menu": "^2.0.0",
"@trpc/client": "^10.45.2",
"@trpc/react-query": "^10.45.2",
"@trpc/server": "^10.45.2",
"@tanstack/react-query": "^4.36.1"
},
"sideEffects": false
}

View File

@@ -34,6 +34,7 @@ import { EditorEvents } from "./tiptap/utils";
import { useThemeColors } from "@notesnook/theme";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { db } from "../../common/database";
import { i18n } from "@lingui/core";
const onShouldStartLoadWithRequest = (request: ShouldStartLoadRequest) => {
if (request.url.includes("https")) {
@@ -143,10 +144,17 @@ export function ReadonlyEditor(props: {
ref={editorRef}
key={"readonly-editor:" + props.editorId}
nestedScrollEnabled
injectedJavaScriptBeforeContentLoaded={`globalThis.readonlyEditor=true;`}
injectedJavaScript="globalThis.readonlyEditor=true;"
injectedJavaScript={`
globalThis.__DEV__ = ${__DEV__}
globalThis.readonlyEditor=true;
globalThis.LINGUI_LOCALE = "${i18n.locale}";
globalThis.LINGUI_LOCALE_DATA = ${JSON.stringify({
[i18n.locale]: i18n.messages
})};
globalThis.loadApp();`}
useSharedProcessPool={false}
javaScriptEnabled={true}
webviewDebuggingEnabled={__DEV__}
focusable={true}
setSupportMultipleWindows={false}
overScrollMode="never"

View File

@@ -37,6 +37,7 @@ export type EditorState = {
scrollPosition: number;
overlay?: boolean;
initialLoadCalled?: boolean;
editorStateRestored?: boolean;
};
export type Settings = {

View File

@@ -861,9 +861,13 @@ export const useEditor = (
state.current.currentlyEditing = true;
state.current.movedAway = false;
if (!DDS.isTab) {
tabBarRef.current?.goToPage(1, false);
if (!state.current.editorStateRestored) {
state.current.isRestoringState = true;
if (!DDS.isTab) {
tabBarRef.current?.goToPage(1, false);
}
}
clearAppState();
state.current.isRestoringState = false;
}, []);

View File

@@ -29,6 +29,7 @@ import { useAttachmentProgress } from "../../hooks/use-attachment-progress";
import { useDBItem } from "../../hooks/use-db-item";
import { useAttachmentStore } from "../../stores/use-attachment-store";
import { SIZE } from "../../utils/size";
import { strings } from "@notesnook/intl";
export const AttachmentGroupProgress = (props: { groupId?: string }) => {
const { colors } = useThemeColors();
@@ -90,11 +91,12 @@ export const AttachmentGroupProgress = (props: { groupId?: string }) => {
}}
numberOfLines={1}
>
Downloading {file?.filename} {formatBytes(file?.size || 0)}{" "}
{strings.downloading()} {file?.filename}{" "}
{formatBytes(file?.size || 0)}{" "}
{fileProgress?.percent ? `(${fileProgress.percent})` : ""}
</Paragraph>
<Paragraph size={10} color={colors.secondary.paragraph}>
Group: {props.groupId}
{strings.group()}: {props.groupId}
</Paragraph>
</View>
{props.groupId === "offline-mode" ? null : (

View File

@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useThemeColors } from "@notesnook/theme";
import React, { useRef, useState } from "react";
import { View } from "react-native";
import Menu, { MenuItem } from "react-native-reanimated-material-menu";
import { Menu, MenuItem } from "react-native-material-menu";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { Dialog } from "../../../components/dialog";
import { Pressable } from "../../../components/ui/pressable";
@@ -146,7 +146,7 @@ export function SettingsPicker<T>({
onChange(item);
}
}}
underlayColor={colors.primary.hover}
pressColor={colors.primary.hover}
style={{
backgroundColor: compareValue(currentValue, item)
? colors.selected.background

View File

@@ -203,19 +203,15 @@ const _SectionItem = ({ item }: { item: SettingSection }) => {
<Input
{...item.inputProperties}
onSubmit={(e) => {
if (e.nativeEvent.text) {
SettingsService.set({
[item.property as string]: e.nativeEvent.text
});
}
SettingsService.set({
[item.property as string]: e.nativeEvent.text
});
item.inputProperties?.onSubmitEditing?.(e);
}}
onChangeText={(text) => {
if (text) {
SettingsService.set({
[item.property as string]: text
});
}
SettingsService.set({
[item.property as string]: text
});
item.inputProperties?.onSubmitEditing?.(text as any);
}}
containerStyle={{ marginTop: 12 }}

View File

@@ -1166,7 +1166,9 @@ export const settingsGroups: SettingSection[] = [
type: "component",
hidden: () => !useUserStore.getState().user,
name: strings.automaticBackupsWithAttachments(),
description: strings.automaticBackupsWithAttachmentsDesc(),
description: [
...strings.automaticBackupsWithAttachmentsDesc()
].join("\n"),
component: "autobackupsattachments"
},
{

View File

@@ -30,24 +30,12 @@ import SettingsService from "./settings";
async function doInBackground(callback: () => Promise<void>) {
if (Platform.OS === "ios") {
try {
const bgTaskId = await beginBackgroundTask();
const result = await callback();
await endBackgroundTask(bgTaskId);
return result;
} catch (e) {
return (e as Error).message;
}
const bgTaskId = await beginBackgroundTask();
const result = await callback();
await endBackgroundTask(bgTaskId);
return result;
} else {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
try {
const result = await callback();
resolve(result);
} catch (e) {
resolve((e as Error).message);
}
});
return await callback();
}
}

View File

@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Platform } from "react-native";
import FingerprintScanner, {
AuthenticateIOS
} from "react-native-fingerprint-scanner";
} from "@ammarahmed/react-native-fingerprint-scanner";
import * as Keychain from "react-native-keychain";
import { MMKV } from "../common/database/mmkv";
import Storage from "../common/database/storage";

View File

@@ -123,6 +123,7 @@ export type PresentSheetOptions = {
};
export function presentSheet(data: Partial<PresentSheetOptions>) {
console.log("PRESENTING...");
eSendEvent(eOpenSheet, data);
}

View File

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

View File

@@ -1063,7 +1063,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2118;
CURRENT_PROJECT_VERSION = 2120;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1137,7 +1137,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.0.21;
MARKETING_VERSION = 3.0.23;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1168,7 +1168,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2118;
CURRENT_PROJECT_VERSION = 2120;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1242,7 +1242,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.0.21;
MARKETING_VERSION = 3.0.23;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1401,7 +1401,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2118;
CURRENT_PROJECT_VERSION = 2120;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1413,7 +1413,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.0.21;
MARKETING_VERSION = 3.0.23;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1444,7 +1444,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2118;
CURRENT_PROJECT_VERSION = 2120;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1457,7 +1457,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.0.21;
MARKETING_VERSION = 3.0.23;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1487,7 +1487,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2118;
CURRENT_PROJECT_VERSION = 2120;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1561,7 +1561,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.0.21;
MARKETING_VERSION = 3.0.23;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1592,7 +1592,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2118;
CURRENT_PROJECT_VERSION = 2120;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1667,7 +1667,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.0.21;
MARKETING_VERSION = 3.0.23;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1744,7 +1744,10 @@
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
OTHER_LDFLAGS = "$(inherited) ";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
@@ -1808,7 +1811,10 @@
ONLY_ACTIVE_ARCH = NO;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
OTHER_LDFLAGS = "$(inherited) ";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;

View File

@@ -970,6 +970,8 @@ PODS:
- React
- react-native-background-actions (2.6.7):
- React-Core
- react-native-begin-background-task (0.1.0):
- React
- react-native-blob-util (0.18.3):
- React-Core
- react-native-config (1.5.1):
@@ -981,7 +983,7 @@ PODS:
- react-native-document-picker (7.1.3):
- React-Core
- react-native-fingerprint-scanner (5.0.0):
- React
- React-Core
- react-native-get-random-values (1.9.0):
- React-Core
- react-native-gzip (1.1.0):
@@ -1486,11 +1488,12 @@ DEPENDENCIES:
- React-Mapbuffer (from `../../node_modules/react-native/ReactCommon`)
- react-native-actions-shortcuts (from `../../node_modules/react-native-actions-shortcuts`)
- react-native-background-actions (from `../../node_modules/react-native-background-actions`)
- react-native-begin-background-task (from `../../node_modules/react-native-begin-background-task`)
- react-native-blob-util (from `../../node_modules/react-native-blob-util`)
- react-native-config (from `../../node_modules/react-native-config`)
- react-native-date-picker (from `../../node_modules/react-native-date-picker`)
- react-native-document-picker (from `../../node_modules/react-native-document-picker`)
- react-native-fingerprint-scanner (from `../../node_modules/react-native-fingerprint-scanner`)
- "react-native-fingerprint-scanner (from `../../node_modules/@ammarahmed/react-native-fingerprint-scanner`)"
- react-native-get-random-values (from `../../node_modules/react-native-get-random-values`)
- react-native-gzip (from `../../node_modules/react-native-gzip`)
- react-native-html-to-pdf-lite (from `../../node_modules/react-native-html-to-pdf-lite`)
@@ -1642,6 +1645,8 @@ EXTERNAL SOURCES:
:path: "../../node_modules/react-native-actions-shortcuts"
react-native-background-actions:
:path: "../../node_modules/react-native-background-actions"
react-native-begin-background-task:
:path: "../../node_modules/react-native-begin-background-task"
react-native-blob-util:
:path: "../../node_modules/react-native-blob-util"
react-native-config:
@@ -1651,7 +1656,7 @@ EXTERNAL SOURCES:
react-native-document-picker:
:path: "../../node_modules/react-native-document-picker"
react-native-fingerprint-scanner:
:path: "../../node_modules/react-native-fingerprint-scanner"
:path: "../../node_modules/@ammarahmed/react-native-fingerprint-scanner"
react-native-get-random-values:
:path: "../../node_modules/react-native-get-random-values"
react-native-gzip:
@@ -1833,11 +1838,12 @@ SPEC CHECKSUMS:
React-Mapbuffer: 6c1cacdbf40b531f549eba249e531a7d0bfd8e7f
react-native-actions-shortcuts: 5d9cf0c9c308333dfcc1e05c3f9afa8c428e2533
react-native-background-actions: 2c251c986f23347f9c1722f05fd296938f60edb1
react-native-begin-background-task: 3b889e07458afc5822a7277cf9cbc7cd077e39ee
react-native-blob-util: 2d36383bb52c15c5451be81cb7ddf22bc34a12a6
react-native-config: 86038147314e2e6d10ea9972022aa171e6b1d4d8
react-native-date-picker: 93e43b3084cea595b4d68b1405d6d99849663bd6
react-native-document-picker: ec07866a30707f23660c0f3ae591d669d3e89096
react-native-fingerprint-scanner: 3e8c46641b5751a31f45e66789d4b6f342be5e6d
react-native-fingerprint-scanner: be63e626b31fb951780a5fac5328b065a61a3d6e
react-native-get-random-values: dee677497c6a740b71e5612e8dbd83e7539ed5bb
react-native-gzip: c5e87ee9e359f02350e3a2ee52eb35eddc398868
react-native-html-to-pdf-lite: 21bfb169bf4cbcd7bec9f736975ee1b3f5292d4a

View File

@@ -25,7 +25,6 @@
"react-native": "0.74.5",
"react-native-actions-shortcuts": "^1.0.1",
"react-native-background-actions": "^2.6.6",
"react-native-begin-background-task": "https://github.com/blockfirm/react-native-begin-background-task.git",
"react-native-blob-util": "^0.18.3",
"react-native-bootsplash": "4.1.4",
"react-native-config": "^1.4.6",
@@ -33,9 +32,7 @@
"react-native-device-info": "^8.4.1",
"react-native-document-picker": "^7.1.1",
"@ammarahmed/react-native-eventsource": "1.1.0",
"react-native-exit-app": "https://github.com/ammarahm-ed/react-native-exit-app.git",
"react-native-file-viewer": "^2.1.1",
"react-native-fingerprint-scanner": "https://github.com/ammarahm-ed/react-native-fingerprint-scanner.git",
"react-native-gesture-handler": "^2.18.0",
"react-native-reanimated": "3.14.0",
"react-native-safe-area-context": "^4.10.8",
@@ -52,9 +49,7 @@
"react-native-modal-datetime-picker": "14.0.0",
"react-native-navigation-bar-color": "2.0.2",
"react-native-notification-sounds": "0.5.5",
"react-native-orientation": "https://github.com/yamill/react-native-orientation.git",
"react-native-pdf": "6.6.2",
"react-native-privacy-snapshot": "github:standardnotes/react-native-privacy-snapshot",
"react-native-scoped-storage": "^1.9.5",
"react-native-securerandom": "^1.0.1",
"react-native-share": "^7.2.0",
@@ -70,13 +65,18 @@
"react-native-screenguard": "^1.0.0",
"@formatjs/intl-locale": "4.0.0",
"@formatjs/intl-pluralrules": "5.2.14",
"detox": "^20.27.6",
"@ammarahmed/react-native-share-extension": "^2.6.0",
"@ammarahmed/react-native-sodium": "1.5.6",
"react-native-mmkv-storage": "^0.10.2",
"@react-native-community/datetimepicker": "^8.2.0"
"@react-native-community/datetimepicker": "^8.2.0",
"react-native-exit-app": "github:ammarahm-ed/react-native-exit-app",
"react-native-orientation": "github:yamill/react-native-orientation",
"react-native-begin-background-task": "github:blockfirm/react-native-begin-background-task",
"react-native-privacy-snapshot": "github:standardnotes/react-native-privacy-snapshot",
"@ammarahmed/react-native-fingerprint-scanner": "^5.0.0"
},
"devDependencies": {
"detox": "^20.27.6",
"@babel/core": "^7.20.0",
"@babel/eslint-parser": "^7.16.5",
"@babel/plugin-transform-named-capturing-groups-regex": "^7.16.5",

View File

@@ -217,7 +217,7 @@ module.exports = (env) => {
/node_modules(.*[/\\])+@tanstack[/\\]react-query/,
/node_modules(.*[/\\])+@trpc[/\\]react-query/,
/node_modules(.*[/\\])+katex/,
/node_modules(.*[/\\])+mime/,
/node_modules(.*[/\\])+react-native-material-menu/,
/node_modules(.*[/\\])+@notesnook[/\\]core/,
/node_modules(.*[/\\])+whatwg-url-without-unicode/,
/node_modules(.*[/\\])+whatwg-url/,

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/mobile",
"version": "3.0.21",
"version": "3.0.22",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "3.0.21",
"version": "3.0.22",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -24,8 +24,7 @@
"@notesnook/themes-server": "file:../../servers/themes",
"diffblazer": "^1.0.1",
"react": "18.2.0",
"react-native": "0.74.5",
"react-native-actions-sheet": "^0.9.7"
"react-native": "0.74.5"
},
"devDependencies": {
"fonteditor-core": "^2.1.11",
@@ -1046,6 +1045,7 @@
"@readme/data-urls": "^3.0.0",
"@streetwriters/kysely": "^0.27.4",
"@streetwriters/showdown": "^3.0.9-alpha",
"@types/mime-db": "^1.43.5",
"async-mutex": "^0.3.2",
"dayjs": "1.11.9",
"dom-serializer": "^2.0.0",
@@ -1058,7 +1058,7 @@
"katex": "0.16.2",
"linkedom": "^0.14.17",
"liqe": "^1.13.0",
"mime": "^4.0.4",
"mime-db": "^1.53.0",
"prismjs": "^1.29.0",
"qclone": "^1.2.0",
"rfdc": "^1.3.0",
@@ -28896,9 +28896,9 @@
"@streetwriters/kysely": "^0.27.4",
"@streetwriters/showdown": "^3.0.1-alpha.2",
"@tanstack/react-query": "^4.36.1",
"@trpc/client": "10.45.2",
"@trpc/react-query": "10.45.2",
"@trpc/server": "10.45.2",
"@trpc/client": "^10.45.2",
"@trpc/react-query": "^10.45.2",
"@trpc/server": "^10.45.2",
"absolutify": "^0.1.0",
"buffer": "^6.0.3",
"dayjs": "^1.10.4",
@@ -28911,14 +28911,14 @@
"phone": "^3.1.14",
"qclone": "^1.2.0",
"react-native-actions-sheet": "0.9.7",
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
"react-native-check-version": "^1.3.0",
"react-native-drax": "^0.10.2",
"react-native-format-currency": "0.0.5",
"react-native-image-zoom-viewer": "^3.0.1",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-material-menu": "^2.0.0",
"react-native-progress": "^5.0.0",
"react-native-qrcode-svg": "^6.0.6",
"react-native-reanimated-material-menu": "github:ammarahm-ed/react-native-reanimated-material-menu",
"react-native-reanimated-progress-bar": "1.0.1",
"react-native-swiper-flatlist": "3.2.2",
"react-native-wheel-color-picker": "^1.3.1",
@@ -28929,70 +28929,6 @@
"zustand": "^3.6.0"
}
},
"app/node_modules/@tanstack/query-core": {
"version": "4.36.1",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
}
},
"app/node_modules/@tanstack/react-query": {
"version": "4.36.1",
"license": "MIT",
"dependencies": {
"@tanstack/query-core": "4.36.1",
"use-sync-external-store": "^1.2.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-native": "*"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
}
}
},
"app/node_modules/@trpc/client": {
"version": "10.45.2",
"funding": [
"https://trpc.io/sponsor"
],
"license": "MIT",
"peerDependencies": {
"@trpc/server": "10.45.2"
}
},
"app/node_modules/@trpc/react-query": {
"version": "10.45.2",
"funding": [
"https://trpc.io/sponsor"
],
"license": "MIT",
"peerDependencies": {
"@tanstack/react-query": "^4.18.0",
"@trpc/client": "10.45.2",
"@trpc/server": "10.45.2",
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"app/node_modules/@trpc/server": {
"version": "10.45.2",
"funding": [
"https://trpc.io/sponsor"
],
"license": "MIT"
},
"native": {
"name": "@notesnook/mobile-native",
"version": "1.0.0",
@@ -29001,6 +28937,7 @@
"@ammarahmed/notifee-react-native": "7.4.7",
"@ammarahmed/react-native-background-fetch": "^4.2.2",
"@ammarahmed/react-native-eventsource": "1.1.0",
"@ammarahmed/react-native-fingerprint-scanner": "^5.0.0",
"@ammarahmed/react-native-share-extension": "^2.6.0",
"@ammarahmed/react-native-sodium": "1.5.6",
"@bam.tech/react-native-image-resizer": "3.0.5",
@@ -29018,21 +28955,19 @@
"@react-navigation/native-stack": "6.6.2",
"@sayem314/react-native-keep-awake": "^1.0.4",
"@shopify/flash-list": "1.7.0",
"detox": "^20.27.6",
"react": "18.2.0",
"react-native": "0.74.5",
"react-native-actions-shortcuts": "^1.0.1",
"react-native-background-actions": "^2.6.6",
"react-native-begin-background-task": "https://github.com/blockfirm/react-native-begin-background-task.git",
"react-native-begin-background-task": "github:blockfirm/react-native-begin-background-task",
"react-native-blob-util": "^0.18.3",
"react-native-bootsplash": "4.1.4",
"react-native-config": "^1.4.6",
"react-native-date-picker": "4.2.6",
"react-native-device-info": "^8.4.1",
"react-native-document-picker": "^7.1.1",
"react-native-exit-app": "https://github.com/ammarahm-ed/react-native-exit-app.git",
"react-native-exit-app": "github:ammarahm-ed/react-native-exit-app",
"react-native-file-viewer": "^2.1.1",
"react-native-fingerprint-scanner": "https://github.com/ammarahm-ed/react-native-fingerprint-scanner.git",
"react-native-gesture-handler": "^2.18.0",
"react-native-get-random-values": "^1.7.0",
"react-native-gzip": "1.1.0",
@@ -29045,7 +28980,7 @@
"react-native-modal-datetime-picker": "14.0.0",
"react-native-navigation-bar-color": "2.0.2",
"react-native-notification-sounds": "0.5.5",
"react-native-orientation": "https://github.com/yamill/react-native-orientation.git",
"react-native-orientation": "github:yamill/react-native-orientation",
"react-native-pdf": "6.6.2",
"react-native-privacy-snapshot": "github:standardnotes/react-native-privacy-snapshot",
"react-native-quick-sqlite": "^8.0.6",
@@ -29090,6 +29025,7 @@
"babel-loader": "^8.2.5",
"babel-plugin-module-resolver": "^4.1.0",
"babel-plugin-transform-remove-console": "6.9.4",
"detox": "^20.27.6",
"eslint": "^8.19.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
@@ -29132,6 +29068,14 @@
"react-native": "*"
}
},
"node_modules/@ammarahmed/react-native-fingerprint-scanner": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-fingerprint-scanner/-/react-native-fingerprint-scanner-5.0.0.tgz",
"integrity": "sha512-X0O6swQkDz1TEjqO+tIZLVt+Olr7j1lq4BwtA8OKn9nJp5kdO+I1JurN7c7zYSrYMJiJcPcuiP8FShjXj8fcsQ==",
"peerDependencies": {
"react-native": ">=0.60 <1.0.0"
}
},
"node_modules/@ammarahmed/react-native-share-extension": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-share-extension/-/react-native-share-extension-2.6.0.tgz",
@@ -29646,6 +29590,21 @@
"node": ">=6.0.0"
}
},
"node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz",
"integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==",
"dev": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
}
},
"node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz",
@@ -29807,6 +29766,23 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-proposal-unicode-property-regex": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz",
"integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==",
"deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.",
"dev": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
},
"engines": {
"node": ">=4"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-syntax-async-generators": {
"version": "7.8.4",
"license": "MIT",
@@ -30273,6 +30249,21 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-transform-dynamic-import": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz",
"integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==",
"dev": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-transform-exponentiation-operator": {
"version": "7.22.5",
"dev": true,
@@ -31005,6 +30996,22 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/preset-env/node_modules/@babel/preset-modules": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz",
"integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==",
"dev": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
"@babel/plugin-transform-dotall-regex": "^7.4.4",
"@babel/types": "^7.4.4",
"esutils": "^2.0.2"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
}
},
"node_modules/@babel/preset-env/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@@ -31029,22 +31036,6 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/preset-modules": {
"version": "0.1.6-no-external-plugins",
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
"integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
"dev": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
"@babel/plugin-transform-dotall-regex": "^7.4.4",
"@babel/types": "^7.4.4",
"esutils": "^2.0.2"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/preset-typescript": {
"version": "7.22.5",
"license": "MIT",
@@ -31747,7 +31738,8 @@
"node_modules/@flatten-js/interval-tree": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@flatten-js/interval-tree/-/interval-tree-1.1.3.tgz",
"integrity": "sha512-xhFWUBoHJFF77cJO1D6REjdgJEMRf2Y2Z+eKEPav8evGKcLSnj1ud5pLXQSbGuxF3VSvT1rWhMfVpXEKJLTL+A=="
"integrity": "sha512-xhFWUBoHJFF77cJO1D6REjdgJEMRf2Y2Z+eKEPav8evGKcLSnj1ud5pLXQSbGuxF3VSvT1rWhMfVpXEKJLTL+A==",
"dev": true
},
"node_modules/@flyerhq/react-native-link-preview": {
"version": "1.6.0",
@@ -34184,10 +34176,79 @@
"url": "https://www.paypal.me/tiviesantos"
}
},
"node_modules/@tanstack/query-core": {
"version": "4.36.1",
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.36.1.tgz",
"integrity": "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
}
},
"node_modules/@tanstack/react-query": {
"version": "4.36.1",
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.36.1.tgz",
"integrity": "sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==",
"dependencies": {
"@tanstack/query-core": "4.36.1",
"use-sync-external-store": "^1.2.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-native": "*"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
}
}
},
"node_modules/@tokenizer/token": {
"version": "0.3.0",
"license": "MIT"
},
"node_modules/@trpc/client": {
"version": "10.45.2",
"resolved": "https://registry.npmjs.org/@trpc/client/-/client-10.45.2.tgz",
"integrity": "sha512-ykALM5kYWTLn1zYuUOZ2cPWlVfrXhc18HzBDyRhoPYN0jey4iQHEFSEowfnhg1RvYnrAVjNBgHNeSAXjrDbGwg==",
"funding": [
"https://trpc.io/sponsor"
],
"peerDependencies": {
"@trpc/server": "10.45.2"
}
},
"node_modules/@trpc/react-query": {
"version": "10.45.2",
"resolved": "https://registry.npmjs.org/@trpc/react-query/-/react-query-10.45.2.tgz",
"integrity": "sha512-BAqb9bGZIscroradlNx+Cc9522R+idY3BOSf5z0jHUtkxdMbjeGKxSSMxxu7JzoLqSIEC+LVzL3VvF8sdDWaZQ==",
"funding": [
"https://trpc.io/sponsor"
],
"peerDependencies": {
"@tanstack/react-query": "^4.18.0",
"@trpc/client": "10.45.2",
"@trpc/server": "10.45.2",
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@trpc/server": {
"version": "10.45.2",
"resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.45.2.tgz",
"integrity": "sha512-wOrSThNNE4HUnuhJG6PfDRp4L2009KDVxsd+2VYH8ro6o/7/jwYZ8Uu5j+VaW+mOmc8EHerHzGcdbGNQSAUPgg==",
"funding": [
"https://trpc.io/sponsor"
]
},
"node_modules/@tsconfig/react-native": {
"version": "3.0.2",
"dev": true,
@@ -35525,6 +35586,7 @@
},
"node_modules/bluebird": {
"version": "3.7.2",
"dev": true,
"license": "MIT"
},
"node_modules/bmp-js": {
@@ -35555,6 +35617,7 @@
},
"node_modules/browser-process-hrtime": {
"version": "1.0.0",
"dev": true,
"license": "BSD-2-Clause"
},
"node_modules/browserslist": {
@@ -35657,6 +35720,7 @@
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/bunyamin/-/bunyamin-1.6.3.tgz",
"integrity": "sha512-m1hAijFhu8pFiidsVc0XEDic46uxPK+mKNLqkb5mluNx0nTolNzx/DjwMqHChQWCgfOLMjKYJJ2uPTQLE6t4Ng==",
"dev": true,
"dependencies": {
"@flatten-js/interval-tree": "^1.1.2",
"multi-sort-stream": "^1.0.4",
@@ -35681,6 +35745,7 @@
},
"node_modules/bunyan": {
"version": "1.8.15",
"dev": true,
"engines": [
"node >=0.10.0"
],
@@ -35697,6 +35762,7 @@
},
"node_modules/bunyan-debug-stream": {
"version": "3.1.0",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^4.1.2"
@@ -35717,6 +35783,7 @@
},
"node_modules/caf": {
"version": "15.0.1",
"dev": true,
"license": "MIT"
},
"node_modules/call-bind": {
@@ -35813,6 +35880,7 @@
},
"node_modules/child-process-promise": {
"version": "2.2.1",
"dev": true,
"license": "MIT",
"dependencies": {
"cross-spawn": "^4.0.2",
@@ -35822,6 +35890,7 @@
},
"node_modules/child-process-promise/node_modules/cross-spawn": {
"version": "4.0.2",
"dev": true,
"license": "MIT",
"dependencies": {
"lru-cache": "^4.0.1",
@@ -35830,6 +35899,7 @@
},
"node_modules/child-process-promise/node_modules/lru-cache": {
"version": "4.1.5",
"dev": true,
"license": "ISC",
"dependencies": {
"pseudomap": "^1.0.2",
@@ -35838,6 +35908,7 @@
},
"node_modules/child-process-promise/node_modules/which": {
"version": "1.3.1",
"dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
@@ -35848,6 +35919,7 @@
},
"node_modules/child-process-promise/node_modules/yallist": {
"version": "2.1.2",
"dev": true,
"license": "ISC"
},
"node_modules/chrome-launcher": {
@@ -36334,6 +36406,7 @@
},
"node_modules/decamelize": {
"version": "4.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -36445,6 +36518,7 @@
"version": "20.27.6",
"resolved": "https://registry.npmjs.org/detox/-/detox-20.27.6.tgz",
"integrity": "sha512-eyVkBC7uFpxgUAuWjvbejwnPOjRHqUWSSE/P0FwsCxWVnrNOSq8IhBnSj82Ic5Dc76CAB+Xw5SWYsHnbhG26Bg==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"ajv": "^8.6.3",
@@ -36502,12 +36576,14 @@
"node_modules/detox-copilot": {
"version": "0.0.23",
"resolved": "https://registry.npmjs.org/detox-copilot/-/detox-copilot-0.0.23.tgz",
"integrity": "sha512-qDSdLwgPUMVawpE0R3agNWd2U69ilTnhf+SodSqqrkmTI0oG67IfkACvwox+K9Slcc8ki6y0Bw6QVBi54MqpaA=="
"integrity": "sha512-qDSdLwgPUMVawpE0R3agNWd2U69ilTnhf+SodSqqrkmTI0oG67IfkACvwox+K9Slcc8ki6y0Bw6QVBi54MqpaA==",
"dev": true
},
"node_modules/detox/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
@@ -36516,6 +36592,7 @@
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
@@ -36530,6 +36607,7 @@
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -36548,6 +36626,7 @@
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
@@ -36559,6 +36638,7 @@
"version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
},
@@ -36570,6 +36650,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -36581,6 +36662,7 @@
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"dev": true,
"dependencies": {
"isexe": "^2.0.0"
},
@@ -36592,6 +36674,7 @@
"version": "7.5.10",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
"dev": true,
"engines": {
"node": ">=8.3.0"
},
@@ -36612,6 +36695,7 @@
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"engines": {
"node": ">=12"
}
@@ -36746,6 +36830,7 @@
},
"node_modules/dtrace-provider": {
"version": "0.8.8",
"dev": true,
"hasInstallScript": true,
"license": "BSD-2-Clause",
"optional": true,
@@ -36763,6 +36848,7 @@
},
"node_modules/duplexer2": {
"version": "0.1.4",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"readable-stream": "^2.0.2"
@@ -36770,6 +36856,7 @@
},
"node_modules/duplexer2/node_modules/readable-stream": {
"version": "2.3.8",
"dev": true,
"license": "MIT",
"dependencies": {
"core-util-is": "~1.0.0",
@@ -36783,6 +36870,7 @@
},
"node_modules/duplexer2/node_modules/string_decoder": {
"version": "1.1.1",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.1.0"
@@ -36790,6 +36878,7 @@
},
"node_modules/easy-stack": {
"version": "1.0.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -37540,6 +37629,7 @@
},
"node_modules/event-pubsub": {
"version": "4.3.0",
"dev": true,
"license": "Unlicense",
"engines": {
"node": ">=4.0.0"
@@ -37588,6 +37678,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/exeunt/-/exeunt-1.1.0.tgz",
"integrity": "sha512-dd++Yn/0Fp+gtJ04YHov7MeAii+LFivJc6KqnJNfplzLVUkUDrfKoQDTLlCgzcW15vY5hKlHasWeIsQJ8agHsw==",
"dev": true,
"engines": {
"node": ">=0.10"
}
@@ -38014,6 +38105,7 @@
},
"node_modules/flat": {
"version": "5.0.2",
"dev": true,
"license": "BSD-3-Clause",
"bin": {
"flat": "cli.js"
@@ -38158,6 +38250,7 @@
},
"node_modules/funpermaproxy": {
"version": "1.1.0",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=8.3.0"
@@ -38724,6 +38817,7 @@
},
"node_modules/ini": {
"version": "1.3.8",
"dev": true,
"license": "ISC"
},
"node_modules/internal-slot": {
@@ -38960,6 +39054,7 @@
},
"node_modules/is-plain-obj": {
"version": "2.1.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -39577,6 +39672,7 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/jest-environment-emit/-/jest-environment-emit-1.0.8.tgz",
"integrity": "sha512-WNqvxBLH0yNojHJQ99Y21963aT7UTavxV3PgiBQFi8zwrlnKU6HvkB6LOvQrbk5I8mI8JEKvcoOrQOvBVMLIXQ==",
"dev": true,
"dependencies": {
"bunyamin": "^1.5.2",
"bunyan": "^2.0.5",
@@ -39619,6 +39715,7 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/bunyan/-/bunyan-2.0.5.tgz",
"integrity": "sha512-Jvl74TdxCN6rSP9W1I6+UOUtwslTDqsSFkDqZlFb/ilaSvQ+bZAnXT/GT97IZ5L+Vph0joPZPhxUyn6FLNmFAA==",
"dev": true,
"engines": [
"node >=0.10.0"
],
@@ -39639,6 +39736,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -40248,6 +40346,7 @@
},
"node_modules/js-message": {
"version": "1.0.7",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.6.0"
@@ -40255,6 +40354,7 @@
},
"node_modules/js-queue": {
"version": "2.0.2",
"dev": true,
"license": "MIT",
"dependencies": {
"easy-stack": "^1.0.1"
@@ -40343,6 +40443,7 @@
},
"node_modules/json-cycle": {
"version": "1.5.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
@@ -40881,6 +40982,7 @@
},
"node_modules/lodash.merge": {
"version": "4.6.2",
"dev": true,
"license": "MIT"
},
"node_modules/lodash.throttle": {
@@ -41662,7 +41764,8 @@
},
"node_modules/mime": {
"version": "2.6.0",
"license": "MIT",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
"integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
"bin": {
"mime": "cli.js"
},
@@ -41729,6 +41832,7 @@
},
"node_modules/moment": {
"version": "2.29.4",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
@@ -41746,10 +41850,12 @@
},
"node_modules/multi-sort-stream": {
"version": "1.0.4",
"dev": true,
"license": "bsd"
},
"node_modules/multipipe": {
"version": "4.0.0",
"dev": true,
"license": "MIT",
"dependencies": {
"duplexer2": "^0.1.2",
@@ -41758,6 +41864,7 @@
},
"node_modules/mv": {
"version": "2.1.1",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -41771,6 +41878,7 @@
},
"node_modules/mv/node_modules/glob": {
"version": "6.0.4",
"dev": true,
"license": "ISC",
"optional": true,
"dependencies": {
@@ -41786,6 +41894,7 @@
},
"node_modules/mv/node_modules/rimraf": {
"version": "2.4.5",
"dev": true,
"license": "ISC",
"optional": true,
"dependencies": {
@@ -41797,6 +41906,7 @@
},
"node_modules/nan": {
"version": "2.17.0",
"dev": true,
"license": "MIT",
"optional": true
},
@@ -41828,6 +41938,7 @@
},
"node_modules/ncp": {
"version": "2.0.0",
"dev": true,
"license": "MIT",
"optional": true,
"bin": {
@@ -41897,6 +42008,7 @@
},
"node_modules/node-ipc": {
"version": "9.2.1",
"dev": true,
"license": "MIT",
"dependencies": {
"event-pubsub": "4.3.0",
@@ -41924,6 +42036,7 @@
},
"node_modules/node-version": {
"version": "1.2.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -42750,6 +42863,7 @@
},
"node_modules/promise-polyfill": {
"version": "6.1.0",
"dev": true,
"license": "MIT"
},
"node_modules/prompts": {
@@ -42774,6 +42888,7 @@
},
"node_modules/proper-lockfile": {
"version": "3.2.0",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.1.11",
@@ -42794,6 +42909,7 @@
},
"node_modules/pseudomap": {
"version": "1.0.2",
"dev": true,
"license": "ISC"
},
"node_modules/punycode": {
@@ -43166,7 +43282,7 @@
},
"node_modules/react-native-begin-background-task": {
"version": "0.1.0",
"license": "MIT",
"resolved": "git+ssh://git@github.com/blockfirm/react-native-begin-background-task.git#c2aa793249db6cc6298a812905f955a99b864e78",
"peerDependencies": {
"react-native": "^0.41.2",
"react-native-windows": "0.41.0-rc.1"
@@ -43254,8 +43370,9 @@
}
},
"node_modules/react-native-check-version": {
"version": "1.1.1",
"license": "MIT",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/react-native-check-version/-/react-native-check-version-1.3.0.tgz",
"integrity": "sha512-3wvE637IKl4B1fMjnDVAv2eGrctwn26C48w/YdE0FIj9DnkNJWm3tGG7yspALcHRGLhI5ZqAaKDd0R5F+8m9EA==",
"peerDependencies": {
"react-native": ">=0.40.0",
"react-native-device-info": ">=0.20.0"
@@ -43329,7 +43446,7 @@
},
"node_modules/react-native-exit-app": {
"version": "1.1.0",
"license": "MIT"
"resolved": "git+ssh://git@github.com/ammarahm-ed/react-native-exit-app.git#3087d4bce1320227384d24b34b354600457a817d"
},
"node_modules/react-native-file-viewer": {
"version": "2.1.5",
@@ -43338,13 +43455,6 @@
"react-native": ">=0.47"
}
},
"node_modules/react-native-fingerprint-scanner": {
"version": "5.0.0",
"license": "MIT",
"peerDependencies": {
"react-native": ">=0.60 <1.0.0"
}
},
"node_modules/react-native-format-currency": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/react-native-format-currency/-/react-native-format-currency-0.0.5.tgz",
@@ -43452,6 +43562,15 @@
"version": "4.0.5",
"license": "MIT"
},
"node_modules/react-native-material-menu": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-native-material-menu/-/react-native-material-menu-2.0.0.tgz",
"integrity": "sha512-SmO9PLE3E469EPbVWZqvdu6JGPPZIm7YjqDcWs2PPoY0k7w2V9tFo3BmmLXNzNZDCVCAi+PPSsL7h/5WkfHcSg==",
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-mmkv-storage": {
"version": "0.10.2",
"license": "MIT",
@@ -43487,7 +43606,7 @@
},
"node_modules/react-native-orientation": {
"version": "3.1.3",
"license": "ISC",
"resolved": "git+ssh://git@github.com/yamill/react-native-orientation.git#b45830cce0837fa668838554e023979497673c82",
"peerDependencies": {
"react-native": ">=0.40"
}
@@ -43567,14 +43686,6 @@
"react-native": "*"
}
},
"node_modules/react-native-reanimated-material-menu": {
"version": "2.0.0",
"license": "MIT",
"peerDependencies": {
"react": ">= 16.3.0",
"react-native": ">= 0.54.0"
}
},
"node_modules/react-native-reanimated-progress-bar": {
"version": "1.0.1",
"license": "MIT",
@@ -44082,6 +44193,7 @@
},
"node_modules/resolve-from": {
"version": "5.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -44115,6 +44227,7 @@
},
"node_modules/retry": {
"version": "0.12.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
@@ -44186,6 +44299,7 @@
},
"node_modules/safe-json-stringify": {
"version": "1.2.0",
"dev": true,
"license": "MIT",
"optional": true
},
@@ -44218,6 +44332,7 @@
},
"node_modules/sanitize-filename": {
"version": "1.6.3",
"dev": true,
"license": "WTFPL OR ISC",
"dependencies": {
"truncate-utf8-bytes": "^1.0.0"
@@ -44302,6 +44417,7 @@
},
"node_modules/serialize-error": {
"version": "8.1.0",
"dev": true,
"license": "MIT",
"dependencies": {
"type-fest": "^0.20.2"
@@ -44315,6 +44431,7 @@
},
"node_modules/serialize-error/node_modules/type-fest": {
"version": "0.20.2",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=10"
@@ -44727,10 +44844,12 @@
},
"node_modules/stream-chain": {
"version": "2.2.5",
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/stream-json": {
"version": "1.8.0",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"stream-chain": "^2.2.5"
@@ -44989,6 +45108,7 @@
},
"node_modules/telnet-client": {
"version": "1.2.8",
"dev": true,
"license": "MIT",
"dependencies": {
"bluebird": "^3.5.4"
@@ -45010,6 +45130,7 @@
},
"node_modules/temp-dir": {
"version": "1.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -45027,6 +45148,7 @@
},
"node_modules/tempfile": {
"version": "2.0.0",
"dev": true,
"license": "MIT",
"dependencies": {
"temp-dir": "^1.0.0",
@@ -45287,6 +45409,7 @@
},
"node_modules/trace-event-lib": {
"version": "1.4.1",
"dev": true,
"license": "MIT",
"dependencies": {
"browser-process-hrtime": "^1.0.0"
@@ -45297,6 +45420,7 @@
},
"node_modules/truncate-utf8-bytes": {
"version": "1.0.2",
"dev": true,
"license": "WTFPL",
"dependencies": {
"utf8-byte-length": "^1.0.1"
@@ -45598,6 +45722,7 @@
},
"node_modules/utf8-byte-length": {
"version": "1.0.4",
"dev": true,
"license": "WTFPL"
},
"node_modules/utif": {
@@ -45620,6 +45745,7 @@
},
"node_modules/uuid": {
"version": "3.4.0",
"dev": true,
"license": "MIT",
"bin": {
"uuid": "bin/uuid"
@@ -46057,6 +46183,7 @@
},
"node_modules/yargs-unparser": {
"version": "2.0.0",
"dev": true,
"license": "MIT",
"dependencies": {
"camelcase": "^6.0.0",
@@ -46070,6 +46197,7 @@
},
"node_modules/yargs-unparser/node_modules/camelcase": {
"version": "6.3.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "3.0.21",
"version": "3.0.23",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -45,7 +45,6 @@
"@notesnook/themes-server": "file:../../servers/themes",
"diffblazer": "^1.0.1",
"react": "18.2.0",
"react-native": "0.74.5",
"react-native-actions-sheet": "^0.9.7"
"react-native": "0.74.5"
}
}
}

View File

@@ -1,11 +0,0 @@
diff --git a/node_modules/react-native-fingerprint-scanner/android/build.gradle b/node_modules/react-native-fingerprint-scanner/android/build.gradle
index eaf3829..6c392ba 100644
--- a/node_modules/react-native-fingerprint-scanner/android/build.gradle
+++ b/node_modules/react-native-fingerprint-scanner/android/build.gradle
@@ -49,5 +49,5 @@ dependencies {
// 1.2.3 is the minimum version compatible with androidx.
// See https://github.com/uccmawei/FingerprintIdentify/issues/74
// (translation https://translate.google.com/translate?sl=zh-CN&tl=en&u=https://github.com/uccmawei/FingerprintIdentify/issues/74)
- implementation "com.wei.android.lib:fingerprintidentify:${safeExtGet("fingerprintidentify", "1.2.6")}"
+ implementation "com.github.uccmawei:FingerprintIdentify:1.2.6"
}

View File

@@ -0,0 +1,19 @@
diff --git a/node_modules/react-native-material-menu/dist/Menu.js b/node_modules/react-native-material-menu/dist/Menu.js
index 64fbf13..c7fcee0 100644
--- a/node_modules/react-native-material-menu/dist/Menu.js
+++ b/node_modules/react-native-material-menu/dist/Menu.js
@@ -132,10 +132,13 @@ class Menu extends react_1.default.Component {
else if (left < SCREEN_INDENT) {
left = SCREEN_INDENT;
}
+ console.log(top, windowHeight - menuHeight - SCREEN_INDENT);
// Flip by Y axis if menu hits bottom screen border
if (top > windowHeight - menuHeight - SCREEN_INDENT) {
+ const diff = top - (windowHeight - menuHeight - SCREEN_INDENT);
+ const fraction = (diff / menuHeight);
transforms.push({
- translateY: react_native_1.Animated.multiply(menuSizeAnimation.y, -1),
+ translateY: react_native_1.Animated.multiply(menuSizeAnimation.y, -(fraction + 1)),
});
top = windowHeight - SCREEN_INDENT;
top = Math.min(windowHeight - SCREEN_INDENT, top + buttonHeight);

View File

@@ -1,600 +0,0 @@
diff --git a/node_modules/react-native-reanimated-material-menu/src/Menu.js b/node_modules/react-native-reanimated-material-menu/src/Menu.js
deleted file mode 100644
index 69065f0..0000000
--- a/node_modules/react-native-reanimated-material-menu/src/Menu.js
+++ /dev/null
@@ -1,268 +0,0 @@
-import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
-import {
- Dimensions, I18nManager, Modal,
- Platform,
- StatusBar,
- StyleSheet,
- TouchableWithoutFeedback,
- View
-} from 'react-native';
-import Animated, { Easing, useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
-
-const STATES = {
- HIDDEN: 'HIDDEN',
- ANIMATING: 'ANIMATING',
- SHOWN: 'SHOWN',
-};
-
-const EASING = Easing.bezier(0.4, 0, 0.2, 1);
-const SCREEN_INDENT = 8;
-
-const Menu = ({ animationDuration = 300, ...props }, ref) => {
- const container = useRef();
- const [state, setState] = useState({
- menuState: STATES.HIDDEN,
-
- top: 0,
- left: 0,
-
- menuWidth: 0,
- menuHeight: 0,
-
- buttonWidth: 0,
- buttonHeight: 0,
- });
- const menuSizeXAnimation = useSharedValue(0);
- const menuSizeYAnimation = useSharedValue(0);
- const opacityAnimation = useSharedValue(0);
-
- const _onMenuLayout = e => {
- if (state.menuState === STATES.ANIMATING) {
- return;
- }
- const { width, height } = e.nativeEvent.layout;
- setState(state => {
- return {
- ...state,
- menuState: STATES.ANIMATING,
- menuWidth: width,
- menuHeight: height,
- }
- }
-
- );
- };
-
- useEffect(() => {
- if (menuState === STATES.ANIMATING) {
-
- menuSizeXAnimation.value = withTiming(state.menuWidth, {
- duration: animationDuration * 2,
- easing: EASING
- })
- menuSizeYAnimation.value = withTiming(state.menuHeight, {
- duration: animationDuration,
- easing: EASING
- })
- opacityAnimation.value = withTiming(1, {
- duration: animationDuration + 25,
- easing: EASING
- })
- }
- }, [state])
-
- const _onDismiss = () => {
- if (props.onHidden) {
- props.onHidden();
- }
- };
- useImperativeHandle(ref, () => {
- return {
- show: () => {
- container.current.measureInWindow((left, top, buttonWidth, buttonHeight) => {
-
- setState(state => {
-
- return {
- ...state,
- buttonHeight,
- buttonWidth,
- left,
- menuState: STATES.SHOWN,
- top,
- }
- });
- });
- },
-
- hide: hide
- }
-
- }, [hide, container])
-
- const hide = onHidden => {
- opacityAnimation.value = withTiming(0, {
- duration: animationDuration,
- easing: EASING
- });
- setTimeout(() => {
- setState(
- state => {
- return {
- ...state,
- menuState: STATES.HIDDEN,
- }
- }
- );
- menuSizeXAnimation.value = 0;
- menuSizeYAnimation.value = 0;
- opacityAnimation.value = 0;
-
- onHidden && onHidden();
- if (Platform.OS !== 'ios' && props.onHidden) {
- props.onHidden();
- }
- }, props.animationDuration)
-
- };
-
- // @@ TODO: Rework this
- const _hide = () => {
- hide();
- };
-
-
- const menuSize = useAnimatedStyle(() => {
- return {
- width: menuSizeXAnimation.value,
- height: menuSizeYAnimation.value,
- }
- });
- const shadowMenuContainerStyle = useAnimatedStyle(() => {
- return {
- opacity: opacityAnimation.value,
- };
- }, [state])
-
- const { isRTL } = I18nManager;
- const dimensions = Dimensions.get('window');
- const { width: windowWidth } = dimensions;
- const windowHeight = dimensions.height - (StatusBar.currentHeight || 0);
- const {
- menuWidth,
- menuHeight,
- buttonWidth,
- buttonHeight,
- } = state;
-
- // Adjust position of menu
- let { left, top } = state;
- const transforms = [];
- if (
- (isRTL && left + buttonWidth - menuWidth > SCREEN_INDENT) ||
- (!isRTL && left + menuWidth > windowWidth - SCREEN_INDENT)
- ) {
- transforms.push({
- translateX: menuSizeXAnimation.value * -1,
- });
-
- left = Math.min(windowWidth - SCREEN_INDENT, left + buttonWidth);
- } else if (left < SCREEN_INDENT) {
- left = SCREEN_INDENT;
- }
-
-
- // Flip by Y axis if menu hits bottom screen border
- if (top > windowHeight - menuHeight - SCREEN_INDENT) {
- transforms.push({
- translateY: menuSizeYAnimation.value * -1,
- });
-
- top = windowHeight - SCREEN_INDENT;
- top = Math.min(windowHeight - SCREEN_INDENT, top + buttonHeight);
- } else if (top < SCREEN_INDENT) {
- top = SCREEN_INDENT;
- }
-
- const extraStyles = {
- transform: transforms,
- top,
-
- // Switch left to right for rtl devices
- ...(isRTL ? { right: left } : { left }),
- }
-
- const { menuState } = state;
- const animationStarted = menuState === STATES.ANIMATING;
- const modalVisible = menuState === STATES.SHOWN || animationStarted;
-
- const { testID, button, style, children } = props;
-
- return <View ref={container} collapsable={false} testID={testID}>
- <View>{button}</View>
-
- <Modal
- visible={modalVisible}
- onRequestClose={_hide}
- supportedOrientations={[
- 'portrait',
- 'portrait-upside-down',
- 'landscape',
- 'landscape-left',
- 'landscape-right',
- ]}
- transparent
- onDismiss={_onDismiss}
- >
- <TouchableWithoutFeedback onPress={_hide} accessible={false}>
- <View style={StyleSheet.absoluteFill}>
- <Animated.View
- onLayout={_onMenuLayout}
- style={[
- styles.shadowMenuContainer,
- shadowMenuContainerStyle,
- extraStyles,
- style,
- ]}
- >
- <Animated.View
- style={[styles.menuContainer, animationStarted && menuSize]}
- >
- {children}
- </Animated.View>
- </Animated.View>
- </View>
- </TouchableWithoutFeedback>
- </Modal>
- </View>
-
-}
-
-
-const styles = StyleSheet.create({
- shadowMenuContainer: {
- position: 'absolute',
- backgroundColor: 'white',
- borderRadius: 4,
- opacity: 0,
- overflow: "hidden",
-
- // Shadow
- ...Platform.select({
- ios: {
- shadowColor: 'black',
- shadowOffset: { width: 0.3 * 5, height: 0.5 * 5 },
- shadowOpacity: 0.2,
- shadowRadius: 0.7 * 5,
- },
- android: {
- elevation: 8,
- },
- }),
- },
- menuContainer: {
- overflow: 'hidden',
- },
-});
-
-export default forwardRef(Menu);
diff --git a/node_modules/react-native-reanimated-material-menu/src/Menu.tsx b/node_modules/react-native-reanimated-material-menu/src/Menu.tsx
new file mode 100644
index 0000000..c3bc1c9
--- /dev/null
+++ b/node_modules/react-native-reanimated-material-menu/src/Menu.tsx
@@ -0,0 +1,303 @@
+import React from 'react';
+
+import {
+ Animated,
+ Dimensions,
+ Easing,
+ I18nManager,
+ LayoutChangeEvent,
+ Modal,
+ Platform,
+ ScrollView,
+ StatusBar,
+ StyleSheet,
+ TouchableWithoutFeedback,
+ View,
+ ViewStyle,
+} from 'react-native';
+
+export interface MenuProps {
+ children?: React.ReactNode;
+ anchor?: React.ReactNode;
+ style?: ViewStyle;
+ onRequestClose?(): void;
+ animationDuration?: number;
+ testID?: string;
+ visible?: boolean;
+}
+
+enum States {
+ Hidden,
+ Animating,
+ Shown,
+}
+
+interface State {
+ buttonHeight: number;
+ buttonWidth: number;
+ left: number;
+ menuHeight: number;
+ menuSizeAnimation: Animated.ValueXY;
+ menuState: States;
+ menuWidth: number;
+ opacityAnimation: Animated.Value;
+ top: number;
+}
+
+const EASING = Easing.bezier(0.4, 0, 0.2, 1);
+const SCREEN_INDENT = 8;
+const SCREEN_INDENT_VERTICAL = 80;
+
+class Menu extends React.Component<MenuProps, State> {
+ _container: View | null = null;
+
+ static defaultProps = {
+ animationDuration: 300,
+ };
+
+ constructor(props: MenuProps) {
+ super(props);
+
+ this.state = {
+ menuState: States.Hidden,
+
+ top: 0,
+ left: 0,
+
+ menuWidth: 0,
+ menuHeight: 0,
+
+ buttonWidth: 0,
+ buttonHeight: 0,
+
+ menuSizeAnimation: new Animated.ValueXY({ x: 0, y: 0 }),
+ opacityAnimation: new Animated.Value(0),
+ };
+ }
+
+ componentDidMount() {
+ if (!this.props.visible) {
+ return;
+ }
+
+ this.show();
+ }
+
+ componentDidUpdate(prevProps: MenuProps) {
+ if (prevProps.visible === this.props.visible) {
+ return;
+ }
+
+ if (this.props.visible) {
+ this.show();
+ } else {
+ this.hide();
+ }
+ }
+
+ private setContainerRef = (ref: View) => {
+ this._container = ref;
+ };
+
+ // Start menu animation
+ private onMenuLayout = (e: LayoutChangeEvent) => {
+ if (this.state.menuState === States.Animating) {
+ return;
+ }
+
+ const { width, height } = e.nativeEvent.layout;
+ let timeout:any = 0;
+ this.setState(
+ {
+ menuState: States.Animating,
+ menuWidth: width,
+ menuHeight: height,
+ },
+ () => {
+ Animated.parallel([
+ Animated.timing(this.state.menuSizeAnimation, {
+ toValue: { x: width, y: height },
+ duration: this.props.animationDuration,
+ easing: EASING,
+ useNativeDriver: false,
+ }),
+ Animated.timing(this.state.opacityAnimation, {
+ toValue: 1,
+ duration: this.props.animationDuration,
+ easing: EASING,
+ useNativeDriver: false,
+ }),
+ ]).start(({finished}) => {
+ if (finished) {
+ clearTimeout(timeout);
+ timeout = setTimeout(() => {
+ this.setState({
+ menuState: States.Shown
+ })
+ },20)
+
+ }
+ });
+ },
+ );
+ };
+
+ show = () => {
+ this._container?.measureInWindow((left, top, buttonWidth, buttonHeight) => {
+ this.setState({
+ buttonHeight,
+ buttonWidth,
+ left,
+ menuState: States.Shown,
+ top,
+ });
+ });
+ };
+
+ hide = () => {
+ Animated.timing(this.state.opacityAnimation, {
+ toValue: 0,
+ duration: this.props.animationDuration,
+ easing: EASING,
+ useNativeDriver: false,
+ }).start(() => {
+ // Reset state
+ this.setState({
+ menuState: States.Hidden,
+ menuSizeAnimation: new Animated.ValueXY({ x: 0, y: 0 }),
+ opacityAnimation: new Animated.Value(0),
+ });
+ });
+ };
+
+ private onRequestClose = () => {
+ this.props.onRequestClose?.();
+ };
+
+ render() {
+ const { isRTL } = I18nManager;
+
+ const dimensions = Dimensions.get('window');
+ const { width: windowWidth } = dimensions;
+ const windowHeight = dimensions.height - (StatusBar.currentHeight || 0);
+
+ const {
+ menuSizeAnimation,
+ menuWidth,
+ menuHeight,
+ buttonWidth,
+ buttonHeight,
+ opacityAnimation,
+ } = this.state;
+ const menuSize = {
+ width: menuSizeAnimation.x,
+ height: menuSizeAnimation.y,
+ };
+
+ // Adjust position of menu
+ let { left, top } = this.state;
+ const transforms:any[] = [];
+
+ if (
+ (isRTL && left + buttonWidth - menuWidth > SCREEN_INDENT) ||
+ (!isRTL && left + menuWidth > windowWidth - SCREEN_INDENT)
+ ) {
+ transforms.push({
+ translateX: Animated.multiply(menuSizeAnimation.x, -1),
+ } as never);
+
+ left = Math.min(windowWidth - SCREEN_INDENT, left + buttonWidth);
+ } else if (left < SCREEN_INDENT) {
+ left = SCREEN_INDENT;
+ }
+
+ // Flip by Y axis if menu hits bottom screen border
+ if (top > windowHeight - menuHeight - SCREEN_INDENT_VERTICAL) {
+ transforms.push({
+ translateY: Animated.multiply(menuSizeAnimation.y, -1),
+ } as never);
+
+ top = windowHeight - SCREEN_INDENT_VERTICAL;
+ top = Math.min(windowHeight - SCREEN_INDENT_VERTICAL, top + buttonHeight);
+ } else if (top < SCREEN_INDENT_VERTICAL) {
+ top = SCREEN_INDENT_VERTICAL;
+ }
+
+ const shadowMenuContainerStyle = {
+ opacity: opacityAnimation,
+ transform: transforms,
+ maxHeight: 500,
+ top,
+
+ // Switch left to right for rtl devices
+ ...(isRTL ? { right: left } : { left }),
+ };
+ const { menuState } = this.state;
+ const animationStarted = menuState === States.Animating;
+ const modalVisible = menuState === States.Shown || animationStarted;
+
+ const { testID, anchor, style, children } = this.props;
+
+ return (
+ <View ref={this.setContainerRef} collapsable={false} testID={testID}>
+ {anchor}
+
+ <Modal
+ visible={modalVisible}
+ onRequestClose={this.onRequestClose}
+ supportedOrientations={[
+ 'portrait',
+ 'portrait-upside-down',
+ 'landscape',
+ 'landscape-left',
+ 'landscape-right',
+ ]}
+ transparent
+ >
+ <TouchableWithoutFeedback onPress={this.onRequestClose} accessible={false}>
+ <View style={StyleSheet.absoluteFill}>
+ <Animated.View
+ onLayout={this.onMenuLayout}
+ style={[styles.shadowMenuContainer, shadowMenuContainerStyle, style]}
+ >
+ <Animated.View style={[styles.menuContainer, animationStarted && menuSize]}>
+ <ScrollView showsVerticalScrollIndicator={menuState !== States.Animating} >
+ {children}
+ </ScrollView>
+ </Animated.View>
+ </Animated.View>
+ </View>
+ </TouchableWithoutFeedback>
+ </Modal>
+ </View>
+ );
+ }
+}
+
+const styles = StyleSheet.create({
+ shadowMenuContainer: {
+ position: 'absolute',
+ backgroundColor: 'white',
+ borderRadius: 4,
+ opacity: 0,
+
+ // Shadow
+ ...Platform.select({
+ ios: {
+ shadowColor: 'black',
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.14,
+ shadowRadius: 2,
+ },
+ android: {
+ elevation: 8,
+ },
+ }),
+ },
+ menuContainer: {
+ overflow: 'hidden',
+ },
+});
+
+
+export default Menu
\ No newline at end of file
diff --git a/node_modules/react-native-reanimated-material-menu/src/MenuItem.js b/node_modules/react-native-reanimated-material-menu/src/MenuItem.js
index 120a870..d6459c0 100644
--- a/node_modules/react-native-reanimated-material-menu/src/MenuItem.js
+++ b/node_modules/react-native-reanimated-material-menu/src/MenuItem.js
@@ -14,6 +14,11 @@ const Touchable =
? TouchableNativeFeedback
: TouchableHighlight;
+/**
+ *
+ * @param {any} param0
+ * @returns
+ */
function MenuItem({
children,
disabled,

View File

@@ -6,3 +6,5 @@ node_modules
.dev.vars
.wrangler
/output
/fonts

View File

@@ -1,18 +1,14 @@
FROM --platform=$BUILDPLATFORM node:20-alpine
FROM --platform=$BUILDPLATFORM oven/bun:1.1.36-alpine
RUN mkdir -p /home/node/app && chown -R node:node /home/node/app
RUN mkdir -p /home/bun/app && chown -R bun:bun /home/bun/app
WORKDIR /home/node/app
WORKDIR /home/bun/app
USER node
USER bun
COPY --chown=node:node build ./build
COPY --chown=bun:bun output .
RUN mv build/package.json .
RUN bun install
RUN npm install
RUN ls
CMD [ "npm", "run", "start" ]
CMD [ "bun", "run", "start" ]

View File

@@ -21,9 +21,10 @@ import { mdiTimerOutline } from "@mdi/js";
import { Icon } from "@notesnook/ui";
import { Flex, Image, Link, Text } from "@theme-ui/components";
import { SxProp } from "@theme-ui/core";
import { PUBLIC_URL } from "../utils/env";
export function MonographChat({ sx }: SxProp) {
type Props = SxProp & { publicUrl: string };
export function MonographChat({ sx, publicUrl }: Props) {
return (
<Flex
sx={{
@@ -62,7 +63,7 @@ export function MonographChat({ sx }: SxProp) {
borderRadius: 10,
width: "100%"
}}
src={`${PUBLIC_URL}/api/og.png?title=Open+sourcing&description=VGhpcyBtb25vZ3JhcGggaXMgZW5jcnlwdGVkLiBFbnRlciBwYXNzd29yZCB0byB2aWV3IGNvbnRlbnRzLg%3D%3D&date=Saturday%2C+February+18%2C+2023`}
src={`${publicUrl}/api/og.jpg?title=Open+sourcing&description=VGhpcyBtb25vZ3JhcGggaXMgZW5jcnlwdGVkLiBFbnRlciBwYXNzd29yZCB0byB2aWV3IGNvbnRlbnRzLg%3D%3D&date=Saturday%2C+February+18%2C+2023`}
/>
<Text
mt="12px"
@@ -76,10 +77,10 @@ export function MonographChat({ sx }: SxProp) {
color: "accent",
overflowWrap: "anywhere"
}}
href={`${PUBLIC_URL}/62db75572020209c36f9f9fb`}
href={`${publicUrl}/62db75572020209c36f9f9fb`}
target="_blank"
>
{PUBLIC_URL}/62db75572020209c36f9f9fb
{publicUrl}/62db75572020209c36f9f9fb
</Link>
</Text>
<Timer time={"5m ago"} sx={{ mb: 1, mr: 1, alignSelf: "end" }} />

View File

@@ -73,6 +73,11 @@ function generateTableOfContents() {
let currentHeading = 0;
for (const heading of headings) {
const isCalloutHeading = heading.closest(".callout");
if (isCalloutHeading) {
continue;
}
const text = heading.textContent || "<empty>";
const nodeName = heading.nodeName;
const headingLevel = levelsMap[nodeName];

View File

@@ -60,7 +60,7 @@ export default function handleRequest(
const styles = constructStyleTagsFromChunks(extractCriticalToChunks(body));
const html = `<!DOCTYPE html><html><head><!--start head-->${head}${styles}<!--end head--></head><body><div id="root">${body}</div></body></html>`;
responseHeaders.set("Content-Type", "text/html");
responseHeaders.set("Content-Type", "text/html; charset=utf-8");
return new Response(html, {
status: responseStatusCode,

View File

@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import type { MetaFunction, LoaderFunctionArgs } from "@remix-run/node";
import { Cipher } from "@notesnook/crypto";
import { convert } from "html-to-text";
import { Flex, Text } from "@theme-ui/components";
import { useLoaderData } from "@remix-run/react";
import { MonographPage } from "../components/monographpost";
@@ -46,12 +45,13 @@ type Monograph = {
type MonographResponse = Omit<Monograph, "content"> & { content: string };
export const meta: MetaFunction<typeof loader> = ({ data }) => {
const imageUrl = `${PUBLIC_URL}/api/og.png?${new URLSearchParams({
if (!data || !data.metadata || !data.monograph) return [];
const imageUrl = `${PUBLIC_URL}/api/og.jpg?${new URLSearchParams({
title: data?.metadata?.title || "",
description: Buffer.from(
data?.metadata?.fullDescription || "",
"utf-8"
).toString("base64"),
description: data?.metadata?.fullDescription
? Buffer.from(data.metadata.fullDescription, "utf-8").toString("base64")
: "",
date: data?.metadata?.datePublished || ""
}).toString()}`;
@@ -88,7 +88,7 @@ export async function loader({ params }: LoaderFunctionArgs) {
metadata
};
} catch (e) {
console.error(e);
// console.error(e);
return {
monograph: null,
metadata: {
@@ -134,15 +134,6 @@ export default function MonographPost() {
);
}
const extractParagraph = (html: string) => {
if (!html) return "";
return convert(html, {
wordwrap: false,
preserveNewlines: false,
decodeEntities: true
});
};
type Metadata = {
title: string;
fullDescription: string;
@@ -150,6 +141,21 @@ type Metadata = {
datePublished: string;
};
function extractFirstWords(html: string, numWords = 30): string {
// Strip HTML tags and normalize whitespace
const plainText = html
.replace(/<[^>]*>/g, " ")
.replace(/\s+/g, " ")
.trim();
// Split into words and take first N
const words = plainText.split(" ").slice(0, numWords);
// Add ellipsis if text was truncated
const excerpt = words.join(" ");
return words.length < plainText.split(" ").length ? excerpt + "..." : excerpt;
}
function trimDescription(
str: string,
length: number,
@@ -174,12 +180,12 @@ function addPeriod(str: string) {
return str + "...";
}
function getMonographMetadata(monograph?: Monograph): Metadata {
function getMonographMetadata(monograph: Monograph): Metadata {
const title = monograph?.title || "Not found";
const text = monograph?.encryptedContent
? "This monograph is encrypted. Enter password to view contents."
: monograph?.content
? extractParagraph(monograph?.content.data)
? extractFirstWords(monograph?.content.data, 100)
: "";
const shortDescription = trimDescription(text, 150, true);
const fullDescription = trimDescription(text, 300, true);

View File

@@ -38,6 +38,7 @@ import { Footer } from "../components/footer";
import { Header } from "../components/header";
import { ForwardRef } from "@theme-ui/components/dist/declarations/src/types";
import { PUBLIC_URL } from "../utils/env";
import { useLoaderData } from "@remix-run/react";
export const meta: MetaFunction = () => {
return generateMetaDescriptors({
@@ -51,6 +52,10 @@ export const meta: MetaFunction = () => {
});
};
export async function loader() {
return { publicUrl: PUBLIC_URL };
}
const ButtonLink = Button as ForwardRef<
HTMLButtonElement,
ButtonProps & LinkProps
@@ -74,6 +79,8 @@ const features = [
}
];
export default function Monograph() {
const { publicUrl } = useLoaderData<typeof loader>();
return (
<Flex
sx={{
@@ -146,6 +153,7 @@ export default function Monograph() {
border: "1px solid var(--border)",
boxShadow: "0px -5px 15px 0px rgba(0,0,0,0.05)"
}}
publicUrl={publicUrl}
/>
</Flex>
<Box

View File

@@ -21,6 +21,6 @@ import { LoaderFunctionArgs } from "@remix-run/node";
export async function loader({ request }: LoaderFunctionArgs) {
const url = new URL(request.url);
url.pathname += ".png";
url.pathname += ".jpg";
return Response.redirect(url, 308);
}

View File

@@ -0,0 +1,52 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 { LoaderFunctionArgs } from "@remix-run/node";
import { makeImage } from "../utils/generate-og-image.server";
import { formatDate } from "@notesnook/core";
export async function loader({ request }: LoaderFunctionArgs) {
const url = new URL(request.url);
const title = url.searchParams.get("title") || "Not found";
const description = url.searchParams.get("description") || "";
const date =
url.searchParams.get("date") ||
formatDate(new Date(), {
type: "date-time",
dateFormat: "YYYY-MM-DD",
timeFormat: "24-hour"
});
return new Response(
await makeImage(
{
date,
description,
title
},
url.search
),
{
status: 200,
headers: {
"Content-Type": "image/jpeg"
}
}
);
}

View File

@@ -18,32 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { LoaderFunctionArgs } from "@remix-run/node";
import { makeImage } from "../utils/generate-og-image.server";
import { formatDate } from "@notesnook/core";
export async function loader({ request }: LoaderFunctionArgs) {
const url = new URL(request.url);
const title = url.searchParams.get("title") || "Not found";
const description = url.searchParams.get("description") || "";
const date =
url.searchParams.get("date") ||
formatDate(new Date(), {
type: "date-time",
dateFormat: "YYYY-MM-DD",
timeFormat: "24-hour"
});
return new Response(
await makeImage({
date,
description,
title
}),
{
status: 200,
headers: {
"Content-Type": "image/png"
}
}
);
url.pathname = url.pathname.replace(/.png/, ".jpg");
return Response.redirect(url, 308);
}

View File

@@ -17,12 +17,15 @@ 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 { json, LoaderFunctionArgs } from "@remix-run/node";
import { cors } from "remix-utils/cors";
import { COMPATIBILITY_VERSION, INSTANCE_NAME } from "../utils/env";
export async function loader() {
return Response.json({
export async function loader({ request }: LoaderFunctionArgs) {
const response = json({
version: COMPATIBILITY_VERSION,
id: "monograph",
instance: INSTANCE_NAME
});
return cors(request, response);
}

View File

@@ -17,13 +17,12 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
const p = "process" in globalThis ? globalThis.process : ({ env: {} } as any);
export const API_HOST =
import.meta.env.API_HOST ||
process.env.API_HOST ||
`https://api.notesnook.com`;
import.meta.env.API_HOST || p.env.API_HOST || `https://api.notesnook.com`;
export const PUBLIC_URL =
import.meta.env.PUBLIC_URL ||
process.env.PUBLIC_URL ||
`http://localhost:${import.meta.env.PORT || process.env.PORT || 5017}`;
p.env.PUBLIC_URL ||
`http://localhost:${import.meta.env.PORT || p.env.PORT || 5173}`;
export const COMPATIBILITY_VERSION = 1;
export const INSTANCE_NAME = process.env.INSTANCE_NAME || "default";
export const INSTANCE_NAME = p.env.INSTANCE_NAME || "default";

View File

@@ -0,0 +1,174 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 { createCanvas, GlobalFonts, loadImage } from "@napi-rs/canvas";
import { LRUCache } from "lru-cache";
import { ThemeDark } from "@notesnook/theme";
import path from "path";
import { fileURLToPath } from "url";
import { split } from "canvas-hypertxt";
import { readFile } from "fs/promises";
export type OGMetadata = { title: string; description: string; date: string };
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const ROOT = path.join(__dirname, "../../");
const fontMap = JSON.parse(
await readFile(path.join(ROOT, "fonts", "fonts.json"), "utf-8")
);
// Register fonts
const OpenSans = path.join(
__dirname,
import.meta.env.DEV ? "../assets/fonts/" : "../../assets/fonts/",
"open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-regular.ttf"
);
const OpenSansBold = path.join(
__dirname,
import.meta.env.DEV ? "../assets/fonts/" : "../../assets/fonts/",
"open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-600.ttf"
);
console.log("OpenSans", GlobalFonts.registerFromPath(OpenSans, "OpenSans"));
console.log(
"registering",
"OpenSansBold",
GlobalFonts.registerFromPath(OpenSansBold, "OpenSansBold")
);
const fontFamilies = {
regular: ["OpenSans"],
bold: ["OpenSansBold"]
};
for (const font of fontMap) {
const id = (font.name + font.weight).replace(/ /g, "");
const result = GlobalFonts.registerFromPath(
path.resolve(ROOT, font.path),
id
);
if (!result)
throw new Error(
`Failed to register font: ${id} at ${path.resolve(ROOT, font.path)}`
);
if (font.weight === "600") fontFamilies.bold.push(id);
else fontFamilies.regular.push(id);
console.log("registering", id, result);
}
const cache = new LRUCache<string, Buffer>({
ttl: 1000 * 60 * 60 * 24,
ttlAutopurge: true
});
const WIDTH = 1200;
const HEIGHT = 630;
const PADDING = 50;
const QUALITY = 80;
const logo = loadImage(
import.meta.env.DEV
? path.resolve(__dirname, "../../public/logo.svg")
: path.resolve(__dirname, "../../client/logo.svg")
);
const boldFontFamily = fontFamilies.bold.join(",");
const regularFontFamily = fontFamilies.regular.join(",");
export async function makeImage(metadata: OGMetadata, cacheKey: string) {
if (cache.has(cacheKey)) {
return cache.get(cacheKey)!;
}
console.time("canvas");
const theme = ThemeDark.scopes.base;
const canvas = createCanvas(WIDTH, HEIGHT);
const ctx = canvas.getContext("2d");
// Background
ctx.fillStyle = theme.primary.background;
ctx.fillRect(0, 0, WIDTH, HEIGHT);
// Bottom border
ctx.fillStyle = "#008837";
ctx.fillRect(0, HEIGHT - 10, WIDTH, 10);
// Draw logo
ctx.drawImage(await logo, PADDING, HEIGHT - PADDING - 85, 80, 80);
// Draw bottom text
ctx.fillStyle = theme.primary.heading;
ctx.font = "600 32px OpenSansBold";
ctx.fillText("Notesnook Monograph", PADDING + 95, HEIGHT - PADDING - 55);
ctx.fillStyle = theme.secondary.paragraph;
ctx.font = "25px OpenSans";
ctx.fillText(
"Anonymous, secure, and encrypted note sharing with password protection.",
PADDING + 95,
HEIGHT - PADDING - 19
);
// Draw date
ctx.fillStyle = theme.secondary.paragraph;
ctx.font = "25px OpenSans";
ctx.fillText(metadata.date, PADDING, PADDING + 25);
// Draw title
ctx.fillStyle = theme.primary.heading;
ctx.font = `600 64px ${boldFontFamily}`;
let y = PADDING + 105;
const titleLines = split(
ctx as any,
metadata.title,
`600 64px ${boldFontFamily}`,
WIDTH - PADDING * 2,
true
);
for (const line of titleLines) {
ctx.fillText(line, PADDING, y);
y += 60;
}
// Draw description
ctx.fillStyle = theme.primary.paragraph;
ctx.font = `30px ${regularFontFamily}`;
const description = Buffer.from(
metadata.description || "",
"base64"
).toString("utf-8");
const descLines = split(
ctx as any,
description,
`30px ${regularFontFamily}`,
WIDTH - PADDING * 2,
true
).slice(0, 4);
for (const line of descLines) {
ctx.fillText(line, PADDING, y);
y += 40;
}
const buffer = canvas.toBuffer("image/jpeg", QUALITY);
console.timeEnd("canvas");
cache.set(cacheKey, buffer);
return buffer;
}

View File

@@ -1,148 +0,0 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 satori from "satori";
import { ThemeDark } from "@notesnook/theme";
import { svg2png, initialize } from "svg2png-wasm";
import svg2pngWasm from "svg2png-wasm/svg2png_wasm_bg.wasm?arraybuffer";
import fontRegular from "../assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-regular.ttf?arraybuffer";
import fontBold from "../assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-600.ttf?arraybuffer";
export type OGMetadata = { title: string; description: string; date: string };
await initialize(svg2pngWasm);
export async function makeImage(metadata: OGMetadata) {
const theme = ThemeDark.scopes.base;
const svg = await satori(
<div
style={{
flexDirection: "column",
display: "flex",
justifyContent: "space-between",
height: "100%",
borderBottom: "10px solid #008837",
width: "100%",
padding: 50,
margin: 0
}}
>
<div style={{ display: "flex", flexDirection: "column" }}>
<p
style={{ fontSize: 25, margin: 0, color: theme.secondary.paragraph }}
>
{metadata.date}
</p>
<h1
style={{
margin: 0,
marginTop: 5,
fontSize: 64,
fontWeight: 600,
color: theme.primary.heading
}}
>
{metadata.title}
</h1>
<p
style={{
margin: 0,
marginTop: 5,
fontSize: 30,
color: theme.primary.paragraph
}}
>
{Buffer.from(metadata.description || "", "base64").toString("utf-8")}
</p>
</div>
<div style={{ display: "flex", flexDirection: "row", flexShrink: 0 }}>
<svg
width="80"
height="80"
viewBox="0 0 1024 1024"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_25_12)">
<path d="M1024 0H0V1024H1024V0Z" fill="white" />
<path
d="M736.652 699.667C719.397 750.078 684.817 792.732 639.064 820.039C593.311 847.346 539.354 857.535 486.794 848.792C434.234 840.049 386.481 812.942 352.032 772.294C317.583 731.646 298.673 680.095 298.667 626.812V516.562L377.788 549.615V626.767C377.78 647.546 382.221 668.085 390.812 687.005C399.402 705.924 411.943 722.785 427.592 736.455C430.562 739.042 433.644 741.562 436.828 743.914C460.184 761.302 488.23 771.266 517.322 772.511C518.312 772.511 519.268 772.59 520.247 772.612C521.225 772.635 522.497 772.612 523.622 772.612C524.747 772.612 525.872 772.612 526.997 772.612C528.122 772.612 528.932 772.612 529.922 772.511C559.002 771.263 587.038 761.308 610.393 743.936C613.565 741.585 616.648 739.076 619.629 736.489C640.186 718.51 655.286 695.123 663.212 668.989L736.652 699.667Z"
fill="black"
/>
<path
d="M748.667 430.748V626.813C748.667 629.344 748.667 631.887 748.509 634.418L669.545 601.399V430.748C669.533 393.064 654.939 356.847 628.82 329.682C602.702 302.518 567.086 286.514 529.431 285.022C491.777 283.53 455.006 296.666 426.82 321.679C398.634 346.692 381.221 381.641 378.227 419.206C377.945 423.008 377.788 426.867 377.788 430.748V479.461L298.667 446.386V205.748H523.667C583.34 205.748 640.57 229.453 682.766 271.649C724.961 313.845 748.667 371.074 748.667 430.748Z"
fill="black"
/>
</g>
<defs>
<clipPath id="clip0_25_12">
<rect width="1024" height="1024" rx="200" fill="white" />
</clipPath>
</defs>
</svg>
<div
style={{ display: "flex", flexDirection: "column", marginLeft: 15 }}
>
<p
style={{
margin: 0,
fontSize: 32,
fontWeight: 600,
color: theme.primary.heading
}}
>
Notesnook Monograph
</p>
<p
style={{
fontSize: 25,
margin: 0,
color: theme.secondary.paragraph
}}
>
Anonymous, secure, and encrypted note sharing with password
protection.
</p>
</div>
</div>
</div>,
{
width: 1200,
height: 630,
fonts: [
{
name: "Open Sans",
data: fontRegular!,
weight: 400,
style: "normal"
},
{
name: "Open Sans",
data: fontBold!,
weight: 600,
style: "normal"
}
]
}
);
return await svg2png(svg, {
backgroundColor: "black"
});
}

View File

@@ -74,7 +74,7 @@ export async function isSpam(monograph: Monograph) {
}
return isSpam;
} catch (e) {
console.error(e);
// console.error(e);
return false;
}
}

View File

@@ -23,7 +23,7 @@ export async function read<T>(key: string, fallback: T): Promise<T> {
try {
return (JSON.parse(await readFile(key, "utf-8")) as T) || fallback;
} catch (e) {
console.error(e);
// console.error(e);
return fallback;
}
}

View File

@@ -28,11 +28,11 @@ const cache: Record<
export async function read<T>(key: string, fallback: T) {
const cached = cache[key];
if (cached && cached.ttl > Date.now() - cached.cachedAt) {
return cached.value;
return cached.value as T;
}
const value = (await provider).read<T>(key, fallback);
cache[key] = {
ttl: 5 * 60000,
ttl: 60 * 60 * 1000,
value,
cachedAt: Date.now()
};
@@ -40,6 +40,10 @@ export async function read<T>(key: string, fallback: T) {
}
export async function write<T>(key: string, data: T) {
if (cache[key]) {
cache[key].value = data;
cache[key].cachedAt = Date.now();
}
return (await provider).write<T>(key, data);
}

View File

@@ -40,7 +40,7 @@ export async function read<T>(key: string, fallback: T): Promise<T> {
key
});
if (typeof response === "object" && !response.success) {
console.error("failed:", response.errors);
// console.error("failed:", response.errors);
return fallback;
}
return (
@@ -48,7 +48,7 @@ export async function read<T>(key: string, fallback: T): Promise<T> {
fallback
);
} catch (e) {
console.error(e);
// console.error(e);
return fallback;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,24 @@
{
"name": "@notesnook/monograph",
"version": "1.1.0",
"version": "1.2.2",
"private": true,
"sideEffects": false,
"type": "module",
"scripts": {
"build": "remix vite:build",
"build": "npm run fonts && remix vite:build",
"dev": "remix vite:dev",
"typecheck": "tsc",
"start": "remix-serve ./build/server/index.js"
"fonts": "node scripts/font-loader.mjs",
"start": "bun server.ts"
},
"dependencies": {
"@emotion/cache": "11.11.0",
"@emotion/react": "11.11.1",
"@emotion/server": "11.11.0",
"@mdi/js": "^7.4.47",
"@lingui/core": "4.11.4",
"@lingui/react": "4.11.4",
"@mdi/js": "^7.4.47",
"@napi-rs/canvas": "0.1.59",
"@notesnook/core": "file:../../packages/core",
"@notesnook/crypto": "file:../../packages/crypto",
"@notesnook/editor": "file:../../packages/editor",
@@ -28,11 +30,12 @@
"@sagi.io/workers-kv": "^0.0.14",
"@theme-ui/components": "^0.16.2",
"@theme-ui/core": "^0.16.2",
"buffer": "^6.0.3",
"canvas-hypertxt": "^1.0.3",
"comlink": "^4.4.1",
"date-fns": "^4.1.0",
"html-to-text": "^9.0.5",
"isbot": "^5.1.17",
"lru-cache": "^11.0.2",
"mac-scrollbar": "^0.13.6",
"nanoid": "^5.0.7",
"react": "18.3.1",
@@ -41,24 +44,24 @@
"react-turnstile": "^1.1.4",
"refractor": "^4.8.1",
"remix-utils": "^7.7.0",
"satori": "^0.11.1",
"slugify": "^1.6.6",
"svg2png-wasm": "^1.4.1",
"zustand": "^4.5.5"
},
"devDependencies": {
"@remix-run/dev": "^2.12.1",
"@remix-run/serve": "^2.12.1",
"@types/bun": "^1.1.10",
"@remix-run/server-runtime": "^2.15.0",
"@types/bun": "^1.1.13",
"@types/html-to-text": "^9.0.4",
"@types/react": "^18.3.9",
"@types/react-dom": "^18.3.0",
"@types/react-modal": "^3.16.3",
"autoprefixer": "^10.4.20",
"google-fonts-helper": "^3.6.0",
"postcss": "^8.4.47",
"vite": "^5.4.8",
"vite-plugin-arraybuffer": "^0.0.8",
"vite-plugin-wasm": "^3.3.0",
"vite-plugin-static-copy": "^2.1.0",
"vite-tsconfig-paths": "^5.0.1",
"wrangler": "3.78.11"
},

View File

@@ -0,0 +1,32 @@
<svg
width="80"
height="80"
viewBox="0 0 1024 1024"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_25_12)">
<rect
x="0"
y="0"
width="1024"
height="1024"
rx="130"
ry="130"
fill="white"
/>
<path
d="M736.652 699.667C719.397 750.078 684.817 792.732 639.064 820.039C593.311 847.346 539.354 857.535 486.794 848.792C434.234 840.049 386.481 812.942 352.032 772.294C317.583 731.646 298.673 680.095 298.667 626.812V516.562L377.788 549.615V626.767C377.78 647.546 382.221 668.085 390.812 687.005C399.402 705.924 411.943 722.785 427.592 736.455C430.562 739.042 433.644 741.562 436.828 743.914C460.184 761.302 488.23 771.266 517.322 772.511C518.312 772.511 519.268 772.59 520.247 772.612C521.225 772.635 522.497 772.612 523.622 772.612C524.747 772.612 525.872 772.612 526.997 772.612C528.122 772.612 528.932 772.612 529.922 772.511C559.002 771.263 587.038 761.308 610.393 743.936C613.565 741.585 616.648 739.076 619.629 736.489C640.186 718.51 655.286 695.123 663.212 668.989L736.652 699.667Z"
fill="black"
/>
<path
d="M748.667 430.748V626.813C748.667 629.344 748.667 631.887 748.509 634.418L669.545 601.399V430.748C669.533 393.064 654.939 356.847 628.82 329.682C602.702 302.518 567.086 286.514 529.431 285.022C491.777 283.53 455.006 296.666 426.82 321.679C398.634 346.692 381.221 381.641 378.227 419.206C377.945 423.008 377.788 426.867 377.788 430.748V479.461L298.667 446.386V205.748H523.667C583.34 205.748 640.57 229.453 682.766 271.649C724.961 313.845 748.667 371.074 748.667 430.748Z"
fill="black"
/>
</g>
<defs>
<clipPath id="clip0_25_12">
<rect width="1024" height="1024" rx="200" fill="white" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,121 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 { createWriteStream, existsSync, writeFileSync } from "fs";
import { constructURL } from "google-fonts-helper";
import { Writable } from "stream";
import { mkdir } from "fs/promises";
const FONTS = [
{ locale: "ja-JP", slug: "Noto+Sans+JP", name: "Noto Sans JP" },
{ locale: "ko-KR", slug: "Noto+Sans+KR", name: "Noto Sans KR" },
{ locale: "zh-CN", slug: "Noto+Sans+SC", name: "Noto Sans SC" },
{ locale: "zh-TW", slug: "Noto+Sans+TC", name: "Noto Sans TC" },
{ locale: "zh-HK", slug: "Noto+Sans+HK", name: "Noto Sans HK" },
{ locale: "th-TH", slug: "Noto+Sans+Thai", name: "Noto Sans Thai" },
{ locale: "bn-IN", slug: "Noto+Sans+Bengali", name: "Noto Sans Bengali" },
{ locale: "ar-AR", slug: "Noto+Sans+Arabic", name: "Noto Sans Arabic" },
{ locale: "ta-IN", slug: "Noto+Sans+Tamil", name: "Noto Sans Tamil" },
{ locale: "ml-IN", slug: "Noto+Sans+Malayalam", name: "Noto Sans Malayalam" },
{ locale: "he-IL", slug: "Noto+Sans+Hebrew", name: "Noto Sans Hebrew" },
{ locale: "te-IN", slug: "Noto+Sans+Telugu", name: "Noto Sans Telugu" },
{
locale: "devanagari",
slug: "Noto+Sans+Devanagari",
name: "Noto Sans Devanagari"
},
{ locale: "kannada", slug: "Noto+Sans+Kannada", name: "Noto Sans Kannada" }
// { locale: "symbol", slug: "Noto+Sans+Symbols", name: "Noto Sans Symbols" },
// {
// locale: "symbol",
// slug: "Noto+Sans+Symbols+2",
// name: "Noto Sans Symbols 2"
// },
// { locale: "math", slug: "Noto+Sans+Math", name: "Noto Sans Math" }
];
async function loadFonts(dir) {
const families = {};
for (const font of FONTS) {
families[font.name] = [400, 600];
}
const css = await (
await fetch(constructURL({ families }), {
headers: {
// Make sure it returns TTF.
"User-Agent":
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1"
}
})
).text();
const fontFaces = css
.split("@font-face")
.filter(Boolean)
.map((fontFace) => {
const src = fontFace.match(
/src: url\((.+)\) format\('(opentype|truetype)'\)/
);
const family = fontFace.match(/font-family: '(.+)'/);
const weight = fontFace.match(/font-weight: (\d+)/);
const style = fontFace.match(/font-style: (\w+)/);
return {
src: src[1],
family: family[1],
weight: weight[1],
style: style[1]
};
});
await mkdir("fonts", { recursive: true });
const fontMap = [];
for (const face of fontFaces) {
console.log("Downloading", face.family, face.weight);
const font = FONTS.find((font) => font.name === face.family);
if (!font) continue;
const fileName = `${font.slug}+${face.weight}.ttf`;
const path = `fonts/${fileName}`;
if (existsSync(path)) continue;
const fileStream = Writable.toWeb(
createWriteStream(path, {
autoClose: true,
emitClose: true,
flush: true
})
);
await (await fetch(face.src)).body.pipeTo(fileStream);
fontMap.push({
name: font.name,
locale: font.locale,
weight: face.weight,
path
});
}
writeFileSync("fonts/fonts.json", JSON.stringify(fontMap, null, 2));
console.log("Done");
}
loadFonts();

48
apps/monograph/server.ts Normal file
View File

@@ -0,0 +1,48 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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/>.
*/
// This server file is used to serve the Remix app in production using Bun.
// run it like so: npm run build; cd output; bun install; bun run start
// Running it directly will give an error.
import type { ServerBuild } from "@remix-run/server-runtime";
import { createRequestHandler } from "@remix-run/server-runtime";
import { resolve } from "node:path";
// @ts-expect-error server is not built yet
import * as build from "./build/server/index";
import { type Serve } from "bun";
const remix = createRequestHandler(
build as unknown as ServerBuild,
Bun.env.NODE_ENV
);
process.env.PORT = process.env.PORT || "3000";
export default {
port: process.env.PORT,
async fetch(request) {
// First we need to send handle static files
const { pathname } = new URL(request.url);
const file = Bun.file(
resolve(__dirname, "./build/client/", `.${pathname}`)
);
if (await file.exists()) return new Response(file);
// Only if a file doesn't exists we send the request to the Remix request handler
return remix(request);
}
} satisfies Serve;

View File

@@ -9,7 +9,7 @@
],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"types": ["vite/client", "vite-plugin-arraybuffer/types"],
"types": ["vite/client", "vite-plugin-arraybuffer/types", "bun"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",

View File

@@ -20,11 +20,11 @@ import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import arraybuffer from "vite-plugin-arraybuffer";
import wasm from "vite-plugin-wasm";
import { ThemeDark } from "@notesnook/theme";
import ThemeDark from "@notesnook/theme/theme-engine/themes/default-dark.json" with {type:"json"};
import type { Plugin, ResolvedConfig } from "vite";
import { writeFile } from "fs/promises";
import path from "path";
import { viteStaticCopy } from "vite-plugin-static-copy";
import * as pkg from "./package.json";
const DEDUPE = [
@@ -44,17 +44,19 @@ const DEFAULT_THEME_KEY =
export default defineConfig(({ isSsrBuild }) => ({
plugins: [
writePlugin({
"package.json": JSON.stringify({
"../package.json": JSON.stringify({
name: pkg.name,
version: pkg.version,
type: "module",
scripts: { start: pkg.scripts.start },
dependencies: {
"@remix-run/serve": pkg.devDependencies["@remix-run/serve"]
}
"@napi-rs/canvas": pkg.dependencies["@napi-rs/canvas"],
"@remix-run/server-runtime": pkg.devDependencies["@remix-run/server-runtime"],
},
})
}),
remix({
buildDirectory: "output/build",
future: {
v3_fetcherPersist: true,
v3_relativeSplatPath: true,
@@ -63,7 +65,13 @@ export default defineConfig(({ isSsrBuild }) => ({
}),
tsconfigPaths(),
arraybuffer(),
wasm()
isSsrBuild ? viteStaticCopy({
targets: [
{ src: "./server.ts", dest: "../../" },
{ src: "./app/assets", dest: "../" },
{ src: "./fonts", dest: "../" }
]
}) : undefined
],
worker: {
format: "es",
@@ -74,13 +82,15 @@ export default defineConfig(({ isSsrBuild }) => ({
}
},
ssr: {
...(process.env.NODE_ENV === "development" ? {} : { noExternal: true }),
...(process.env.NODE_ENV === "development"
? {}
: { noExternal: true, external: ["@napi-rs/canvas"] }),
target: "node"
},
build: {
target: isSsrBuild ? "node20" : undefined,
rollupOptions: {
external: ["svg2png-wasm/svg2png_wasm_bg.wasm"]
external: ["@napi-rs/canvas"]
}
},
define: {

View File

@@ -1,4 +1,4 @@
Subtotal: ₹300
Sales tax: ₹200
Discount: -₹300
Total: ₹300/mo
Total: ₹300

View File

@@ -1,4 +1,4 @@
Subtotal: ₹300
Sales tax: ₹200
Discount: -₹300
Total: ₹300/mo
Total: ₹300

View File

@@ -1,4 +1,4 @@
Subtotal: ₹300
Sales tax: ₹200
Discount: -₹300
Total: ₹300/mo
Total: ₹300

View File

@@ -1,4 +1,4 @@
Subtotal: $100
Sales tax: $0
Discount: -$100
Total: $100/mo
Total: $100

View File

@@ -1,4 +1,4 @@
Subtotal: $100
Sales tax: $0
Discount: -$100
Total: $100/mo
Total: $100

View File

@@ -1,4 +1,4 @@
Subtotal: $100
Sales tax: $0
Discount: -$100
Total: $100/mo
Total: $100

View File

@@ -231,6 +231,23 @@ test("add tags to note", async ({ page }) => {
expect(noteTags.every((t, i) => t === tags[i])).toBe(true);
});
test("add tags to locked note", async ({ page }) => {
const tags = ["incognito", "secret-stuff"];
const app = new AppModel(page);
await app.goto();
const notes = await app.goToNotes();
const note = await notes.createNote(NOTE);
await note?.contextMenu.lock(PASSWORD);
await note?.openLockedNote(PASSWORD);
await notes.editor.setTags(tags);
await page.waitForTimeout(200);
const noteTags = await notes.editor.getTags();
expect(noteTags).toHaveLength(tags.length);
expect(noteTags.every((t, i) => t === tags[i])).toBe(true);
});
for (const format of ["html", "txt", "md"] as const) {
test(`export note as ${format}`, async ({ page }) => {
const app = new AppModel(page);

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/web",
"version": "3.0.21",
"version": "3.0.22",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/web",
"version": "3.0.21",
"version": "3.0.22",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {

View File

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

View File

@@ -20,7 +20,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { getCurrentHash, getCurrentPath, makeURL } from "./navigation";
import Config from "./utils/config";
import type { AuthProps } from "./views/auth";
import { initializeFeatureChecks } from "./utils/feature-check";
import {
initializeFeatureChecks,
isFeatureSupported
} from "./utils/feature-check";
import { initializeLogger } from "./utils/logger";
type Route<TProps = null> = {
@@ -142,9 +145,24 @@ function isSessionExpired(path: Routes): RouteWithPath<AuthProps> | null {
return null;
}
function checkPrerequisites() {
if (!window.isSecureContext)
throw new Error("Please run Notesnook in a secure (https) context.");
if (!navigator.locks)
throw new Error("Your browser does not support the Web Locks API.");
if (!crypto.subtle)
throw new Error("Your browser does not support the SubtleCrypto API.");
if (!window.indexedDB && !isFeatureSupported("opfs"))
throw new Error("Your browser does not support IndexedDB or OPFS.");
if (!window.WebAssembly)
throw new Error("Your browser does not support WebAssembly.");
}
export async function init() {
await initializeFeatureChecks();
checkPrerequisites();
const { path, route } = getRoute();
const [{ default: Component }] = await Promise.all([

View File

@@ -118,7 +118,7 @@ class _SQLiteWorker {
} else this.retryCounter[sql] = 0;
if (ex instanceof Error || ex instanceof SQLiteError)
ex.message += ` (query: ${sql})`;
ex.message += ` (error preparing query: ${sql})`;
throw ex;
}
}
@@ -146,7 +146,7 @@ class _SQLiteWorker {
return rows;
} catch (e) {
if (e instanceof Error || e instanceof SQLiteError)
e.message += ` (query: ${sql})`;
e.message += ` (error exec query: ${sql})`;
throw e;
} finally {
await this.sqlite
@@ -201,16 +201,16 @@ class _SQLiteWorker {
this.initialized = false;
}
async export() {
const vfs = await this.getVFS(this.name, this.async);
const stream = new ReadableStream(new DatabaseSource(vfs, this.name));
async export(name: string, options: SQLiteOptions) {
const vfs = await this.getVFS(name, options.async);
const stream = new ReadableStream(new DatabaseSource(vfs, name));
return transfer(stream, [stream]);
}
async delete() {
async delete(name: string, options: SQLiteOptions) {
await this.close();
if (this.vfs) await this.vfs.delete();
else await (await this.getVFS(this.name, this.async)).delete();
else await (await this.getVFS(name, options.async)).delete();
}
async getVFS(dbName: string, async: boolean) {

View File

@@ -76,7 +76,10 @@ export class WaSqliteWorkerMultipleTabDriver implements Driver {
activated: true,
closed: false
});
this.connection = new WaSqliteWorkerConnection(service.proxy);
this.connection = new WaSqliteWorkerConnection(
service.proxy,
this.config.async
);
}
return;
}
@@ -122,7 +125,10 @@ export class WaSqliteWorkerMultipleTabDriver implements Driver {
await service.getProviderPort();
console.timeEnd("waiting for provider port");
this.connection = new WaSqliteWorkerConnection(service.proxy);
this.connection = new WaSqliteWorkerConnection(
service.proxy,
this.config.async
);
servicePool.set(this.serviceName, {
service,
@@ -202,12 +208,22 @@ export class WaSqliteWorkerMultipleTabDriver implements Driver {
async delete() {
const service = servicePool.get(this.serviceName);
if (!service || !service.service) return;
await service.service?.proxy?.delete();
await service.service?.proxy?.delete(this.config.dbName, {
async: this.config.async,
encrypted: this.config.encrypted,
url: this.config.async ? SQLiteAsyncURI : SQLiteSyncURI
});
service.closed = true;
}
async export() {
return servicePool.get(this.serviceName)?.service?.proxy?.export();
return servicePool
.get(this.serviceName)
?.service?.proxy?.export(this.config.dbName, {
async: this.config.async,
encrypted: this.config.encrypted,
url: this.config.async ? SQLiteAsyncURI : SQLiteSyncURI
});
}
}
@@ -228,7 +244,10 @@ export class WaSqliteWorkerSingleTabDriver implements Driver {
encrypted: this.config.encrypted,
url: this.config.async ? SQLiteAsyncURI : SQLiteSyncURI
});
this.connection = new WaSqliteWorkerConnection(this.worker);
this.connection = new WaSqliteWorkerConnection(
this.worker,
this.config.async
);
}
async acquireConnection(): Promise<DatabaseConnection> {
@@ -262,16 +281,28 @@ export class WaSqliteWorkerSingleTabDriver implements Driver {
}
async delete() {
await this.worker.delete();
await this.worker.delete(this.config.dbName, {
async: this.config.async,
encrypted: this.config.encrypted,
url: this.config.async ? SQLiteAsyncURI : SQLiteSyncURI
});
}
async export() {
return await this.worker.export();
return await this.worker.export(this.config.dbName, {
async: this.config.async,
encrypted: this.config.encrypted,
url: this.config.async ? SQLiteAsyncURI : SQLiteSyncURI
});
}
}
class WaSqliteWorkerConnection implements DatabaseConnection {
constructor(private readonly worker: SQLiteWorker | Remote<SQLiteWorker>) {}
#queryMutex = new Mutex();
constructor(
private readonly worker: SQLiteWorker | Remote<SQLiteWorker>,
private readonly sequential = false
) {}
streamQuery<R>(): AsyncIterableIterator<QueryResult<R>> {
throw new Error("wasqlite driver doesn't support streaming");
@@ -279,6 +310,17 @@ class WaSqliteWorkerConnection implements DatabaseConnection {
async executeQuery<R>(
compiledQuery: CompiledQuery<unknown>
): Promise<QueryResult<R>> {
if (this.sequential) {
return this.#queryMutex.runExclusive(async () =>
this.#_executeQuery(compiledQuery)
);
}
return this.#_executeQuery(compiledQuery);
}
#_executeQuery<R>(
compiledQuery: CompiledQuery<unknown>
): Promise<QueryResult<R>> {
const { parameters, sql, query } = compiledQuery;
const mode =

View File

@@ -66,7 +66,7 @@ class Vault {
subtitle: strings.deleteVaultDesc(),
inputs: {
password: {
label: strings.password(),
label: strings.accountPassword(),
autoComplete: "current-password"
}
},

View File

@@ -834,6 +834,7 @@ function UnlockNoteView(props: UnlockNoteViewProps) {
if (!note || !note.content)
throw new Error("note with this id does not exist.");
const tags = await db.notes.tags(note.id);
useEditorStore.getState().addSession({
type: session.note.readonly ? "readonly" : "default",
locked: true,
@@ -841,6 +842,7 @@ function UnlockNoteView(props: UnlockNoteViewProps) {
note: session.note,
saveState: SaveState.Saved,
sessionId: `${Date.now()}`,
tags,
pinned: session.pinned,
preview: session.preview,
content: note.content

View File

@@ -449,12 +449,18 @@ function TiptapWrapper(
const handleWheel = (e: WheelEvent) => {
if (e.ctrlKey) {
if (e.deltaY === 0) return;
e.preventDefault();
const delta =
Math.ceil(-e.deltaY / 10 / EDITOR_ZOOM.STEP) * EDITOR_ZOOM.STEP;
(e.deltaY > 0 && e.deltaY < 10) || (e.deltaY > -10 && e.deltaY < 0)
? -e.deltaY
: e.deltaY > 0
? -EDITOR_ZOOM.STEP
: EDITOR_ZOOM.STEP;
const zoom = Math.min(
EDITOR_ZOOM.MAX,
Math.max(EDITOR_ZOOM.MIN, editorConfig.zoom + delta)
Math.max(EDITOR_ZOOM.MIN, Math.round(editorConfig.zoom + delta))
);
setEditorConfig({ zoom });
}

View File

@@ -17,13 +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 {
forwardRef,
useEffect,
useLayoutEffect,
useRef,
useState
} from "react";
import { forwardRef, useEffect, useRef, useState } from "react";
import { Flex, Button } from "@theme-ui/components";
import { Plus } from "../icons";
import {
@@ -50,7 +44,6 @@ import {
} from "react-virtuoso";
import { getRandom, useResolvedItem } from "@notesnook/common";
import { Context } from "./types";
import { AppEventManager, AppEvents } from "../../common/app-events";
export const CustomScrollbarsVirtualList = forwardRef<
HTMLDivElement,
@@ -96,7 +89,7 @@ function ListContainer(props: ListContainerProps) {
const listRef = useRef<VirtuosoHandle>(null);
const listContainerRef = useRef(null);
const activeItem = useRef<{ focus: boolean; id: string }>();
// const activeItem = useRef<{ focus: boolean; id: string }>();
useEffect(() => {
return () => {
@@ -104,42 +97,6 @@ function ListContainer(props: ListContainerProps) {
};
}, []);
useLayoutEffect(() => {
if (activeItem.current) {
items
.ids()
.then(
(ids) =>
listRef.current &&
activeItem.current &&
revealItemInList(
listRef.current,
activeItem.current.id,
ids,
activeItem.current.focus
)
);
}
const event = AppEventManager.subscribe(
AppEvents.revealItemInList,
(id, focus) => {
if (activeItem.current?.id === id) return;
activeItem.current = { id, focus };
items
.ids()
.then(
(ids) =>
listRef.current &&
revealItemInList(listRef.current, id, ids, focus)
);
}
);
return () => {
event.unsubscribe();
};
}, [items]);
const { onMouseUp, onKeyDown } = useKeyboardListNavigation({
length: items.length,
reset: () => toggleSelection(false),
@@ -442,20 +399,3 @@ export function waitForElement(
}
});
}
function revealItemInList(
list: VirtuosoHandle,
itemId: string,
ids: string[],
focus: boolean
) {
const index = ids.indexOf(itemId);
if (index === -1) return;
waitForElement(
list,
index,
`id_${itemId}`,
(element) => focus && element.focus(),
{ align: "center" }
);
}

View File

@@ -172,7 +172,9 @@ export function PaddleCheckout(props: PaddleCheckoutProps) {
items: [
{
prices: checkout.prices.customer.items,
recurring: { prices: checkout.recurring_prices.customer.items }
recurring: {
prices: [checkout.recurring_prices.customer.items[0].recurring]
}
}
]
});

View File

@@ -74,6 +74,7 @@ export function PlansList(props: PlansListProps) {
// }
onClick={() => onPlanSelected(plan)}
sx={{
flexShrink: 0,
flex: 1,
textAlign: "start",
alignItems: "center",

View File

@@ -202,6 +202,8 @@ export interface CheckoutPrices {
unit_price: CheckoutPrice;
// line_price: CheckoutPrice;
discounts: CheckoutDiscount[];
recurring: CheckoutPrices;
// tax_rate: number;
}

View File

@@ -127,12 +127,19 @@ export const IssueDialog = DialogManager.register(function IssueDialog(
<Link
href="https://github.com/streetwriters/notesnook/issues"
title="github.com/streetwriters/notesnook/issues"
/>{" "}
target="_blank"
>
github.com/streetwriters/notesnook/issues
</Link>
{strings.issueNotice[1]()}{" "}
<Link
href="https://discord.gg/zQBK97EE22"
title={strings.issueNotice[2]()}
/>
target="_blank"
>
{strings.issueNotice[2]()}
</Link>
/
</Text>
<Text variant="subBody" mt={1}>
{getDeviceInfo([`Pro: ${isUserPremium()}`])

View File

@@ -28,6 +28,9 @@ import { useStore as useUserStore } from "../../stores/user-store";
import { desktop } from "../../common/desktop-bridge";
import { PATHS } from "@notesnook/desktop";
const getDesktopBackupsDirectoryPath = () =>
useSettingStore.getState().backupStorageLocation || PATHS.backupsDirectory;
export const BackupExportSettings: SettingsGroup[] = [
{
key: "backup",
@@ -164,7 +167,10 @@ export const BackupExportSettings: SettingsGroup[] = [
{
key: "backup-directory",
title: strings.selectBackupDir(),
description: strings.selectBackupDirDesc(),
description: () =>
strings
.selectBackupDirDesc(getDesktopBackupsDirectoryPath())
.join("\n\n"),
isHidden: () => !IS_DESKTOP_APP,
components: [
{
@@ -176,9 +182,7 @@ export const BackupExportSettings: SettingsGroup[] = [
(await verifyAccount());
if (!verified) return;
const backupStorageLocation =
useSettingStore.getState().backupStorageLocation ||
PATHS.backupsDirectory;
const backupStorageLocation = getDesktopBackupsDirectoryPath();
const location = await desktop?.integration.selectDirectory.query(
{
title: strings.selectBackupDir(),

View File

@@ -166,7 +166,11 @@ export function Importer() {
<Box as="ol" sx={{ my: 1 }}>
<Text as="li" variant="body">
Go to{" "}
<Link href="https://importer.notesnook.com/" target="_blank">
<Link
href="https://importer.notesnook.com/"
target="_blank"
sx={{ color: "accent" }}
>
https://importer.notesnook.com/
</Link>
</Text>

View File

@@ -466,7 +466,7 @@ function SettingItem(props: { item: Setting }) {
gap: 4
}}
>
<Flex sx={{ flexDirection: "column", flexShrink: 0 }}>
<Flex sx={{ flexDirection: "column", flex: 1 }}>
<Text variant={"subtitle"}>{item.title}</Text>
{item.description && (
<Text
@@ -485,7 +485,8 @@ function SettingItem(props: { item: Setting }) {
alignItems: "center",
justifyContent: "end",
gap: 2,
"& > label": { width: "auto" }
"& > label": { width: "auto" },
"& > *": { flexShrink: 0 }
}}
>
{components.map((component, index) => {

View File

@@ -371,8 +371,8 @@ class KeyStore extends BaseStore<KeyStore> {
};
clear = async () => {
await this.#metadataStore.clear();
await this.#secretStore.clear();
await this.#metadataStore?.clear();
await this.#secretStore?.clear();
this.#key = undefined;
this.set({ credentials: [], secrets: {}, isLocked: false });
};

View File

@@ -421,7 +421,7 @@ class EditorStore extends BaseStore<EditorStore> {
continue;
updateSession(session.id, undefined, {
tags: await getTags(session.note.id)
tags: await db.notes.tags(session.note.id)
});
}
} else if (
@@ -432,7 +432,7 @@ class EditorStore extends BaseStore<EditorStore> {
event.item.toType === "note"
) {
updateSession(event.item.toId, undefined, {
tags: await getTags(event.item.toId)
tags: await db.notes.tags(event.item.toId)
});
}
} else if (event.collection === "tags") {
@@ -445,7 +445,7 @@ class EditorStore extends BaseStore<EditorStore> {
continue;
console.log("UDPATE");
updateSession(session.id, undefined, {
tags: await getTags(session.note.id)
tags: await db.notes.tags(session.note.id)
});
}
}
@@ -671,7 +671,7 @@ class EditorStore extends BaseStore<EditorStore> {
const attachmentsLength = await db.attachments
.ofNote(note.id, "all")
.count();
const tags = await getTags(note.id);
const tags = await db.notes.tags(note.id);
const colors = await db.relations.to(note, "color").get();
if (note.readonly) {
this.addSession(
@@ -871,12 +871,19 @@ class EditorStore extends BaseStore<EditorStore> {
};
newSession = () => {
this.addSession({
type: "new",
id: getId(),
context: useNoteStore.getState().context,
saveState: SaveState.NotSaved
});
const state = useEditorStore.getState();
const session = state.sessions.find((session) => session.type === "new");
if (session) {
session.context = useNoteStore.getState().context;
this.activateSession(session.id);
} else {
this.addSession({
type: "new",
id: getId(),
context: useNoteStore.getState().context,
saveState: SaveState.NotSaved
});
}
};
closeSessions = (...ids: string[]) => {
@@ -1020,12 +1027,3 @@ async function waitForSync() {
db.eventManager.subscribe(EVENTS.syncCompleted, resolve, true);
});
}
async function getTags(noteId: string) {
return await db.relations
.to({ id: noteId, type: "note" }, "tag")
.selector.items(undefined, {
sortBy: "dateCreated",
sortDirection: "asc"
});
}

View File

@@ -19,6 +19,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import snarkdown from "snarkdown";
export function mdToHtml(markdown: string) {
return snarkdown(markdown);
function addAttributes(
html: string,
tag: keyof HTMLElementTagNameMap,
attributes: Record<string, string>
) {
const temp = document.createElement("div");
temp.innerHTML = html;
const elements = temp.querySelectorAll(tag);
elements.forEach((element) => {
Object.entries(attributes).forEach(([key, value]) => {
element.setAttribute(key, value);
});
});
return temp.innerHTML;
}
export function mdToHtml(markdown: string) {
return addAttributes(snarkdown(markdown), "a", { target: "_blank" });
}

View File

@@ -985,7 +985,14 @@ export function AuthField(props: FieldProps) {
p: "12px",
borderRadius: "default",
bg: "background",
boxShadow: "0px 0px 5px 0px #00000019"
boxShadow: "0px 0px 5px 0px #00000019",
"::-moz-appearance": "textfield",
"::-webkit-inner-spin-button": {
"-webkit-appearance": "none"
},
"::-webkit-outer-spin-button": {
"-webkit-appearance": "none"
}
}
}}
/>

View File

@@ -0,0 +1,30 @@
# Contributing guidelines
Please read the [contributing guidelines](../../CONTRIBUTING.md) beforehand.
### Setting web clipper locally
#### Running the web clipper
1. Install packages and setup the repo. Run this command in the repository root:
```sh
npm install
```
1. Run the Notesnook webapp:
```sh
npm run start:web
```
1. Navigate to the web clipper folder:
```sh
cd extensions/web-clipper
```
1. Run the web clipper:
```sh
npm run dev:chrome
```
#### Viewing the web clipper
1. Open chrome and go to `chrome://extensions`.
1. Turn on "Developer Mode".
1. Click on "Load unpacked" and select the `extensions/web-clipper/build` folder.

View File

@@ -75,6 +75,8 @@ function attachMessagePort() {
height: document.body.clientHeight,
width: document.body.clientWidth
};
default:
return false;
}
});
}

View File

@@ -0,0 +1,9 @@
- Added full support for localization in Notesnook
- Improved search experience
- Allow user to cancel logging in
- Fixed scrolling focused line into view
- Support self hosted monograph server
- Fixed markdown link pasting in editor
- Many other bug fixes and improvements
Thank you for using Notesnook!

View File

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

View File

@@ -197,6 +197,15 @@ test("update note", () =>
expect(note?.favorite).toBe(true);
}));
test("get note tags", () =>
noteTest({
...TEST_NOTE
}).then(async ({ db, id }) => {
const tag = await db.tags.add({ title: "hello" });
await db.relations.add({ type: "tag", id: tag }, { type: "note", id });
expect(await db.notes.tags(id)).toEqual([await db.tags.tag(tag)]);
}));
test("get favorite notes", () =>
noteTest({
...TEST_NOTE,

View File

@@ -16,6 +16,7 @@
"@readme/data-urls": "^3.0.0",
"@streetwriters/kysely": "^0.27.4",
"@streetwriters/showdown": "^3.0.9-alpha",
"@types/mime-db": "^1.43.5",
"async-mutex": "^0.3.2",
"dayjs": "1.11.9",
"dom-serializer": "^2.0.0",
@@ -28,7 +29,7 @@
"katex": "0.16.2",
"linkedom": "^0.14.17",
"liqe": "^1.13.0",
"mime": "^4.0.4",
"mime-db": "^1.53.0",
"prismjs": "^1.29.0",
"qclone": "^1.2.0",
"rfdc": "^1.3.0",
@@ -2266,6 +2267,11 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/mime-db": {
"version": "1.43.5",
"resolved": "https://registry.npmjs.org/@types/mime-db/-/mime-db-1.43.5.tgz",
"integrity": "sha512-/bfTiIUTNPUBnwnYvUxXAre5MhD88jgagLEQiQtIASjU+bwxd8kS/ASDA4a8ufd8m0Lheu6eeMJHEUpLHoJ28A=="
},
"node_modules/@types/node": {
"version": "18.11.9",
"dev": true,
@@ -3447,19 +3453,12 @@
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
"dev": true
},
"node_modules/mime": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz",
"integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==",
"funding": [
"https://github.com/sponsors/broofa"
],
"license": "MIT",
"bin": {
"mime": "bin/cli.js"
},
"node_modules/mime-db": {
"version": "1.53.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz",
"integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==",
"engines": {
"node": ">=16"
"node": ">= 0.6"
}
},
"node_modules/mimic-fn": {

View File

@@ -68,6 +68,7 @@
"@readme/data-urls": "^3.0.0",
"@streetwriters/kysely": "^0.27.4",
"@streetwriters/showdown": "^3.0.9-alpha",
"@types/mime-db": "^1.43.5",
"async-mutex": "^0.3.2",
"dayjs": "1.11.9",
"dom-serializer": "^2.0.0",
@@ -80,7 +81,7 @@
"katex": "0.16.2",
"linkedom": "^0.14.17",
"liqe": "^1.13.0",
"mime": "^4.0.4",
"mime-db": "^1.53.0",
"prismjs": "^1.29.0",
"qclone": "^1.2.0",
"rfdc": "^1.3.0",

View File

@@ -79,6 +79,7 @@ import { Sanitizer } from "../database/sanitizer.js";
import { createTriggers, dropTriggers } from "../database/triggers.js";
import { NNMigrationProvider } from "../database/migrations.js";
import { ConfigStorage } from "../database/config.js";
import { LazyPromise } from "../utils/lazy-promise.js";
type EventSourceConstructor = new (
uri: string,
@@ -101,6 +102,9 @@ class Database {
sseMutex = new Mutex();
_fs?: FileStorage;
_compressor?: Promise<ICompressor>;
private databaseReady = new LazyPromise<
Kysely<DatabaseSchema> | Transaction<DatabaseSchema>
>();
storage: StorageAccessor = () => {
if (!this.options?.storage)
@@ -148,11 +152,12 @@ class Database {
return this._sql;
};
private _kv?: KVStorage;
kv: KVStorageAccessor = () => this._kv || new KVStorage(this.sql);
private _config?: ConfigStorage;
config: ConfigStorageAccessor = () =>
this._config || new ConfigStorage(this.sql);
private _kv = new KVStorage(this.databaseReady.promise);
kv: KVStorageAccessor = () => this._kv;
private _config: ConfigStorage = new ConfigStorage(
this.databaseReady.promise
);
config: ConfigStorageAccessor = () => this._config;
private _transaction?: QueueValue<Transaction<DatabaseSchema>>;
transaction = async (
@@ -291,6 +296,7 @@ class Database {
migrationProvider: new NNMigrationProvider(),
onInit: (db) => this.onInit(db)
})) as unknown as Kysely<DatabaseSchema>;
this.databaseReady.resolve(this._sql);
await this.sanitizer.init();

View File

@@ -176,6 +176,15 @@ export class Notes implements ICollection {
return note;
}
async tags(id: string) {
return this.db.relations
.to({ id, type: "note" }, "tag")
.selector.items(undefined, {
sortBy: "dateCreated",
sortDirection: "asc"
});
}
// note(idOrNote: string | Note) {
// if (!idOrNote) return;
// const note =

View File

@@ -17,41 +17,47 @@ 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 { DatabaseAccessor, RawDatabaseSchema } from "./index.js";
import { LazyDatabaseAccessor, RawDatabaseSchema } from "./index.js";
export class ConfigStorage {
private readonly db: DatabaseAccessor<RawDatabaseSchema>;
constructor(db: DatabaseAccessor) {
this.db = db as unknown as DatabaseAccessor<RawDatabaseSchema>;
private readonly db: LazyDatabaseAccessor<RawDatabaseSchema>;
constructor(db: LazyDatabaseAccessor) {
this.db = db as unknown as LazyDatabaseAccessor<RawDatabaseSchema>;
}
async getItem(name: string): Promise<unknown | undefined> {
const result = await this.db()
.selectFrom("config")
.where("name", "==", name)
.select("value")
.limit(1)
.executeTakeFirst();
const result = await this.db.then((db) =>
db
.selectFrom("config")
.where("name", "==", name)
.select("value")
.limit(1)
.executeTakeFirst()
);
if (!result?.value) return;
return JSON.parse(result.value);
}
async setItem(name: string, value: unknown) {
await this.db()
.replaceInto("config")
.values({
name,
value: JSON.stringify(value),
dateModified: Date.now()
})
.execute();
await this.db.then((db) =>
db
.replaceInto("config")
.values({
name,
value: JSON.stringify(value),
dateModified: Date.now()
})
.execute()
);
}
async removeItem(name: string) {
await this.db().deleteFrom("config").where("name", "==", name).execute();
await this.db.then((db) =>
db.deleteFrom("config").where("name", "==", name).execute()
);
}
async clear() {
await this.db().deleteFrom("config").execute();
await this.db.then((db) => db.deleteFrom("config").execute());
}
}

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