Compare commits

..

1 Commits

Author SHA1 Message Date
ammarahm-ed
c1708260b8 mobile: fix tag not updated on list item on rename 2023-04-07 14:45:30 +05:00
172 changed files with 1747 additions and 2709 deletions

View File

@@ -8,9 +8,9 @@ Thank you so much for considering to contribute to Notesnook! If you have no ide
Ugh! Bugs!
> A bug is when software behaves in a way you didn't expect, which the developer didn't intend.
> A bug is when software behaves in a way that you didn't expect, and the developer didn't intend.
To help us understand what's happening, we first want to **make sure you're using the latest version of Notesnook**.
To help us understand what's going on, we first want to **make sure you're using the latest version of Notesnook**.
Once you've **confirmed that the bug still exists in the latest version**, you'll want to check to make sure it's not something we already know about in the [opened GitHub issues](https://github.com/streetwriters/notesnook/issues).
@@ -30,7 +30,7 @@ Before you open a new feature request, please make sure it's not a duplicate. **
### Helping out in the issue tracker
New issues are always opened that need to be triaged, sorted & organized, so the developers can easily find the most critical and/or relevant bugs to fix. Any help in this regard is appreciated.
There are always new issues getting opened that need to be triaged, sorted & organized, so the developers can easily find the most critical and/or relevant bugs to fix. Any help in this regard is appreciated.
In addition to this, you can help out in the following ways:
@@ -62,7 +62,7 @@ Once you are done, [open a new pull request](https://docs.github.com/en/pull-req
1. Fork [the repository](https://github.com/streetwriters/notesnook) and create your branch from `master` (you can name your branch anything).
2. Run `npm run bootstrap` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
3. If youve fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`npm run test`).
5. Format your code with prettier (`npm run prettier`).
6. Make sure your code lints (`npm run lint`). Tip: `npm run linc` to only check changed files.
@@ -131,11 +131,11 @@ We use an automatic code formatter called [Prettier](https://prettier.io/). Run
Then, our linter will catch most issues that may exist in your code. You can check the status of your code styling by simply running `npm run lint`.
However, there are still some styles that the linter cannot pick up. If you are unsure about something, looking at [Airbnb's Style Guide](https://github.com/airbnb/javascript) will guide you in the right direction.
However, there are still some styles that the linter cannot pick up. If you are unsure about something, looking at [Airbnbs Style Guide](https://github.com/airbnb/javascript) will guide you in the right direction.
## Git Branch Organization
Submit all changes directly to the [`master branch`](https://github.com/streetwriters/notesnook). We don't use separate branches for development or for upcoming releases. This requires us to always keep the `master` branch in a deployable state which means:
Submit all changes directly to the [`master branch`](https://github.com/streetwriters/notesnook). We dont use separate branches for development or for upcoming releases. This requires us to always keep the `master` branch in a deployable state which means:
1. All tests must be passing at all times
2. There should be as few breaking changes as possible

View File

@@ -10,11 +10,11 @@
## Overview
Notesnook is a free (as in speech) & open-source note-taking app focused on user privacy & ease of use. To ensure zero knowledge principles, Notesnook encrypts everything on your device using `XChaCha20-Poly1305` & `Argon2`.
Notesnook is a free (as in speech) & open source note taking app focused on user privacy & ease of use. To ensure zero knowledge principles, Notesnook encrypts everything on your device using `XChaCha20-Poly1305` & `Argon2`.
Notesnook is our **proof** that privacy does _not_ (always) have to come at the cost of convenience. We aim to provide users peace of mind & 100% confidence that their notes are safe and secure. The decision to go fully open source is one of the most crucial steps towards that.
Notesnook is our **proof** that privacy does _not_ (always) have to come at the cost of convenience. Our goal is to provide users peace of mind & 100% confidence that their notes are safe and secure. The decision to go fully open source is one of the most crucial steps towards that.
This repository contains all the code required to build & use the Notesnook web, desktop & mobile clients. If you are looking for a full feature list or screenshots, please check the [website](https://notesnook.com/).
This repository contains all the code required to build & use the Notesnook web, desktop & mobile clients. If you are looking for a full features list or screenshots, please check the [website](https://notesnook.com/).
## Developer guide

View File

@@ -19,9 +19,9 @@
## Build instructions
> **Before you start, it is recommended that you read [the contributing guidelines](/CONTRIBUTING.md).**
> **Before you start it is recommended that you read [the contributing guidelines](/CONTRIBUTING.md).**
### Setting up the development environment
### Setting up development environment
Requirements:
@@ -57,7 +57,7 @@ npm install
### Running the app on Android
[Setup an Android emulator from Android Studio](https://developer.android.com/studio/run/managing-avds) if you haven't already, and then run the following command to start the app in the Emulator:
[Setup an Android emulator from Android Studio](https://developer.android.com/studio/run/managing-avds) if you haven't already and then run the following command to start the app in the Emulator:
```bash
npm run start:android
@@ -78,11 +78,11 @@ npm run start:ios
## Developer guide
> This project is in a transition state between Javascript & Typescript. We are gradually porting everything over to Typescript, so if you can help with that, it'd be great!
> This project is in a transition state between Javascript & Typescript. We are gradually porting everything over to Typescript so if you can help with that, it'd be great!
### The tech stack
We try to keep the stack as lean as possible:
We try to keep the stack as lean as possible
1. React Native
2. Typescript/Javascript
@@ -95,17 +95,17 @@ We try to keep the stack as lean as possible:
The app codebase is distributed over two primary directories. `native/` and `app/`.
- `native/`: Includes `android/` and `ios/` folders and everything related to react native core functionality like bundling, development, and packaging. Any react-native dependency with native code, i.e., android & ios folders, is installed here.
- `native/`: Includes `android/` and `ios/` folders and everything related to react native core functionality like bundling, development and packaging. Any react-native dependency that has native code i.e android & ios folders, is installed here.
- `app/`: Includes all the app code other than the native part. All JS-only dependencies are installed here.
- `components/`: Each component serves a specific purpose in the app UI. For example, the `Paragraph` component is used to render paragraphs in the app, and a `Header` component is used to render a `header` on all screens.
- `common/`: Features that are integral to the app's functionality. For example, the notesnook core is initialized here.
- `app/`: Includes all the app code other than the native part. All JS only dependencies are installed here.
- `components/`: Each component serves a specific purpose in the app UI, for example the `Paragraph` component is used to render paragraphs in the app and a `Header` component is used to render a `header` on all screens.
- `common/`: Features that have integral role in app functionality, for example, notesnook core is initialized here.
- `hooks/`: Hooks for different app logic
- `navigation/`: Includes app navigation-specific code. Here the app navigation, editor & side menu are rendered side by side in fluid tabs.
- `navigation/`: Includes app navigation specific code. Here the app navigation, editor & side menu are rendered side by side in fluid tabs.
- `screens`: Navigator screens.
- `services`: Parts of code that do a specific function. For example, the `sync` service runs Sync from anywhere in the app.
- `stores`: We use `zustand` for global state management in the app. Multiple stores provide the state for different parts of the app.
- `utils`: General purpose stuff such as constant values, utility functions, etc.
- `services`: Parts of code that do a specific function, for example, the `sync` service is responsibe for running Sync from anywhere in the app.
- `stores`: We use `zustand` for global state management in the app. There are multiple stores that provide the state for different parts of the app.
- `utils`: General purpose stuff such as constant values, utility functions etc.
There are several other folders at the root:
@@ -115,11 +115,11 @@ There are several other folders at the root:
### Running the tests
When you are done making the required changes, you must run the tests to ensure you didn't break anything. We use Detox as the testing framework & the tests can be started as follows:
When you are done making the required changes, you will need to run the tests to make sure you didn't break anything. We use Detox as the testing framework & the tests can be started as follows:
### Android
To run the tests on Android, you will need to create an emulator device on your system:
To run the tests on android, you will need to create an emulator device on your system:
```
$ANDROID_HOME/tools/bin/avdmanager create avd -n Pixel_5_API_31 -d pixel --package "system-images;android-31;default;x86_64"
@@ -127,13 +127,13 @@ $ANDROID_HOME/tools/bin/avdmanager create avd -n Pixel_5_API_31 -d pixel --packa
If you face problems, follow the detailed guide in [Detox documentation](https://wix.github.io/Detox/docs/introduction/android-dev-env). Keep the emulator name set to `Pixel_5_API_31`.
Once you have created an emulator device, build the Android apks:
Once you have created an emulator device, build the android apks
```
npm run build:android
```
Finally, run the tests:
Finally run the tests
```
npm run test:android
@@ -141,9 +141,9 @@ npm run test:android
### iOS
To run e2e tests on the iOS simulator, you must be on a Mac with XCode installed.
To run e2e tests on iOS simulator, you must be on a Mac with XCode installed.
First, install [AppleSimulatorUtils](https://github.com/wix/AppleSimulatorUtils):
First install [AppleSimulatorUtils](https://github.com/wix/AppleSimulatorUtils)
```
brew tap wix/brew
@@ -156,7 +156,7 @@ Now build the iOS app for testing:
npm run build:ios
```
Finally, run the tests:
Finally run the tests:
```
npm run test:ios

View File

@@ -31,6 +31,14 @@ import { TipManager } from "./services/tip-manager";
import { useUserStore } from "./stores/use-user-store";
import { View } from "react-native";
import { useState } from "react";
import NetInfo from "@react-native-community/netinfo";
NetInfo.configure({
reachabilityUrl: "https://notesnook.com",
reachabilityTest: (response) => {
if (!response) return false;
return response?.status >= 200 && response?.status < 300;
}
});
SettingsService.init();
SettingsService.checkOrientation();

View File

@@ -68,3 +68,14 @@ db.host(
ISSUES_HOST: "https://issues.streetwriters.co"
}
);
export async function loadDatabase() {
// if (!DB) {
// let module = await import(/* webpackChunkName: "notes-core" */ 'notes-core/api/index');
// DB = module.default;
// }
// db = new DB(Storage, Platform.OS === 'ios' ? EventSource : AndroidEventSource, filesystem);
// if (DOMParser) {
// await DOMParser.prepare();
// }
}

View File

@@ -148,7 +148,7 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
<Button
key={item.title}
title={item.title}
fontSize={SIZE.xs}
fontSize={SIZE.xs + 1}
type="gray"
onPress={() => onPress(item)}
width={null}

View File

@@ -67,7 +67,7 @@ export const Title = ({ text, style = {}, inline }) => {
right: 0
}}
iconSize={24}
fontSize={SIZE.xs}
fontSize={SIZE.xs + 1}
style={{
borderRadius: 100,
paddingVertical: 0,

View File

@@ -209,7 +209,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
}}
>
<Paragraph
size={SIZE.xs}
size={SIZE.xs + 1}
style={{
marginRight: 10
}}
@@ -221,7 +221,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
style={{
marginRight: 10
}}
size={SIZE.xs}
size={SIZE.xs + 1}
color={colors.icon}
>
{formatBytes(attachment.length)}
@@ -232,7 +232,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
style={{
marginRight: 10
}}
size={SIZE.xs}
size={SIZE.xs + 1}
color={colors.icon}
>
{attachment.noteIds.length} note
@@ -248,7 +248,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
context: "local"
});
}}
size={SIZE.xs}
size={SIZE.xs + 1}
color={colors.icon}
>
{attachment.metadata.hash}
@@ -304,7 +304,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
}}
key={item.id}
>
<Paragraph size={SIZE.xs}>{item.title}</Paragraph>
<Paragraph size={SIZE.xs + 1}>{item.title}</Paragraph>
</PressableButton>
))}
</>

View File

@@ -22,6 +22,7 @@ import { TouchableOpacity, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database";
import { useAttachmentProgress } from "../../hooks/use-attachment-progress";
import { useAttachmentStore } from "../../stores/use-attachment-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { formatBytes } from "../../utils";
import { SIZE } from "../../utils/size";
@@ -35,18 +36,16 @@ function getFileExtension(filename) {
var ext = /^.+\.([^.]+)$/.exec(filename);
return ext == null ? "" : ext[1];
}
/**
*
* @param {any} param0
* @returns
*/
export const AttachmentItem = ({ attachment, encryption, setAttachments }) => {
const colors = useThemeStore((state) => state.colors);
const [currentProgress, setCurrentProgress] = useAttachmentProgress(
attachment,
encryption
);
const encryptionProgress = useAttachmentStore(
(state) => state.encryptionProgress
);
const onPress = () => {
Actions.present(attachment, setAttachments, attachment.metadata.hash);
};
@@ -122,7 +121,9 @@ export const AttachmentItem = ({ attachment, encryption, setAttachments }) => {
</View>
</View>
{currentProgress ? (
{currentProgress ||
(encryptionProgress && encryptionProgress !== "0.00") ||
encryption ? (
<TouchableOpacity
activeOpacity={0.9}
onPress={() => {
@@ -139,7 +140,13 @@ export const AttachmentItem = ({ attachment, encryption, setAttachments }) => {
>
<ProgressCircleComponent
size={SIZE.xxl}
progress={currentProgress?.value ? currentProgress?.value / 100 : 0}
progress={
encryptionProgress
? encryptionProgress
: currentProgress?.value
? currentProgress?.value / 100
: 0
}
showsText
textStyle={{
fontSize: 10

View File

@@ -44,10 +44,8 @@ import Paragraph from "../ui/typography/paragraph";
import { LoginSteps, useLogin } from "./use-login";
function getObfuscatedEmail(email) {
if (!email) return "";
const [username, provider] = email.split("@");
if (username.length === 1) return `****@${provider}`;
return email.replace(/(.{1})(.*)(?=@)/, function (gp1, gp2, gp3) {
if (!email) return null;
return email.replace(/(.{2})(.*)(?=@)/, function (gp1, gp2, gp3) {
for (let i = 0; i < gp3.length; i++) {
gp2 += "*";
}

View File

@@ -86,7 +86,7 @@ export const ProFeatures = ({ count = 6 }) => {
await sleep(300);
eSendEvent(eOpenPremiumDialog);
}}
size={SIZE.xs}
size={SIZE.xs + 1}
style={{
textDecorationLine: "underline",
color: colors.icon

View File

@@ -17,14 +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/>.
*/
import notifee from "@notifee/react-native";
import React, { useCallback, useEffect, useRef } from "react";
import { Platform, View } from "react-native";
import RNBootSplash from "react-native-bootsplash";
import { checkVersion } from "react-native-check-version";
import Config from "react-native-config";
import { enabled } from "react-native-privacy-snapshot";
import { DatabaseLogger, db } from "../../common/database";
import { DatabaseLogger, db, loadDatabase } from "../../common/database";
import { useAppState } from "../../hooks/use-app-state";
import BiometricService from "../../services/biometrics";
import { eSendEvent, presentSheet } from "../../services/event-manager";
@@ -37,9 +35,7 @@ import { useNoteStore } from "../../stores/use-notes-store";
import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { useUserStore } from "../../stores/use-user-store";
import { AndroidModule } from "../../utils";
import { eOpenAnnouncementDialog } from "../../utils/events";
import { getGithubVersion } from "../../utils/github-version";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
import { SVG } from "../auth/background";
@@ -54,6 +50,9 @@ import { SvgView } from "../ui/svg";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { Walkthrough } from "../walkthroughs";
import Config from "react-native-config";
import { getGithubVersion } from "../../utils/github-version";
import notifee from "@notifee/react-native";
const Launcher = React.memo(
function Launcher() {
@@ -70,8 +69,7 @@ const Launcher = React.memo(
const introCompleted = useSettingStore(
(state) => state.settings.introCompleted
);
const verifying = useRef(false);
const dbInitCompleted = useRef(false);
const loadNotes = useCallback(async () => {
if (verifyUser) {
return;
@@ -82,25 +80,28 @@ const Launcher = React.memo(
initialize();
setImmediate(() => {
setLoading(false);
if (!dbInitCompleted.current) {
setImmediate(() => doAppLoadActions());
}
});
});
});
}, [setLoading, verifyUser]);
}, [doAppLoadActions, setLoading, verifyUser]);
const init = useCallback(async () => {
if (!db.isInitialized) {
if (!dbInitCompleted.current) {
await RNBootSplash.hide({ fade: true });
await loadDatabase();
DatabaseLogger.info("Initializing database");
await db.init();
dbInitCompleted.current = true;
}
if (db.migrations.required() && !verifyUser) {
presentSheet({
component: <Migrate />,
onClose: async () => {
if (!db.isInitialized) {
await db.init();
}
await db.init();
loadNotes();
},
disableClosing: true
@@ -119,6 +120,9 @@ const Launcher = React.memo(
if (!loading) {
doAppLoadActions();
}
return () => {
dbInitCompleted.current = false;
};
}, [doAppLoadActions, loading]);
const doAppLoadActions = useCallback(async () => {
@@ -194,11 +198,6 @@ const Launcher = React.memo(
const onUnlockBiometrics = useCallback(async () => {
if (!(await BiometricService.isBiometryAvailable())) return;
if (Platform.OS === "android") {
const activityName = await AndroidModule.getActivityName();
if (activityName !== "MainActivity") return;
}
let verified = await BiometricService.validateUser(
"Unlock to access your notes",
""
@@ -207,9 +206,6 @@ const Launcher = React.memo(
setVerifyUser(false);
enabled(false);
password.current = null;
setTimeout(() => {
verifying.current = false;
}, 1);
}
}, [setVerifyUser]);
@@ -218,9 +214,7 @@ const Launcher = React.memo(
}, [init, verifyUser]);
useEffect(() => {
if (verifying.current) return;
if (verifyUser && appState === "active") {
verifying.current = true;
onUnlockBiometrics();
}
}, [appState, onUnlockBiometrics, verifyUser]);

View File

@@ -108,7 +108,7 @@ const ReminderItem = React.memo(
color={colors.errorText}
/>
<Paragraph
size={SIZE.xs}
size={SIZE.xs + 1}
color={colors.icon}
style={{ marginLeft: 5 }}
>
@@ -133,7 +133,7 @@ const ReminderItem = React.memo(
>
<Icon name="reload" size={SIZE.md} color={colors.accent} />
<Paragraph
size={SIZE.xs}
size={SIZE.xs + 1}
color={colors.icon}
style={{ marginLeft: 5 }}
>
@@ -147,7 +147,7 @@ const ReminderItem = React.memo(
<ReminderTime
reminder={item}
checkIsActive={false}
fontSize={SIZE.xs}
fontSize={SIZE.xs + 1}
style={{
justifyContent: "flex-start",
borderWidth: 0,

View File

@@ -218,14 +218,12 @@ const List = ({
}
/>
</Animated.View>
{listData ? (
<JumpToSectionDialog
screen={screen}
data={listData}
type={screen === "Notes" ? "home" : type}
scrollRef={scrollRef}
/>
) : null}
<JumpToSectionDialog
screen={screen}
data={listData}
type={screen === "Notes" ? "home" : type}
scrollRef={scrollRef}
/>
</>
);
};

View File

@@ -143,7 +143,7 @@ export const Expiring = () => {
await sleep(300);
eSendEvent(eOpenPremiumDialog, promo);
}}
size={SIZE.xs}
size={SIZE.xs + 1}
style={{
textDecorationLine: "underline",
color: colors.icon,

View File

@@ -17,10 +17,10 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useCallback, useEffect, useRef, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import { View } from "react-native";
import Animated, { FadeInUp, FadeOutUp } from "react-native-reanimated";
import useKeyboard from "../../hooks/use-keyboard";
import { editorState } from "../../screens/editor/tiptap/utils";
import { DDS } from "../../services/device-detection";
import {
eSendEvent,
@@ -31,7 +31,6 @@ import { useThemeStore } from "../../stores/use-theme-store";
import { getElevation } from "../../utils";
import {
eCloseActionSheet,
eCloseSheet,
eOpenPremiumDialog,
eShowGetPremium
} from "../../utils/events";
@@ -40,12 +39,12 @@ import { sleep } from "../../utils/time";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { useCallback } from "react";
export const PremiumToast = ({ context = "global", offset = 0 }) => {
const colors = useThemeStore((state) => state.colors);
const [msg, setMsg] = useState(null);
const timer = useRef();
const keyboard = useKeyboard();
const open = useCallback(
(event) => {
@@ -73,6 +72,7 @@ export const PremiumToast = ({ context = "global", offset = 0 }) => {
useEffect(() => {
eSubscribeEvent(eShowGetPremium, open);
return () => {
clearTimeout(timer.current);
eUnSubscribeEvent(eShowGetPremium, open);
};
}, [open]);
@@ -80,7 +80,9 @@ export const PremiumToast = ({ context = "global", offset = 0 }) => {
const onPress = async () => {
open(null);
eSendEvent(eCloseActionSheet);
eSendEvent(eCloseSheet);
if (editorState().isFocused) {
//tiny.call(EditorWebView, tiny.blur);
}
await sleep(300);
eSendEvent(eOpenPremiumDialog);
};
@@ -100,13 +102,9 @@ export const PremiumToast = ({ context = "global", offset = 0 }) => {
flexDirection: "row",
alignSelf: "center",
justifyContent: "space-between",
top: offset + keyboard.keyboardHeight,
top: offset,
maxWidth: DDS.isLargeTablet() ? 400 : "98%"
}}
onTouchEnd={() => {
setMsg(null);
clearTimeout(timer.current);
}}
>
<View
style={{

View File

@@ -62,10 +62,10 @@ export const DateMeta = ({ item }) => {
paddingVertical: 3
}}
>
<Paragraph size={SIZE.xs} color={colors.icon}>
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
{getNameFromKey(key)}
</Paragraph>
<Paragraph size={SIZE.xs} color={colors.icon}>
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
{timeConverter(item[key])}
</Paragraph>
</View>

View File

@@ -108,7 +108,7 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
backgroundColor: "transparent",
paddingHorizontal: 0
}}
fontSize={SIZE.xs}
fontSize={SIZE.xs + 1}
/>
) : null}
</View>

View File

@@ -115,12 +115,12 @@ export const Items = ({ item, buttons, close }) => {
onPress={item.func}
key={item.id}
testID={"icon-" + item.id}
activeOpacity={1}
customStyle={{
alignSelf: "flex-start",
alignItems: "center",
width: topBarItemWidth,
marginBottom: 10,
marginRight: isLast ? 0 : 10,
paddingHorizontal: 0,
width: topBarItemWidth
backgroundColor: "transparent"
}}
>
<PressableButton
@@ -150,7 +150,11 @@ export const Items = ({ item, buttons, close }) => {
/>
</PressableButton>
<Paragraph size={SIZE.xxs + 1} style={{ textAlign: "center" }}>
<Paragraph
size={SIZE.xxs + 1}
style={{ textAlign: "center" }}
textBreakStrategy="simple"
>
{item.title}
</Paragraph>
</PressableButton>
@@ -183,7 +187,7 @@ export const Items = ({ item, buttons, close }) => {
horizontal
style={{
paddingHorizontal: 12,
paddingVertical: 12
paddingTop: 12
}}
>
{topBarItems.map(renderTopBarItem)}

View File

@@ -23,18 +23,13 @@ import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database";
import Notebook from "../../screens/notebook";
import { TopicNotes } from "../../screens/notes/topic-notes";
import {
eSendEvent,
presentSheet,
ToastEvent
} from "../../services/event-manager";
import { presentSheet, ToastEvent } from "../../services/event-manager";
import Navigation from "../../services/navigation";
import { useNotebookStore } from "../../stores/use-notebook-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import { eClearEditor } from "../../utils/events";
export default function Notebooks({ note, close, full }) {
const colors = useThemeStore((state) => state.colors);
@@ -117,7 +112,6 @@ export default function Notebooks({ note, close, full }) {
size={SIZE.sm}
onPress={() => {
navigateNotebook(item.id);
eSendEvent(eClearEditor);
close();
}}
>
@@ -140,7 +134,6 @@ export default function Notebooks({ note, close, full }) {
key={topic.id}
onPress={() => {
navigateTopic(topic.id, item.id);
eSendEvent(eClearEditor);
close();
}}
onLongPress={async () => {
@@ -162,7 +155,7 @@ export default function Notebooks({ note, close, full }) {
title={topic.title}
type="gray"
height={30}
fontSize={SIZE.xs}
fontSize={SIZE.xs + 1}
icon="bookmark-outline"
style={{
marginRight: 5,
@@ -191,7 +184,7 @@ export default function Notebooks({ note, close, full }) {
{noteNotebooks.length > 1 && !full ? (
<Button
title={`See all linked notebooks`}
fontSize={SIZE.xs}
fontSize={SIZE.xs + 1}
style={{
alignSelf: "flex-end",
marginRight: 12,

View File

@@ -91,7 +91,7 @@ export const Synced = ({ item, close }) => {
console.error(e);
}
}}
fontSize={SIZE.xs}
fontSize={SIZE.xs + 1}
title="Learn more"
height={30}
type="grayAccent"

View File

@@ -57,7 +57,7 @@ export const Tags = ({ item, close }) => {
icon="plus"
iconPosition="right"
height={30}
fontSize={SIZE.xs}
fontSize={SIZE.xs + 1}
style={{
marginRight: 5,
borderRadius: 100,
@@ -107,7 +107,7 @@ const TagItem = ({ tag, close }) => {
title={"#" + tag}
type="grayBg"
height={20}
fontSize={SIZE.xs}
fontSize={SIZE.xs + 1}
style={style}
textStyle={{
textDecorationLine: "underline"

View File

@@ -39,7 +39,7 @@ export const Topics = ({ item, close }) => {
height={30}
onPress={() => open(topic)}
icon="bookmark-outline"
fontSize={SIZE.xs}
fontSize={SIZE.xs + 1}
style={{
marginRight: 5,
paddingHorizontal: 8,

View File

@@ -25,22 +25,22 @@ import {
TouchableOpacity,
View
} from "react-native";
import { FlatList } from "react-native-actions-sheet";
import { notesnook } from "../../../../e2e/test.ids";
import { db } from "../../../common/database";
import { DDS } from "../../../services/device-detection";
import { ToastEvent, presentSheet } from "../../../services/event-manager";
import { presentSheet, ToastEvent } from "../../../services/event-manager";
import Navigation from "../../../services/navigation";
import { useMenuStore } from "../../../stores/use-menu-store";
import { useRelationStore } from "../../../stores/use-relation-store";
import { SIZE, ph, pv } from "../../../utils/size";
import { ph, pv, SIZE } from "../../../utils/size";
import { sleep } from "../../../utils/time";
import DialogHeader from "../../dialog/dialog-header";
import { Button } from "../../ui/button";
import { IconButton } from "../../ui/icon-button";
import Input from "../../ui/input";
import Seperator from "../../ui/seperator";
import Heading from "../../ui/typography/heading";
import { MoveNotes } from "../move-notes/movenote";
import { FlatList } from "react-native-actions-sheet";
let refs = [];
export class AddNotebookSheet extends React.Component {
@@ -245,34 +245,13 @@ export class AddNotebookSheet extends React.Component {
willFocus && this.topicInputRef.current?.focus();
};
renderTopicItem = ({ item, index }) => (
<TopicItem
item={item}
onPress={(item, index) => {
this.prevIndex = index;
this.prevItem = item;
this.topicInputRef.current?.setNativeProps({
text: item
});
this.topicInputRef.current?.focus();
this.currentInputValue = item;
this.setState({
editTopic: true
});
}}
onDelete={this.onDelete}
index={index}
colors={this.props.colors}
/>
);
render() {
const { colors } = this.props;
const { topics, topicInputFocused, notebook } = this.state;
return (
<View
style={{
maxHeight: DDS.isTab ? "90%" : "97%",
maxHeight: DDS.isTab ? "90%" : "96%",
borderRadius: DDS.isTab ? 5 : 0,
paddingHorizontal: 12
}}
@@ -287,34 +266,17 @@ export class AddNotebookSheet extends React.Component {
}}
blurOnSubmit={false}
/>
<View
style={{
flexDirection: "row",
width: "100%",
justifyContent: "space-between",
alignItems: "center"
}}
>
<Heading size={SIZE.lg}>
{notebook && notebook.dateCreated
? "Edit Notebook"
: "New Notebook"}
</Heading>
<Button
title="Save"
type="accent"
height={40}
style={{
borderRadius: 100,
paddingHorizontal: 24
}}
fontSize={SIZE.md}
onPress={this.addNewNotebook}
/>
</View>
<Seperator />
<DialogHeader
title={
notebook && notebook.dateCreated ? "Edit Notebook" : "New Notebook"
}
paragraph={
notebook && notebook.dateCreated
? "You are editing " + this.title + " notebook."
: "Notebooks are the best way to organize your notes."
}
/>
<Seperator half />
<Input
fwdRef={(ref) => (this.titleRef = ref)}
@@ -382,11 +344,48 @@ export class AddNotebookSheet extends React.Component {
keyExtractor={(item, index) => item + index.toString()}
keyboardShouldPersistTaps="always"
keyboardDismissMode="interactive"
ListFooterComponent={
topics.length === 0 ? null : <View style={{ height: 50 }} />
}
renderItem={this.renderTopicItem}
ListFooterComponent={<View style={{ height: 50 }} />}
renderItem={({ item, index }) => (
<TopicItem
item={item}
onPress={(item, index) => {
this.prevIndex = index;
this.prevItem = item;
this.topicInputRef.current?.setNativeProps({
text: item
});
this.topicInputRef.current?.focus();
this.currentInputValue = item;
this.setState({
editTopic: true
});
}}
onDelete={this.onDelete}
index={index}
colors={colors}
/>
)}
/>
<Seperator />
<Button
width="100%"
fontSize={SIZE.md}
title={
notebook && notebook.dateCreated
? "Save changes"
: "Create notebook"
}
type="accent"
onPress={this.addNewNotebook}
/>
{/*
{Platform.OS === 'ios' && (
<View
style={{
height: 40
}}
/>
)} */}
</View>
);
}

View File

@@ -42,7 +42,6 @@ import { SelectionProvider } from "./context";
import { FilteredList } from "./filtered-list";
import { ListItem } from "./list-item";
import { useItemSelectionStore } from "./store";
import { useRelationStore } from "../../../stores/use-relation-store";
const MoveNoteSheet = ({ note, actionSheetRef }) => {
const colors = useThemeStore((state) => state.colors);
@@ -264,7 +263,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
if (itemState[id] === "selected") {
if (item.type === "notebook") {
for (let noteId of noteIds) {
await db.relations.add(item, { id: noteId, type: "note" });
db.relations.add(item, { id: noteId, type: "note" });
}
} else {
await db.notes.addToNotebook(
@@ -279,7 +278,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
} else if (itemState[id] === "deselected") {
if (item.type === "notebook") {
for (let noteId of noteIds) {
await db.relations.unlink(item, { id: noteId, type: "note" });
db.relations.unlink(item, { id: noteId, type: "note" });
}
} else {
await db.notes.removeFromNotebook(
@@ -297,7 +296,6 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
setNotebooks();
eSendEvent(eOnTopicSheetUpdate);
SearchService.updateAndSearch();
useRelationStore.getState().update();
actionSheetRef.current?.hide();
};

View File

@@ -22,7 +22,7 @@ import { View } from "react-native";
import { ScrollView } from "react-native-actions-sheet";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../../common/database";
import { ToastEvent, presentSheet } from "../../../services/event-manager";
import { presentSheet, ToastEvent } from "../../../services/event-manager";
import Navigation from "../../../services/navigation";
import { useTagStore } from "../../../stores/use-tag-store";
import { useThemeStore } from "../../../stores/use-theme-store";

View File

@@ -158,7 +158,11 @@ export const MoveNotes = ({
{item.title}
</Paragraph>
{item.type == "note" && item.headline ? (
<Paragraph numberOfLines={1} color={colors.icon} size={SIZE.xs}>
<Paragraph
numberOfLines={1}
color={colors.icon}
size={SIZE.xs + 1}
>
{item.headline}
</Paragraph>
) : null}

View File

@@ -43,7 +43,8 @@ import Seperator from "../../ui/seperator";
import SheetWrapper from "../../ui/sheet";
import { QRCode } from "../../ui/svg/lazy";
import Paragraph from "../../ui/typography/paragraph";
import RNFetchBlob from "rn-fetch-blob";
let RNFetchBlob;
class RecoveryKeySheet extends React.Component {
constructor(props) {
@@ -114,6 +115,7 @@ class RecoveryKeySheet extends React.Component {
this.svg.current?.toDataURL(async (data) => {
try {
let path;
RNFetchBlob = (await import("rn-fetch-blob")).default;
let fileName = "nn_" + this.user.email + "_recovery_key_qrcode";
fileName = sanitizeFilename(fileName, { replacement: "_" });
fileName = fileName + ".png";
@@ -149,6 +151,7 @@ class RecoveryKeySheet extends React.Component {
fileName = sanitizeFilename(fileName, { replacement: "_" });
fileName = fileName + ".txt";
RNFetchBlob = (await import("rn-fetch-blob")).default;
if (Platform.OS === "android") {
let file = await ScopedStorage.createDocument(
fileName,

View File

@@ -119,7 +119,7 @@ export default function ReminderNotify({
marginTop: 10
}}
>
<Paragraph size={SIZE.xs}>Remind me in:</Paragraph>
<Paragraph size={SIZE.xs + 1}>Remind me in:</Paragraph>
{QuickActions.map((item) => {
return (
<Button
@@ -127,7 +127,7 @@ export default function ReminderNotify({
key={item.title}
title={item.title}
height={30}
fontSize={SIZE.xs}
fontSize={SIZE.xs + 1}
style={{ marginLeft: 10, borderRadius: 100 }}
onPress={() => onSnooze(item.time)}
/>
@@ -150,7 +150,7 @@ export default function ReminderNotify({
<Paragraph
style={{
color: colors.icon,
fontSize: SIZE.xs,
fontSize: SIZE.xs + 1,
marginBottom: 10
}}
>

View File

@@ -21,9 +21,9 @@ import { Platform, TextInput, View } from "react-native";
import { ActionSheetRef, ScrollView } from "react-native-actions-sheet";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import {
presentSheet,
PresentSheetOptions,
ToastEvent,
presentSheet
ToastEvent
} from "../../../services/event-manager";
import { useThemeStore } from "../../../stores/use-theme-store";
import { SIZE } from "../../../utils/size";
@@ -33,7 +33,6 @@ import Input from "../../ui/input";
import dayjs from "dayjs";
import DatePicker from "react-native-date-picker";
import { db } from "../../../common/database";
import { DDS } from "../../../services/device-detection";
import Navigation from "../../../services/navigation";
import Notifications, { Reminder } from "../../../services/notifications";
import PremiumService from "../../../services/premium";
@@ -42,7 +41,6 @@ import { useRelationStore } from "../../../stores/use-relation-store";
import { NoteType } from "../../../utils/types";
import { Dialog } from "../../dialog";
import { ReminderTime } from "../../ui/reminder-time";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
type ReminderSheetProps = {
@@ -200,8 +198,7 @@ export default function ReminderSheet({
if (!_reminder) {
ToastEvent.show({
heading: "Failed to add a new reminder",
context: "local"
heading: "Failed to add a new reminder"
});
}
if (reference) {
@@ -215,64 +212,42 @@ export default function ReminderSheet({
useRelationStore.getState().update();
close?.();
} catch (e) {
ToastEvent.error(e as Error, undefined, "local");
ToastEvent.error(e as Error);
}
}
return (
<View
style={{
paddingHorizontal: 12,
maxHeight: DDS.isTab ? "90%" : "99.99%"
paddingHorizontal: 12
}}
>
<View
style={{
flexDirection: "row",
width: "100%",
justifyContent: "space-between",
alignItems: "center"
}}
>
<Heading size={SIZE.lg}>Set reminder</Heading>
<Button
title="Save"
type="accent"
height={40}
style={{
borderRadius: 100,
paddingHorizontal: 24
}}
fontSize={SIZE.md}
onPress={saveReminder}
/>
</View>
<Dialog context="local" />
<ScrollView>
<ScrollView keyboardShouldPersistTaps="always">
<Input
fwdRef={titleRef}
defaultValue={reminder?.title || referencedItem?.title}
placeholder="Remind me of..."
onChangeText={(text) => (title.current = text)}
wrapperStyle={{
marginTop: 10
}}
containerStyle={{ borderWidth: 0, borderBottomWidth: 1 }}
/>
<Input
defaultValue={
reminder ? reminder?.description : referencedItem?.headline
}
placeholder="Add a short note"
placeholder="Add a quick note"
onChangeText={(text) => (details.current = text)}
containerStyle={{
maxHeight: 80
borderWidth: 0,
borderBottomWidth: 1,
maxHeight: 80,
marginTop: 10
}}
multiline
textAlignVertical="top"
inputStyle={{
minHeight: 80,
paddingVertical: 12
height: 80
}}
height={80}
wrapperStyle={{
@@ -310,16 +285,6 @@ export default function ReminderSheet({
mode as keyof typeof ReminderModes
] as Reminder["mode"]
);
if (mode === "Repeat") {
setSelectedDays((days) => {
if (days.length > 0) return days;
if (days.indexOf(date.getDay()) > -1) {
return days;
}
days.push(date.getDay());
return [...days];
});
}
}}
/>
))}
@@ -467,7 +432,6 @@ export default function ReminderSheet({
fadeToColor={colors.bg}
theme={colors.night ? "dark" : "light"}
is24hourSource="locale"
androidVariant="nativeAndroid"
mode={reminderMode === ReminderModes.Repeat ? "time" : "datetime"}
/>
@@ -550,7 +514,7 @@ export default function ReminderSheet({
}}
>
<>
<Paragraph size={SIZE.xs} color={colors.icon}>
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
{recurringMode === RecurringModes.Daily
? "Repeats daily " + `at ${dayjs(date).format("hh:mm A")}.`
: selectedDays.length === 7 &&
@@ -584,6 +548,15 @@ export default function ReminderSheet({
alignSelf: "flex-start"
}}
/>
<Button
style={{
width: "100%"
}}
title="Save"
type="accent"
fontSize={SIZE.md}
onPress={saveReminder}
/>
</ScrollView>
</View>
);
@@ -597,7 +570,6 @@ ReminderSheet.present = (
presentSheet({
context: isSheet ? "local" : undefined,
enableGesturesInScrollView: true,
noBottomPadding: true,
component: (ref, close, update) => (
<ReminderSheet
actionSheetRef={ref}

View File

@@ -44,8 +44,7 @@ import { Button } from "../../ui/button";
import Seperator from "../../ui/seperator";
import SheetWrapper from "../../ui/sheet";
import Paragraph from "../../ui/typography/paragraph";
import RNFetchBlob from "rn-fetch-blob";
let RNFetchBlob;
const RestoreDataSheet = () => {
const [visible, setVisible] = useState(false);
const [restoring, setRestoring] = useState(false);
@@ -214,6 +213,7 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
return;
}
} else {
RNFetchBlob = (await import("rn-fetch-blob")).default;
let path = await storage.checkAndCreateDir("/backups/");
files = await RNFetchBlob.fs.lstat(path);
}

View File

@@ -25,6 +25,7 @@ import Navigation from "../../../services/navigation";
import { useThemeStore } from "../../../stores/use-theme-store";
import { GROUP, SORT } from "../../../utils/constants";
import { refreshNotesPage } from "../../../utils/events";
import layoutmanager from "../../../utils/layout-manager";
import { SIZE } from "../../../utils/size";
import { Button } from "../../ui/button";
import Seperator from "../../ui/seperator";
@@ -38,6 +39,7 @@ const Sort = ({ type, screen }) => {
const updateGroupOptions = async (_groupOptions) => {
await db.settings.setGroupOptions(type, _groupOptions);
layoutmanager.withSpringAnimation(600);
setGroupOptions(_groupOptions);
setTimeout(() => {
if (screen !== "TopicSheet") Navigation.queueRoutesForUpdate(screen);

View File

@@ -65,7 +65,6 @@ import { deleteItems } from "../../../utils/functions";
import { presentDialog } from "../../dialog/functions";
import { Properties } from "../../properties";
import Sort from "../sort";
import Heading from "../../ui/typography/heading";
type ConfigItem = { id: string; type: string };
class TopicSheetConfig {

View File

@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useCallback, useEffect, useRef, useState } from "react";
import { TouchableOpacity, View } from "react-native";
import { Keyboard, TouchableOpacity, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { notesnook } from "../../../e2e/test.ids";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
@@ -34,25 +34,24 @@ import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
let toastMessages = [];
export const Toast = ({ context = "global" }) => {
const colors = useThemeStore((state) => state.colors);
const [keyboard, setKeyboard] = useState(false);
const [data, setData] = useState({});
const insets = useGlobalSafeAreaInsets();
const hideTimeout = useRef();
const [visible, setVisible] = useState(false);
const toastMessages = useRef([]);
const show = useCallback(
const showToastFunc = useCallback(
async (data) => {
if (!data) return;
if (data.context !== context) return;
if (
toastMessages.current.findIndex((m) => m.message === data.message) >= 0
) {
if (toastMessages.findIndex((m) => m.message === data.message) >= 0) {
return;
}
toastMessages.current.push(data);
if (toastMessages.current?.length > 1) return;
toastMessages.push(data);
if (toastMessages?.length > 1) return;
setData(data);
setVisible(true);
@@ -60,16 +59,16 @@ export const Toast = ({ context = "global" }) => {
clearTimeout(hideTimeout.current);
}
hideTimeout.current = setTimeout(() => {
hide();
hideToastFunc();
}, data.duration);
},
[context, hide]
[context, hideToastFunc]
);
const next = useCallback(
const showNext = useCallback(
(data) => {
if (!data) {
hide();
hideToastFunc();
return;
}
setData(data);
@@ -77,28 +76,27 @@ export const Toast = ({ context = "global" }) => {
clearTimeout(hideTimeout.current);
}
hideTimeout.current = setTimeout(() => {
hide();
hideToastFunc();
}, data?.duration);
},
[hide]
[hideToastFunc]
);
const hide = useCallback(() => {
const hideToastFunc = useCallback(() => {
if (hideTimeout.current) {
clearTimeout(hideTimeout.current);
}
let msg =
toastMessages.current.length > 1 ? toastMessages.current.shift() : null;
let msg = toastMessages.length > 1 ? toastMessages.shift() : null;
if (msg) {
setVisible(false);
next(msg);
showNext(msg);
setTimeout(() => {
setVisible(true);
}, 300);
} else {
setVisible(false);
toastMessages.current.shift();
toastMessages.shift();
setTimeout(() => {
setData({});
if (hideTimeout.current) {
@@ -106,22 +104,44 @@ export const Toast = ({ context = "global" }) => {
}
}, 100);
}
}, [next]);
}, [showNext]);
const _onKeyboardShow = () => {
setKeyboard(true);
};
const _onKeyboardHide = () => {
setKeyboard(false);
};
useEffect(() => {
eSubscribeEvent(eShowToast, show);
eSubscribeEvent(eHideToast, hide);
toastMessages = [];
let sub1 = Keyboard.addListener("keyboardDidShow", _onKeyboardShow);
let sub2 = Keyboard.addListener("keyboardDidHide", _onKeyboardHide);
eSubscribeEvent(eShowToast, showToastFunc);
eSubscribeEvent(eHideToast, hideToastFunc);
return () => {
toastMessages.current = [];
eUnSubscribeEvent(eShowToast, show);
eUnSubscribeEvent(eHideToast, hide);
if (hideTimeout.current) {
clearTimeout(hideTimeout.current);
}
toastMessages = [];
sub1?.remove();
sub2?.remove();
eUnSubscribeEvent(eShowToast, showToastFunc);
eUnSubscribeEvent(eHideToast, hideToastFunc);
};
}, [hide, show]);
}, [hideToastFunc, keyboard, showToastFunc]);
return (
visible && (
<TouchableOpacity
onPress={hide}
onPress={() => {
if (hideTimeout.current) {
clearTimeout(hideTimeout.current);
}
hideToastFunc();
}}
activeOpacity={1}
style={{
width: DDS.isTab ? 400 : "100%",
@@ -189,7 +209,7 @@ export const Toast = ({ context = "global" }) => {
color={colors.pri}
size={SIZE.md}
onPress={() => {
hide();
hideToastFunc();
}}
>
{data.heading}
@@ -204,7 +224,7 @@ export const Toast = ({ context = "global" }) => {
paddingRight: 10
}}
onPress={() => {
hide();
hideToastFunc();
}}
>
{data.message}

View File

@@ -40,8 +40,6 @@ import { getElevation } from "../../../utils";
import { SIZE } from "../../../utils/size";
import { IconButton } from "../icon-button";
import Paragraph from "../typography/paragraph";
import phone from "phone";
interface InputProps extends TextInputProps {
fwdRef?: RefObject<TextInput>;
validationType?:
@@ -151,6 +149,7 @@ const Input = ({
isError = customValidator && value === customValidator();
break;
case "phonenumber": {
const { default: phone } = await import("phone");
const result = phone(value, {
strictDetection: true,
validateMobilePrefix: true

View File

@@ -65,7 +65,7 @@ export const Notice = ({
flexShrink: 1
}}
selectable={selectable}
size={isSmall ? SIZE.xs : SIZE.sm}
size={isSmall ? SIZE.xs + 1 : SIZE.sm}
>
{text}
</Paragraph>

View File

@@ -520,7 +520,7 @@ export const useActions = ({ close = () => null, item }) => {
}
async function showAttachments() {
AttachmentDialog.present(item);
AttachmentDialog.present();
}
async function exportNote() {
@@ -623,7 +623,7 @@ export const useActions = ({ close = () => null, item }) => {
},
{
id: "favorite",
title: item.favorite ? "Unfavorite" : "Favorite",
title: !item.favorite ? "Favorite" : "Unfavorite",
icon: item.favorite ? "star-off" : "star-outline",
func: addToFavorites,
close: false,
@@ -701,7 +701,7 @@ export const useActions = ({ close = () => null, item }) => {
},
{
id: "read-only",
title: "Readonly",
title: "Read only",
icon: "pencil-lock",
func: toggleReadyOnlyMode,
on: item.readonly

View File

@@ -558,7 +558,7 @@ export const useAppEvents = () => {
let shareExtensionOpened = MMKV.getString("shareExtensionOpened");
if (notesAddedFromIntent) {
if (Platform.OS === "ios") {
await db.initCollections();
await db.init();
await db.notes.init();
}
useNoteStore.getState().setNotes();
@@ -571,8 +571,8 @@ export const useAppEvents = () => {
if (notesAddedFromIntent || shareExtensionOpened) {
let id = useEditorStore.getState().currentEditingNote;
let note = id && db.notes.note(id).data;
eSendEvent("loadingNote", note);
eSendEvent("webview_reset");
setTimeout(() => eSendEvent("loadingNote", note), 1);
MMKV.removeItem("shareExtensionOpened");
}
} catch (e) {

View File

@@ -20,43 +20,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useEffect, useState } from "react";
import { useAttachmentStore } from "../stores/use-attachment-store";
type AttachmentProgress = {
type: string;
value?: number;
percent?: string;
};
export const useAttachmentProgress = (
attachment: any,
encryption?: boolean
) => {
export const useAttachmentProgress = (attachment, encryption) => {
const progress = useAttachmentStore((state) => state.progress);
const [currentProgress, setCurrentProgress] = useState<
AttachmentProgress | undefined
>(
const [currentProgress, setCurrentProgress] = useState(
encryption
? {
type: "encrypt"
}
: undefined
: null
);
useEffect(() => {
const attachmentProgress = progress?.[attachment.metadata.hash];
if (attachmentProgress) {
const type = attachmentProgress.type;
const loaded =
attachmentProgress.type === "download"
? attachmentProgress.recieved
: attachmentProgress.sent;
const value = loaded / attachmentProgress.total;
let prog = progress[attachment.metadata.hash];
if (prog) {
let type = prog.type;
let loaded = prog.type === "download" ? prog.recieved : prog.sent;
prog = loaded / prog.total;
prog = (prog * 100).toFixed(0);
setCurrentProgress({
value: value * 100,
percent: (value * 100).toFixed(0) + "%",
value: prog,
percent: prog + "%",
type: type
});
} else {
setCurrentProgress(undefined);
setCurrentProgress(null);
}
}, [attachment.metadata.hash, progress]);

View File

@@ -0,0 +1,61 @@
/*
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 React, { useCallback, useEffect } from "react";
import BiometicService from "../services/biometrics";
import { eSubscribeEvent, eUnSubscribeEvent } from "../services/event-manager";
import { db } from "../common/database";
const VaultStatusCache = {
exists: false,
biometryEnrolled: false,
isBiometryAvailable: false
};
export const useVaultStatus = () => {
const [vaultStatus, setVaultStatus] = React.useState(VaultStatusCache);
const checkVaultStatus = useCallback(() => {
db.vault.exists().then(async (exists) => {
let available = await BiometicService.isBiometryAvailable();
let fingerprint = await BiometicService.hasInternetCredentials();
if (
VaultStatusCache.exists === exists &&
VaultStatusCache.biometryEnrolled === fingerprint &&
VaultStatusCache.isBiometryAvailable === available
)
return;
setVaultStatus({
exists: exists,
biometryEnrolled: fingerprint,
isBiometryAvailable: available ? true : false
});
});
}, []);
useEffect(() => {
checkVaultStatus();
eSubscribeEvent("vaultUpdated", () => checkVaultStatus());
return () => {
eUnSubscribeEvent("vaultUpdated", () => checkVaultStatus());
};
}, [checkVaultStatus]);
return vaultStatus;
};

View File

@@ -10,7 +10,7 @@
"buffer": "^6.0.3",
"dayjs": "^1.10.4",
"entities": "^3.0.1",
"html-to-text": "9.0.5",
"html-to-text": "8.1.0",
"phone": "^3.1.14",
"qclone": "^1.2.0",
"react-native-actions-sheet": "0.9.0-alpha.18",

View File

@@ -24,7 +24,6 @@ import React, {
useCallback,
useEffect,
useImperativeHandle,
useRef,
useState
} from "react";
import { Platform, ViewStyle } from "react-native";
@@ -90,7 +89,7 @@ const Editor = React.memo(
noToolbar,
noHeader
});
const renderKey = useRef(`editor-0`);
useImperativeHandle(ref, () => ({
get: () => editor
}));
@@ -125,9 +124,8 @@ const Editor = React.memo(
);
const onError = useCallback(() => {
renderKey.current =
renderKey.current === `editor-0` ? `editor-1` : `editor-0`;
editor.setLoading(true);
setTimeout(() => editor.setLoading(false), 10);
}, [editor]);
useEffect(() => {
@@ -154,7 +152,6 @@ const Editor = React.memo(
testID={notesnook.editor.id}
ref={editor.ref}
onLoad={editor.onLoad}
key={renderKey.current}
onRenderProcessGone={onError}
nestedScrollEnabled
onError={onError}
@@ -166,7 +163,6 @@ const Editor = React.memo(
injectedJavaScript={`globalThis.sessionId="${editor.sessionId}";`}
javaScriptEnabled={true}
focusable={true}
onContentProcessDidTerminate={onError}
setSupportMultipleWindows={false}
overScrollMode="never"
scrollEnabled={false}

View File

@@ -101,9 +101,8 @@ const EditorOverlay = ({ editorId = "", editor }) => {
setTimeout(() => {
if (!loadingState.current.startTime) {
translateValue.value = 6000;
opacity.value = 0;
}
}, 3000);
}, 1000);
eSubscribeEvent("loadingNote" + editorId, load);
return () => {
clearTimers();

View File

@@ -106,7 +106,6 @@ typeof globalThis.editorTitle !== "undefined" && editorTitle.current && editorTi
if (editorController.content) editorController.content.current = null;
editorController.onUpdate();
editorController.setTitle(null);
editorController.countWords(0);
typeof globalThis.statusBar !== "undefined" && statusBar.current.set({date:"",saved:""});
`
);
@@ -238,10 +237,6 @@ typeof globalThis.statusBar !== "undefined" && statusBar.current.set({date:"",sa
'response = window.dispatchEvent(new Event("handleBackPress",{cancelable:true}));'
);
};
keyboardShown = async (keyboardShown: boolean) => {
return this.doAsync(`globalThis['keyboardShown']=${keyboardShown};`);
};
//todo add replace image function
}

View File

@@ -48,8 +48,6 @@ export type Settings = {
keyboardShown?: boolean;
doubleSpacedLines?: boolean;
corsProxy: string;
fontSize: string;
fontFamily: string;
};
export type EditorProps = {

View File

@@ -25,8 +25,6 @@ import { useCallback, useEffect, useRef } from "react";
import {
BackHandler,
InteractionManager,
Keyboard,
KeyboardEventListener,
NativeEventSubscription
} from "react-native";
import { WebViewMessageEvent } from "react-native-webview";
@@ -34,12 +32,13 @@ import { db } from "../../../common/database";
import ManageTagsSheet from "../../../components/sheets/manage-tags";
import { RelationsList } from "../../../components/sheets/relations-list";
import ReminderSheet from "../../../components/sheets/reminder";
import useKeyboard from "../../../hooks/use-keyboard";
import { DDS } from "../../../services/device-detection";
import {
ToastEvent,
eSendEvent,
eSubscribeEvent,
eUnSubscribeEvent
eUnSubscribeEvent,
ToastEvent
} from "../../../services/event-manager";
import Navigation from "../../../services/navigation";
import { useEditorStore } from "../../../stores/use-editor-store";
@@ -136,30 +135,8 @@ export const useEditorEvents = (
const doubleSpacedLines = useSettingStore(
(state) => state.settings?.doubleSpacedLines
);
const defaultFontSize = useSettingStore(
(state) => state.settings.defaultFontSize
);
const defaultFontFamily = useSettingStore(
(state) => state.settings.defaultFontFamily
);
const tools = useDragState((state) => state.data);
useEffect(() => {
const handleKeyboardDidShow: KeyboardEventListener = () => {
editor.commands.keyboardShown(true);
};
const handleKeyboardDidHide: KeyboardEventListener = () => {
editor.commands.keyboardShown(false);
};
const subscriptions = [
Keyboard.addListener("keyboardDidShow", handleKeyboardDidShow),
Keyboard.addListener("keyboardDidHide", handleKeyboardDidHide)
];
return () => {
subscriptions.forEach((subscription) => subscription.remove());
};
}, [editor.commands]);
const { keyboardShown } = useKeyboard();
useEffect(() => {
editor.commands.setSettings({
@@ -170,10 +147,9 @@ export const useEditorEvents = (
tools: tools || getDefaultPresets().default,
noHeader: noHeader,
noToolbar: readonly || editorPropReadonly || noToolbar,
keyboardShown: keyboardShown || false,
doubleSpacedLines: doubleSpacedLines,
corsProxy: corsProxy,
fontSize: defaultFontSize,
fontFamily: defaultFontFamily
corsProxy: corsProxy
});
}, [
fullscreen,
@@ -184,13 +160,12 @@ export const useEditorEvents = (
deviceMode,
tools,
editor.commands,
keyboardShown,
doubleSpacedLines,
editorPropReadonly,
noHeader,
noToolbar,
corsProxy,
defaultFontSize,
defaultFontFamily
corsProxy
]);
const onBackPress = useCallback(async () => {

View File

@@ -43,9 +43,9 @@ import { NoteType } from "../../../utils/types";
import Commands from "./commands";
import { Content, EditorState, Note, SavePayload } from "./types";
import {
EditorEvents,
clearAppState,
defaultState,
EditorEvents,
getAppState,
isContentInvalid,
isEditorLoaded,
@@ -80,8 +80,8 @@ export const useEditor = (
const lockedSessionId = useRef<string>();
const postMessage = useCallback(
async <T>(type: string, data: T, waitFor = 300) =>
await post(editorRef, sessionIdRef.current, type, data, waitFor),
async <T>(type: string, data: T) =>
await post(editorRef, sessionIdRef.current, type, data),
[sessionIdRef]
);
@@ -120,11 +120,27 @@ export const useEditor = (
[editorId]
);
const onReady = useCallback(async () => {
if (!(await isEditorLoaded(editorRef, sessionIdRef.current))) {
overlay(true);
setLoading(true);
}
}, [overlay]);
useEffect(() => {
state.current.saveCount = 0;
async () => {
await commands.setSessionId(sessionIdRef.current);
if (sessionIdRef.current) {
if (!state.current?.ready) return;
await onReady();
}
};
}, [sessionId, loading, commands, onReady]);
useEffect(() => {
if (loading) {
setLoading(false);
} else {
state.current.ready = false;
}
}, [loading]);
@@ -340,6 +356,7 @@ export const useEditor = (
) => {
state.current.currentlyEditing = true;
const editorState = useEditorStore.getState();
if (item && item.type === "new") {
currentNote.current && (await reset());
const nextSessionId = makeSessionId(item as NoteType);
@@ -347,7 +364,7 @@ export const useEditor = (
sessionIdRef.current = nextSessionId;
sessionHistoryId.current = Date.now();
await commands.setSessionId(nextSessionId);
if (state.current?.ready) await commands.focus();
await commands.focus();
lastContentChangeTime.current = 0;
useEditorStore.getState().setReadonly(false);
} else {
@@ -361,7 +378,7 @@ export const useEditor = (
!currentContent.current?.data ||
currentContent.current?.data.length < 50000
) {
if (state.current.ready) overlay(false);
overlay(false);
} else {
overlay(true);
}
@@ -375,11 +392,7 @@ export const useEditor = (
currentNote.current = item as NoteType;
await commands.setStatus(timeConverter(item.dateEdited), "Saved");
await postMessage(EditorEvents.title, item.title);
await postMessage(
EditorEvents.html,
currentContent.current?.data,
10000
);
await postMessage(EditorEvents.html, currentContent.current?.data);
useEditorStore.getState().setReadonly(item.readonly);
await commands.setTags(currentNote.current);
commands.setSettings();
@@ -554,7 +567,6 @@ export const useEditor = (
state.current.isRestoringState = true;
state.current.currentlyEditing = true;
state.current.movedAway = false;
if (!DDS.isTab) {
tabBarRef.current?.goToPage(1, false);
}
@@ -574,6 +586,10 @@ export const useEditor = (
state.current.isRestoringState = false;
}, [loadNote, overlay]);
useEffect(() => {
isDefaultEditor && restoreEditorState();
}, [isDefaultEditor, restoreEditorState]);
useEffect(() => {
eSubscribeEvent(eOnLoadNote + editorId, loadNote);
return () => {
@@ -581,40 +597,19 @@ export const useEditor = (
};
}, [editorId, loadNote, restoreEditorState, isDefaultEditor]);
const onContentChanged = () => {
lastContentChangeTime.current = Date.now();
};
useEffect(() => {
state.current.saveCount = 0;
}, [sessionId, loading]);
const onReady = useCallback(async () => {
if (!(await isEditorLoaded(editorRef, sessionIdRef.current))) {
eSendEvent("webview_reset");
} else {
isDefaultEditor && restoreEditorState();
}
}, [isDefaultEditor, restoreEditorState]);
const onLoad = useCallback(async () => {
if (currentNote.current) overlay(true);
clearTimeout(timers.current["editor:loaded"]);
timers.current["editor:loaded"] = setTimeout(async () => {
postMessage(EditorEvents.theme, theme || useThemeStore.getState().colors);
commands.setInsets(
isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 }
);
await commands.setSessionId(sessionIdRef.current);
await onReady();
await commands.setSettings();
if (currentNote.current) {
loadNote({ ...currentNote.current, forced: true });
} else {
await commands.setPlaceholder(placeholderTip.current);
}
state.current.ready = true;
}, 300);
state.current.ready = true;
onReady();
postMessage(EditorEvents.theme, theme || useThemeStore.getState().colors);
commands.setInsets(
isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 }
);
if (currentNote.current) {
loadNote({ ...currentNote.current, forced: true });
} else {
await commands.setPlaceholder(placeholderTip.current);
}
commands.setSettings();
}, [
onReady,
postMessage,
@@ -622,10 +617,13 @@ export const useEditor = (
commands,
isDefaultEditor,
insets,
loadNote,
overlay
loadNote
]);
const onContentChanged = () => {
lastContentChangeTime.current = Date.now();
};
return {
ref: editorRef,
onLoad,
@@ -641,7 +639,6 @@ export const useEditor = (
saveContent,
onContentChanged,
editorId: editorId,
markImageLoaded,
overlay
markImageLoaded
};
};

View File

@@ -73,8 +73,7 @@ export async function post<T>(
ref: RefObject<WebView>,
sessionId: string,
type: string,
value: T | null = null,
waitFor = 300
value: T | null = null
) {
if (!sessionId) {
console.warn("post called without sessionId of type:", type);
@@ -86,7 +85,7 @@ export async function post<T>(
sessionId: sessionId
};
setImmediate(() => ref.current?.postMessage(JSON.stringify(message)));
const response = await getResponse(type, waitFor);
const response = await getResponse(type);
return response;
}
@@ -98,8 +97,7 @@ type WebviewResponseData = {
};
export const getResponse = async (
type: string,
waitFor = 300
type: string
): Promise<WebviewResponseData | false> => {
return new Promise((resolve) => {
const callback = (data: WebviewResponseData) => {
@@ -109,7 +107,7 @@ export const getResponse = async (
eSubscribeEvent(type, callback);
setTimeout(() => {
resolve(false);
}, waitFor);
}, 5000);
});
};

View File

@@ -51,7 +51,6 @@ export const EditorWrapper = ({ width }) => {
if (editorState().movedAway) return;
if (state === "active") {
editorController.current.onReady();
editorController.current.overlay(false);
}
};

View File

@@ -118,7 +118,6 @@ const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => {
get: () => {
const notebook = db.notebooks?.notebook(params?.current?.item?.id)
?.data as NotebookType;
if (!notebook) return [];
return db.relations?.from(notebook, "note");
}
});

View File

@@ -26,7 +26,6 @@ import { Licenses } from "./licenses";
import SoundPicker from "./sound-picker";
import { Subscription } from "./subscription";
import { TrashIntervalSelector } from "./trash-interval-selector";
import { FontSelector } from "./font-selector";
export const components: { [name: string]: ReactElement } = {
colorpicker: <AccentColorPicker />,
homeselector: <HomagePageSelector />,
@@ -36,6 +35,5 @@ export const components: { [name: string]: ReactElement } = {
"debug-logs": <DebugLogs />,
"sound-picker": <SoundPicker />,
licenses: <Licenses />,
"trash-interval-selector": <TrashIntervalSelector />,
"font-selector": <FontSelector />
"trash-interval-selector": <TrashIntervalSelector />
};

View File

@@ -20,6 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { ToolId } from "@notesnook/editor/dist/toolbar";
import React, { RefObject } from "react";
import { View } from "react-native";
import ActionSheet from "react-native-actions-sheet";
import { ScrollView } from "react-native-gesture-handler";
import { PressableButton } from "../../../components/ui/pressable";
import { SvgView } from "../../../components/ui/svg";
import Paragraph from "../../../components/ui/typography/paragraph";
@@ -32,14 +34,13 @@ import {
getToolIcon,
getUngroupedTools
} from "./toolbar-definition";
import { ActionSheetRef, ScrollView } from "react-native-actions-sheet";
export default function ToolSheet({
group,
fwdRef
}: {
group: DraggableItem;
fwdRef: RefObject<ActionSheetRef>;
fwdRef: RefObject<ActionSheet>;
}) {
const colors = useThemeStore((state) => state.colors);
const data = useDragState((state) => state.data);
@@ -49,7 +50,6 @@ export default function ToolSheet({
(item: ToolId) => {
const tool = findToolById(item);
const iconSvgString = tool ? getToolIcon(tool.icon as ToolId) : null;
if (item === "none") return;
return (
<PressableButton
key={item}
@@ -109,7 +109,12 @@ export default function ToolSheet({
padding: 12
}}
>
<ScrollView nestedScrollEnabled={true}>
<ScrollView
onMomentumScrollEnd={() => {
fwdRef.current?.handleChildScrollEnd();
}}
nestedScrollEnabled={true}
>
{!ungrouped || ungrouped.length === 0 ? (
<Paragraph
style={{

View File

@@ -1,110 +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 React, { useRef, useState } from "react";
import { View } from "react-native";
import Menu, { MenuItem } from "react-native-reanimated-material-menu";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { PressableButton } from "../../components/ui/pressable";
import Paragraph from "../../components/ui/typography/paragraph";
import SettingsService from "../../services/settings";
import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { SIZE } from "../../utils/size";
import { getFontById, getFonts } from "@notesnook/editor/dist/utils/font";
export const FontSelector = () => {
const colors = useThemeStore((state) => state.colors);
const defaultFontFamily = useSettingStore(
(state) => state.settings.defaultFontFamily
);
const menuRef = useRef();
const [width, setWidth] = useState(0);
const onChange = (item) => {
menuRef.current?.hide();
SettingsService.set({
defaultFontFamily: item
});
};
return (
<View
onLayout={(event) => {
setWidth(event.nativeEvent.layout.width);
}}
style={{
width: "100%"
}}
>
<Menu
ref={menuRef}
animationDuration={200}
style={{
borderRadius: 5,
backgroundColor: colors.bg,
width: width,
marginTop: 60
}}
onRequestClose={() => {
menuRef.current?.hide();
}}
anchor={
<PressableButton
onPress={async () => {
menuRef.current?.show();
}}
type="grayBg"
customStyle={{
flexDirection: "row",
alignItems: "center",
marginTop: 10,
width: "100%",
justifyContent: "space-between",
padding: 12
}}
>
<Paragraph>{getFontById(defaultFontFamily).title}</Paragraph>
<Icon color={colors.icon} name="menu-down" size={SIZE.md} />
</PressableButton>
}
>
{getFonts().map((item) => (
<MenuItem
key={item.id}
onPress={async () => {
onChange(item.id);
}}
style={{
backgroundColor:
defaultFontFamily === item.id ? colors.nav : "transparent",
width: "100%",
maxWidth: width
}}
textStyle={{
fontSize: SIZE.md,
color: defaultFontFamily === item.id ? colors.accent : colors.pri
}}
>
{item.title}
</MenuItem>
))}
</Menu>
</View>
);
};

View File

@@ -37,7 +37,6 @@ import { useThemeStore } from "../../stores/use-theme-store";
import { SIZE } from "../../utils/size";
import { components } from "./components";
import { RouteParams, SettingSection } from "./types";
import { IconButton } from "../../components/ui/icon-button";
const _SectionItem = ({ item }: { item: SettingSection }) => {
const colors = useThemeStore((state) => state.colors);
@@ -65,12 +64,6 @@ const _SectionItem = ({ item }: { item: SettingSection }) => {
backgroundColor: colors.errorBg
}
: {};
const updateInput = (value: any) => {
inputRef?.current?.setNativeProps({
text: value + ""
});
};
return isHidden ? null : (
<PressableButton
disabled={item.type === "component"}
@@ -197,95 +190,6 @@ const _SectionItem = ({ item }: { item: SettingSection }) => {
defaultValue={item.inputProperties?.defaultValue}
/>
)}
{item.type === "input-selector" && (
<View
style={{
flexDirection: "row",
alignItems: "center",
marginTop: 12
}}
>
<IconButton
name="minus"
color={colors.pri}
onPress={() => {
const rawValue = SettingsService.get()[
item.property as keyof SettingStore["settings"]
] as string;
if (rawValue) {
const currentValue = parseInt(rawValue);
if (currentValue <= 0) return;
const nextValue = currentValue - 1;
SettingsService.set({
[item.property as string]: nextValue
});
updateInput(nextValue);
}
}}
size={SIZE.xl}
/>
<Input
{...item.inputProperties}
onSubmit={(e) => {
if (e.nativeEvent.text) {
SettingsService.set({
[item.property as string]: e.nativeEvent.text
});
}
item.inputProperties?.onSubmitEditing?.(e);
}}
onChangeText={(text) => {
if (text) {
if (item.minInputValue) {
text =
parseInt(text) < item.minInputValue
? item.minInputValue + ""
: text;
}
SettingsService.set({
[item.property as string]: text
});
}
item.inputProperties?.onSubmitEditing?.(text as any);
}}
keyboardType="decimal-pad"
containerStyle={{
width: 45
}}
wrapperStyle={{
maxWidth: 45,
marginBottom: 0,
marginHorizontal: 6
}}
fwdRef={inputRef}
onLayout={() => {
if (item.property) {
updateInput(SettingsService.get()[item.property]);
}
}}
defaultValue={item.inputProperties?.defaultValue}
/>
<IconButton
name="plus"
color={colors.pri}
onPress={() => {
const rawValue = SettingsService.get()[
item.property as keyof SettingStore["settings"]
] as string;
if (rawValue) {
const currentValue = parseInt(rawValue);
const nextValue = currentValue + 1;
SettingsService.set({
[item.property as string]: nextValue
});
updateInput(nextValue);
}
}}
size={SIZE.xl}
/>
</View>
)}
</View>
</View>

View File

@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import notifee from "@notifee/react-native";
import dayjs from "dayjs";
import React from "react";
import { Linking, Platform } from "react-native";
@@ -318,6 +317,7 @@ export const settingsGroups: SettingSection[] = [
await PremiumService.setPremiumStatus();
await BiometicService.resetCredentials();
MMKV.clearStore();
await db.init();
await clearAllStores();
SettingsService.init();
setTimeout(() => {
@@ -451,13 +451,13 @@ export const settingsGroups: SettingSection[] = [
},
{
id: "customize",
name: "Customization",
name: "Customize",
sections: [
{
id: "personalization",
type: "screen",
name: "Theme",
description: "Change app look and feel with color themes",
description: "Change app look and feel",
icon: "shape",
sections: [
{
@@ -515,7 +515,7 @@ export const settingsGroups: SettingSection[] = [
id: "behaviour",
type: "screen",
name: "Behaviour",
description: "Change how the app behaves in different situations",
description: "Change app homepage",
sections: [
{
id: "default-home",
@@ -533,63 +533,6 @@ export const settingsGroups: SettingSection[] = [
component: "trash-interval-selector"
}
]
},
{
id: "editor",
name: "Editor",
type: "screen",
icon: "note-edit-outline",
description: "Customize the editor to fit your needs",
sections: [
{
id: "configure-toolbar",
type: "screen",
name: "Configure toolbar",
description: "Make the toolbar adaptable to your needs.",
component: "configuretoolbar"
},
{
id: "reset-toolbar",
name: "Reset toolbar",
description: "Reset toolbar configuration to default",
modifer: () => {
useDragState.getState().setPreset("default");
}
},
{
id: "double-spaced-lines",
name: "Use double spaced lines",
description:
"New lines will be double spaced (old ones won't be affected).",
type: "switch",
property: "doubleSpacedLines",
icon: "format-line-spacing",
onChange: () => {
ToastEvent.show({
heading: "Line spacing changed",
type: "success"
});
}
},
{
id: "default-font-size",
name: "Default font size",
description: "Set the default font size in editor",
type: "input-selector",
minInputValue: 8,
icon: "format-size",
property: "defaultFontSize"
},
{
id: "default-font-family",
name: "Default font family",
description: "Set the default font family in editor",
type: "component",
icon: "format-font",
property: "defaultFontFamily",
component: "font-selector"
}
]
}
]
},
@@ -986,6 +929,42 @@ export const settingsGroups: SettingSection[] = [
}
]
},
{
id: "editor",
name: "Editor",
sections: [
{
id: "configure-toolbar",
type: "screen",
name: "Configure toolbar",
description: "Make the toolbar adaptable to your needs.",
component: "configuretoolbar"
},
{
id: "reset-toolbar",
name: "Reset toolbar",
description: "Reset toolbar configuration to default",
modifer: () => {
useDragState.getState().setPreset("default");
}
},
{
id: "double-spaced-lines",
name: "Use double spaced lines",
description:
"New lines will be double spaced (old ones won't be affected).",
type: "switch",
property: "doubleSpacedLines",
icon: "format-line-spacing",
onChange: () => {
ToastEvent.show({
heading: "Line spacing changed",
type: "success"
});
}
}
]
},
{
id: "help-support",
name: "Help and support",

View File

@@ -22,14 +22,7 @@ import { Settings } from "../../stores/use-setting-store";
export type SettingSection = {
id: string;
type?:
| "screen"
| "switch"
| "component"
| "danger"
| "input"
| "input-selector"
| "dropdown-selector";
type?: "screen" | "switch" | "component" | "danger" | "input";
name?: string | ((current?: unknown) => string);
description?: string | ((current: unknown) => string);
icon?: string;
@@ -42,8 +35,6 @@ export type SettingSection = {
hidden?: (current: unknown) => boolean;
onChange?: (property: boolean) => void;
inputProperties?: TextInput["props"];
options?: any[];
minInputValue?: number;
};
export type SettingsGroup = {

View File

@@ -109,7 +109,7 @@ const SettingsUserSection = ({ item }) => {
flexGrow: 1
}}
>
<Heading color={colors.accent} size={SIZE.xs}>
<Heading color={colors.accent} size={SIZE.xs + 1}>
{SUBSCRIPTION_STATUS_STRINGS[
user.subscription?.type
]?.toUpperCase() || "Basic"}

View File

@@ -45,7 +45,6 @@ import { sleep } from "../utils/time";
import { useRelationStore } from "../stores/use-relation-store";
import { useReminderStore } from "../stores/use-reminder-store";
import { presentDialog } from "../components/dialog/functions";
import NetInfo from "@react-native-community/netinfo";
export type Reminder = {
id: string;
@@ -86,21 +85,13 @@ async function getNextMonthlyReminderDate(
return await getNextMonthlyReminderDate(reminder, dayjs().year() + 1);
}
async function initDatabase(notes = true) {
if (!db.isInitialized) {
await db.initCollections();
}
if (notes) {
await db.notes?.init();
}
}
const onEvent = async ({ type, detail }: Event) => {
const { notification, pressAction, input } = detail;
if (type === EventType.DELIVERED && Platform.OS === "android") {
const reminder = db.reminders?.reminder(notification?.id?.split("_")[0]);
if (reminder && reminder.recurringMode === "month") {
await initDatabase();
await db.init();
await db.notes?.init();
await scheduleNotification(reminder);
}
return;
@@ -109,10 +100,10 @@ const onEvent = async ({ type, detail }: Event) => {
notifee.decrementBadgeCount();
if (notification?.data?.type === "quickNote") return;
MMKV.removeItem("appState");
await initDatabase();
await db.init();
await db.notes?.init();
if (notification?.data?.type === "reminder") {
const reminder = db.reminders?.reminder(notification.id?.split("_")[0]);
if (!reminder) return;
await sleep(1000);
const ReminderNotify =
require("../components/sheets/reminder-notify").default;
@@ -140,7 +131,8 @@ const onEvent = async ({ type, detail }: Event) => {
notifee.decrementBadgeCount();
switch (pressAction?.id) {
case "REMINDER_SNOOZE": {
await initDatabase();
await db.init();
await db.notes?.init();
const reminder = db.reminders?.reminder(
notification?.id?.split("_")[0]
);
@@ -159,7 +151,8 @@ const onEvent = async ({ type, detail }: Event) => {
break;
}
case "REMINDER_DISABLE": {
await initDatabase();
await db.init();
await db.notes?.init();
const reminder = db.reminders?.reminder(
notification?.id?.split("_")[0]
);
@@ -175,7 +168,8 @@ const onEvent = async ({ type, detail }: Event) => {
break;
}
case "UNPIN": {
await initDatabase();
await db.init();
await db.notes?.init();
remove(notification?.id as string);
const reminder = db.reminders?.reminder(
notification?.id?.split("_")[0]
@@ -194,7 +188,7 @@ const onEvent = async ({ type, detail }: Event) => {
case "Hide":
unpinQuickNote();
break;
case "ReplyInput": {
case "ReplyInput":
displayNotification({
title: "Quick note",
message: 'Tap on "Take note" to add a note.',
@@ -204,25 +198,16 @@ const onEvent = async ({ type, detail }: Event) => {
reply_button_text: "Take note",
reply_placeholder_text: "Write something..."
});
if (!db.isInitialized) await db.init();
await db.notes?.init();
await db.init();
await db.notes?.add({
content: {
type: "tiptap",
data: `<p>${input} </p>`
}
});
const status = await NetInfo.fetch();
if (status.isInternetReachable) {
try {
await db.sync(false, false);
} catch (e) {
console.log(e, (e as Error).stack);
}
}
await db.notes?.init();
useNoteStore.getState().setNotes();
break;
}
}
}
};

View File

@@ -98,7 +98,7 @@ async function getProducts() {
}
function get() {
if (__DEV__ || Config.isTesting === "true") return true;
if (__DEV__ || Config.isTesting) return true;
return SUBSCRIPTION_STATUS.BASIC !== premiumStatus;
}
@@ -128,7 +128,7 @@ const onUserStatusCheck = async (type) => {
userstore.setPremium(get());
}
let status = false;
let status = get();
let message = null;
if (!status) {
switch (type) {

View File

@@ -27,6 +27,14 @@ import { DatabaseLogger } from "../common/database/index";
import { ToastEvent } from "./event-manager";
import SettingsService from "./settings";
NetInfo.configure({
reachabilityUrl: "https://notesnook.com",
reachabilityTest: (response) => {
if (!response) return false;
return response?.status >= 200 && response?.status < 300;
}
});
export const ignoredMessages = [
"Sync already running",
"Not allowed to start service intent",

View File

@@ -24,6 +24,7 @@ import { db } from "../common/database";
import { MMKV } from "../common/database/mmkv";
import PremiumService from "../services/premium";
import { SUBSCRIPTION_STATUS } from "../utils/constants";
import layoutmanager from "../utils/layout-manager";
export interface MessageStore extends State {
message: Message;
setMessage: (message: Message) => void;
@@ -95,7 +96,13 @@ export const useMessageStore = create<MessageStore>((set, get) => ({
icon: "account-outline"
},
setMessage: (message) => {
set({ message: { ...message } });
setTimeout(() => {
if (get().message.visible || message.visible) {
layoutmanager.withAnimation();
}
set({ message: { ...message } });
}, 1);
},
announcements: [],
remove: async (id) => {

View File

@@ -69,7 +69,6 @@ export type RouteParams = {
AppLock: AppLockRouteParams;
Auth: AuthParams;
Reminders: GenericRouteParam;
SettingsGroup: GenericRouteParam;
};
export type RouteName = keyof RouteParams;

View File

@@ -73,8 +73,6 @@ export type Settings = {
corsProxy: string;
disableRealtimeSync?: boolean;
notificationSound?: Sound & { platform: PlatformOSType };
defaultFontSize: string;
defaultFontFamily: string;
};
type DimensionsType = {
@@ -151,9 +149,7 @@ export const useSettingStore = create<SettingStore>((set) => ({
defaultSnoozeTime: "5",
corsProxy: "https://cors.notesnook.com",
reminderNotificationMode: "urgent",
notificationSound: undefined,
defaultFontFamily: "sans-serif",
defaultFontSize: "16"
notificationSound: undefined
},
sheetKeyboardHandler: true,
fullscreen: false,

View File

@@ -27,6 +27,7 @@ import { db } from "../common/database";
import { tabBarRef } from "./global-refs";
let prevTarget = null;
let htmlToText;
export const TOOLTIP_POSITIONS = {
LEFT: 1,
@@ -170,8 +171,3 @@ export function showTooltip(event, text, position = 2) {
clickToHide: true
});
}
export function toTitleCase(value) {
if (!value) return;
return value.slice(0, 1).toUpperCase() + value.slice(1);
}

View File

@@ -16,25 +16,16 @@ 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 { expose } from "comlink";
const module = {
async waitForInternet() {
let retries = 10;
while (retries-- > 0) {
try {
const response = await fetch("https://api.notesnook.com/health");
if (response.ok) return true;
} catch {
// ignore
}
function withAnimation(_duration = 300) {
return;
}
// wait a bit before trying again.
await new Promise((resolve) => setTimeout(resolve, 2500));
}
return false;
}
function withSpringAnimation(_duration = 300) {
return;
}
export default {
withAnimation,
withSpringAnimation
};
expose(module);
export type NetworkCheck = typeof module;

View File

@@ -46,18 +46,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
* @return {String} Sanitized filename
*/
const illegalRe = /[/?<>\\:*|"]/g;
var illegalRe = /[/?<>\\:*|"]/g;
//var controlRe = /[x00-x1f\x80-\x9f]/g;
const reservedRe = /^\.+$/;
const windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i;
const windowsTrailingRe = /[. ]+$/;
const whitespace = /\s+/g;
var reservedRe = /^\.+$/;
var windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i;
var windowsTrailingRe = /[. ]+$/;
var whitespace = /\s+/g;
function sanitize(input: string, replacement: string) {
function sanitize(input, replacement) {
if (typeof input !== "string") {
throw new Error("Input must be string");
}
const sanitized = input
var sanitized = input
.replace(whitespace, replacement)
.replace(illegalRe, replacement)
.replace(reservedRe, replacement)
@@ -67,10 +67,7 @@ function sanitize(input: string, replacement: string) {
return sanitized.slice(0, 254).toLowerCase();
}
export function sanitizeFilename(
input: string,
options: { replacement: string }
) {
const replacement = (options && options.replacement) || "";
export function sanitizeFilename(input, options) {
var replacement = (options && options.replacement) || "";
return sanitize(input, replacement);
}

View File

@@ -80,10 +80,10 @@ export const normalize = (size) => {
}
};
export const SIZE = {
xxs: normalize(11) * scale.fontScale,
xs: normalize(12.5) * scale.fontScale,
sm: normalize(15) * scale.fontScale,
md: normalize(16.5) * scale.fontScale,
xxs: normalize(10.5) * scale.fontScale,
xs: normalize(12) * scale.fontScale,
sm: normalize(14.5) * scale.fontScale,
md: normalize(16) * scale.fontScale,
lg: normalize(22) * scale.fontScale,
xl: normalize(24) * scale.fontScale,
xxl: normalize(28) * scale.fontScale,
@@ -91,10 +91,10 @@ export const SIZE = {
};
export function updateSize() {
SIZE.xxs = normalize(11) * scale.fontScale;
SIZE.xs = normalize(12.5) * scale.fontScale;
SIZE.sm = normalize(15) * scale.fontScale;
SIZE.md = normalize(16.5) * scale.fontScale;
SIZE.xxs = normalize(10.5) * scale.fontScale;
SIZE.xs = normalize(12) * scale.fontScale;
SIZE.sm = normalize(14.5) * scale.fontScale;
SIZE.md = normalize(16) * scale.fontScale;
SIZE.lg = normalize(22) * scale.fontScale;
SIZE.xl = normalize(24) * scale.fontScale;
SIZE.xxl = normalize(28) * scale.fontScale;

View File

@@ -48,7 +48,7 @@ describe("NOTE TESTS", () => {
await prepare();
let note = await createNote();
await tapById(notesnook.listitem.menu);
await visibleByText("Created at:");
await visibleByText(note.body);
});
it("Favorite and unfavorite a note", async () => {
@@ -124,9 +124,8 @@ describe("NOTE TESTS", () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-trash");
await tapById("icon-delete");
await navigate("Trash");
await sleep(500);
await tapById(notesnook.listitem.menu);
await tapByText("Restore note");
await device.pressBack();

View File

@@ -56,7 +56,7 @@ async function createNotebook(
await tapById("topic-add-button");
}
}
await tapByText("Save");
await tapByText("Create notebook");
await sleep(500);
}
@@ -120,7 +120,7 @@ describe("NOTEBOOKS", () => {
await visibleByText(note.body);
});
it.only("Add new topic to notebook", async () => {
it("Add new topic to notebook", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
@@ -133,7 +133,7 @@ describe("NOTEBOOKS", () => {
await elementById("input-title").typeText("Topic");
await tapByText("Add");
await sleep(500);
await tapByText("Topic");
await visibleById("topic-sheet-item-0");
});
it("Edit topic", async () => {
@@ -146,6 +146,7 @@ describe("NOTEBOOKS", () => {
await sleep(500);
await tapByText("Notebook 1");
await sleep(300);
await visibleById("topic-sheet-item-0");
await tapById(notesnook.ids.notebook.menu);
await tapByText("Edit topic");
await elementById("input-title").typeText(" (edited)");
@@ -253,7 +254,7 @@ describe("NOTEBOOKS", () => {
"Topic 2"
);
await tapById("topic-add-button");
await tapByText("Save");
await tapByText("Save changes");
await sleep(500);
await visibleByText("Notebook 1 (Edited)");
await visibleByText("Description of Notebook 1 (Edited)");
@@ -273,7 +274,7 @@ describe("NOTEBOOKS", () => {
await tapById(notesnook.ids.notebook.menu);
await tapByText("Move to trash");
await sleep(2000);
await tapByText("Delete");
await tapByText("No");
await sleep(4000);
await navigate("Trash");
await visibleByText("Notebook 1");
@@ -295,9 +296,7 @@ describe("NOTEBOOKS", () => {
await tapById(notesnook.ids.notebook.menu);
await tapByText("Move to trash");
await sleep(2000);
await tapByText("Move all notes in this notebook to trash");
await sleep(500);
await tapByText("Delete");
await tapByText("Yes");
await sleep(4000);
await navigate("Trash");
await visibleByText("Notebook 1");
@@ -320,9 +319,7 @@ describe("NOTEBOOKS", () => {
await tapById(notesnook.ids.notebook.menu);
await tapByText("Delete topic");
await sleep(2000);
await tapByText("Move all notes in this topic to trash");
await sleep(500);
await tapByText("Delete");
await tapByText("Yes");
await device.pressBack();
await sleep(4000);
await navigate("Trash");

View File

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

View File

@@ -51,7 +51,8 @@
android:theme="@style/BootTheme"
android:largeHeap="true"
android:supportsRtl="false"
tools:replace="android:supportsRtl">
tools:replace="android:supportsRtl"
android:usesCleartextTraffic="true">
<receiver android:exported="false" android:name=".NoteWidget">
<intent-filter>

View File

@@ -4,7 +4,6 @@ package com.streetwriters.notesnook;
import android.graphics.Color;
import android.view.WindowManager;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactApplicationContext;
@@ -37,16 +36,6 @@ public class RCTNNativeModule extends ReactContextBaseJavaModule {
}
}
@ReactMethod
public void getActivityName(Promise promise) {
try {
promise.resolve(getCurrentActivity().getClass().getSimpleName());
} catch (Exception e) {
promise.resolve(null);
}
}
@ReactMethod
public void setSecureMode(final boolean mode) {

View File

@@ -1,4 +1,7 @@
- Fix note not saved from share widget when offline
- Do not allow insecure http requests from app
- New and improved share extension with support for organizing notes
with tags & notebooks!
- Improved UX for topics sheet in Notebooks
- Improved editor performance
- Many bug fixes and minor improvements
Thank you for using Notesnook!

View File

@@ -7,15 +7,6 @@ import Config from 'react-native-config';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import appJson from './app.json';
import Notifications from "../app/services/notifications";
import NetInfo from "@react-native-community/netinfo";
NetInfo.configure({
reachabilityUrl: "https://notesnook.com",
reachabilityTest: (response) => {
if (!response) return false;
return response?.status >= 200 && response?.status < 300;
}
});
Notifications.init();
const appName = appJson.name;

View File

@@ -993,7 +993,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2039;
CURRENT_PROJECT_VERSION = 2034;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1067,7 +1067,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.4.16;
MARKETING_VERSION = 2.4.11;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1097,7 +1097,7 @@
CODE_SIGN_ENTITLEMENTS = Notesnook/Notesnook.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2039;
CURRENT_PROJECT_VERSION = 2034;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@@ -1170,7 +1170,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.4.16;
MARKETING_VERSION = 2.4.11;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1328,7 +1328,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2039;
CURRENT_PROJECT_VERSION = 2034;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1340,7 +1340,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.16;
MARKETING_VERSION = 2.4.11;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1370,7 +1370,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2039;
CURRENT_PROJECT_VERSION = 2034;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1382,7 +1382,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.16;
MARKETING_VERSION = 2.4.11;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1411,7 +1411,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2039;
CURRENT_PROJECT_VERSION = 2034;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1485,7 +1485,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.16;
MARKETING_VERSION = 2.4.11;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1515,7 +1515,7 @@
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2039;
CURRENT_PROJECT_VERSION = 2034;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1589,7 +1589,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.4.16;
MARKETING_VERSION = 2.4.11;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -1,4 +1,7 @@
- Fix note not saved from share widget when offline
- Do not allow insecure http requests from app
- New and improved share extension with support for organizing notes
with tags & notebooks!
- Improved UX for Topics sheet in Notebooks
- Improved editor performance
- Many bug fixes and minor improvements
Thank you for using Notesnook!

View File

@@ -86,17 +86,7 @@ module.exports = (env) => {
"@notesnook": path.join(__dirname, "../../../packages"),
"@streetwriters/showdown": path.join(__dirname, "../node_modules/@streetwriters/showdown"),
"qclone": path.join(__dirname, "../node_modules/qclone"),
"@notifee/react-native": path.join(__dirname, "../node_modules/@ammarahmed/notifee-react-native"),
"html-to-text": path.join(__dirname, "../node_modules/html-to-text"),
"leac": path.join(__dirname, "../node_modules/leac"),
"parseley": path.join(__dirname, "../node_modules/parseley"),
"htmlparser2": path.join(__dirname, "../node_modules/htmlparser2"),
"selderee": path.join(__dirname, "../node_modules/selderee"),
"minimist": path.join(__dirname, "../node_modules/minimist"),
"entities": path.join(__dirname, "../node_modules/entities"),
"deepmerge": path.join(__dirname, "../node_modules/deepmerge"),
"@selderee/plugin-htmlparser2": path.join(__dirname, "../node_modules/@selderee/plugin-htmlparser2"),
"peberminta": path.join(__dirname, "../node_modules/peberminta"),
"@notifee/react-native": path.join(__dirname, "../node_modules/@ammarahmed/notifee-react-native")
},
},
/**
@@ -168,7 +158,6 @@ module.exports = (env) => {
/node_modules(.*[/\\])+@microsoft/,
/node_modules(.*[/\\])+@msgpack/,
/node_modules(.*[/\\])+liqe/,
/node_modules(.*[/\\])+leac/,
/node_modules(.*[/\\])+selderee/,
/node_modules(.*[/\\])+html-to-text/,
/node_modules(.*[/\\])+buffer/,

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/mobile",
"version": "2.4.13",
"version": "2.4.10",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "2.4.13",
"version": "2.4.10",
"license": "GPL-3.0-or-later",
"workspaces": [
"native/",
@@ -35,7 +35,7 @@
"dayjs": "^1.10.4",
"entities": "^3.0.1",
"fflate": "^0.7.3",
"html-to-text": "9.0.5",
"html-to-text": "8.1.0",
"phone": "^3.1.14",
"qclone": "^1.2.0",
"react-native-actions-sheet": "0.9.0-alpha.18",
@@ -5620,12 +5620,12 @@
}
},
"node_modules/@selderee/plugin-htmlparser2": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz",
"integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==",
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.6.0.tgz",
"integrity": "sha512-J3jpy002TyBjd4N/p6s+s90eX42H2eRhK3SbsZuvTDv977/E8p2U3zikdiehyJja66do7FlxLomZLPlvl2/xaA==",
"dependencies": {
"domhandler": "^5.0.3",
"selderee": "^0.11.0"
"domhandler": "^4.2.0",
"selderee": "^0.6.0"
},
"funding": {
"url": "https://ko-fi.com/killymxi"
@@ -8142,6 +8142,57 @@
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/css-select/node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
"entities": "^4.2.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/css-select/node_modules/domhandler": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"dependencies": {
"domelementtype": "^2.3.0"
},
"engines": {
"node": ">= 4"
},
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/css-select/node_modules/domutils": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
"integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
"dependencies": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.1"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/css-select/node_modules/entities": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
"integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/css-tree": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
@@ -8237,9 +8288,9 @@
"dev": true
},
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
"engines": {
"node": ">=0.10.0"
}
@@ -8522,6 +8573,11 @@
"node": ">=8"
}
},
"node_modules/discontinuous-range": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
"integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ=="
},
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -8535,25 +8591,22 @@
}
},
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
"integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
"entities": "^4.2.0"
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/dom-serializer/node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"engines": {
"node": ">=0.12"
},
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
@@ -8575,11 +8628,11 @@
]
},
"node_modules/domhandler": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
"dependencies": {
"domelementtype": "^2.3.0"
"domelementtype": "^2.2.0"
},
"engines": {
"node": ">= 4"
@@ -8589,13 +8642,13 @@
}
},
"node_modules/domutils": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
"integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"dependencies": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.1"
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
@@ -10914,6 +10967,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"bin": {
"he": "bin/he"
}
},
"node_modules/hermes-engine": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/hermes-engine/-/hermes-engine-0.11.0.tgz",
@@ -10963,24 +11024,28 @@
"dev": true
},
"node_modules/html-to-text": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz",
"integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-8.1.0.tgz",
"integrity": "sha512-Z9iYAqYK2c18GswSbnxJSeMs7lyJgwR2oIkDOyOHGBbYsPsG4HvT379jj3Lcbfko8A5ceyyMHAfkmp/BiXA9/Q==",
"dependencies": {
"@selderee/plugin-htmlparser2": "^0.11.0",
"deepmerge": "^4.3.1",
"dom-serializer": "^2.0.0",
"htmlparser2": "^8.0.2",
"selderee": "^0.11.0"
"@selderee/plugin-htmlparser2": "^0.6.0",
"deepmerge": "^4.2.2",
"he": "^1.2.0",
"htmlparser2": "^6.1.0",
"minimist": "^1.2.5",
"selderee": "^0.6.0"
},
"bin": {
"html-to-text": "bin/cli.js"
},
"engines": {
"node": ">=14"
"node": ">=10.23.2"
}
},
"node_modules/htmlparser2": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
"integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
@@ -10989,19 +11054,16 @@
}
],
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1",
"entities": "^4.4.0"
"domelementtype": "^2.0.1",
"domhandler": "^4.0.0",
"domutils": "^2.5.2",
"entities": "^2.0.0"
}
},
"node_modules/htmlparser2/node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"engines": {
"node": ">=0.12"
},
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
@@ -14315,14 +14377,6 @@
"node": ">=6"
}
},
"node_modules/leac": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz",
"integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==",
"funding": {
"url": "https://ko-fi.com/killymxi"
}
},
"node_modules/leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -16134,6 +16188,11 @@
"node": "*"
}
},
"node_modules/moo": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
"integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q=="
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -16239,6 +16298,27 @@
"ncp": "bin/ncp"
}
},
"node_modules/nearley": {
"version": "2.20.1",
"resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz",
"integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==",
"dependencies": {
"commander": "^2.19.0",
"moo": "^0.5.0",
"railroad-diagrams": "^1.0.0",
"randexp": "0.4.6"
},
"bin": {
"nearley-railroad": "bin/nearley-railroad.js",
"nearley-test": "bin/nearley-test.js",
"nearley-unparse": "bin/nearley-unparse.js",
"nearleyc": "bin/nearleyc.js"
},
"funding": {
"type": "individual",
"url": "https://nearley.js.org/#give-to-nearley"
}
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -16897,12 +16977,12 @@
}
},
"node_modules/parseley": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.0.tgz",
"integrity": "sha512-uLqDm6IQVb6m50a3dIxF66hI8VWr7wFDYUULtHa1ITRh9mwYIXzFpPTkPM66Cm5V0t+bMyeSHgUCGzoXTV96LQ==",
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/parseley/-/parseley-0.7.0.tgz",
"integrity": "sha512-xyOytsdDu077M3/46Am+2cGXEKM9U9QclBDv7fimY7e+BBlxh2JcBp2mgNsmkyA9uvgyTjVzDi7cP1v4hcFxbw==",
"dependencies": {
"leac": "^0.6.0",
"peberminta": "^0.9.0"
"moo": "^0.5.1",
"nearley": "^2.20.1"
},
"funding": {
"url": "https://ko-fi.com/killymxi"
@@ -16989,14 +17069,6 @@
"node": ">=8"
}
},
"node_modules/peberminta": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz",
"integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==",
"funding": {
"url": "https://ko-fi.com/killymxi"
}
},
"node_modules/phin": {
"version": "2.9.3",
"resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
@@ -17735,6 +17807,31 @@
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
},
"node_modules/railroad-diagrams": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
"integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A=="
},
"node_modules/randexp": {
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
"integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
"dependencies": {
"discontinuous-range": "1.0.0",
"ret": "~0.1.10"
},
"engines": {
"node": ">=0.12"
}
},
"node_modules/randexp/node_modules/ret": {
"version": "0.1.15",
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
"engines": {
"node": ">=0.12"
}
},
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -19062,11 +19159,11 @@
"integrity": "sha512-ZQruFgZnIWH+WyO9t5rWt4ZEGqCKPwhiw+YbzTwpmT9elgLrLcfuyUiSnwwjUiVy9r4VM3urtbNF1xmEh9IL2w=="
},
"node_modules/selderee": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz",
"integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==",
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.6.0.tgz",
"integrity": "sha512-ibqWGV5aChDvfVdqNYuaJP/HnVBhlRGSRrlbttmlMpHcLuTqqbMH36QkSs9GEgj5M88JDYLI8eyP94JaQ8xRlg==",
"dependencies": {
"parseley": "^0.12.0"
"parseley": "^0.7.0"
},
"funding": {
"url": "https://ko-fi.com/killymxi"
@@ -24214,7 +24311,7 @@
"dayjs": "^1.10.4",
"entities": "^3.0.1",
"fflate": "^0.7.3",
"html-to-text": "9.0.5",
"html-to-text": "8.1.0",
"phone": "^3.1.14",
"qclone": "^1.2.0",
"react-native-actions-sheet": "0.9.0-alpha.18",
@@ -25476,12 +25573,12 @@
"integrity": "sha512-1k57PXJIfhZrjA4ZQr+goTEYer8MBhe1At6XjVYJq1oBuhG/CIu0gX1EH9fU9exC65e5uDs/zAwjl6Ps+3lCgg=="
},
"@selderee/plugin-htmlparser2": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz",
"integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==",
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.6.0.tgz",
"integrity": "sha512-J3jpy002TyBjd4N/p6s+s90eX42H2eRhK3SbsZuvTDv977/E8p2U3zikdiehyJja66do7FlxLomZLPlvl2/xaA==",
"requires": {
"domhandler": "^5.0.3",
"selderee": "^0.11.0"
"domhandler": "^4.2.0",
"selderee": "^0.6.0"
}
},
"@shopify/flash-list": {
@@ -27423,6 +27520,41 @@
"domhandler": "^5.0.2",
"domutils": "^3.0.1",
"nth-check": "^2.0.1"
},
"dependencies": {
"dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"requires": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
"entities": "^4.2.0"
}
},
"domhandler": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"requires": {
"domelementtype": "^2.3.0"
}
},
"domutils": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
"integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
"requires": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.1"
}
},
"entities": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
"integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA=="
}
}
},
"css-tree": {
@@ -27495,9 +27627,9 @@
"dev": true
},
"deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
},
"defaults": {
"version": "1.0.4",
@@ -27691,6 +27823,11 @@
"path-type": "^4.0.0"
}
},
"discontinuous-range": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
"integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ=="
},
"doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -27701,19 +27838,19 @@
}
},
"dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
"integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
"requires": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
"entities": "^4.2.0"
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
},
"dependencies": {
"entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
}
}
},
@@ -27728,21 +27865,21 @@
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
},
"domhandler": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
"requires": {
"domelementtype": "^2.3.0"
"domelementtype": "^2.2.0"
}
},
"domutils": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
"integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"requires": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.1"
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
}
},
"dooboolab-welcome": {
@@ -29457,6 +29594,11 @@
"type-fest": "^0.8.0"
}
},
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
"hermes-engine": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/hermes-engine/-/hermes-engine-0.11.0.tgz",
@@ -29503,32 +29645,33 @@
"dev": true
},
"html-to-text": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz",
"integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-8.1.0.tgz",
"integrity": "sha512-Z9iYAqYK2c18GswSbnxJSeMs7lyJgwR2oIkDOyOHGBbYsPsG4HvT379jj3Lcbfko8A5ceyyMHAfkmp/BiXA9/Q==",
"requires": {
"@selderee/plugin-htmlparser2": "^0.11.0",
"deepmerge": "^4.3.1",
"dom-serializer": "^2.0.0",
"htmlparser2": "^8.0.2",
"selderee": "^0.11.0"
"@selderee/plugin-htmlparser2": "^0.6.0",
"deepmerge": "^4.2.2",
"he": "^1.2.0",
"htmlparser2": "^6.1.0",
"minimist": "^1.2.5",
"selderee": "^0.6.0"
}
},
"htmlparser2": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
"integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
"requires": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1",
"entities": "^4.4.0"
"domelementtype": "^2.0.1",
"domhandler": "^4.0.0",
"domutils": "^2.5.2",
"entities": "^2.0.0"
},
"dependencies": {
"entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
}
}
},
@@ -31990,11 +32133,6 @@
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="
},
"leac": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz",
"integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg=="
},
"leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -33403,6 +33541,11 @@
"dev": true,
"optional": true
},
"moo": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
"integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q=="
},
"ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -33489,6 +33632,17 @@
"dev": true,
"optional": true
},
"nearley": {
"version": "2.20.1",
"resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz",
"integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==",
"requires": {
"commander": "^2.19.0",
"moo": "^0.5.0",
"railroad-diagrams": "^1.0.0",
"randexp": "0.4.6"
}
},
"negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -33965,12 +34119,12 @@
}
},
"parseley": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.0.tgz",
"integrity": "sha512-uLqDm6IQVb6m50a3dIxF66hI8VWr7wFDYUULtHa1ITRh9mwYIXzFpPTkPM66Cm5V0t+bMyeSHgUCGzoXTV96LQ==",
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/parseley/-/parseley-0.7.0.tgz",
"integrity": "sha512-xyOytsdDu077M3/46Am+2cGXEKM9U9QclBDv7fimY7e+BBlxh2JcBp2mgNsmkyA9uvgyTjVzDi7cP1v4hcFxbw==",
"requires": {
"leac": "^0.6.0",
"peberminta": "^0.9.0"
"moo": "^0.5.1",
"nearley": "^2.20.1"
}
},
"parseurl": {
@@ -34030,11 +34184,6 @@
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true
},
"peberminta": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz",
"integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ=="
},
"phin": {
"version": "2.9.3",
"resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
@@ -34585,6 +34734,27 @@
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
},
"railroad-diagrams": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
"integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A=="
},
"randexp": {
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
"integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
"requires": {
"discontinuous-range": "1.0.0",
"ret": "~0.1.10"
},
"dependencies": {
"ret": {
"version": "0.1.15",
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
}
}
},
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -35540,11 +35710,11 @@
"integrity": "sha512-ZQruFgZnIWH+WyO9t5rWt4ZEGqCKPwhiw+YbzTwpmT9elgLrLcfuyUiSnwwjUiVy9r4VM3urtbNF1xmEh9IL2w=="
},
"selderee": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz",
"integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==",
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.6.0.tgz",
"integrity": "sha512-ibqWGV5aChDvfVdqNYuaJP/HnVBhlRGSRrlbttmlMpHcLuTqqbMH36QkSs9GEgj5M88JDYLI8eyP94JaQ8xRlg==",
"requires": {
"parseley": "^0.12.0"
"parseley": "^0.7.0"
}
},
"semver": {

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "2.4.16",
"version": "2.4.11",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [

View File

@@ -0,0 +1,59 @@
diff --git a/node_modules/html-to-text/lib/formatter.js b/node_modules/html-to-text/lib/formatter.js
index d6cb1e6..68d0337 100644
--- a/node_modules/html-to-text/lib/formatter.js
+++ b/node_modules/html-to-text/lib/formatter.js
@@ -1,4 +1,4 @@
-const he = require('he');
+const {decode} = require('entities');
const { get, numberToLetterSequence, numberToRoman, splitClassesAndIds, trimCharacter } = require('./helper');
@@ -146,7 +146,7 @@ function withBrackets (str, brackets) {
function formatImage (elem, walk, builder, formatOptions) {
const attribs = elem.attribs || {};
const alt = (attribs.alt)
- ? he.decode(attribs.alt, builder.options.decodeOptions)
+ ? decode(attribs.alt, builder.options.decodeOptions)
: '';
const src = (!attribs.src)
? ''
@@ -176,7 +176,7 @@ function formatAnchor (elem, walk, builder, formatOptions) {
href = (formatOptions.baseUrl && href[0] === '/')
? formatOptions.baseUrl + href
: href;
- return he.decode(href, builder.options.decodeOptions);
+ return decode(href, builder.options.decodeOptions);
}
const href = getHref();
if (!href) {
diff --git a/node_modules/html-to-text/lib/html-to-text.js b/node_modules/html-to-text/lib/html-to-text.js
index 9ebda73..8c5345f 100644
--- a/node_modules/html-to-text/lib/html-to-text.js
+++ b/node_modules/html-to-text/lib/html-to-text.js
@@ -1,6 +1,6 @@
const { hp2Builder } = require('@selderee/plugin-htmlparser2');
const merge = require('deepmerge');
-const he = require('he');
+const {decode, EntityLevel} = require("entities");
const htmlparser = require('htmlparser2');
const selderee = require('selderee');
@@ -27,8 +27,7 @@ const DEFAULT_OPTIONS = {
returnDomByDefault: true
},
decodeOptions: {
- isAttributeValue: false,
- strict: false
+ level:EntityLevel.HTML
},
formatters: {},
limits: {
@@ -377,7 +376,7 @@ function recursiveWalk (walk, dom, builder) {
for (const elem of dom) {
switch (elem.type) {
case 'text': {
- builder.addInline(he.decode(elem.data, options.decodeOptions));
+ builder.addInline(decode(elem.data, options.decodeOptions));
break;
}
case 'tag': {

View File

@@ -17,22 +17,23 @@ 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 { FlashList } from "@shopify/flash-list";
import React, { useEffect, useRef, useState } from "react";
import {
ActivityIndicator,
Platform,
StatusBar,
Text,
TextInput,
TouchableOpacity,
View,
useWindowDimensions
useWindowDimensions,
View
} from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../app/common/database";
import { getElevation } from "../app/utils";
import { initDatabase, useShareStore } from "./store";
import { useShareStore } from "./store";
import { FlashList } from "@shopify/flash-list";
const ListItem = ({ item, mode, close }) => {
const colors = useShareStore((state) => state.colors);
@@ -226,7 +227,8 @@ export const Search = ({ close, getKeyboardHeight, quicknote, mode }) => {
const onSearch = async () => {
if (!searchableItems.current) {
await initDatabase();
await db.init();
await db.notes.init();
searchableItems.current = get();
}
if (timer.current) {
@@ -247,7 +249,8 @@ export const Search = ({ close, getKeyboardHeight, quicknote, mode }) => {
useEffect(() => {
(async () => {
await initDatabase();
await db.init();
await db.notes.init();
searchableItems.current = get();
setSearchResults(searchableItems.current);
})();
@@ -335,13 +338,7 @@ export const Search = ({ close, getKeyboardHeight, quicknote, mode }) => {
renderItem={renderItem}
estimatedItemSize={50}
ListHeaderComponent={
mode === "selectTags" &&
(searchResults.length === 0 ||
(searchKeyword.current &&
searchKeyword.current.length > 0 &&
!searchResults.find(
(item) => item.title === searchKeyword.current
))) ? (
searchResults.length === 0 && searchKeyword.current ? (
<ListItem
item={{
type: "tag",

View File

@@ -26,12 +26,12 @@ import {
Keyboard,
Platform,
SafeAreaView,
ScrollView,
StatusBar,
Text,
TouchableOpacity,
useWindowDimensions,
View,
useWindowDimensions
ScrollView
} from "react-native";
import {
SafeAreaProvider,
@@ -45,10 +45,10 @@ import Storage from "../app/common/database/storage";
import { eSendEvent } from "../app/services/event-manager";
import { getElevation } from "../app/utils";
import { eOnLoadNote } from "../app/utils/events";
import { Editor } from "./editor";
import { sleep } from "../app/utils/time";
import { Search } from "./search";
import { initDatabase, useShareStore } from "./store";
import NetInfo from "@react-native-community/netinfo";
import { useShareStore } from "./store";
import { Editor } from "./editor";
const getLinkPreview = (url) => {
return getPreviewData(url, 5000);
};
@@ -63,15 +63,16 @@ async function sanitizeHtml(site) {
}
function makeHtmlFromUrl(url) {
return `<a href='${url}' target='_blank'>${url}</a>`;
return `<a style="overflow-wrap:anywhere;white-space:pre-wrap" href='${url}' target='_blank'>${url}</a>`;
}
function makeHtmlFromPlainText(text) {
if (!text) return "";
return `<p>${text
.replace(/[\n]+/g, "\n")
.replace(/(?:\r\n|\r|\n)/g, "</p><p>")}</p>`;
return `<p style="overflow-wrap:anywhere;white-space:pre-wrap" >${text.replace(
/(?:\r\n|\r|\n)/g,
"<br>"
)}</p>`;
}
function getBaseUrl(site) {
@@ -313,7 +314,9 @@ const ShareView = ({ quicknote = false }) => {
const onPress = async () => {
setLoading(true);
await initDatabase();
await db.init();
await db.notes.init();
await sleep(1500);
if (!noteContent.current) return;
if (appendNote && !db.notes.note(appendNote.id)) {
useShareStore.getState().setAppendNote(null);
@@ -356,14 +359,6 @@ const ShareView = ({ quicknote = false }) => {
}
}
}
const status = await NetInfo.fetch();
if (status.isInternetReachable) {
try {
await db.sync(false, false);
} catch (e) {
console.log(e, e.stack);
}
}
await Storage.write("notesAddedFromIntent", "added");
close();
setLoading(false);

View File

@@ -25,14 +25,6 @@ import {
COLOR_SCHEME_LIGHT
} from "../app/utils/color-scheme";
import { MMKV } from "../app/common/database/mmkv";
import { db } from "../app/common/database";
export async function initDatabase() {
if (!db.isInitialized) {
await db.init();
}
await db.notes.init();
}
const StorageKeys = {
appendNote: "shareMenuAppendNote",

View File

@@ -12,7 +12,7 @@
### The desktop app?
You can find all the desktop-related code in [the `desktop/` directory](./desktop/). Since it uses the web app directly, we are keeping both together. (We should probably move it to its own project at some point.)
You can find all the desktop related code in [the `desktop/` directory](./desktop/). Since it uses the web app directly, we are keeping both together. (We should probably move it to it's own project at some point.)
### Downloads & releases
@@ -21,7 +21,7 @@ You can find all the desktop-related code in [the `desktop/` directory](./deskto
## Build instructions
> **Before you start, it is recommended that you read [the contributing guidelines](/CONTRIBUTING.md).**
**Before you start it is recommended that you read [the contributing guidelines](/CONTRIBUTING.md).**
### Setting up the development environment
@@ -66,11 +66,11 @@ npx serve apps/web/build
## Developer guide
> This project is in a transition state between Javascript & Typescript. We are gradually porting everything over to Typescript, so if you can help with that, it'd be great!
> This project is in a transition state between Javascript & Typescript. We are gradually porting everything over to Typescript so if you can help with that, it'd be great!
### The tech stack
We try to keep the stack as lean as possible:
We try to keep the stack as lean as possible
1. React v17: UI framework
2. Typescript/Javascript: The logical side of the app
@@ -82,21 +82,21 @@ We try to keep the stack as lean as possible:
### Project structure
1. `src/`: 99% of the source code lives here & this is also where you'll spend most of your time.
1. `index.tsx`: **the app entry point** responsible for loading the appropriate view based on the current route.
2. `app.js`: **the default route** that contains the whole note-taking experience (notes list, navigation, editor, etc.)
3. `views/`: Contains **all the views**, including views for login, settings, notes, notebooks & topics.
4. `components/`: All the **reusable UI components** are here (e.g., button, editor, etc.)
5. `stores/`: Contains the glue code & **logic for all the UI interactions**. For example, when you pin a note, the `src/stores/note-store.js` is responsible for everything, including refreshing the list to reflect the changes.
6. `navigation/`: All the **routing & navigation** logic lives here. The app uses two kinds of routers:
1. `routes.js`: This contains all the main routes like `/notes`, `/notebooks` with information on what to render when the user goes to a particular route.
2. `hash-routes.js`: The hash routes are used for temporary navigation, like opening dialogs or opening a note. These look like `#/notes/6307bbd65d5d5d5cb86f6f74/edit`.
7. `interfaces/`: This is where the **platform-specific storage & encryption logic** lives. These interface implementations are used by the `@notesnook/core` to provide capabilities such as persistence & encryption.
8. `hooks/`: Contains all the **general-purpose React hooks**
9. `utils/`: These are **general-purpose utilities** for performing various tasks such as downloading files, storing configuration, etc.
1. `src/`: 99% of the source code lives here & this is also where you'll be spending most of your time.
1. `index.tsx`: **the app entrypoint** responsible for loading the appropriate view based on the current route.
2. `app.js`: **the default route** that contains the whole note taking experience (notes list, navigation, editor etc.)
3. `views/`: Contains **all the views** including views for login, settings, notes, notebooks & topics.
4. `components/`: All the **reusuable UI components** are here (e.g. button, editor, etc.)
5. `stores/`: Contains the glue code & **logic for all the UI interaction**. For example, when you pin a note the `src/stores/note-store.js` is responsible for everything including refreshing the list to reflect the changes.
6. `navigation/`: All the **routing & navigation** logic lives here. The app uses 2 kinds of routers:
1. `routes.js`: This contains all the main routes like `/notes`, `/notebooks` with information on what to render when user goes to a particular route.
2. `hash-routes.js`: The hash routes are used for temporary navigation like opening dialogs, opening a note. These look like `#/notes/6307bbd65d5d5d5cb86f6f74/edit`.
7. `interfaces/`: This is where the **platform specific storage & encryption logic** lives. These interface implementations are used by the `@notesnook/core` to provide capabilities such as persistence & encryption.
8. `hooks/`: Contains all the **general purpose React hooks**
9. `utils/`: These are **general-purpose utilities** for performing various tasks such as downloading files, storing configuration etc.
10. `common/`: This directory contains **the shared logic between the whole app**. For example, this is where the database is instantiated for use throughout the app.
11. `commands/`: These are **commands the desktop app uses** for things like checking for updates, storing backups etc.
2. `desktop/`: The Electron layer for **the desktop app lives here**. (This should be moved outside into its own project).
11. `commands/`: These are **commands used by the desktop app** for things like checking for updates, storing backups etc.
2. `desktop/`: The Electron layer for **the desktop app lives here**. (This should be moved outside into it's own project).
### Running the tests

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/desktop",
"version": "2.4.10",
"version": "2.4.7",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/desktop",
"version": "2.4.10",
"version": "2.4.7",
"dependencies": {
"diary": "^0.3.1",
"electron-updater": "^5.3.0",

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "2.4.10",
"version": "2.4.7",
"appAppleId": "1544027013",
"private": true,
"main": "./build/electron.js",

View File

@@ -22,8 +22,8 @@ import { isDevelopment, getPath } from "./utils";
import { createReadStream } from "fs";
import { extname, normalize } from "path";
import { logger } from "./logger";
import { URL } from "url";
import { Blob } from "buffer";
import { URL } from "url";
const FILE_NOT_FOUND = -6;
const BASE_PATH = isDevelopment() ? "../public" : "";
@@ -75,10 +75,9 @@ function registerProtocol() {
...request,
body,
headers: {
...request.headers
// origin: `${PROTOCOL}://${HOSTNAME}/`
...request.headers,
origin: `${PROTOCOL}://${HOSTNAME}/`
},
referrer: request.referrer,
redirect: "manual"
});
} catch (e) {
@@ -104,7 +103,7 @@ function registerProtocol() {
);
}
const bypassedRoutes = [];
const bypassedRoutes = ["/notes/index_v14.json", "/notes/welcome-web"];
function shouldInterceptRequest(url) {
let shouldIntercept = url.hostname === HOSTNAME;
return shouldIntercept && !bypassedRoutes.includes(url.pathname);
@@ -118,14 +117,14 @@ async function getBody(request) {
/**
* @type {Electron.Session}
*/
const session = globalThis?.window?.webContents?.session;
const session = globalThis.window.webContents.session;
const blobParts = [];
if (!request.uploadData || !request.uploadData.length) return null;
for (let data of request.uploadData) {
if (data.type === "rawData") {
blobParts.push(new Uint8Array(data.bytes));
} else if (session && data.type === "blob") {
} else if (data.type === "blob") {
const buffer = await session.getBlobData(data.blobUUID);
blobParts.push(new Uint8Array(buffer));
}

View File

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

View File

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

View File

@@ -138,6 +138,11 @@
overflow: hidden;
}
* {
font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI",
Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
@keyframes fadeUp {
0% {
transform: translateY(500px);

View File

@@ -45,8 +45,11 @@ import { isTesting } from "./utils/platform";
import { updateStatus, removeStatus, getStatus } from "./hooks/use-status";
import { showToast } from "./utils/toast";
import { interruptedOnboarding } from "./components/dialogs/onboarding-dialog";
import { WebExtensionRelay } from "./utils/web-extension-relay";
import { hashNavigate } from "./navigation";
const relay = new WebExtensionRelay();
export default function AppEffects({ setShow }) {
const refreshNavItems = useStore((store) => store.refreshNavItems);
const updateLastSynced = useStore((store) => store.updateLastSynced);
@@ -100,6 +103,7 @@ export default function AppEffects({ setShow }) {
await showOnboardingDialog(interruptedOnboarding());
await showFeatureDialog("highlights");
await scheduleBackups();
relay.connect();
})();
return () => {

View File

@@ -35,9 +35,6 @@ import StatusBar from "./components/status-bar";
import { EditorLoader } from "./components/loaders/editor-loader";
import { FlexScrollContainer } from "./components/scroll-container";
import CachedRouter from "./components/cached-router";
import { WebExtensionRelay } from "./utils/web-extension-relay";
new WebExtensionRelay();
const GlobalMenuWrapper = React.lazy(() =>
import("./components/global-menu-wrapper")

View File

@@ -94,9 +94,7 @@ export function showAddNotebookDialog() {
isOpen={true}
onDone={async (nb: Record<string, unknown>) => {
// add the notebook to db
const notebook = await db.notebooks?.add({ ...nb });
if (!notebook) return perform(false);
await db.notebooks?.add({ ...nb });
notebookStore.refresh();
showToast("success", "Notebook added successfully!");
@@ -332,12 +330,6 @@ export function showMoveNoteDialog(noteIds: string[]) {
));
}
export function showBillingHistoryDialog() {
return showDialog("BillingHistoryDialog", (Dialog, perform) => (
<Dialog onClose={(res: boolean) => perform(res)} />
));
}
function getDialogData(type: string) {
switch (type) {
case "create_vault":

View File

@@ -1,145 +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 { useEffect, useState } from "react";
import { Perform } from "../../common/dialog-controller";
import Dialog from "./dialog";
import { db } from "../../common/db";
import { Loading } from "../icons";
import { Flex, Link, Text } from "@theme-ui/components";
import { formatDate } from "@notesnook/core/utils/date";
type Transaction = {
order_id: string;
checkout_id: string;
amount: string;
currency: string;
status: keyof typeof TransactionStatusToText;
created_at: Date;
passthrough: null;
product_id: number;
is_subscription: boolean;
is_one_off: boolean;
subscription: Subscription;
user: User;
receipt_url: string;
};
type Subscription = {
subscription_id: number;
status: string;
};
type User = {
user_id: number;
email: string;
marketing_consent: boolean;
};
const TransactionStatusToText = {
completed: "Completed",
refunded: "Refunded",
partially_refunded: "Partially refunded",
disputed: "Disputed"
};
export type BillingHistoryDialogProps = {
onClose: Perform;
};
export default function BillingHistoryDialog(props: BillingHistoryDialogProps) {
const [transactions, setTransactions] = useState<Transaction[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | undefined>();
useEffect(() => {
(async function () {
try {
setError(undefined);
setIsLoading(true);
const transactions = await db.subscriptions?.transactions();
if (!transactions) return;
setTransactions(transactions);
} catch (e) {
if (e instanceof Error) setError(e);
} finally {
setIsLoading(false);
}
})();
}, []);
return (
<Dialog
isOpen={true}
title={"Billing history"}
description={"View all the transactions you have made with Notesnook."}
onClose={() => props.onClose(false)}
negativeButton={{ text: "Close", onClick: () => props.onClose(false) }}
width={600}
>
{isLoading ? (
<Loading sx={{ mt: 2 }} />
) : error ? (
<Flex sx={{ bg: "errorBg", p: 1, borderRadius: "default" }}>
<Text variant="error">
{error.message}
<br />
{error.stack}
</Text>
</Flex>
) : (
<Flex sx={{ flexDirection: "column", gap: 1, mt: 1 }}>
{transactions.map((transaction) => (
<Flex
key={transaction.order_id}
sx={{
justifyContent: "space-between",
borderBottom: "1px solid var(--border)",
pb: 1
}}
>
<Flex sx={{ flexDirection: "column" }}>
<Text variant="subtitle">Order #{transaction.order_id}</Text>
<Text variant="body" sx={{ color: "fontTertiary" }}>
{formatDate(new Date(transaction.created_at).getTime())} {" "}
{TransactionStatusToText[transaction.status]}
</Text>
</Flex>
<Flex sx={{ flexDirection: "column", alignItems: "end" }}>
<Text variant="body">
{transaction.amount} {transaction.currency}
</Text>
<Link
href={transaction.receipt_url}
target="_blank"
rel="noreferer nofollow"
variant="text.subBody"
sx={{ color: "primary" }}
>
View receipt
</Link>
</Flex>
</Flex>
))}
</Flex>
)}
</Dialog>
);
}

View File

@@ -41,8 +41,6 @@ import { useCheckoutStore } from "./store";
import { getCurrencySymbol } from "./helpers";
import { Theme } from "@notesnook/theme";
import { isMacStoreApp } from "../../../utils/platform";
import { isUserSubscribed } from "../../../hooks/use-is-user-premium";
import { SUBSCRIPTION_STATUS } from "../../../common/constants";
type BuyDialogProps = {
couponCode?: string;
@@ -54,9 +52,7 @@ export function BuyDialog(props: BuyDialogProps) {
const { onClose, couponCode, plan } = props;
const theme = useTheme() as Theme;
const onApplyCoupon = useCheckoutStore((store) => store.applyCoupon);
const isCheckoutCompleted = useCheckoutStore((store) => store.isCompleted);
const onApplyCoupon = useCheckoutStore((store) => store.onApplyCoupon);
useEffect(() => {
return () => {
useCheckoutStore.getState().reset();
@@ -120,7 +116,7 @@ export function BuyDialog(props: BuyDialogProps) {
boxShadow: "4px 5px 18px 2px #00000038",
borderRadius: "dialog",
flexDirection: ["column", "column", "row"],
width: ["95%", "80%", isCheckoutCompleted ? "400px" : "60%"],
width: ["95%", "80%", "60%"],
maxHeight: ["95%", "80%", "80%"],
alignSelf: "center",
overflowY: ["scroll", "scroll", "hidden"]
@@ -141,7 +137,7 @@ export function BuyDialog(props: BuyDialogProps) {
flexShrink: 0,
alignItems: "center",
justifyContent: "center",
width: ["100%", "100%", isCheckoutCompleted ? "100%" : 350]
width: ["100%", "100%", 350]
}}
p={4}
py={50}
@@ -160,16 +156,13 @@ type SideBarProps = {
};
function SideBar(props: SideBarProps) {
const { initialPlan, onClose } = props;
const [showPlans, setShowPlans] = useState(false);
const onPlanSelected = useCheckoutStore((state) => state.selectPlan);
const [showPlans, setShowPlans] = useState(!!initialPlan);
const onPlanSelected = useCheckoutStore((state) => state.onPlanSelected);
const selectedPlan = useCheckoutStore((state) => state.selectedPlan);
const pricingInfo = useCheckoutStore((state) => state.pricingInfo);
const user = useUserStore((store) => store.user);
const couponCode = useCheckoutStore((store) => store.couponCode);
const onApplyCoupon = useCheckoutStore((store) => store.applyCoupon);
const isCheckoutCompleted = useCheckoutStore((store) => store.isCompleted);
if (isCheckoutCompleted) return <CheckoutCompleted onClose={onClose} />;
const onApplyCoupon = useCheckoutStore((store) => store.onApplyCoupon);
if (user && selectedPlan)
return (
@@ -183,13 +176,7 @@ function SideBar(props: SideBarProps) {
/>
);
if (user && !showPlans && isUserSubscribed(user)) {
return (
<AlreadyPremium user={user} onShowPlans={() => setShowPlans(true)} />
);
}
if (user && (showPlans || !!initialPlan))
if (user && showPlans)
return (
<PlansList
onPlansLoaded={(plans) => {
@@ -234,17 +221,13 @@ function SideBar(props: SideBarProps) {
function Details() {
const user = useUserStore((store) => store.user);
const selectedPlan = useCheckoutStore((state) => state.selectedPlan);
const onPriceUpdated = useCheckoutStore((state) => state.updatePrice);
const completeCheckout = useCheckoutStore((state) => state.completeCheckout);
const isCheckoutCompleted = useCheckoutStore((store) => store.isCompleted);
const onPriceUpdated = useCheckoutStore((state) => state.onPriceUpdated);
const couponCode = useCheckoutStore((store) => store.couponCode);
const setIsApplyingCoupon = useCheckoutStore(
(store) => store.setIsApplyingCoupon
);
const theme = useThemeStore((store) => store.theme);
if (isCheckoutCompleted) return null;
if (selectedPlan && user)
return (
<PaddleCheckout
@@ -252,7 +235,6 @@ function Details() {
theme={theme}
user={user}
coupon={couponCode}
onCompleted={completeCheckout}
onCouponApplied={() => setIsApplyingCoupon(true)}
onPriceUpdated={(pricingInfo) => {
onPriceUpdated(pricingInfo);
@@ -365,72 +347,6 @@ function TrialOrUpgrade(props: TrialOrUpgradeProps) {
);
}
type AlreadyPremiumProps = {
user: User | undefined;
onShowPlans: () => void;
};
function AlreadyPremium(props: AlreadyPremiumProps) {
const { user, onShowPlans } = props;
const isCanceled =
user?.subscription?.type === SUBSCRIPTION_STATUS.PREMIUM_CANCELED;
return (
<>
<Rocket width={200} />
<Text variant="heading" mt={4} sx={{ textAlign: "center" }}>
Notesnook Pro
</Text>
{isCanceled ? (
<>
<Text variant="body" mt={1} sx={{ textAlign: "center" }}>
Resubscribing to Notesnook Pro will replace your existing
subscription.
</Text>
<Button
variant="primary"
mt={2}
sx={{ borderRadius: 100, px: 6 }}
onClick={onShowPlans}
data-test-id="see-all-plans"
>
Continue
</Button>
</>
) : (
<Text variant="body" mt={1} sx={{ textAlign: "center" }}>
You are already subscribed to Notesnook Pro.
</Text>
)}
</>
);
}
function CheckoutCompleted(props: { onClose: () => void }) {
const { onClose } = props;
return (
<>
<Rocket width={200} />
<Text variant="heading" mt={4} sx={{ textAlign: "center" }}>
Thank you!
</Text>
<Text variant="body" mt={1} sx={{ textAlign: "center" }}>
You have successfully subscribed to Notesnook Pro.
</Text>
<Button
variant="primary"
mt={2}
sx={{ borderRadius: 100, px: 6 }}
onClick={onClose}
data-test-id="see-all-plans"
>
Continue
</Button>
</>
);
}
type SelectedPlanProps = {
plan: Plan;
pricingInfo: PricingInfo | undefined;
@@ -444,7 +360,7 @@ function SelectedPlan(props: SelectedPlanProps) {
store.setIsApplyingCoupon
]);
const onApplyCoupon = useCheckoutStore((store) => store.applyCoupon);
const onApplyCoupon = useCheckoutStore((store) => store.onApplyCoupon);
const couponInputRef = useRef<HTMLInputElement>(null);
const applyCoupon = useCallback(() => {

View File

@@ -33,25 +33,13 @@ import {
// const isDev = false; // process.env.NODE_ENV === "development";
// const VENDOR_ID = isDev ? 1506 : 128190;
const PADDLE_ORIGIN =
process.env.NODE_ENV === "development"
? "https://sandbox-buy.paddle.com"
: "https://buy.paddle.com";
const CHECKOUT_CREATE_ORIGIN =
process.env.NODE_ENV === "development"
? "https://sandbox-create-checkout.paddle.com"
: "https://create-checkout.paddle.com";
const CHECKOUT_SERVICE_ORIGIN =
process.env.NODE_ENV === "development"
? "https://sandbox-checkout-service.paddle.com"
: "https://checkout-service.paddle.com";
const PADDLE_ORIGIN = "https://buy.paddle.com";
const SUBSCRIBED_EVENTS: PaddleEvents[] = [
PaddleEvents["Checkout.Loaded"],
PaddleEvents["Checkout.Coupon.Applied"],
PaddleEvents["Checkout.Coupon.Remove"],
PaddleEvents["Checkout.Location.Submit"],
PaddleEvents["Checkout.Complete"]
PaddleEvents["Checkout.Location.Submit"]
];
type PaddleCheckoutProps = {
@@ -60,11 +48,10 @@ type PaddleCheckoutProps = {
plan: Plan;
onPriceUpdated?: (pricingInfo: PricingInfo) => void;
onCouponApplied?: () => void;
onCompleted?: () => void;
coupon?: string;
};
export function PaddleCheckout(props: PaddleCheckoutProps) {
const { plan, onPriceUpdated, coupon, onCouponApplied, onCompleted } = props;
const { plan, onPriceUpdated, coupon, onCouponApplied } = props;
const [sourceUrl, setSourceUrl] = useState<string>();
const [isLoading, setIsLoading] = useState(true);
const [checkoutId, setCheckoutId] = useState<string>();
@@ -74,7 +61,7 @@ export function PaddleCheckout(props: PaddleCheckoutProps) {
const reloadCheckout = useCallback(() => {
if (!checkoutRef.current) return;
setIsLoading(true);
checkoutRef.current.src = `${PADDLE_ORIGIN}/checkout/?checkout_id=${checkoutId}&display_mode=inline&apple_pay_enabled=false`;
checkoutRef.current.src = `https://buy.paddle.com/checkout/?checkout_id=${checkoutId}&display_mode=inline&apple_pay_enabled=false`;
}, [checkoutId]);
const updatePrice = useCallback(
@@ -109,10 +96,6 @@ export function PaddleCheckout(props: PaddleCheckoutProps) {
)
return;
if (event_name === PaddleEvents["Checkout.Complete"]) {
onCompleted && onCompleted();
return;
}
if (event_name === PaddleEvents["Checkout.Loaded"]) setIsLoading(false);
const pricingInfo = await updatePrice(checkout.id);
@@ -125,7 +108,7 @@ export function PaddleCheckout(props: PaddleCheckoutProps) {
return () => {
window.removeEventListener("message", onMessage);
};
}, [onPriceUpdated, updatePrice, plan, onCompleted]);
}, [onPriceUpdated, updatePrice, plan]);
useEffect(() => {
if (
@@ -190,7 +173,7 @@ export function PaddleCheckout(props: PaddleCheckoutProps) {
async function getCheckoutURL(params: PaddleCheckoutProps) {
const { plan, theme, user } = params;
const BASE_URL = `${CHECKOUT_CREATE_ORIGIN}/checkout/product/${plan.id}`;
const BASE_URL = `https://create-checkout.paddle.com/checkout/product/${plan.id}`;
const queryParams = new URLSearchParams();
queryParams.set("product", plan.id);
queryParams.set("passthrough", JSON.stringify({ userId: user.id }));
@@ -244,7 +227,7 @@ async function applyCoupon(
checkoutId: string,
couponCode: string
): Promise<CheckoutData | false> {
const url = ` ${CHECKOUT_SERVICE_ORIGIN}/checkout/${checkoutId}/coupon`;
const url = ` https://checkout-service.paddle.com/checkout/${checkoutId}/coupon`;
const body = { data: { coupon_code: couponCode } };
const headers = new Headers();
headers.set("content-type", "application/json");
@@ -263,7 +246,7 @@ async function applyCoupon(
}
async function removeCoupon(checkoutId: string): Promise<CheckoutData | false> {
const url = ` ${CHECKOUT_SERVICE_ORIGIN}/checkout/${checkoutId}/coupon`;
const url = ` https://checkout-service.paddle.com/checkout/${checkoutId}/coupon`;
const response = await fetch(url, {
method: "DELETE"
@@ -277,7 +260,7 @@ async function removeCoupon(checkoutId: string): Promise<CheckoutData | false> {
}
async function sendCheckoutEvent(checkoutId: string, eventName: PaddleEvents) {
const url = ` ${CHECKOUT_SERVICE_ORIGIN}/checkout/${checkoutId}/event`;
const url = ` https://checkout-service.paddle.com/checkout/${checkoutId}/event`;
const body = { data: { event_name: eventName } };
const headers = new Headers();
headers.set("content-type", "application/json");
@@ -292,7 +275,7 @@ async function sendCheckoutEvent(checkoutId: string, eventName: PaddleEvents) {
async function getCheckoutData(
checkoutId: string
): Promise<CheckoutData | undefined> {
const url = `${CHECKOUT_SERVICE_ORIGIN}/checkout/${checkoutId}`;
const url = `https://checkout-service.paddle.com/checkout/${checkoutId}`;
const response = await fetch(url);
if (!response.ok) return undefined;
const json = (await response.json()) as CheckoutDataResponse;

View File

@@ -32,7 +32,7 @@ export const DEFAULT_PLANS: Plan[] = [
country: "PK",
currency: "USD",
discount: 0,
id: process.env.NODE_ENV === "development" ? "9822" : "648884",
id: "648884",
price: { gross: 4.49, net: 0, tax: 0 }
},
{
@@ -40,7 +40,7 @@ export const DEFAULT_PLANS: Plan[] = [
country: "PK",
currency: "USD",
discount: 0,
id: process.env.NODE_ENV === "development" ? "50305" : "658759",
id: "658759",
price: { gross: 49.99, net: 0, tax: 0 }
}
];
@@ -52,8 +52,7 @@ export const PLAN_METADATA: Record<Period, PlanMetadata> = {
let CACHED_PLANS: Plan[];
export async function getPlans(): Promise<Plan[] | null> {
if (isTesting() || process.env.NODE_ENV === "development")
return DEFAULT_PLANS;
if (isTesting()) return DEFAULT_PLANS;
if (CACHED_PLANS) return CACHED_PLANS;
const url = `https://notesnook.com/api/v1/prices/products/web`;

View File

@@ -22,44 +22,35 @@ import create from "zustand";
import produce from "immer";
interface ICheckoutStore {
isCompleted: boolean;
completeCheckout: () => void;
selectedPlan?: Plan;
selectPlan: (plan?: Plan) => void;
onPlanSelected: (plan?: Plan) => void;
pricingInfo?: PricingInfo;
updatePrice: (pricingInfo?: PricingInfo) => void;
onPriceUpdated: (pricingInfo?: PricingInfo) => void;
isApplyingCoupon: boolean;
setIsApplyingCoupon: (isApplyingCoupon: boolean) => void;
couponCode?: string;
applyCoupon: (couponCode?: string) => void;
onApplyCoupon: (couponCode?: string) => void;
reset: () => void;
}
export const useCheckoutStore = create<ICheckoutStore>((set) => ({
isCompleted: false,
selectedPlan: undefined,
pricingInfo: undefined,
couponCode: undefined,
isApplyingCoupon: false,
completeCheckout: () =>
set(
produce((state: ICheckoutStore) => {
state.isCompleted = true;
})
),
selectPlan: (plan) =>
onPlanSelected: (plan) =>
set(
produce((state: ICheckoutStore) => {
state.selectedPlan = plan;
state.pricingInfo = undefined;
})
),
updatePrice: (pricingInfo) =>
onPriceUpdated: (pricingInfo) =>
set(
produce((state: ICheckoutStore) => {
state.pricingInfo = pricingInfo;
})
),
applyCoupon: (couponCode) =>
onApplyCoupon: (couponCode) =>
set(
produce((state: ICheckoutStore) => {
state.couponCode = couponCode;

View File

@@ -57,9 +57,6 @@ const EmailChangeDialog = React.lazy(() => import("./email-change-dialog"));
const LanguageSelectorDialog = React.lazy(
() => import("./language-selector-dialog")
);
const BillingHistoryDialog = React.lazy(
() => import("./billing-history-dialog")
);
export const Dialogs = {
AddNotebookDialog,
@@ -88,6 +85,5 @@ export const Dialogs = {
AddReminderDialog,
ReminderPreviewDialog,
EmailChangeDialog,
LanguageSelectorDialog,
BillingHistoryDialog
LanguageSelectorDialog
};

View File

@@ -21,19 +21,14 @@ import { Button, Flex } from "@theme-ui/components";
import { useMenuTrigger } from "../../hooks/use-menu";
import { ChevronDown } from "../icons";
export default function DropdownButton(props) {
export default function DropdownButton({ title, options }) {
const { openMenu } = useMenuTrigger();
const { options, title, sx, buttonStyle, chevronStyle } = props;
if (!options || !options.length) return null;
return (
<Flex sx={sx}>
<Flex>
<Button
sx={{
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
...buttonStyle
}}
sx={{ borderTopRightRadius: 0, borderBottomRightRadius: 0 }}
onClick={options[0].onClick}
>
{options[0].title()}
@@ -43,8 +38,7 @@ export default function DropdownButton(props) {
px={1}
sx={{
borderBottomLeftRadius: 0,
borderTopLeftRadius: 0,
...chevronStyle
borderTopLeftRadius: 0
}}
onClick={() => openMenu(options.slice(1), { title })}
>

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