mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-31 11:08:47 +02:00
Compare commits
1 Commits
fix-color-
...
vscode-ext
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
370cd08b16 |
@@ -21,6 +21,7 @@ const SCOPES = [
|
||||
"theme",
|
||||
"core",
|
||||
"clipper",
|
||||
"vscode",
|
||||
"config",
|
||||
"ci",
|
||||
"setup",
|
||||
|
||||
@@ -21,7 +21,4 @@ packages/editor/styles/
|
||||
packages/editor/languages/
|
||||
|
||||
# editor mobile
|
||||
packages/editor-mobile/build.bundle
|
||||
|
||||
# snapshots
|
||||
tap-snapshots
|
||||
packages/editor-mobile/build.bundle
|
||||
4
.github/dco.yml
vendored
4
.github/dco.yml
vendored
@@ -1,4 +0,0 @@
|
||||
allowRemediationCommits:
|
||||
individual: true
|
||||
require:
|
||||
members: false
|
||||
2
.github/workflows/android.publish.yml
vendored
2
.github/workflows/android.publish.yml
vendored
@@ -95,7 +95,7 @@ jobs:
|
||||
|
||||
- name: Publish to Playstore
|
||||
id: deploy
|
||||
uses: r0adkll/upload-google-play@v1.1.1
|
||||
uses: r0adkll/upload-google-play@v1
|
||||
with:
|
||||
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
|
||||
packageName: com.streetwriters.notesnook
|
||||
|
||||
54
.github/workflows/editor.tests.yml
vendored
54
.github/workflows/editor.tests.yml
vendored
@@ -1,54 +0,0 @@
|
||||
name: Test @notesnook/editor
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
paths:
|
||||
- "packages/editor/**"
|
||||
# re-run workflow if workflow file changes
|
||||
- ".github/workflows/editor.tests.yml"
|
||||
pull_request:
|
||||
types:
|
||||
- "ready_for_review"
|
||||
- "opened"
|
||||
- "synchronize"
|
||||
- "reopened"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
cache: "npm"
|
||||
cache-dependency-path: |
|
||||
apps/mobile/package-lock.json
|
||||
apps/web/package-lock.json
|
||||
packages/core/package-lock.json
|
||||
packages/crypto/package-lock.json
|
||||
packages/crypto-worker/package-lock.json
|
||||
packages/editor-mobile/package-lock.json
|
||||
packages/editor/package-lock.json
|
||||
packages/logger/package-lock.json
|
||||
packages/streamable-fs/package-lock.json
|
||||
packages/theme/package-lock.json
|
||||
|
||||
- name: Install packages
|
||||
run: |
|
||||
npm i --ignore-scripts --prefer-offline --no-audit
|
||||
npx lerna bootstrap --ignore=@notesnook/mobile -- --prefer-offline --no-audit
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Run all @notesnook/editor tests
|
||||
run: npx nx test @notesnook/editor
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -6,5 +6,4 @@ dist
|
||||
nx-cloud.env
|
||||
.idea
|
||||
.eslintcache
|
||||
.env.local
|
||||
.nyc_output
|
||||
.env.local
|
||||
10
.vscode/extensions.json
vendored
10
.vscode/extensions.json
vendored
@@ -1,11 +1,5 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"ms-playwright.playwright",
|
||||
"firefox-devtools.vscode-firefox-debug",
|
||||
"msjsdiag.vscode-react-native"
|
||||
]
|
||||
"recommendations": ["dbaeumer.vscode-eslint", "amodio.tsl-problem-matcher"]
|
||||
}
|
||||
|
||||
33
.vscode/launch.json
vendored
Normal file
33
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/extensions/vs-code"
|
||||
],
|
||||
"outFiles": ["${workspaceFolder}/extensions/vs-code/dist/**/*.js"],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
},
|
||||
{
|
||||
"name": "Extension Tests",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}/extensions/vs-code",
|
||||
"--extensionTestsPath=${workspaceFolder}/extensions/vs-code/out/test/suite/index"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/extensions/vs-code/out/**/*.js",
|
||||
"${workspaceFolder}/extensions/vs-code/dist/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "tasks: watch-tests"
|
||||
}
|
||||
]
|
||||
}
|
||||
72
.vscode/notesnook.code-workspace
vendored
72
.vscode/notesnook.code-workspace
vendored
@@ -1,72 +0,0 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"name": "✨ notesnook",
|
||||
"path": ".."
|
||||
},
|
||||
{
|
||||
"name": "🚀 @notesnook/mobile",
|
||||
"path": "../apps/mobile"
|
||||
},
|
||||
{
|
||||
"name": "🚀 @notesnook/web",
|
||||
"path": "../apps/web"
|
||||
},
|
||||
{
|
||||
"name": "🚀 @notesnook/desktop",
|
||||
"path": "../apps/web/desktop"
|
||||
},
|
||||
{
|
||||
"name": "🚀 @notesnook/web-clipper",
|
||||
"path": "../extensions/web-clipper"
|
||||
},
|
||||
{
|
||||
"name": "📦 @notesnook/clipper",
|
||||
"path": "../packages/clipper"
|
||||
},
|
||||
{
|
||||
"name": "📦 @notesnook/core",
|
||||
"path": "../packages/core"
|
||||
},
|
||||
{
|
||||
"name": "📦 @notesnook/crypto",
|
||||
"path": "../packages/crypto"
|
||||
},
|
||||
{
|
||||
"name": "📦 @notesnook/crypto-worker",
|
||||
"path": "../packages/crypto-worker"
|
||||
},
|
||||
{
|
||||
"name": "📦 @notesnook/editor",
|
||||
"path": "../packages/editor"
|
||||
},
|
||||
{
|
||||
"name": "📦 @notesnook/editor-mobile",
|
||||
"path": "../packages/editor-mobile"
|
||||
},
|
||||
{
|
||||
"name": "📦 @notesnook/logger",
|
||||
"path": "../packages/logger"
|
||||
},
|
||||
{
|
||||
"name": "📦 @notesnook/streamable-fs",
|
||||
"path": "../packages/streamable-fs"
|
||||
},
|
||||
{
|
||||
"name": "📦 @notesnook/theme",
|
||||
"path": "../packages/theme"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"javascript.format.enable": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnSaveMode": "modificationsIfAvailable",
|
||||
"eslint.packageManager": "npm",
|
||||
"eslint.run": "onSave",
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"eslint.execArgv": ["--cache"]
|
||||
}
|
||||
}
|
||||
13
.vscode/settings.json
vendored
Normal file
13
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"files.exclude": {
|
||||
"out": false, // set this to true to hide the "out" folder with the compiled JS files
|
||||
"dist": false // set this to true to hide the "dist" folder with the compiled JS files
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true, // set this to false to include "out" folder in search results
|
||||
"dist": true // set this to false to include "dist" folder in search results
|
||||
},
|
||||
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
||||
"typescript.tsc.autoDetect": "off"
|
||||
}
|
||||
39
.vscode/tasks.json
vendored
Normal file
39
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"path": "extensions/vs-code",
|
||||
"problemMatcher": "$ts-webpack-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never",
|
||||
"group": "watchers"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch-tests",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"path": "extensions/vs-code",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never",
|
||||
"group": "watchers"
|
||||
},
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "tasks: watch-tests",
|
||||
"dependsOn": ["npm: watch", "npm: watch-tests"],
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -28,6 +28,12 @@ If you've thought of a way that Notesnook could be better, we want to hear about
|
||||
|
||||
Before you open a new feature request, please make sure it's not a duplicate. **Duplicate feature requests & bug reports are closed immediately.**
|
||||
|
||||
### Improving documentation
|
||||
|
||||
All app related documentation can be found in the `docs/` directory. All the files in this directory are simple markdown files which you can edit either directly on GitHub, or in your favorite text editor.
|
||||
|
||||
If you find a mistake in the documentation or would like to contribute something new to the documentation, feel free to open a pull request; creating an issue for this is not required.
|
||||
|
||||
### Helping out in the issue tracker
|
||||
|
||||
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.
|
||||
@@ -85,11 +91,7 @@ The DCO is a declaration attached to every contribution made by every developer
|
||||
Signed-off-by: Jane Smith <jane.smith@email.com>
|
||||
```
|
||||
|
||||
On VSCode, it looks like this:
|
||||
|
||||

|
||||
|
||||
You may type this line on your own when writing your commit messages. However, if your [user.name](http://user.name) and [user.email](http://user.email) are set in your git configs, you can use `-s` or `--signoff` to add the `Signed-off-by` line to the end of the commit message automatically.
|
||||
You may type this line on your own when writing your commit messages. However, if your [user.name](http://user.name) and [user.email](http://user.email) are set in your git configs, you can use `-s` or `--signoff` to add the `Signed-off-by` line to the end of the commit message.
|
||||
|
||||
#### Include `<scope>:` in your commit message
|
||||
|
||||
|
||||
2
apps/mobile/.vscode/launch.json
vendored
2
apps/mobile/.vscode/launch.json
vendored
@@ -25,4 +25,4 @@
|
||||
"request": "attach"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
1
apps/mobile/.vscode/settings.json
vendored
Normal file
1
apps/mobile/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -16,7 +16,7 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import "react-native-gesture-handler";
|
||||
|
||||
import React, { useEffect } from "react";
|
||||
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
||||
import { SafeAreaProvider } from "react-native-safe-area-context";
|
||||
@@ -32,6 +32,8 @@ import { useUserStore } from "./stores/use-user-store";
|
||||
import { View } from "react-native";
|
||||
import { useState } from "react";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
//import { BackgroundSync } from "./services/background-sync";
|
||||
|
||||
NetInfo.configure({
|
||||
reachabilityUrl: "https://notesnook.com",
|
||||
reachabilityTest: (response) => {
|
||||
|
||||
@@ -26,6 +26,7 @@ import { eSendEvent } from "../../services/event-manager";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import BaseDialog from "../dialog/base-dialog";
|
||||
import SheetProvider from "../sheet-provider";
|
||||
import { Progress } from "../sheets/progress";
|
||||
import { Button } from "../ui/button";
|
||||
@@ -82,6 +83,9 @@ export const Login = ({ changeMode }) => {
|
||||
<>
|
||||
<ForgotPassword />
|
||||
<SheetProvider context="two_factor_verify" />
|
||||
{loading ? (
|
||||
<BaseDialog transparent={true} visible={true} animation="fade" />
|
||||
) : null}
|
||||
<Animated.View
|
||||
entering={FadeInDown}
|
||||
exiting={FadeOutUp}
|
||||
@@ -130,7 +134,6 @@ export const Login = ({ changeMode }) => {
|
||||
marginTop: 5
|
||||
}}
|
||||
onPress={() => {
|
||||
if (loading) return;
|
||||
changeMode(1);
|
||||
}}
|
||||
size={SIZE.md}
|
||||
@@ -169,7 +172,7 @@ export const Login = ({ changeMode }) => {
|
||||
errorMessage="Email is invalid"
|
||||
placeholder="Enter your email"
|
||||
defaultValue={email.current}
|
||||
editable={step === LoginSteps.emailAuth && !loading}
|
||||
editable={step === LoginSteps.emailAuth}
|
||||
onSubmit={() => {
|
||||
passwordInputRef.current?.focus();
|
||||
}}
|
||||
@@ -191,7 +194,6 @@ export const Login = ({ changeMode }) => {
|
||||
autoCorrect={false}
|
||||
placeholder="Password"
|
||||
marginBottom={0}
|
||||
editable={!loading}
|
||||
defaultValue={password.current}
|
||||
onSubmit={() => login()}
|
||||
/>
|
||||
@@ -203,7 +205,6 @@ export const Login = ({ changeMode }) => {
|
||||
paddingHorizontal: 0
|
||||
}}
|
||||
onPress={() => {
|
||||
if (loading) return;
|
||||
SheetManager.show("forgotpassword_sheet", email.current);
|
||||
}}
|
||||
textStyle={{
|
||||
@@ -227,10 +228,7 @@ export const Login = ({ changeMode }) => {
|
||||
borderRadius: 100
|
||||
}}
|
||||
loading={loading}
|
||||
onPress={() => {
|
||||
if (loading) return;
|
||||
login();
|
||||
}}
|
||||
onPress={login}
|
||||
type="accent"
|
||||
title={
|
||||
loading
|
||||
@@ -250,7 +248,6 @@ export const Login = ({ changeMode }) => {
|
||||
marginTop: 10
|
||||
}}
|
||||
onPress={() => {
|
||||
if (loading) return;
|
||||
setStep(LoginSteps.emailAuth);
|
||||
setLoading(false);
|
||||
}}
|
||||
|
||||
@@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React, { useRef, useState } from "react";
|
||||
import { Dimensions, View } from "react-native";
|
||||
import Animated, { FadeInDown, FadeOutUp } from "react-native-reanimated";
|
||||
import umami from "../../common/analytics";
|
||||
import { db } from "../../common/database";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import { ToastEvent } from "../../services/event-manager";
|
||||
@@ -30,6 +31,7 @@ import { useUserStore } from "../../stores/use-user-store";
|
||||
import { openLinkInBrowser } from "../../utils/functions";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import BaseDialog from "../dialog/base-dialog";
|
||||
import { Button } from "../ui/button";
|
||||
import Input from "../ui/input";
|
||||
import { SvgView } from "../ui/svg";
|
||||
@@ -77,6 +79,7 @@ export const Signup = ({ changeMode, trial }) => {
|
||||
clearMessage();
|
||||
setEmailVerifyMessage();
|
||||
hideAuth();
|
||||
umami.pageView("/account-created", "/welcome/signup");
|
||||
await sleep(300);
|
||||
if (trial) {
|
||||
PremiumService.sheet(null, null, true);
|
||||
@@ -96,6 +99,9 @@ export const Signup = ({ changeMode, trial }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading ? (
|
||||
<BaseDialog transparent={true} visible={true} animation="fade" />
|
||||
) : null}
|
||||
<Animated.View
|
||||
entering={FadeInDown}
|
||||
exiting={FadeOutUp}
|
||||
@@ -144,7 +150,6 @@ export const Signup = ({ changeMode, trial }) => {
|
||||
textAlign: "center"
|
||||
}}
|
||||
onPress={() => {
|
||||
if (loading) return;
|
||||
changeMode(0);
|
||||
}}
|
||||
size={SIZE.md}
|
||||
@@ -232,10 +237,7 @@ export const Signup = ({ changeMode, trial }) => {
|
||||
borderRadius: 100
|
||||
}}
|
||||
loading={loading}
|
||||
onPress={() => {
|
||||
if (loading) return;
|
||||
signup();
|
||||
}}
|
||||
onPress={signup}
|
||||
type="accent"
|
||||
title={loading ? null : "Agree and continue"}
|
||||
/>
|
||||
|
||||
@@ -54,7 +54,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
|
||||
app: "Enter the 6 digit code from your authenticator app to continue logging in",
|
||||
sms: "Enter the 6 digit code sent to your phone number to continue logging in",
|
||||
email: "Enter the 6 digit code sent to your email to continue logging in",
|
||||
recoveryCode: "Enter the recovery code to continue logging in"
|
||||
recoveryCode: "Enter the 8 digit recovery code to continue logging in"
|
||||
};
|
||||
|
||||
const secondaryMethodsText = {
|
||||
@@ -65,7 +65,9 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
|
||||
};
|
||||
|
||||
const onNext = async () => {
|
||||
if (!code.current || code.current.length < 6) return;
|
||||
const length = currentMethod.method === "recoveryCode" ? 8 : 6;
|
||||
|
||||
if (!code.current || code.current.length !== length) return;
|
||||
setLoading(true);
|
||||
inputRef.current?.blur();
|
||||
await onMfaLogin(
|
||||
@@ -200,26 +202,22 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
|
||||
<>
|
||||
<Input
|
||||
placeholder={
|
||||
currentMethod.method === "recoveryCode"
|
||||
? "xxxxx-xxxxx"
|
||||
: "xxxxxx"
|
||||
currentMethod.method === "recoveryCode" ? "xxxxxxxx" : "xxxxxx"
|
||||
}
|
||||
testID={"input.totp"}
|
||||
maxLength={
|
||||
currentMethod.method === "recoveryCode" ? undefined : 6
|
||||
}
|
||||
maxLength={currentMethod.method === "recoveryCode" ? 8 : 6}
|
||||
fwdRef={inputRef}
|
||||
textAlign="center"
|
||||
onChangeText={(value) => {
|
||||
code.current = value;
|
||||
//onNext();
|
||||
onNext();
|
||||
}}
|
||||
inputStyle={{
|
||||
fontSize: SIZE.lg,
|
||||
height: 60,
|
||||
textAlign: "center",
|
||||
letterSpacing: 10,
|
||||
width: 250
|
||||
width: null
|
||||
}}
|
||||
keyboardType={
|
||||
currentMethod.method === "recoveryCode" ? "default" : "numeric"
|
||||
@@ -302,9 +300,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
|
||||
|
||||
TwoFactorVerification.present = (onMfaLogin, data, context) => {
|
||||
presentSheet({
|
||||
component: () => (
|
||||
<TwoFactorVerification onMfaLogin={onMfaLogin} mfaInfo={data} />
|
||||
),
|
||||
component: <TwoFactorVerification onMfaLogin={onMfaLogin} mfaInfo={data} />,
|
||||
context: context || "two_factor_verify",
|
||||
onClose: () => {
|
||||
onMfaLogin();
|
||||
|
||||
@@ -75,7 +75,6 @@ export const useLogin = (onFinishLogin) => {
|
||||
mfa.code,
|
||||
mfa.method
|
||||
);
|
||||
|
||||
if (success) {
|
||||
setStep(LoginSteps.passwordAuth);
|
||||
setLoading(false);
|
||||
@@ -94,8 +93,6 @@ export const useLogin = (onFinishLogin) => {
|
||||
}
|
||||
}
|
||||
}, mfaInfo);
|
||||
} else {
|
||||
finishWithError(new Error("Unable to send 2FA code"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { Text, View, ViewStyle } from "react-native";
|
||||
import { Text, View } from "react-native";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { Button } from "../ui/button";
|
||||
@@ -41,7 +41,6 @@ type DialogHeaderProps = {
|
||||
padding?: number;
|
||||
centered?: boolean;
|
||||
titlePart?: string;
|
||||
style?: ViewStyle;
|
||||
};
|
||||
|
||||
const DialogHeader = ({
|
||||
@@ -51,8 +50,7 @@ const DialogHeader = ({
|
||||
paragraphColor,
|
||||
padding,
|
||||
centered,
|
||||
titlePart,
|
||||
style
|
||||
titlePart
|
||||
}: DialogHeaderProps) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
|
||||
@@ -64,8 +62,7 @@ const DialogHeader = ({
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
minHeight: 50,
|
||||
paddingHorizontal: padding,
|
||||
...style
|
||||
paddingHorizontal: padding
|
||||
}}
|
||||
>
|
||||
<View
|
||||
|
||||
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import {
|
||||
Dimensions,
|
||||
Image,
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
View
|
||||
} from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import umami from "../../common/analytics";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
@@ -49,6 +50,9 @@ export const SVG_Z =
|
||||
|
||||
const Intro = ({ navigation }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
);
|
||||
const isTelemetryEnabled = useSettingStore(
|
||||
(state) => state.settings.telemetry
|
||||
);
|
||||
@@ -60,6 +64,12 @@ const Intro = ({ navigation }) => {
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!introCompleted) {
|
||||
umami.pageView("/welcome", "", []);
|
||||
}
|
||||
}, [introCompleted]);
|
||||
|
||||
return (
|
||||
<View
|
||||
testID="notesnook.splashscreen"
|
||||
@@ -161,6 +171,8 @@ const Intro = ({ navigation }) => {
|
||||
navigation.navigate("AppLock", {
|
||||
welcome: true
|
||||
});
|
||||
|
||||
umami.pageView("/home", "/welcome");
|
||||
}}
|
||||
style={{
|
||||
paddingHorizontal: 24,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
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 Clipboard from "@react-native-clipboard/clipboard";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import Animated, { SlideInUp, SlideOutDown } from "react-native-reanimated";
|
||||
import { db } from "../../../common/database";
|
||||
import { openVault, ToastEvent } from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { useMenuStore } from "../../../stores/use-menu-store";
|
||||
import { useNotebookStore } from "../../../stores/use-notebook-store";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { useTrashStore } from "../../../stores/use-trash-store";
|
||||
import { dWidth, getElevation, toTXT } from "../../../utils";
|
||||
import { deleteItems } from "../../../utils/functions";
|
||||
import { presentDialog } from "../../dialog/functions";
|
||||
import { Button } from "../../ui/button";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
|
||||
export const ActionStrip = ({ note, setActionStrip }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const selectionMode = useSelectionStore((state) => state.selectionMode);
|
||||
const setNotebooks = useNotebookStore((state) => state.setNotebooks);
|
||||
const setMenuPins = useMenuStore((state) => state.setMenuPins);
|
||||
const setSelectedItem = useSelectionStore((state) => state.setSelectedItem);
|
||||
const setSelectionMode = useSelectionStore((state) => state.setSelectionMode);
|
||||
|
||||
const [isPinnedToMenu, setIsPinnedToMenu] = useState(false);
|
||||
const [width, setWidth] = useState(dWidth - 16);
|
||||
useEffect(() => {
|
||||
if (note.type === "note") return;
|
||||
setIsPinnedToMenu(db.shortcuts.exists(note.id));
|
||||
}, [note.id, note.type]);
|
||||
|
||||
const updateNotes = () => {
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
};
|
||||
|
||||
const actions = [
|
||||
{
|
||||
title: "Pin " + note.type,
|
||||
icon: note.pinned ? "pin-off" : "pin",
|
||||
visible: note.type === "note" || note.type === "notebook",
|
||||
onPress: async () => {
|
||||
if (!note.id) return;
|
||||
|
||||
if (note.type === "note") {
|
||||
await db.notes.note(note.id).pin();
|
||||
} else {
|
||||
await db.notebooks.notebook(note.id).pin();
|
||||
setNotebooks();
|
||||
}
|
||||
updateNotes();
|
||||
setActionStrip(false);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Add to favorites",
|
||||
icon: note.favorite ? "star-off" : "star",
|
||||
onPress: async () => {
|
||||
if (!note.id) return;
|
||||
if (note.type === "note") {
|
||||
await db.notes.note(note.id).favorite();
|
||||
} else {
|
||||
await db.notebooks.notebook(note.id).favorite();
|
||||
}
|
||||
updateNotes();
|
||||
setActionStrip(false);
|
||||
},
|
||||
visible: note.type === "note",
|
||||
color: !note.favorite ? "orange" : null
|
||||
},
|
||||
|
||||
{
|
||||
title: isPinnedToMenu
|
||||
? "Remove Shortcut from Menu"
|
||||
: "Add Shortcut to Menu",
|
||||
icon: isPinnedToMenu ? "link-variant-remove" : "link-variant",
|
||||
onPress: async () => {
|
||||
try {
|
||||
if (isPinnedToMenu) {
|
||||
await db.shortcuts.remove(note.id);
|
||||
ToastEvent.show({
|
||||
heading: "Shortcut removed from menu",
|
||||
type: "success"
|
||||
});
|
||||
} else {
|
||||
if (note.type === "topic") {
|
||||
await db.shortcuts.add({
|
||||
item: {
|
||||
type: "topic",
|
||||
id: note.id,
|
||||
notebookId: note.notebookId
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await db.shortcuts.add({
|
||||
item: {
|
||||
type: "notebook",
|
||||
id: note.id
|
||||
}
|
||||
});
|
||||
}
|
||||
ToastEvent.show({
|
||||
heading: "Shortcut added to menu",
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
setIsPinnedToMenu(db.shortcuts.exists(note.id));
|
||||
setMenuPins();
|
||||
|
||||
setActionStrip(false);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
visible: note.type !== "note" && note.type !== "reminder"
|
||||
},
|
||||
{
|
||||
title: "Copy Note",
|
||||
icon: "content-copy",
|
||||
visible: note.type === "note",
|
||||
onPress: async () => {
|
||||
if (note.locked) {
|
||||
openVault({
|
||||
copyNote: true,
|
||||
novault: true,
|
||||
locked: true,
|
||||
item: note,
|
||||
title: "Copy note",
|
||||
description: "Unlock note to copy to clipboard."
|
||||
});
|
||||
} else {
|
||||
let text = await toTXT(note);
|
||||
text = `${note.title}\n \n ${text}`;
|
||||
Clipboard.setString(text);
|
||||
ToastEvent.show({
|
||||
heading: "Note copied to clipboard",
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
setActionStrip(false);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Restore " + note.itemType,
|
||||
icon: "delete-restore",
|
||||
onPress: async () => {
|
||||
await db.trash.restore(note.id);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes",
|
||||
"Trash",
|
||||
"Notebooks"
|
||||
);
|
||||
|
||||
ToastEvent.show({
|
||||
heading:
|
||||
note.type === "note"
|
||||
? "Note restored from trash"
|
||||
: "Notebook restored from trash",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
setActionStrip(false);
|
||||
},
|
||||
visible: note.type === "trash"
|
||||
},
|
||||
{
|
||||
title: "Delete" + note.itemType,
|
||||
icon: "delete",
|
||||
visible: note.type === "trash",
|
||||
onPress: () => {
|
||||
presentDialog({
|
||||
title: "Permanent delete",
|
||||
paragraph: `Are you sure you want to delete this ${note.itemType} permanantly from trash?`,
|
||||
positiveText: "Delete",
|
||||
negativeText: "Cancel",
|
||||
positivePress: async () => {
|
||||
await db.trash.delete(note.id);
|
||||
useTrashStore.getState().setTrash();
|
||||
useSelectionStore.getState().setSelectionMode(false);
|
||||
ToastEvent.show({
|
||||
heading: "Permanantly deleted items",
|
||||
type: "success",
|
||||
context: "local"
|
||||
});
|
||||
},
|
||||
positiveType: "errorShade"
|
||||
});
|
||||
setActionStrip(false);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Delete" + note.type,
|
||||
icon: "delete",
|
||||
visible: note.type !== "trash",
|
||||
onPress: async () => {
|
||||
if (note.type === "reminder") {
|
||||
presentDialog({
|
||||
title: `Delete ${note.type}`,
|
||||
paragraph: "This reminder will be removed",
|
||||
positivePress: async () => {
|
||||
const routes = [];
|
||||
await db.reminders.remove(note.id);
|
||||
routes.push("Reminders");
|
||||
Navigation.queueRoutesForUpdate(...routes);
|
||||
useRelationStore.getState().update();
|
||||
},
|
||||
positiveText: "Delete",
|
||||
positiveType: "errorShade"
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await deleteItems(note);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
setActionStrip(false);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Close",
|
||||
icon: "close",
|
||||
onPress: () => setActionStrip(false),
|
||||
color: colors.light,
|
||||
bg: colors.red,
|
||||
visible: true
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
onLayout={(event) => {
|
||||
setWidth(event.nativeEvent.layout.width);
|
||||
}}
|
||||
entering={SlideInUp.springify().mass(0.4)}
|
||||
exiting={SlideOutDown}
|
||||
style={{
|
||||
position: "absolute",
|
||||
zIndex: 999,
|
||||
width: "102%",
|
||||
height: "100%",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type="accent"
|
||||
title="Select"
|
||||
icon="check"
|
||||
tooltipText="Select Item"
|
||||
onPress={() => {
|
||||
if (!selectionMode) {
|
||||
setSelectionMode(true);
|
||||
}
|
||||
setSelectedItem(note);
|
||||
setActionStrip(false);
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 12,
|
||||
...getElevation(5)
|
||||
}}
|
||||
height={30}
|
||||
/>
|
||||
{actions.map((item) =>
|
||||
item.visible ? (
|
||||
<View
|
||||
key={item.icon}
|
||||
style={{
|
||||
width: width / 1.4 / actions.length,
|
||||
height: width / 1.4 / actions.length,
|
||||
backgroundColor: item.bg || colors.nav,
|
||||
borderRadius: 100,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
...getElevation(5),
|
||||
marginLeft: 15
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
color={item.color || colors.heading}
|
||||
onPress={item.onPress}
|
||||
tooltipText={item.title}
|
||||
top={60}
|
||||
bottom={60}
|
||||
name={item.icon}
|
||||
size={width / 2.8 / actions.length}
|
||||
/>
|
||||
</View>
|
||||
) : null
|
||||
)}
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
@@ -17,13 +17,19 @@ 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 } from "react";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../../../services/event-manager";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { history } from "../../../utils";
|
||||
import { PressableButton } from "../../ui/pressable";
|
||||
import { ActionStrip } from "./action-strip";
|
||||
import { Filler } from "./back-fill";
|
||||
import { SelectionIcon } from "./selection";
|
||||
import { useRef } from "react";
|
||||
|
||||
const SelectionWrapper = ({
|
||||
children,
|
||||
@@ -36,6 +42,7 @@ const SelectionWrapper = ({
|
||||
}) => {
|
||||
const itemId = useRef(item.id);
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const [actionStrip, setActionStrip] = useState(false);
|
||||
const notebooksListMode = useSettingStore(
|
||||
(state) => state.settings.notebooksListMode
|
||||
);
|
||||
@@ -49,18 +56,35 @@ const SelectionWrapper = ({
|
||||
|
||||
if (item.id !== itemId.current) {
|
||||
itemId.current = item.id;
|
||||
if (actionStrip) {
|
||||
setActionStrip(false);
|
||||
}
|
||||
}
|
||||
|
||||
const _onLongPress = () => {
|
||||
if (!useSelectionStore.getState().selectionMode) {
|
||||
useSelectionStore.getState().setSelectionMode(true);
|
||||
}
|
||||
useSelectionStore.getState().setSelectedItem(item);
|
||||
if (history.selectedItemsList.length > 0) return;
|
||||
setActionStrip(!actionStrip);
|
||||
};
|
||||
|
||||
const _onPress = async () => {
|
||||
if (actionStrip) {
|
||||
setActionStrip(false);
|
||||
return;
|
||||
}
|
||||
await onPress();
|
||||
};
|
||||
|
||||
const closeStrip = () => {
|
||||
setActionStrip(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
eSubscribeEvent("navigate", closeStrip);
|
||||
|
||||
return () => {
|
||||
eUnSubscribeEvent("navigate", closeStrip);
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<PressableButton
|
||||
customColor={isSheet ? colors.transGray : "transparent"}
|
||||
@@ -87,10 +111,15 @@ const SelectionWrapper = ({
|
||||
) : null}
|
||||
<SelectionIcon
|
||||
compactMode={compactMode}
|
||||
setActionStrip={setActionStrip}
|
||||
item={item}
|
||||
onLongPress={onLongPress}
|
||||
/>
|
||||
{children}
|
||||
|
||||
{actionStrip ? (
|
||||
<ActionStrip note={item} setActionStrip={setActionStrip} />
|
||||
) : null}
|
||||
</PressableButton>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@ import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
|
||||
export const SelectionIcon = ({ item }) => {
|
||||
export const SelectionIcon = ({ setActionStrip, item }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
|
||||
const selectionMode = useSelectionStore((state) => state.selectionMode);
|
||||
@@ -35,6 +35,7 @@ export const SelectionIcon = ({ item }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (selectionMode) {
|
||||
setActionStrip(false);
|
||||
let exists = selectedItemsList.filter(
|
||||
(o) => o.dateCreated === item.dateCreated
|
||||
);
|
||||
@@ -49,9 +50,16 @@ export const SelectionIcon = ({ item }) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [selectedItemsList, item.id, selectionMode, item.dateCreated, selected]);
|
||||
}, [
|
||||
selectedItemsList,
|
||||
item.id,
|
||||
selectionMode,
|
||||
setActionStrip,
|
||||
item.dateCreated,
|
||||
selected
|
||||
]);
|
||||
|
||||
return selectionMode ? (
|
||||
return selectionMode && selected ? (
|
||||
<View
|
||||
style={{
|
||||
width: 40,
|
||||
@@ -65,13 +73,11 @@ export const SelectionIcon = ({ item }) => {
|
||||
}}
|
||||
pointerEvents="none"
|
||||
>
|
||||
{selected ? (
|
||||
<Icon
|
||||
size={SIZE.xl}
|
||||
color={selected ? colors.accent : colors.icon}
|
||||
name={"check"}
|
||||
/>
|
||||
) : null}
|
||||
<Icon
|
||||
size={SIZE.xl}
|
||||
color={selected ? colors.accent : colors.icon}
|
||||
name="check"
|
||||
/>
|
||||
</View>
|
||||
) : null;
|
||||
};
|
||||
|
||||
@@ -18,8 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { ActivityIndicator, Platform, ScrollView, View } from "react-native";
|
||||
import { ActivityIndicator, ScrollView, View } from "react-native";
|
||||
import { LAUNCH_ROCKET } from "../../assets/images/assets";
|
||||
import umami from "../../common/analytics";
|
||||
import { db } from "../../common/database";
|
||||
import { usePricing } from "../../hooks/use-pricing";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
@@ -47,6 +48,7 @@ import { Walkthrough } from "../walkthroughs";
|
||||
import { features } from "./features";
|
||||
import { Group } from "./group";
|
||||
import { PricingPlans } from "./pricing-plans";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
export const Component = ({ close, promo }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
@@ -58,6 +60,7 @@ export const Component = ({ close, promo }) => {
|
||||
|
||||
const onPress = async () => {
|
||||
if (user) {
|
||||
umami.pageView("/pro-plans", "/pro-screen");
|
||||
presentSheet({
|
||||
context: "pricing_plans",
|
||||
component: (
|
||||
@@ -66,6 +69,7 @@ export const Component = ({ close, promo }) => {
|
||||
});
|
||||
} else {
|
||||
close();
|
||||
umami.pageView("/signup", "/pro-screen");
|
||||
setTimeout(() => {
|
||||
eSendEvent(eOpenLoginDialog, AuthMode.trialSignup);
|
||||
}, 400);
|
||||
|
||||
@@ -17,9 +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, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { ActivityIndicator, Platform, Text, View } from "react-native";
|
||||
import * as RNIap from "react-native-iap";
|
||||
import umami from "../../common/analytics";
|
||||
import { db } from "../../common/database";
|
||||
import { usePricing } from "../../hooks/use-pricing";
|
||||
import {
|
||||
@@ -47,6 +48,7 @@ import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { Walkthrough } from "../walkthroughs";
|
||||
import { PricingItem } from "./pricing-item";
|
||||
import { useCallback } from "react";
|
||||
|
||||
const promoCyclesMonthly = {
|
||||
1: "first month",
|
||||
@@ -139,6 +141,10 @@ export const PricingPlans = ({
|
||||
setBuying(false);
|
||||
return;
|
||||
}
|
||||
umami.pageView(
|
||||
"/iap-native",
|
||||
`${compact ? "pro-sheet" : "pro-screen"}/pro-plans`
|
||||
);
|
||||
await RNIap.requestSubscription(
|
||||
product?.productId,
|
||||
false,
|
||||
|
||||
@@ -22,10 +22,12 @@ import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import { db } from "../../common/database";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { useMenuStore } from "../../stores/use-menu-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { dWidth } from "../../utils";
|
||||
import { COLORS_NOTE } from "../../utils/color-scheme";
|
||||
import { refreshNotesPage } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
@@ -33,7 +35,9 @@ import { PressableButton } from "../ui/pressable";
|
||||
export const ColorTags = ({ item }) => {
|
||||
const [note, setNote] = useState(item);
|
||||
const setColorNotes = useMenuStore((state) => state.setColorNotes);
|
||||
const isTablet = useSettingStore((state) => state.deviceMode) !== "mobile";
|
||||
const dimensions = useSettingStore((state) => state.dimensions);
|
||||
let width = dimensions.width > 600 ? 600 : 500;
|
||||
|
||||
const changeColor = async (color) => {
|
||||
if (note.color === color.name) {
|
||||
await db.notes.note(note.id).uncolor();
|
||||
@@ -67,12 +71,11 @@ export const ColorTags = ({ item }) => {
|
||||
key={color.value}
|
||||
onPress={() => changeColor(color)}
|
||||
customStyle={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
width: DDS.isTab ? width / 10 : dWidth / 9,
|
||||
height: DDS.isTab ? width / 10 : dWidth / 9,
|
||||
borderRadius: 100,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: isTablet ? 10 : undefined
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{note.color?.toLowerCase() === color.name ? (
|
||||
@@ -87,10 +90,12 @@ export const ColorTags = ({ item }) => {
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
flexGrow: isTablet ? undefined : 1,
|
||||
paddingHorizontal: 12,
|
||||
width: "100%",
|
||||
marginVertical: 10,
|
||||
marginTop: 20,
|
||||
alignItems: "center",
|
||||
justifyContent: isTablet ? "center" : "space-between"
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
>
|
||||
{Object.keys(COLORS_NOTE).map(_renderColor)}
|
||||
|
||||
@@ -74,6 +74,10 @@ export const DateMeta = ({ item }) => {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingVertical: 5,
|
||||
marginTop: 5,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.nav,
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -27,32 +27,20 @@ import SearchService from "../../services/search";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { COLORS_NOTE } from "../../utils/color-scheme";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import SheetProvider from "../sheet-provider";
|
||||
import { ReminderTime } from "../ui/reminder-time";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { ColorTags } from "./color-tags";
|
||||
import { DateMeta } from "./date-meta";
|
||||
import { DevMode } from "./dev-mode";
|
||||
import { Items } from "./items";
|
||||
import Notebooks from "./notebooks";
|
||||
import { Synced } from "./synced";
|
||||
import { Tags, TagStrip } from "./tags";
|
||||
|
||||
const Line = ({ top = 6, bottom = 6 }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
height: 1,
|
||||
backgroundColor: colors.nav,
|
||||
width: "100%",
|
||||
marginTop: top,
|
||||
marginBottom: bottom
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
import { Tags } from "./tags";
|
||||
import { Topics } from "./topics";
|
||||
import {
|
||||
getUpcomingReminder
|
||||
} from "@notesnook/core/collections/reminders";
|
||||
import { ReminderTime } from "../ui/reminder-time";
|
||||
export const Properties = ({
|
||||
close = () => {},
|
||||
item,
|
||||
@@ -62,18 +50,13 @@ export const Properties = ({
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const alias = item.alias || item.title;
|
||||
const isColor = !!COLORS_NOTE[item.title];
|
||||
const reminders = db.relations.from(item, "reminder");
|
||||
const current = getUpcomingReminder(reminders);
|
||||
|
||||
const onScrollEnd = () => {
|
||||
getRef().current?.handleChildScrollEnd();
|
||||
};
|
||||
|
||||
if (!item || !item.id) {
|
||||
return (
|
||||
<Paragraph style={{ marginVertical: 10, alignSelf: "center" }}>
|
||||
Start writing to save your note.
|
||||
</Paragraph>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<ScrollView
|
||||
nestedScrollEnabled
|
||||
@@ -88,53 +71,85 @@ export const Properties = ({
|
||||
maxHeight: "100%"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
marginTop: 5,
|
||||
zIndex: 10
|
||||
}}
|
||||
>
|
||||
<Heading size={SIZE.lg}>
|
||||
{item.type === "tag" && !isColor ? (
|
||||
<Heading size={SIZE.xl} color={colors.accent}>
|
||||
#
|
||||
{!item || !item.id ? (
|
||||
<Paragraph style={{ marginVertical: 10, alignSelf: "center" }}>
|
||||
Start writing to save your note.
|
||||
</Paragraph>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
marginTop: 5,
|
||||
zIndex: 10
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
<Heading size={SIZE.lg}>
|
||||
{item.type === "tag" && !isColor ? (
|
||||
<Heading size={SIZE.xl} color={colors.accent}>
|
||||
#
|
||||
</Heading>
|
||||
) : null}
|
||||
{alias}
|
||||
</Heading>
|
||||
|
||||
{item.headline || item.description ? (
|
||||
<Paragraph
|
||||
style={{
|
||||
marginBottom: 5
|
||||
}}
|
||||
color={colors.icon}
|
||||
>
|
||||
{(item.type === "notebook" ||
|
||||
item.itemType === "notebook" ||
|
||||
item.type === "reminder") &&
|
||||
item?.description
|
||||
? item.description
|
||||
: null}
|
||||
{(item.type === "note" || item.itemType === "note") &&
|
||||
item?.headline
|
||||
? item.headline
|
||||
: null}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
|
||||
{item.type === "reminder" ? (
|
||||
<ReminderTime
|
||||
reminder={item}
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
borderWidth: 0,
|
||||
height: 30,
|
||||
alignSelf: "flex-start"
|
||||
}}
|
||||
fontSize={SIZE.xs + 1}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{item.type === "note" ? <Tags close={close} item={item} /> : null}
|
||||
|
||||
<Topics item={item} close={close} />
|
||||
</View>
|
||||
|
||||
{item.type === "note" ? (
|
||||
<Notebooks note={item} close={close} />
|
||||
) : null}
|
||||
{alias}
|
||||
</Heading>
|
||||
|
||||
{item.type === "note" ? <TagStrip close={close} item={item} /> : null}
|
||||
|
||||
{item.type === "reminder" ? (
|
||||
<ReminderTime
|
||||
reminder={item}
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
borderWidth: 0,
|
||||
height: 30,
|
||||
alignSelf: "flex-start",
|
||||
backgroundColor: "transparent",
|
||||
paddingHorizontal: 0
|
||||
}}
|
||||
fontSize={SIZE.xs + 1}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
<Line top={12} />
|
||||
|
||||
<DateMeta item={item} />
|
||||
<Line bottom={0} />
|
||||
{item.type === "note" ? <Tags close={close} item={item} /> : null}
|
||||
<DateMeta item={item} />
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
borderTopWidth: 1,
|
||||
borderColor: colors.nav
|
||||
}}
|
||||
>
|
||||
{item.notebooks ? <Notebooks note={item} close={close} /> : null}
|
||||
</View>
|
||||
/>
|
||||
|
||||
{item.type === "note" ? <ColorTags close={close} item={item} /> : null}
|
||||
|
||||
<Items
|
||||
item={item}
|
||||
@@ -156,7 +171,6 @@ export const Properties = ({
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<SheetProvider context="properties" />
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
@@ -169,50 +183,50 @@ Properties.present = (item, buttons = [], isSheet) => {
|
||||
switch (type) {
|
||||
case "trash":
|
||||
props[0] = item;
|
||||
props.push(["delete", "restore"]);
|
||||
props.push(["PermDelete", "Restore"]);
|
||||
break;
|
||||
case "note":
|
||||
android = Platform.OS === "android" ? ["pin-to-notifications"] : [];
|
||||
android = Platform.OS === "android" ? ["PinToNotif"] : [];
|
||||
props[0] = db.notes.note(item.id).data;
|
||||
props.push([
|
||||
"notebooks",
|
||||
"add-reminder",
|
||||
"share",
|
||||
"export",
|
||||
"copy",
|
||||
"publish",
|
||||
"pin",
|
||||
"favorite",
|
||||
"attachments",
|
||||
"lock-unlock",
|
||||
"trash",
|
||||
"remove-from-topic",
|
||||
"history",
|
||||
"read-only",
|
||||
"reminders",
|
||||
"local-only",
|
||||
"duplicate",
|
||||
"Add to notebook",
|
||||
"Add-Reminder",
|
||||
"Share",
|
||||
"Export",
|
||||
"Copy",
|
||||
"Publish",
|
||||
"Pin",
|
||||
"Favorite",
|
||||
"Attachments",
|
||||
"Vault",
|
||||
"Delete",
|
||||
"RemoveTopic",
|
||||
"History",
|
||||
"ReadOnly",
|
||||
"Reminders",
|
||||
"Local only",
|
||||
"Duplicate",
|
||||
...android,
|
||||
...buttons
|
||||
]);
|
||||
break;
|
||||
case "notebook":
|
||||
props[0] = db.notebooks.notebook(item.id).data;
|
||||
props.push(["edit-notebook", "pin", "add-shortcut", "trash"]);
|
||||
props.push(["Edit Notebook", "Pin", "Add Shortcut", "Delete"]);
|
||||
break;
|
||||
case "topic":
|
||||
props[0] = db.notebooks
|
||||
.notebook(item.notebookId)
|
||||
.topics.topic(item.id)._topic;
|
||||
props.push(["move-notes", "edit-topic", "add-shortcut", "trash"]);
|
||||
props.push(["Move notes", "Edit Topic", "Add Shortcut", "Delete"]);
|
||||
break;
|
||||
case "tag":
|
||||
props[0] = db.tags.tag(item.id);
|
||||
props.push(["add-shortcut", "trash", "rename-tag"]);
|
||||
props.push(["Add Shortcut", "Delete", "Rename Tag"]);
|
||||
break;
|
||||
case "reminder": {
|
||||
props[0] = db.reminders.reminder(item.id);
|
||||
props.push(["edit-reminder", "trash", "disable-reminder"]);
|
||||
props.push(["Edit reminder", "Delete", "ReminderOnOff"]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,19 +33,19 @@ export const Items = ({ item, buttons, close }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const dimensions = useSettingStore((state) => state.dimensions);
|
||||
const actions = useActions({ item, close });
|
||||
const data = actions.filter((i) => buttons.indexOf(i.id) > -1 && !i.hidden);
|
||||
const data = actions.filter((i) => buttons.indexOf(i.name) > -1 && !i.hidden);
|
||||
|
||||
let width = dimensions.width > 600 ? 600 : dimensions.width;
|
||||
let columnItemsCount = DDS.isLargeTablet() ? 7 : 5;
|
||||
let columnItemWidth = DDS.isTab
|
||||
? (width - 12) / columnItemsCount
|
||||
: (width - 12) / columnItemsCount;
|
||||
? (width - 24) / columnItemsCount
|
||||
: (width - 24) / columnItemsCount;
|
||||
|
||||
const _renderRowItem = ({ item }) => (
|
||||
<View
|
||||
onPress={item.func}
|
||||
key={item.id}
|
||||
testID={"icon-" + item.id}
|
||||
key={item.name}
|
||||
testID={"icon-" + item.name}
|
||||
style={{
|
||||
alignItems: "center",
|
||||
width: columnItemWidth,
|
||||
@@ -58,7 +58,7 @@ export const Items = ({ item, buttons, close }) => {
|
||||
customStyle={{
|
||||
height: columnItemWidth - 12,
|
||||
width: columnItemWidth - 12,
|
||||
borderRadius: 10,
|
||||
borderRadius: 5,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
textAlign: "center",
|
||||
@@ -72,14 +72,14 @@ export const Items = ({ item, buttons, close }) => {
|
||||
color={
|
||||
item.on
|
||||
? colors.accent
|
||||
: item.id.match(/(delete|trash)/g)
|
||||
: item.name === "Delete" || item.name === "PermDelete"
|
||||
? colors.errorText
|
||||
: colors.icon
|
||||
}
|
||||
/>
|
||||
</PressableButton>
|
||||
|
||||
<Paragraph size={SIZE.xs} style={{ textAlign: "center" }}>
|
||||
<Paragraph size={SIZE.xs + 1} style={{ textAlign: "center" }}>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
@@ -108,119 +108,31 @@ export const Items = ({ item, buttons, close }) => {
|
||||
/>
|
||||
);
|
||||
|
||||
const renderTopBarItem = ({ item }) => {
|
||||
return (
|
||||
<PressableButton
|
||||
onPress={item.func}
|
||||
key={item.id}
|
||||
testID={"icon-" + item.id}
|
||||
customStyle={{
|
||||
alignItems: "center",
|
||||
width: topBarItemWidth,
|
||||
marginBottom: 10,
|
||||
marginRight: 10,
|
||||
backgroundColor: "transparent"
|
||||
}}
|
||||
>
|
||||
<PressableButton
|
||||
onPress={item.func}
|
||||
type={item.on ? "shade" : "gray"}
|
||||
customStyle={{
|
||||
height: topBarItemWidth,
|
||||
width: topBarItemWidth,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
textAlign: "center",
|
||||
textAlignVertical: "center",
|
||||
marginBottom: DDS.isTab ? 7 : 3.5,
|
||||
borderRadius: 100
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name={item.icon}
|
||||
size={DDS.isTab ? SIZE.xxl : SIZE.md + 4}
|
||||
color={
|
||||
item.on
|
||||
? colors.accent
|
||||
: item.name === "Delete" || item.name === "PermDelete"
|
||||
? colors.errorText
|
||||
: colors.icon
|
||||
}
|
||||
/>
|
||||
</PressableButton>
|
||||
|
||||
<Paragraph
|
||||
size={SIZE.xxs + 1}
|
||||
style={{ textAlign: "center" }}
|
||||
textBreakStrategy="simple"
|
||||
>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</PressableButton>
|
||||
);
|
||||
};
|
||||
|
||||
const topBarItemsList = [
|
||||
"pin",
|
||||
"favorite",
|
||||
"copy",
|
||||
"share",
|
||||
"export",
|
||||
"lock-unlock",
|
||||
"publish"
|
||||
];
|
||||
const bottomBarItems = data.filter(
|
||||
(item) => topBarItemsList.indexOf(item.id) > -1
|
||||
);
|
||||
|
||||
const bottomGridItems = data.filter(
|
||||
(item) => topBarItemsList.indexOf(item.id) === -1
|
||||
);
|
||||
|
||||
const topBarItemWidth =
|
||||
(width - (bottomBarItems.length * 10 + 14)) / bottomBarItems.length;
|
||||
|
||||
return item.type === "note" ? (
|
||||
<>
|
||||
<FlatList
|
||||
data={bottomBarItems}
|
||||
keyExtractor={(item) => item.title}
|
||||
horizontal
|
||||
disableVirtualization={true}
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
paddingTop: 12
|
||||
}}
|
||||
renderItem={renderTopBarItem}
|
||||
/>
|
||||
|
||||
<FlatList
|
||||
data={bottomGridItems}
|
||||
keyExtractor={(item) => item.title}
|
||||
key={columnItemsCount + "key"}
|
||||
numColumns={columnItemsCount}
|
||||
disableVirtualization={true}
|
||||
style={{
|
||||
marginTop: item.type !== "note" ? 10 : 0,
|
||||
paddingTop: 10
|
||||
}}
|
||||
columnWrapperStyle={{
|
||||
justifyContent: "flex-start"
|
||||
}}
|
||||
contentContainerStyle={{
|
||||
alignSelf: "center",
|
||||
width: buttons.length < 5 ? "100%" : null,
|
||||
paddingLeft: buttons.length < 5 ? 10 : 0
|
||||
}}
|
||||
renderItem={_renderRowItem}
|
||||
/>
|
||||
</>
|
||||
<FlatList
|
||||
data={data}
|
||||
keyExtractor={(item) => item.title}
|
||||
key={columnItemsCount + "key"}
|
||||
numColumns={columnItemsCount}
|
||||
style={{
|
||||
marginTop: item.type !== "note" ? 10 : 0,
|
||||
paddingTop: 10
|
||||
}}
|
||||
columnWrapperStyle={{
|
||||
justifyContent: "flex-start"
|
||||
}}
|
||||
contentContainerStyle={{
|
||||
alignSelf: "center",
|
||||
width: buttons.length < 5 ? "100%" : null,
|
||||
paddingLeft: buttons.length < 5 ? 10 : 0
|
||||
}}
|
||||
renderItem={_renderRowItem}
|
||||
/>
|
||||
) : (
|
||||
<FlatList
|
||||
data={data}
|
||||
keyExtractor={(item) => item.title}
|
||||
renderItem={renderColumnItem}
|
||||
disableVirtualization={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -23,34 +23,39 @@ 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 { 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 { PressableButton } from "../ui/pressable";
|
||||
import Heading from "../ui/typography/heading";
|
||||
|
||||
export default function Notebooks({ note, close, full }) {
|
||||
export default function Notebooks({ note, close }) {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const notebooks = useNotebookStore((state) => state.notebooks);
|
||||
|
||||
function getNotebooks(item) {
|
||||
if (!item.notebooks || item.notebooks.length < 1) return [];
|
||||
let filteredNotebooks = [];
|
||||
for (let notebookReference of item.notebooks) {
|
||||
let notebook = {
|
||||
...(notebooks.find((item) => item.id === notebookReference.id) || {})
|
||||
};
|
||||
if (notebook.id) {
|
||||
notebook.topics = notebook.topics.filter((topic) => {
|
||||
return notebookReference.topics.findIndex((t) => t === topic.id) > -1;
|
||||
});
|
||||
filteredNotebooks.push(notebook);
|
||||
let notebooks = [];
|
||||
for (let notebook of item.notebooks) {
|
||||
let item_notebook = db.notebooks.notebook(notebook.id);
|
||||
if (item_notebook) {
|
||||
let data = {
|
||||
id: notebook.id,
|
||||
title: item_notebook.title,
|
||||
topics: notebook.topics
|
||||
.map((item) => {
|
||||
let topic = item_notebook.topics.topic(item)?._topic;
|
||||
if (!topic) return null;
|
||||
return {
|
||||
id: topic.id,
|
||||
title: topic.title
|
||||
};
|
||||
})
|
||||
.filter((i) => i !== null)
|
||||
};
|
||||
notebooks.push(data);
|
||||
}
|
||||
}
|
||||
return filteredNotebooks;
|
||||
return notebooks;
|
||||
}
|
||||
const noteNotebooks = getNotebooks(note);
|
||||
|
||||
const navigateNotebook = (id) => {
|
||||
let item = db.notebooks.notebook(id)?.data;
|
||||
@@ -64,139 +69,84 @@ export default function Notebooks({ note, close, full }) {
|
||||
TopicNotes.navigate(item, true);
|
||||
};
|
||||
|
||||
const renderItem = (item) => (
|
||||
<View
|
||||
key={item.id}
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
paddingHorizontal: 12,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexShrink: 1,
|
||||
flexGrow: 1,
|
||||
padding: 6,
|
||||
borderWidth: full ? 0 : 1,
|
||||
borderColor: colors.nav,
|
||||
borderRadius: 10,
|
||||
backgroundColor: full ? "transparent" : colors.nav
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="book-outline"
|
||||
color={colors.accent}
|
||||
size={SIZE.sm}
|
||||
style={{
|
||||
marginRight: 5
|
||||
}}
|
||||
/>
|
||||
<Heading
|
||||
numberOfLines={1}
|
||||
style={{
|
||||
maxWidth: "50%"
|
||||
}}
|
||||
size={SIZE.sm}
|
||||
onPress={() => {
|
||||
navigateNotebook(item.id);
|
||||
close();
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</Heading>
|
||||
|
||||
<ScrollView
|
||||
horizontal={true}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
marginLeft: 8,
|
||||
borderLeftColor: colors.nav,
|
||||
borderLeftWidth: 1,
|
||||
paddingLeft: 8
|
||||
}}
|
||||
>
|
||||
{item.topics.map((topic) => (
|
||||
<Button
|
||||
key={topic.id}
|
||||
onPress={() => {
|
||||
navigateTopic(topic.id, item.id);
|
||||
close();
|
||||
}}
|
||||
onLongPress={async () => {
|
||||
await db.notes.removeFromNotebook(
|
||||
{
|
||||
id: item.id,
|
||||
topic: topic.id
|
||||
},
|
||||
note
|
||||
);
|
||||
useNotebookStore.getState().setNotebooks();
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
ToastEvent.show({
|
||||
heading: "Note removed from topic",
|
||||
context: "local",
|
||||
type: "success"
|
||||
});
|
||||
}}
|
||||
title={topic.title}
|
||||
type="gray"
|
||||
height={30}
|
||||
fontSize={SIZE.xs + 1}
|
||||
icon="bookmark-outline"
|
||||
style={{
|
||||
marginRight: 5,
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 8
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<View style={{ width: 10 }} />
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
|
||||
return !note.notebooks || note.notebooks.length === 0 ? null : (
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
borderRadius: 10,
|
||||
marginTop: 6
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.nav
|
||||
}}
|
||||
>
|
||||
{full
|
||||
? noteNotebooks.map(renderItem)
|
||||
: noteNotebooks.slice(0, 1).map(renderItem)}
|
||||
|
||||
{noteNotebooks.length > 1 && !full ? (
|
||||
<Button
|
||||
title={`See all linked notebooks`}
|
||||
fontSize={SIZE.xs + 1}
|
||||
style={{
|
||||
alignSelf: "flex-end",
|
||||
marginRight: 12,
|
||||
paddingHorizontal: 0,
|
||||
backgroundColor: "transparent"
|
||||
}}
|
||||
type="gray"
|
||||
textStyle={{
|
||||
textDecorationLine: "underline"
|
||||
}}
|
||||
height={20}
|
||||
{getNotebooks(note).map((item) => (
|
||||
<PressableButton
|
||||
key={item.id}
|
||||
onPress={() => {
|
||||
presentSheet({
|
||||
context: "properties",
|
||||
component: (ref, close) => (
|
||||
<Notebooks note={note} close={close} full={true} />
|
||||
)
|
||||
});
|
||||
navigateNotebook(item.id);
|
||||
close();
|
||||
}}
|
||||
/>
|
||||
) : undefined}
|
||||
customStyle={{
|
||||
justifyContent: "flex-start",
|
||||
paddingHorizontal: 12,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexShrink: 1,
|
||||
flexGrow: 1,
|
||||
marginTop: 5,
|
||||
paddingVertical: 6
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="book-outline"
|
||||
color={colors.accent}
|
||||
size={SIZE.sm + 1}
|
||||
style={{
|
||||
marginRight: 5
|
||||
}}
|
||||
/>
|
||||
<Heading
|
||||
numberOfLines={1}
|
||||
style={{
|
||||
maxWidth: "50%"
|
||||
}}
|
||||
size={SIZE.sm}
|
||||
>
|
||||
{item.title}
|
||||
</Heading>
|
||||
|
||||
<ScrollView
|
||||
horizontal={true}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
marginLeft: 8,
|
||||
borderLeftColor: colors.nav,
|
||||
borderLeftWidth: 1,
|
||||
paddingLeft: 8
|
||||
}}
|
||||
>
|
||||
{item.topics.map((topic) => (
|
||||
<Button
|
||||
key={topic.id}
|
||||
onPress={() => {
|
||||
navigateTopic(topic.id, item.id);
|
||||
close();
|
||||
}}
|
||||
title={topic.title}
|
||||
type="grayBg"
|
||||
height={22}
|
||||
fontSize={SIZE.xs + 1}
|
||||
icon="bookmark-outline"
|
||||
style={{
|
||||
marginRight: 5,
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 8
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<View style={{ width: 10 }} />
|
||||
</ScrollView>
|
||||
</PressableButton>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import { eOpenTagsDialog } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { Button } from "../ui/button";
|
||||
import { ColorTags } from "./color-tags";
|
||||
export const Tags = ({ item, close }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
|
||||
@@ -35,53 +34,41 @@ export const Tags = ({ item, close }) => {
|
||||
<View
|
||||
style={{
|
||||
marginTop: 5,
|
||||
paddingTop: 6,
|
||||
paddingBottom: 6,
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 12,
|
||||
alignSelf: "center"
|
||||
marginBottom: 5
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onPress={async () => {
|
||||
close();
|
||||
await sleep(300);
|
||||
eSendEvent(eOpenTagsDialog, item);
|
||||
}}
|
||||
buttonType={{
|
||||
text: colors.accent
|
||||
}}
|
||||
title="Add tags"
|
||||
type="grayBg"
|
||||
icon="plus"
|
||||
iconPosition="right"
|
||||
height={30}
|
||||
fontSize={SIZE.xs + 1}
|
||||
<View
|
||||
style={{
|
||||
marginRight: 5,
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 8
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
alignItems: "center"
|
||||
}}
|
||||
/>
|
||||
<ColorTags item={item} />
|
||||
</View>
|
||||
) : null;
|
||||
};
|
||||
|
||||
export const TagStrip = ({ item, close }) => {
|
||||
return item.tags?.length > 0 ? (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{item.tags.map((item) =>
|
||||
item ? <TagItem key={item} tag={item} close={close} /> : null
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
onPress={async () => {
|
||||
close();
|
||||
await sleep(300);
|
||||
eSendEvent(eOpenTagsDialog, item);
|
||||
}}
|
||||
buttonType={{
|
||||
text: colors.accent
|
||||
}}
|
||||
title="Add tags"
|
||||
type="grayBg"
|
||||
icon="plus"
|
||||
iconPosition="right"
|
||||
height={25}
|
||||
fontSize={SIZE.xs + 1}
|
||||
style={{
|
||||
marginRight: 5,
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 8
|
||||
}}
|
||||
/>
|
||||
{item.tags.map((item) =>
|
||||
item ? <TagItem key={item} tag={item} close={close} /> : null
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
) : null;
|
||||
};
|
||||
@@ -96,24 +83,23 @@ const TagItem = ({ tag, close }) => {
|
||||
};
|
||||
|
||||
const style = {
|
||||
paddingHorizontal: 0,
|
||||
paddingHorizontal: 8,
|
||||
marginVertical: 5,
|
||||
borderRadius: 100,
|
||||
marginRight: 10,
|
||||
marginTop: 0,
|
||||
backgroundColor: "transparent"
|
||||
marginRight: 5
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
onPress={onPress}
|
||||
title={"#" + tag}
|
||||
// buttonType={{
|
||||
// text: colors.accent
|
||||
// }}
|
||||
type="grayBg"
|
||||
height={20}
|
||||
height={25}
|
||||
fontSize={SIZE.xs + 1}
|
||||
style={style}
|
||||
textStyle={{
|
||||
textDecorationLine: "underline"
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -159,8 +159,7 @@ export const SelectionHeader = React.memo(() => {
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
zIndex: 999,
|
||||
paddingHorizontal: 12,
|
||||
marginVertical: 10
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
<View
|
||||
|
||||
@@ -58,7 +58,7 @@ const SheetProvider = ({ context = "global" }) => {
|
||||
if (visible || dialogData) {
|
||||
setDialogData(null);
|
||||
setVisible(false);
|
||||
await sleep(0);
|
||||
await sleep(500);
|
||||
}
|
||||
setDialogData(data);
|
||||
setVisible(true);
|
||||
|
||||
@@ -1,75 +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, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { FlatList } from "react-native-gesture-handler";
|
||||
import { db } from "../../../common/database";
|
||||
import { ListHeaderInputItem } from "./list-header-item.js";
|
||||
|
||||
export const FilteredList = ({
|
||||
data,
|
||||
itemType,
|
||||
onAddItem,
|
||||
hasHeaderSearch,
|
||||
...restProps
|
||||
}) => {
|
||||
const [filtered, setFiltered] = useState(data);
|
||||
const query = useRef();
|
||||
const onChangeText = useCallback(
|
||||
(value) => {
|
||||
query.current = value;
|
||||
try {
|
||||
if (!value) return setFiltered(data);
|
||||
const results = db.lookup[itemType + "s"]([...data], value);
|
||||
setFiltered(results);
|
||||
} catch (e) {
|
||||
console.warn(e.message);
|
||||
}
|
||||
},
|
||||
[data, itemType]
|
||||
);
|
||||
const onSubmit = async (value) => {
|
||||
return await onAddItem(value);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
onChangeText(query.current);
|
||||
}, [data, onChangeText]);
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
{...restProps}
|
||||
data={filtered}
|
||||
ListHeaderComponent={
|
||||
hasHeaderSearch ? (
|
||||
<ListHeaderInputItem
|
||||
onSubmit={onSubmit}
|
||||
onChangeText={onChangeText}
|
||||
itemType={itemType}
|
||||
testID={"list-input" + itemType}
|
||||
placeholder={`Search or add a new ${itemType}`}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="none"
|
||||
nestedScrollEnabled
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -17,15 +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, {
|
||||
createRef,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState
|
||||
} from "react";
|
||||
import React, { createRef, useEffect, useState } from "react";
|
||||
import { Keyboard, TouchableOpacity, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { FlatList } from "react-native-gesture-handler";
|
||||
import { notesnook } from "../../../../e2e/test.ids";
|
||||
import { db } from "../../../common/database";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
@@ -37,17 +32,23 @@ import SearchService from "../../../services/search";
|
||||
import { useNotebookStore } from "../../../stores/use-notebook-store";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { getTotalNotes } from "../../../utils";
|
||||
import { eOpenMoveNoteDialog } from "../../../utils/events";
|
||||
import layoutmanager from "../../../utils/layout-manager";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { Dialog } from "../../dialog";
|
||||
import DialogHeader from "../../dialog/dialog-header";
|
||||
import { presentDialog } from "../../dialog/functions";
|
||||
import { Button } from "../../ui/button";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import Input from "../../ui/input";
|
||||
import { PressableButton } from "../../ui/pressable";
|
||||
import SheetWrapper from "../../ui/sheet";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { SelectionProvider } from "./context";
|
||||
import { FilteredList } from "./filtered-list";
|
||||
import { ListItem } from "./list-item";
|
||||
|
||||
import { useCallback } from "react";
|
||||
let newNotebookTitle = null;
|
||||
const notebookInput = createRef();
|
||||
const actionSheetRef = createRef();
|
||||
const AddToNotebookSheet = () => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
@@ -68,6 +69,7 @@ const AddToNotebookSheet = () => {
|
||||
|
||||
const _onClose = () => {
|
||||
setVisible(false);
|
||||
newNotebookTitle = null;
|
||||
setNote(null);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
@@ -91,37 +93,130 @@ export default AddToNotebookSheet;
|
||||
|
||||
const MoveNoteComponent = ({ note }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const [multiSelect, setMultiSelect] = useState(false);
|
||||
|
||||
const notebooks = useNotebookStore((state) =>
|
||||
state.notebooks.filter((n) => n?.type === "notebook")
|
||||
);
|
||||
const [edited, setEdited] = useState(false);
|
||||
|
||||
const selectedItemsList = useSelectionStore(
|
||||
(state) => state.selectedItemsList
|
||||
);
|
||||
const setNotebooks = useNotebookStore((state) => state.setNotebooks);
|
||||
const [itemState, setItemState] = useState({});
|
||||
|
||||
const onAddNotebook = async (title) => {
|
||||
if (!title || title.trim().length === 0) {
|
||||
ToastEvent.show({
|
||||
const [expanded, setExpanded] = useState("");
|
||||
const [notebookInputFocused, setNotebookInputFocused] = useState(false);
|
||||
const [noteExists, setNoteExists] = useState([]);
|
||||
const addNewNotebook = async () => {
|
||||
if (!newNotebookTitle || newNotebookTitle.trim().length === 0)
|
||||
return ToastEvent.show({
|
||||
heading: "Notebook title is required",
|
||||
type: "error",
|
||||
context: "local"
|
||||
});
|
||||
|
||||
let id = await db.notebooks.add({
|
||||
title: newNotebookTitle,
|
||||
description: null,
|
||||
topics: [],
|
||||
id: null
|
||||
});
|
||||
|
||||
setExpanded(id);
|
||||
openAddTopicDialog(db.notebooks.notebook(id).data);
|
||||
notebookInput.current?.clear();
|
||||
notebookInput.current?.blur();
|
||||
setNotebooks();
|
||||
updateNoteExists();
|
||||
};
|
||||
|
||||
const addNewTopic = async (value, item) => {
|
||||
if (!value || value.trim().length === 0) {
|
||||
ToastEvent.show({
|
||||
heading: "Topic title is required",
|
||||
type: "error",
|
||||
context: "local"
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
await db.notebooks.add({
|
||||
title: title,
|
||||
description: null,
|
||||
topics: [],
|
||||
id: null
|
||||
});
|
||||
await db.notebooks.notebook(item.id).topics.add(value);
|
||||
setNotebooks();
|
||||
updateNoteExists();
|
||||
return true;
|
||||
};
|
||||
|
||||
const handlePress = async (item) => {
|
||||
let noteIds =
|
||||
selectedItemsList.length > 0
|
||||
? selectedItemsList.map((n) => n.id)
|
||||
: [note?.id];
|
||||
|
||||
if (getCount(item)) {
|
||||
await db.notes.removeFromNotebook(
|
||||
{
|
||||
id: item.notebookId,
|
||||
topic: item.id
|
||||
},
|
||||
...noteIds
|
||||
);
|
||||
} else {
|
||||
await db.notes.addToNotebook(
|
||||
{
|
||||
topic: item.id,
|
||||
id: item.notebookId,
|
||||
rebuildCache: true
|
||||
},
|
||||
...noteIds
|
||||
);
|
||||
}
|
||||
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
|
||||
setNotebooks();
|
||||
updateNoteExists();
|
||||
SearchService.updateAndSearch();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
updateNoteExists();
|
||||
}, [updateNoteExists]);
|
||||
|
||||
const updateNoteExists = useCallback(() => {
|
||||
if (!note?.id && selectedItemsList?.length === 0) return;
|
||||
|
||||
let notes =
|
||||
selectedItemsList.length > 0
|
||||
? selectedItemsList.map((n) => n.id)
|
||||
: [note?.id];
|
||||
let ids = [];
|
||||
|
||||
let notebooks = db.notebooks.all;
|
||||
for (let i = 0; i < notebooks.length; i++) {
|
||||
if (notebooks[i].topics) {
|
||||
for (let t = 0; t < notebooks[i].topics.length; t++) {
|
||||
let topic = notebooks[i].topics[t];
|
||||
if (topic.type !== "topic") continue;
|
||||
for (let id of notes) {
|
||||
const noteIds = db.notes?.topicReferences.get(topic.id);
|
||||
if (noteIds.indexOf(id) > -1) {
|
||||
if (ids.indexOf(notebooks[i].id) === -1) {
|
||||
ids.push(notebooks[i].id);
|
||||
}
|
||||
if (ids.indexOf(topic.id) === -1) ids.push(topic.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setNoteExists(ids);
|
||||
}, [note?.id, selectedItemsList]);
|
||||
|
||||
const openAddTopicDialog = (item) => {
|
||||
presentDialog({
|
||||
context: "move_note",
|
||||
@@ -131,223 +226,27 @@ const MoveNoteComponent = ({ note }) => {
|
||||
paragraph: "Add a new topic in " + item.title,
|
||||
positiveText: "Add",
|
||||
positivePress: (value) => {
|
||||
return onAddTopic(value, item);
|
||||
return addNewTopic(value, item);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onAddTopic = useCallback(
|
||||
async (value, item) => {
|
||||
if (!value || value.trim().length === 0) {
|
||||
ToastEvent.show({
|
||||
heading: "Topic title is required",
|
||||
type: "error",
|
||||
context: "local"
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
await db.notebooks.notebook(item.id).topics.add(value);
|
||||
setNotebooks();
|
||||
return true;
|
||||
},
|
||||
[setNotebooks]
|
||||
);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const getSelectedNotesCountInItem = React.useCallback(
|
||||
(item) => {
|
||||
switch (item.type) {
|
||||
case "notebook": {
|
||||
const noteIds = [];
|
||||
for (let topic of item.topics) {
|
||||
noteIds.push(...(db.notes?.topicReferences.get(topic.id) || []));
|
||||
}
|
||||
let count = 0;
|
||||
selectedItemsList.forEach((item) =>
|
||||
noteIds.indexOf(item.id) > -1 ? count++ : undefined
|
||||
);
|
||||
return count;
|
||||
}
|
||||
case "topic": {
|
||||
const noteIds = db.notes?.topicReferences.get(item.id);
|
||||
let count = 0;
|
||||
selectedItemsList.forEach((item) =>
|
||||
noteIds.indexOf(item.id) > -1 ? count++ : undefined
|
||||
);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
},
|
||||
[selectedItemsList]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
resetItemState();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const resetItemState = useCallback(
|
||||
(state) => {
|
||||
setItemState(() => {
|
||||
const itemState = {};
|
||||
const notebooks = db.notebooks.all;
|
||||
for (let notebook of notebooks) {
|
||||
itemState[notebook.id] = state
|
||||
? state
|
||||
: areAllSelectedItemsInAllTopics(notebook, selectedItemsList) &&
|
||||
getSelectedNotesCountInItem(notebook, selectedItemsList) > 0
|
||||
? "selected"
|
||||
: getSelectedNotesCountInItem(notebook, selectedItemsList) > 0
|
||||
? "intermediate"
|
||||
: "deselected";
|
||||
if (itemState[notebook.id] === "selected") {
|
||||
contextValue.select(notebook);
|
||||
}
|
||||
for (let topic of notebook.topics) {
|
||||
itemState[topic.id] = state
|
||||
? state
|
||||
: areAllSelectedItemsInTopic(topic, selectedItemsList) &&
|
||||
getSelectedNotesCountInItem(topic, selectedItemsList)
|
||||
? "selected"
|
||||
: getSelectedNotesCountInItem(topic, selectedItemsList) > 0
|
||||
? "intermediate"
|
||||
: "deselected";
|
||||
if (itemState[topic.id] === "selected") {
|
||||
contextValue.select(topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
return itemState;
|
||||
});
|
||||
},
|
||||
[contextValue, getSelectedNotesCountInItem, selectedItemsList]
|
||||
);
|
||||
|
||||
const getItemsForItem = (item) => {
|
||||
switch (item.type) {
|
||||
case "notebook":
|
||||
return item.topics?.filter((t) => t.type === "topic");
|
||||
}
|
||||
};
|
||||
|
||||
function areAllSelectedItemsInAllTopics(notebook, items) {
|
||||
return items.every((item) => {
|
||||
return notebook.topics.every((topic) => {
|
||||
return db.notes.topicReferences.get(topic.id).indexOf(item.id) > -1;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function areAllSelectedItemsInTopic(topic, items) {
|
||||
return items.every((item) => {
|
||||
return db.notes.topicReferences.get(topic.id).indexOf(item.id) > -1;
|
||||
});
|
||||
}
|
||||
|
||||
const updateItemState = useCallback(function (item, state) {
|
||||
setItemState((itemState) => {
|
||||
const mergeState = {
|
||||
[item.id]: state
|
||||
};
|
||||
const notebooks = db.notebooks.all;
|
||||
const notebook =
|
||||
item.type === "notebook"
|
||||
? item
|
||||
: notebooks.find((n) => n.id === item.notebookId);
|
||||
const intermediate = notebook.topics.some((topic) => {
|
||||
return topic.id === item.id
|
||||
? state === "selected"
|
||||
: itemState[topic.id] === "selected";
|
||||
});
|
||||
if (intermediate) mergeState[notebook.id] = "intermediate";
|
||||
const selected = notebook.topics.every((topic) => {
|
||||
return topic.id === item.id
|
||||
? state === "selected"
|
||||
: itemState[topic.id] === "selected";
|
||||
});
|
||||
if (selected) mergeState[notebook.id] = "selected";
|
||||
if (!selected && !intermediate) mergeState[notebook.id] = "deselected";
|
||||
|
||||
return {
|
||||
...itemState,
|
||||
...mergeState
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
enabled: multiSelect,
|
||||
toggleSelection: (item) => {
|
||||
setItemState((itemState) => {
|
||||
if (itemState[item.id] === "selected") {
|
||||
updateItemState(item, "deselected");
|
||||
} else {
|
||||
updateItemState(item, "selected");
|
||||
}
|
||||
|
||||
return itemState;
|
||||
});
|
||||
},
|
||||
setMultiSelect: setMultiSelect,
|
||||
deselect: (item) => {
|
||||
updateItemState(item, "deselected");
|
||||
},
|
||||
select: (item) => {
|
||||
updateItemState(item, "selected");
|
||||
},
|
||||
deselectAll: (state) => {
|
||||
resetItemState(state);
|
||||
}
|
||||
}),
|
||||
[multiSelect, resetItemState, updateItemState]
|
||||
);
|
||||
|
||||
const getItemFromId = (id) => {
|
||||
for (const nb of notebooks) {
|
||||
if (nb.id === id) return nb;
|
||||
for (const tp of nb.topics) {
|
||||
if (tp.id === id) return tp;
|
||||
const getCount = (topic) => {
|
||||
if (!topic) return;
|
||||
let notes =
|
||||
selectedItemsList.length > 0
|
||||
? selectedItemsList.map((n) => n.id)
|
||||
: [note?.id];
|
||||
let count = 0;
|
||||
for (let id of notes) {
|
||||
const noteIds = db.notes?.topicReferences.get(topic.id);
|
||||
if (noteIds.indexOf(id) > -1) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onSave = async () => {
|
||||
for (const id in itemState) {
|
||||
const item = getItemFromId(id);
|
||||
if (item.type === "notebook") continue;
|
||||
const noteIds = selectedItemsList.map((n) => n.id);
|
||||
if (itemState[id] === "selected") {
|
||||
await db.notes.addToNotebook(
|
||||
{
|
||||
topic: item.id,
|
||||
id: item.notebookId,
|
||||
rebuildCache: true
|
||||
},
|
||||
...noteIds
|
||||
);
|
||||
} else if (itemState[id] === "deselected") {
|
||||
await db.notes.removeFromNotebook(
|
||||
{
|
||||
id: item.notebookId,
|
||||
topic: item.id,
|
||||
rebuildCache: true
|
||||
},
|
||||
...noteIds
|
||||
);
|
||||
}
|
||||
}
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
setNotebooks();
|
||||
SearchService.updateAndSearch();
|
||||
actionSheetRef.current?.hide();
|
||||
return count > 0 && notes.length > 0
|
||||
? `${count} of ${notes.length} selected notes exist in this topic. (Tap to remove)`
|
||||
: null;
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -367,162 +266,215 @@ const MoveNoteComponent = ({ note }) => {
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
justifyContent: "space-between",
|
||||
flexDirection: "row",
|
||||
alignItems: "flex-start"
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
>
|
||||
<DialogHeader
|
||||
style={{
|
||||
minHeight: 10,
|
||||
flexShrink: 1
|
||||
}}
|
||||
title="Select notebooks"
|
||||
paragraph={
|
||||
!multiSelect
|
||||
? "Long press to enable multi-select."
|
||||
: "Select topics you want to add note(s) to."
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
height={35}
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 24,
|
||||
alignSelf: "flex-start"
|
||||
}}
|
||||
title="Save"
|
||||
type={"accent"}
|
||||
onPress={onSave}
|
||||
title="Add to notebook"
|
||||
paragraph={"Add your notes to notebooks to find them easily."}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{multiSelect ? (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
title="Reset selection"
|
||||
height={30}
|
||||
<FlatList
|
||||
nestedScrollEnabled={true}
|
||||
onMomentumScrollEnd={() => {
|
||||
actionSheetRef.current?.handleChildScrollEnd();
|
||||
}}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="none"
|
||||
data={notebooks}
|
||||
ListFooterComponent={
|
||||
<View
|
||||
style={{
|
||||
alignSelf: "flex-start",
|
||||
paddingHorizontal: 0
|
||||
}}
|
||||
onPress={() => {
|
||||
resetItemState();
|
||||
setMultiSelect(false);
|
||||
height: 200
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
<SelectionProvider value={contextValue}>
|
||||
<FilteredList
|
||||
onMomentumScrollEnd={() => {
|
||||
actionSheetRef.current?.handleChildScrollEnd();
|
||||
}}
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
ListEmptyComponent={
|
||||
notebooks.length > 0 ? null : (
|
||||
}
|
||||
ListHeaderComponent={
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
marginTop: 10
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
fwdRef={notebookInput}
|
||||
onChangeText={(value) => {
|
||||
newNotebookTitle = value;
|
||||
}}
|
||||
testID={notesnook.ids.dialogs.addTo.addNotebook}
|
||||
blurOnSubmit={false}
|
||||
onFocusInput={() => {
|
||||
setNotebookInputFocused(true);
|
||||
}}
|
||||
onBlurInput={() => {
|
||||
setNotebookInputFocused(false);
|
||||
}}
|
||||
button={{
|
||||
icon: "check",
|
||||
color: notebookInputFocused ? colors.accent : colors.icon,
|
||||
onPress: addNewNotebook
|
||||
}}
|
||||
onSubmit={addNewNotebook}
|
||||
placeholder="Create a new notebook"
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
renderItem={({ item }) => (
|
||||
<View
|
||||
style={{
|
||||
borderWidth: 1,
|
||||
borderColor: expanded ? colors.nav : "transparent",
|
||||
borderRadius: 6,
|
||||
overflow: "hidden",
|
||||
marginBottom: 10
|
||||
}}
|
||||
>
|
||||
<PressableButton
|
||||
onPress={() => {
|
||||
if (!item.topics || item.topics.length === 0) {
|
||||
setExpanded(item.id);
|
||||
openAddTopicDialog(item);
|
||||
return;
|
||||
}
|
||||
layoutmanager.withAnimation(200);
|
||||
setExpanded(item.id === expanded ? null : item.id);
|
||||
setNotebookInputFocused(false);
|
||||
}}
|
||||
type="grayBg"
|
||||
customStyle={{
|
||||
height: 50,
|
||||
width: "100%",
|
||||
borderRadius: 5,
|
||||
alignItems: "flex-start"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
height: 50,
|
||||
justifyContent: "space-between",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
<Icon name="book-outline" color={colors.icon} size={100} />
|
||||
<Paragraph style={{ marginBottom: 10 }}>
|
||||
You do not have any notebooks.
|
||||
</Paragraph>
|
||||
<View>
|
||||
<Heading
|
||||
color={
|
||||
noteExists.indexOf(item.id) > -1 ? colors.accent : null
|
||||
}
|
||||
size={SIZE.md}
|
||||
>
|
||||
{item.title}
|
||||
</Heading>
|
||||
{item.topics?.length > 0 ? (
|
||||
<Paragraph size={SIZE.xs} color={colors.icon}>
|
||||
{getTotalNotes(item) + " notes" + " & "}
|
||||
{item.topics.length === 1
|
||||
? item.topics.length + " topic"
|
||||
: item.topics.length + " topics"}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
<IconButton
|
||||
name={expanded === item.id ? "plus" : "chevron-down"}
|
||||
color={expanded === item.id ? colors.accent : colors.pri}
|
||||
size={SIZE.xl}
|
||||
onPress={() => {
|
||||
if (expanded !== item.id) {
|
||||
setExpanded(item.id);
|
||||
return;
|
||||
}
|
||||
layoutmanager.withAnimation(200);
|
||||
setExpanded(item.id);
|
||||
openAddTopicDialog(item);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
data={notebooks}
|
||||
hasHeaderSearch={true}
|
||||
renderItem={({ item, index }) => (
|
||||
<ListItem
|
||||
item={item}
|
||||
key={item.id}
|
||||
index={index}
|
||||
intermediate={itemState[item.id] === "intermediate"}
|
||||
removed={
|
||||
itemState[item.id] === "deselected" &&
|
||||
getSelectedNotesCountInItem(item) > 0
|
||||
}
|
||||
isSelected={itemState[item.id] === "selected"}
|
||||
infoText={
|
||||
<>
|
||||
{item.topics.length === 1
|
||||
? item.topics.length + " topic"
|
||||
: item.topics.length + " topics"}
|
||||
</>
|
||||
}
|
||||
getListItems={getItemsForItem}
|
||||
getSublistItemProps={(topic) => ({
|
||||
selected: itemState[topic.id] === "selected",
|
||||
intermediate: itemState[topic.id] === "intermediate",
|
||||
isSelected: itemState[topic.id] === "selected",
|
||||
removed:
|
||||
itemState[topic.id] === "deselected" &&
|
||||
getSelectedNotesCountInItem(topic) > 0,
|
||||
style: {
|
||||
marginBottom: 0,
|
||||
height: 40
|
||||
},
|
||||
onPress: (item) => {
|
||||
const currentState = itemState[item.id];
|
||||
if (currentState !== "selected") {
|
||||
resetItemState("deselected");
|
||||
contextValue.select(item);
|
||||
updateItemState(
|
||||
notebooks.find((n) => n.id === item.notebookId),
|
||||
"intermediate"
|
||||
);
|
||||
} else {
|
||||
contextValue.deselect(item);
|
||||
}
|
||||
},
|
||||
key: item.id,
|
||||
type: "transparent"
|
||||
})}
|
||||
icon={(expanded) => ({
|
||||
name: expanded ? "chevron-up" : "chevron-down",
|
||||
color: expanded ? colors.accent : colors.pri
|
||||
})}
|
||||
onScrollEnd={() => {
|
||||
actionSheetRef.current?.handleChildScrollEnd();
|
||||
}}
|
||||
hasSubList={true}
|
||||
hasHeaderSearch={false}
|
||||
type="grayBg"
|
||||
sublistItemType="topic"
|
||||
onAddItem={(title) => {
|
||||
return onAddTopic(title, item);
|
||||
}}
|
||||
onAddSublistItem={(item) => {
|
||||
openAddTopicDialog(item);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
itemType="notebook"
|
||||
onAddItem={async (title) => {
|
||||
return await onAddNotebook(title);
|
||||
}}
|
||||
ListFooterComponent={
|
||||
<View
|
||||
style={{
|
||||
height: 200
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</SelectionProvider>
|
||||
</PressableButton>
|
||||
|
||||
{expanded === item.id ? (
|
||||
<FlatList
|
||||
nestedScrollEnabled
|
||||
data={item.topics?.filter((t) => t.type === "topic")}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="none"
|
||||
onMomentumScrollEnd={() => {
|
||||
actionSheetRef.current?.handleChildScrollEnd();
|
||||
}}
|
||||
style={{
|
||||
width: "100%",
|
||||
alignSelf: "flex-end",
|
||||
maxHeight: 500
|
||||
}}
|
||||
renderItem={({ item, index }) => (
|
||||
<PressableButton
|
||||
onPress={() => handlePress(item, index)}
|
||||
type="gray"
|
||||
customStyle={{
|
||||
minHeight: 50,
|
||||
borderTopWidth: index === 0 ? 0 : 1,
|
||||
borderTopColor: index === 0 ? null : colors.nav,
|
||||
width: "100%",
|
||||
borderRadius: 0,
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 12,
|
||||
justifyContent: "space-between",
|
||||
paddingVertical: 12
|
||||
}}
|
||||
>
|
||||
<View>
|
||||
<Paragraph color={colors.heading}>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
<Paragraph color={colors.icon} size={SIZE.xs}>
|
||||
{getTotalNotes(item) + " notes"}
|
||||
</Paragraph>
|
||||
{getCount(item) ? (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: colors.nav,
|
||||
borderRadius: 5,
|
||||
paddingHorizontal: 5,
|
||||
paddingVertical: 2,
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.icon} size={SIZE.xs}>
|
||||
{getCount(item)}
|
||||
</Paragraph>
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
{noteExists.indexOf(item.id) > -1 ? (
|
||||
<Button
|
||||
onPress={() => handlePress(item, index)}
|
||||
icon="check"
|
||||
testID="icon-check"
|
||||
iconColor={colors.accent}
|
||||
iconSize={SIZE.lg}
|
||||
height={35}
|
||||
width={35}
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 0
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</PressableButton>
|
||||
)}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,87 +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 { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import Input from "../../ui/input";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
|
||||
export const ListHeaderInputItem = ({
|
||||
onSubmit,
|
||||
onChangeText,
|
||||
placeholder,
|
||||
testID
|
||||
}) => {
|
||||
const [focused, setFocused] = useState(false);
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const [inputValue, setInputValue] = useState();
|
||||
const inputRef = useRef();
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
marginTop: 10,
|
||||
marginBottom: 5
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
fwdRef={inputRef}
|
||||
onChangeText={(value) => {
|
||||
setInputValue(value);
|
||||
onChangeText?.(value);
|
||||
}}
|
||||
testID={testID}
|
||||
blurOnSubmit={false}
|
||||
onFocusInput={() => {
|
||||
setFocused(true);
|
||||
}}
|
||||
onBlurInput={() => {
|
||||
setFocused(false);
|
||||
}}
|
||||
button={{
|
||||
icon: inputValue ? "plus" : "magnify",
|
||||
color: focused ? colors.accent : colors.icon,
|
||||
onPress: async () => {
|
||||
const result = await onSubmit(inputValue);
|
||||
if (result) {
|
||||
inputRef.current?.blur();
|
||||
}
|
||||
}
|
||||
}}
|
||||
placeholder={placeholder}
|
||||
marginBottom={5}
|
||||
/>
|
||||
{inputValue ? (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: colors.shade,
|
||||
padding: 5,
|
||||
borderRadius: 5,
|
||||
marginBottom: 10
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.accent}>
|
||||
Tap on + to add {`"${inputValue}"`}
|
||||
</Paragraph>
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -1,233 +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, { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import { PressableButton } from "../../ui/pressable";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { useSelectionContext } from "./context";
|
||||
import { FilteredList } from "./filtered-list";
|
||||
|
||||
const _ListItem = ({
|
||||
item,
|
||||
index,
|
||||
icon,
|
||||
infoText,
|
||||
intermediate,
|
||||
hasSubList,
|
||||
onPress,
|
||||
onScrollEnd,
|
||||
getListItems,
|
||||
style,
|
||||
type,
|
||||
sublistItemType,
|
||||
onAddItem,
|
||||
getSublistItemProps,
|
||||
removed,
|
||||
isSelected,
|
||||
hasHeaderSearch,
|
||||
onAddSublistItem
|
||||
}) => {
|
||||
const { enabled, toggleSelection, setMultiSelect, select, deselect } =
|
||||
useSelectionContext();
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
function selectItem() {
|
||||
const currentState = isSelected;
|
||||
toggleSelection(item);
|
||||
if (item.type === "notebook") {
|
||||
item.topics.forEach((item) => {
|
||||
if (currentState) {
|
||||
deselect(item);
|
||||
} else {
|
||||
select(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
overflow: "hidden",
|
||||
marginBottom: 10,
|
||||
...style
|
||||
}}
|
||||
>
|
||||
<PressableButton
|
||||
onPress={() => {
|
||||
if (hasSubList) return setExpanded(!expanded);
|
||||
if (enabled) return selectItem();
|
||||
onPress?.(item);
|
||||
}}
|
||||
type={type}
|
||||
onLongPress={() => {
|
||||
setMultiSelect(true);
|
||||
selectItem();
|
||||
}}
|
||||
customStyle={{
|
||||
height: style?.height || 50,
|
||||
width: "100%",
|
||||
alignItems: "flex-start"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
height: 50,
|
||||
justifyContent: "space-between",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
size={22}
|
||||
customStyle={{
|
||||
marginRight: 5,
|
||||
width: 23,
|
||||
height: 23
|
||||
}}
|
||||
color={
|
||||
removed
|
||||
? colors.red
|
||||
: intermediate || isSelected
|
||||
? colors.accent
|
||||
: colors.icon
|
||||
}
|
||||
onPress={() => {
|
||||
if (item.type === "notebook") {
|
||||
setMultiSelect(true);
|
||||
}
|
||||
selectItem();
|
||||
if (enabled) return;
|
||||
onPress?.(item);
|
||||
}}
|
||||
testID={
|
||||
removed
|
||||
? "close-circle-outline"
|
||||
: isSelected
|
||||
? "check-circle-outline"
|
||||
: intermediate
|
||||
? "minus-circle-outline"
|
||||
: "checkbox-blank-circle-outline"
|
||||
}
|
||||
name={
|
||||
removed
|
||||
? "close-circle-outline"
|
||||
: isSelected
|
||||
? "check-circle-outline"
|
||||
: intermediate
|
||||
? "minus-circle-outline"
|
||||
: "checkbox-blank-circle-outline"
|
||||
}
|
||||
/>
|
||||
<View>
|
||||
{hasSubList && expanded ? (
|
||||
<Heading size={SIZE.md}>{item.title}</Heading>
|
||||
) : (
|
||||
<Paragraph size={SIZE.sm}>{item.title}</Paragraph>
|
||||
)}
|
||||
|
||||
{infoText ? (
|
||||
<Paragraph size={SIZE.xs} color={colors.icon}>
|
||||
{infoText}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
{onAddSublistItem ? (
|
||||
<IconButton
|
||||
name={"plus"}
|
||||
testID="add-item-icon"
|
||||
color={colors}
|
||||
size={SIZE.xl}
|
||||
onPress={() => {
|
||||
onAddSublistItem(item);
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{icon ? (
|
||||
<IconButton
|
||||
name={icon(expanded).name}
|
||||
color={icon(expanded).color}
|
||||
size={icon(expanded).size || SIZE.xl}
|
||||
onPress={
|
||||
hasSubList
|
||||
? () => setExpanded(!expanded)
|
||||
: icon(expanded).onPress
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
</PressableButton>
|
||||
|
||||
{expanded && hasSubList ? (
|
||||
<FilteredList
|
||||
nestedScrollEnabled
|
||||
data={getListItems(item)}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="none"
|
||||
onMomentumScrollEnd={onScrollEnd}
|
||||
style={{
|
||||
width: "95%",
|
||||
alignSelf: "flex-end",
|
||||
maxHeight: 500
|
||||
}}
|
||||
itemType={sublistItemType}
|
||||
hasHeaderSearch={hasHeaderSearch}
|
||||
renderItem={({ item, index }) => (
|
||||
<ListItem
|
||||
item={item}
|
||||
{...getSublistItemProps(item)}
|
||||
index={index}
|
||||
onScrollEnd={onScrollEnd}
|
||||
/>
|
||||
)}
|
||||
onAddItem={onAddItem}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export const ListItem = React.memo(_ListItem, (prev, next) => {
|
||||
if (prev.selected === undefined) return false;
|
||||
if (prev.isSelected !== next.isSelected) return false;
|
||||
if (prev.selected !== next.selected) return false;
|
||||
if (prev.intermediate !== next.intermediate) return false;
|
||||
return true;
|
||||
});
|
||||
@@ -259,7 +259,6 @@ export default function ReminderSheet({
|
||||
marginTop: 10
|
||||
}}
|
||||
multiline
|
||||
textAlignVertical="top"
|
||||
inputStyle={{
|
||||
height: 80
|
||||
}}
|
||||
|
||||
@@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React, { useCallback } from "react";
|
||||
import { FlatList, View } from "react-native";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import umami from "../../common/analytics";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
@@ -75,6 +76,7 @@ export const SideMenu = React.memo(
|
||||
name: "Notesnook Pro",
|
||||
icon: "crown",
|
||||
func: () => {
|
||||
umami.pageView("/pro-screen", "/sidemenu");
|
||||
eSendEvent(eOpenPremiumDialog);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -59,8 +59,8 @@ const SheetWrapper = ({
|
||||
zIndex: 10,
|
||||
paddingTop: 5,
|
||||
paddingBottom: 0,
|
||||
borderTopRightRadius: 20,
|
||||
borderTopLeftRadius: 20,
|
||||
borderTopRightRadius: 10,
|
||||
borderTopLeftRadius: 10,
|
||||
alignSelf: "center",
|
||||
borderBottomRightRadius: 0,
|
||||
borderBottomLeftRadius: 0
|
||||
@@ -104,12 +104,12 @@ const SheetWrapper = ({
|
||||
ExtraOverlayComponent={
|
||||
<>
|
||||
{overlay}
|
||||
<Toast context="local" />
|
||||
<PremiumToast
|
||||
context="sheet"
|
||||
close={() => fwdRef?.current?.hide()}
|
||||
offset={50}
|
||||
/>
|
||||
<Toast context="local" />
|
||||
</>
|
||||
}
|
||||
onClose={_onClose}
|
||||
|
||||
@@ -21,6 +21,7 @@ import Clipboard from "@react-native-clipboard/clipboard";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { Platform } from "react-native";
|
||||
import Share from "react-native-share";
|
||||
import { notesnook } from "../../e2e/test.ids";
|
||||
import { db } from "../common/database";
|
||||
import { presentDialog } from "../components/dialog/functions";
|
||||
import NoteHistory from "../components/note-history";
|
||||
@@ -601,35 +602,29 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
};
|
||||
const actions = [
|
||||
{
|
||||
id: "notebooks",
|
||||
title: "Link Notebooks",
|
||||
name: "Add to notebook",
|
||||
title: "Add to notebook",
|
||||
icon: "book-outline",
|
||||
func: addTo
|
||||
},
|
||||
{
|
||||
id: "add-tag",
|
||||
title: "Add tags",
|
||||
icon: "pound",
|
||||
func: addTo
|
||||
},
|
||||
{
|
||||
id: "add-reminder",
|
||||
title: "Remind me",
|
||||
icon: "clock-plus-outline",
|
||||
name: "Add-Reminder",
|
||||
title: "Add reminder",
|
||||
icon: "bell-plus",
|
||||
func: () => {
|
||||
ReminderSheet.present(null, { id: item.id, type: "note" });
|
||||
},
|
||||
close: true
|
||||
},
|
||||
{
|
||||
id: "lock-unlock",
|
||||
name: "Vault",
|
||||
title: item.locked ? "Unlock" : "Lock",
|
||||
icon: item.locked ? "lock-open-outline" : "key-outline",
|
||||
icon: item.locked ? "shield-off-outline" : "shield-outline",
|
||||
func: addToVault,
|
||||
on: item.locked
|
||||
},
|
||||
{
|
||||
id: "publish",
|
||||
name: "Publish",
|
||||
title: isPublished ? "Published" : "Publish",
|
||||
icon: "cloud-upload-outline",
|
||||
on: isPublished,
|
||||
@@ -637,157 +632,15 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
},
|
||||
|
||||
{
|
||||
id: "export",
|
||||
name: "Export",
|
||||
title: "Export",
|
||||
icon: "export",
|
||||
func: exportNote
|
||||
},
|
||||
{
|
||||
id: "move-notes",
|
||||
title: "Add notes",
|
||||
icon: "plus",
|
||||
func: async () => {
|
||||
close();
|
||||
await sleep(500);
|
||||
MoveNotes.present(db.notebooks.notebook(item.notebookId).data, item);
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "pin",
|
||||
title: item.pinned ? "Unpin" : "Pin",
|
||||
icon: item.pinned ? "pin-off-outline" : "pin-outline",
|
||||
func: pinItem,
|
||||
close: false,
|
||||
check: true,
|
||||
on: item.pinned,
|
||||
pro: true
|
||||
},
|
||||
{
|
||||
id: "favorite",
|
||||
title: !item.favorite ? "Favorite" : "Unfavorite",
|
||||
icon: item.favorite ? "star-off" : "star-outline",
|
||||
func: addToFavorites,
|
||||
close: false,
|
||||
check: true,
|
||||
on: item.favorite,
|
||||
pro: true,
|
||||
color: "orange"
|
||||
},
|
||||
{
|
||||
id: "pin-to-notifications",
|
||||
title:
|
||||
notifPinned !== null
|
||||
? "Unpin from Notifications"
|
||||
: "Pin to notifications",
|
||||
icon: "message-badge-outline",
|
||||
on: notifPinned !== null,
|
||||
func: pinToNotifications
|
||||
},
|
||||
|
||||
{
|
||||
id: "edit-notebook",
|
||||
title: "Edit notebook",
|
||||
icon: "square-edit-outline",
|
||||
func: async () => {
|
||||
close();
|
||||
await sleep(300);
|
||||
eSendEvent(eOpenAddNotebookDialog, item);
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "edit-topic",
|
||||
title: "Edit topic",
|
||||
icon: "square-edit-outline",
|
||||
func: async () => {
|
||||
close();
|
||||
await sleep(300);
|
||||
eSendEvent(eOpenAddTopicDialog, {
|
||||
notebookId: item.notebookId,
|
||||
toEdit: item
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "copy",
|
||||
title: "Copy",
|
||||
icon: "content-copy",
|
||||
func: copyContent
|
||||
},
|
||||
{
|
||||
id: "restore",
|
||||
title: "Restore " + item.itemType,
|
||||
icon: "delete-restore",
|
||||
func: restoreTrashItem
|
||||
},
|
||||
|
||||
{
|
||||
id: "add-shortcut",
|
||||
title: isPinnedToMenu ? "Remove Shortcut" : "Add Shortcut",
|
||||
icon: isPinnedToMenu ? "link-variant-remove" : "link-variant",
|
||||
func: createMenuShortcut,
|
||||
close: false,
|
||||
check: true,
|
||||
on: isPinnedToMenu,
|
||||
pro: true
|
||||
},
|
||||
{
|
||||
id: "rename-tag",
|
||||
title: "Rename tag",
|
||||
icon: "square-edit-outline",
|
||||
func: renameTag
|
||||
},
|
||||
{
|
||||
id: "share",
|
||||
title: "Share",
|
||||
icon: "share-variant",
|
||||
func: shareNote
|
||||
},
|
||||
{
|
||||
id: "read-only",
|
||||
title: "Read only",
|
||||
icon: "pencil-lock",
|
||||
func: toggleReadyOnlyMode,
|
||||
on: item.readonly
|
||||
},
|
||||
{
|
||||
id: "local-only",
|
||||
title: "Local only",
|
||||
icon: "sync-off",
|
||||
func: toggleLocalOnly,
|
||||
on: item.localOnly
|
||||
},
|
||||
{
|
||||
id: "duplicate",
|
||||
title: "Duplicate",
|
||||
icon: "content-duplicate",
|
||||
func: duplicateNote
|
||||
},
|
||||
{
|
||||
id: "dark-mode",
|
||||
title: "Dark mode",
|
||||
icon: "theme-light-dark",
|
||||
func: switchTheme,
|
||||
switch: true,
|
||||
on: colors.night ? true : false,
|
||||
close: false,
|
||||
pro: true
|
||||
},
|
||||
{
|
||||
id: "edit-reminder",
|
||||
title: "Edit reminder",
|
||||
icon: "pencil",
|
||||
func: async () => {
|
||||
close();
|
||||
await sleep(300);
|
||||
ReminderSheet.present(item);
|
||||
},
|
||||
close: false
|
||||
},
|
||||
|
||||
{
|
||||
id: "reminders",
|
||||
name: "Reminders",
|
||||
title: "Reminders",
|
||||
icon: "clock-outline",
|
||||
icon: "bell",
|
||||
func: async () => {
|
||||
close();
|
||||
RelationsList.present({
|
||||
@@ -807,20 +660,165 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
close: false
|
||||
},
|
||||
{
|
||||
id: "attachments",
|
||||
name: "Attachments",
|
||||
title: "Attachments",
|
||||
icon: "attachment",
|
||||
func: showAttachments
|
||||
},
|
||||
{
|
||||
id: "history",
|
||||
name: "History",
|
||||
title: "History",
|
||||
icon: "history",
|
||||
func: openHistory
|
||||
},
|
||||
{
|
||||
name: "Move notes",
|
||||
title: "Add notes",
|
||||
icon: "plus",
|
||||
func: async () => {
|
||||
close();
|
||||
await sleep(500);
|
||||
MoveNotes.present(db.notebooks.notebook(item.notebookId).data, item);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Pin",
|
||||
title: item.pinned ? "Unpin" : "Pin",
|
||||
icon: item.pinned ? "pin-off-outline" : "pin-outline",
|
||||
func: pinItem,
|
||||
close: false,
|
||||
check: true,
|
||||
on: item.pinned,
|
||||
nopremium: true,
|
||||
id: notesnook.ids.dialogs.actionsheet.pin
|
||||
},
|
||||
{
|
||||
name: "Favorite",
|
||||
title: !item.favorite ? "Favorite" : "Unfavorite",
|
||||
icon: item.favorite ? "star-off" : "star-outline",
|
||||
func: addToFavorites,
|
||||
close: false,
|
||||
check: true,
|
||||
on: item.favorite,
|
||||
nopremium: true,
|
||||
id: notesnook.ids.dialogs.actionsheet.favorite,
|
||||
color: "orange"
|
||||
},
|
||||
{
|
||||
name: "PinToNotif",
|
||||
title:
|
||||
notifPinned !== null
|
||||
? "Unpin from Notifications"
|
||||
: "Pin to Notifications",
|
||||
icon: "bell",
|
||||
on: notifPinned !== null,
|
||||
func: pinToNotifications
|
||||
},
|
||||
|
||||
{
|
||||
id: "disable-reminder",
|
||||
name: "Edit Notebook",
|
||||
title: "Edit notebook",
|
||||
icon: "square-edit-outline",
|
||||
func: async () => {
|
||||
close();
|
||||
await sleep(300);
|
||||
eSendEvent(eOpenAddNotebookDialog, item);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Edit Topic",
|
||||
title: "Edit topic",
|
||||
icon: "square-edit-outline",
|
||||
func: async () => {
|
||||
close();
|
||||
await sleep(300);
|
||||
eSendEvent(eOpenAddTopicDialog, {
|
||||
notebookId: item.notebookId,
|
||||
toEdit: item
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Copy",
|
||||
title: "Copy",
|
||||
icon: "content-copy",
|
||||
func: copyContent
|
||||
},
|
||||
{
|
||||
name: "Restore",
|
||||
title: "Restore " + item.itemType,
|
||||
icon: "delete-restore",
|
||||
func: restoreTrashItem
|
||||
},
|
||||
|
||||
{
|
||||
name: "Add Shortcut",
|
||||
title: isPinnedToMenu ? "Remove Shortcut" : "Add Shortcut",
|
||||
icon: isPinnedToMenu ? "link-variant-remove" : "link-variant",
|
||||
func: createMenuShortcut,
|
||||
close: false,
|
||||
check: true,
|
||||
on: isPinnedToMenu,
|
||||
nopremium: true,
|
||||
id: notesnook.ids.dialogs.actionsheet.pinMenu
|
||||
},
|
||||
{
|
||||
name: "Rename Tag",
|
||||
title: "Rename tag",
|
||||
icon: "square-edit-outline",
|
||||
func: renameTag
|
||||
},
|
||||
{
|
||||
name: "Share",
|
||||
title: "Share",
|
||||
icon: "share-variant",
|
||||
func: shareNote
|
||||
},
|
||||
{
|
||||
name: "ReadOnly",
|
||||
title: "Read only",
|
||||
icon: "pencil-lock",
|
||||
func: toggleReadyOnlyMode,
|
||||
on: item.readonly
|
||||
},
|
||||
{
|
||||
name: "Local only",
|
||||
title: "Local only",
|
||||
icon: "sync-off",
|
||||
func: toggleLocalOnly,
|
||||
on: item.localOnly
|
||||
},
|
||||
{
|
||||
name: "Duplicate",
|
||||
title: "Duplicate",
|
||||
icon: "content-duplicate",
|
||||
func: duplicateNote
|
||||
},
|
||||
{
|
||||
name: "Dark Mode",
|
||||
title: "Dark mode",
|
||||
icon: "theme-light-dark",
|
||||
func: switchTheme,
|
||||
switch: true,
|
||||
on: colors.night ? true : false,
|
||||
close: false,
|
||||
nopremium: true,
|
||||
id: notesnook.ids.dialogs.actionsheet.night
|
||||
},
|
||||
{
|
||||
name: "Edit reminder",
|
||||
title: "Edit reminder",
|
||||
icon: "pencil",
|
||||
func: async () => {
|
||||
close();
|
||||
await sleep(300);
|
||||
ReminderSheet.present(item);
|
||||
},
|
||||
close: false
|
||||
},
|
||||
|
||||
{
|
||||
name: "ReminderOnOff",
|
||||
title: !item.disabled ? "Turn off reminder" : "Turn on reminder",
|
||||
icon: !item.disabled ? "bell-off-outline" : "bell",
|
||||
func: async () => {
|
||||
@@ -843,14 +841,14 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "remove-from-topic",
|
||||
name: "RemoveTopic",
|
||||
title: "Remove from topic",
|
||||
hidden: !isNoteInTopic(),
|
||||
icon: "minus-circle-outline",
|
||||
func: removeNoteFromTopic
|
||||
},
|
||||
{
|
||||
id: "trash",
|
||||
name: "Delete",
|
||||
title:
|
||||
item.type !== "notebook" && item.type !== "note"
|
||||
? "Delete " + item.type
|
||||
@@ -860,13 +858,13 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
func: deleteItem
|
||||
},
|
||||
{
|
||||
id: "delete",
|
||||
name: "PermDelete",
|
||||
title: "Delete " + item.itemType,
|
||||
icon: "delete",
|
||||
func: deleteTrashItem
|
||||
}
|
||||
// {
|
||||
// id: "ReferencedIn",
|
||||
// name: "ReferencedIn",
|
||||
// title: "References",
|
||||
// icon: "link",
|
||||
// func: async () => {
|
||||
|
||||
@@ -26,13 +26,12 @@ import {
|
||||
Linking,
|
||||
NativeEventEmitter,
|
||||
NativeModules,
|
||||
Platform,
|
||||
Keyboard
|
||||
Platform
|
||||
} from "react-native";
|
||||
import * as RNIap from "react-native-iap";
|
||||
import { enabled } from "react-native-privacy-snapshot";
|
||||
import { Walkthrough } from "../components/walkthroughs";
|
||||
import { editorController, editorState } from "../screens/editor/tiptap/utils";
|
||||
import { editorState } from "../screens/editor/tiptap/utils";
|
||||
import {
|
||||
clearMessage,
|
||||
setEmailVerifyMessage,
|
||||
@@ -495,10 +494,6 @@ export const useAppEvents = () => {
|
||||
!useUserStore.getState().verifyUser
|
||||
) {
|
||||
useUserStore.getState().setVerifyUser(true);
|
||||
if (Platform.OS === "ios") {
|
||||
editorController.current?.commands.blur();
|
||||
Keyboard.dismiss();
|
||||
}
|
||||
}
|
||||
if (
|
||||
SettingsService.get().privacyScreen ||
|
||||
|
||||
@@ -28,9 +28,8 @@ import {
|
||||
NativeEventSubscription
|
||||
} from "react-native";
|
||||
import { WebViewMessageEvent } from "react-native-webview";
|
||||
import umami from "../../../common/analytics/index";
|
||||
import { db } from "../../../common/database";
|
||||
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 {
|
||||
@@ -54,13 +53,15 @@ import {
|
||||
eOpenPublishNoteDialog,
|
||||
eOpenTagsDialog
|
||||
} from "../../../utils/events";
|
||||
import { openLinkInBrowser } from "../../../utils/functions";
|
||||
import { tabBarRef } from "../../../utils/global-refs";
|
||||
import { NoteType } from "../../../utils/types";
|
||||
import { useDragState } from "../../settings/editor/state";
|
||||
import { EventTypes } from "./editor-events";
|
||||
import { EditorMessage, EditorProps, useEditorType } from "./types";
|
||||
import { EditorEvents, editorState } from "./utils";
|
||||
import { openLinkInBrowser } from "../../../utils/functions";
|
||||
import { EventTypes } from "./editor-events";
|
||||
import { RelationsList } from "../../../components/sheets/relations-list";
|
||||
import ReminderSheet from "../../../components/sheets/reminder";
|
||||
|
||||
const publishNote = async (editor: useEditorType) => {
|
||||
const user = useUserStore.getState().user;
|
||||
@@ -362,6 +363,7 @@ export const useEditorEvents = (
|
||||
if (editor.state.current?.isFocused) {
|
||||
editor.state.current.isFocused = true;
|
||||
}
|
||||
umami.pageView("/pro-screen", "/editor");
|
||||
eSendEvent(eOpenPremiumDialog);
|
||||
break;
|
||||
case EventTypes.monograph:
|
||||
|
||||
@@ -30,8 +30,6 @@ import SearchService from "../../services/search";
|
||||
import SettingsService from "../../services/settings";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useNotebookStore } from "../../stores/use-notebook-store";
|
||||
import { Config } from "react-native-config";
|
||||
|
||||
const onPressFloatingButton = () => {
|
||||
AddNotebookEvent();
|
||||
};
|
||||
@@ -70,7 +68,7 @@ export const Notebooks = ({
|
||||
SearchService.prepareSearch = prepareSearch;
|
||||
useNavigationStore.getState().setButtonAction(onPressFloatingButton);
|
||||
//@ts-ignore need to update typings in core to fix this
|
||||
if (db.notebooks.all.length === 0 && !Config.isTesting) {
|
||||
if (db.notebooks.all.length === 0) {
|
||||
Walkthrough.present("notebooks");
|
||||
} else {
|
||||
Walkthrough.update("notebooks");
|
||||
|
||||
@@ -31,14 +31,13 @@ import { TopicType } from "../../utils/types";
|
||||
import { editorController, editorState } from "../editor/tiptap/utils";
|
||||
|
||||
export function toCamelCase(title: string) {
|
||||
if (!title) return "";
|
||||
return title.slice(0, 1).toUpperCase() + title.slice(1);
|
||||
}
|
||||
|
||||
export function getAlias(params: Partial<NotesScreenParams>) {
|
||||
if (!params) return "";
|
||||
const { item } = params;
|
||||
return (item as TopicType)?.alias || item?.title || "";
|
||||
return (item as TopicType)?.alias || item?.title;
|
||||
}
|
||||
|
||||
export function openMonographsWebpage() {
|
||||
|
||||
@@ -21,6 +21,7 @@ import React, { useState } from "react";
|
||||
import { Dimensions, LayoutAnimation, Platform, View } from "react-native";
|
||||
import Animated, { FadeInDown, FadeOutUp } from "react-native-reanimated";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import umami from "../../common/analytics";
|
||||
import { SVG_Z } from "../../components/intro";
|
||||
import { WelcomeNotice } from "../../components/intro/welcome";
|
||||
import { Button } from "../../components/ui/button";
|
||||
@@ -204,6 +205,7 @@ const AppLock = ({ route }) => {
|
||||
type: "linear"
|
||||
}
|
||||
});
|
||||
umami.pageView("/privacymode", "/welcome");
|
||||
setStep(1);
|
||||
}}
|
||||
style={{
|
||||
|
||||
@@ -22,7 +22,6 @@ import { AccentColorPicker, HomagePageSelector } from "./appearance";
|
||||
import { AutomaticBackupsSelector } from "./backup-restore";
|
||||
import DebugLogs from "./debug";
|
||||
import { ConfigureToolbar } from "./editor/configure-toolbar";
|
||||
import { Licenses } from "./licenses";
|
||||
import SoundPicker from "./sound-picker";
|
||||
import { Subscription } from "./subscription";
|
||||
export const components: { [name: string]: ReactElement } = {
|
||||
@@ -32,6 +31,5 @@ export const components: { [name: string]: ReactElement } = {
|
||||
subscription: <Subscription />,
|
||||
configuretoolbar: <ConfigureToolbar />,
|
||||
"debug-logs": <DebugLogs />,
|
||||
"sound-picker": <SoundPicker />,
|
||||
licenses: <Licenses />
|
||||
"sound-picker": <SoundPicker />
|
||||
};
|
||||
|
||||
@@ -1,466 +0,0 @@
|
||||
/* eslint-disable header/header */
|
||||
export const LICENSES = [
|
||||
{
|
||||
name: "@callstack/repack",
|
||||
licenseType: "MIT",
|
||||
author: "zamotany <zamotany.oss@gmail.com>",
|
||||
link: "https://github.com/callstack/repack"
|
||||
},
|
||||
{
|
||||
name: "@react-native-clipboard/clipboard",
|
||||
licenseType: "MIT",
|
||||
author: "M.Haris Baig <harisbaig100@gmail.com>",
|
||||
link: "https://github.com/react-native-clipboard/clipboard"
|
||||
},
|
||||
{
|
||||
name: "@react-native-community/checkbox",
|
||||
licenseType: "MIT",
|
||||
author: "Nicholas Lee <nicholaslee119@gmail.com>",
|
||||
link: "https://github.com/react-native-community/react-native-checkbox"
|
||||
},
|
||||
{
|
||||
name: "@react-native-community/netinfo",
|
||||
licenseType: "MIT",
|
||||
author: "Matt Oakes <hello@mattoakes.net>",
|
||||
link: "https://github.com/react-native-netinfo/react-native-netinfo"
|
||||
},
|
||||
{
|
||||
name: "@react-native-community/toolbar-android",
|
||||
licenseType: "MIT",
|
||||
author: "Connectdotz <vsun@connectdotz.com>",
|
||||
link: "https://github.com/react-native-community/toolbar-android"
|
||||
},
|
||||
{
|
||||
name: "@react-native-masked-view/masked-view",
|
||||
licenseType: "MIT",
|
||||
author: "Mike Nedosekin <crespo8800@gmail.com>",
|
||||
link: "https://github.com/react-native-masked-view/masked-view"
|
||||
},
|
||||
{
|
||||
name: "@react-navigation/elements",
|
||||
licenseType: "MIT",
|
||||
author: "n/a",
|
||||
link: "https://github.com/react-navigation/react-navigation"
|
||||
},
|
||||
{
|
||||
name: "@react-navigation/native",
|
||||
licenseType: "MIT",
|
||||
author: "n/a",
|
||||
link: "https://github.com/react-navigation/react-navigation"
|
||||
},
|
||||
{
|
||||
name: "@react-navigation/native-stack",
|
||||
licenseType: "MIT",
|
||||
author: "n/a",
|
||||
link: "https://github.com/react-navigation/react-navigation"
|
||||
},
|
||||
{
|
||||
name: "@sayem314/react-native-keep-awake",
|
||||
licenseType: "MIT",
|
||||
author: "Sayem Chowdhury",
|
||||
link: "https://github.com/sayem314/react-native-keep-awake"
|
||||
},
|
||||
{
|
||||
name: "deprecated-react-native-prop-types",
|
||||
licenseType: "MIT",
|
||||
author: "n/a",
|
||||
link: "git@github.com:facebookarchive/react-native-deprecated-modules"
|
||||
},
|
||||
{
|
||||
name: "react",
|
||||
licenseType: "MIT",
|
||||
author: "n/a",
|
||||
link: "https://github.com/facebook/react"
|
||||
},
|
||||
{
|
||||
name: "react-native",
|
||||
licenseType: "MIT",
|
||||
author: "n/a",
|
||||
link: "https://github.com/facebook/react-native"
|
||||
},
|
||||
{
|
||||
name: "react-native-background-actions",
|
||||
licenseType: "MIT",
|
||||
author: "Rapsssito contact@rodrigomartin.dev",
|
||||
link: "https://github.com/Rapsssito/react-native-background-actions"
|
||||
},
|
||||
{
|
||||
name: "react-native-begin-background-task",
|
||||
licenseType: "MIT",
|
||||
author: "Ivan Zotov <github@ivanzotov.com>",
|
||||
link: "n/a"
|
||||
},
|
||||
{
|
||||
name: "react-native-bootsplash",
|
||||
licenseType: "MIT",
|
||||
author: "Mathieu Acthernoene <zoontek@gmail.com>",
|
||||
link: "https://github.com/zoontek/react-native-bootsplash"
|
||||
},
|
||||
{
|
||||
name: "react-native-config",
|
||||
licenseType: "MIT",
|
||||
author: "Pedro Belo",
|
||||
link: "https://github.com/luggit/react-native-config"
|
||||
},
|
||||
{
|
||||
name: "react-native-device-info",
|
||||
licenseType: "MIT",
|
||||
author:
|
||||
"Rebecca Hughes <rebecca@learnium.net> (https://github.com/rebeccahughes)",
|
||||
link: "https://github.com/react-native-device-info/react-native-device-info"
|
||||
},
|
||||
{
|
||||
name: "react-native-document-picker",
|
||||
licenseType: "MIT",
|
||||
author:
|
||||
"Elyx0 <elyx00@gmail.com> (https://github.com/rnmods), Vojtech Novak (hello@react-native-training.eu)",
|
||||
link: "https://github.com/rnmods/react-native-document-picker"
|
||||
},
|
||||
{
|
||||
name: "react-native-eventsource",
|
||||
licenseType: "MIT",
|
||||
author: "João Ribeiro jonnybgod@gmail.com",
|
||||
link: "n/a"
|
||||
},
|
||||
{
|
||||
name: "react-native-exit-app",
|
||||
licenseType: "MIT",
|
||||
author: "Wumke",
|
||||
link: "n/a"
|
||||
},
|
||||
{
|
||||
name: "react-native-file-viewer",
|
||||
licenseType: "MIT",
|
||||
author: "Vincenzo Scamporlino",
|
||||
link: "git://github.com/vinzscam/react-native-file-viewer"
|
||||
},
|
||||
{
|
||||
name: "react-native-fingerprint-scanner",
|
||||
licenseType: "MIT",
|
||||
author: "Hieu Van (https://github.com/hieuvp)",
|
||||
link: "n/a"
|
||||
},
|
||||
{
|
||||
name: "react-native-gesture-handler",
|
||||
licenseType: "MIT",
|
||||
author: "Krzysztof Magiera krzys.magiera@gmail.com",
|
||||
link: "https://github.com/software-mansion/react-native-gesture-handler"
|
||||
},
|
||||
{
|
||||
name: "react-native-get-random-values",
|
||||
licenseType: "MIT",
|
||||
author: "n/a",
|
||||
link: "https://github.com/LinusU/react-native-get-random-values"
|
||||
},
|
||||
{
|
||||
name: "react-native-html-to-pdf-lite",
|
||||
licenseType: "MIT",
|
||||
author: "Oni Benjamin <onibenjo@gmail.com>",
|
||||
link: "https://github.com/Onibenjo/react-native-html-to-pdf"
|
||||
},
|
||||
{
|
||||
name: "react-native-iap",
|
||||
licenseType: "MIT",
|
||||
author: "dooboolab",
|
||||
link: "https://github.com/dooboolab/react-native-iap"
|
||||
},
|
||||
{
|
||||
name: "react-native-image-picker",
|
||||
licenseType: "MIT",
|
||||
author: "Johan du Toit (Johan-dutoit)",
|
||||
link: "https://github.com/react-native-image-picker/react-native-image-picker"
|
||||
},
|
||||
{
|
||||
name: "react-native-keychain",
|
||||
licenseType: "MIT",
|
||||
author: "Joel Arvidsson joel@oblador.se",
|
||||
link: "git://github.com/oblador/react-native-keychain"
|
||||
},
|
||||
{
|
||||
name: "react-native-mmkv-storage",
|
||||
licenseType: "MIT",
|
||||
author: "Ammar Ahmed for @ammarahm-ed",
|
||||
link: "https://github.com/ammarahm-ed/react-native-mmkv-storage"
|
||||
},
|
||||
{
|
||||
name: "react-native-orientation",
|
||||
licenseType: "ISC",
|
||||
author: "Yamill Vallecillo <yamill@gmail.com> (github.com/yamill)",
|
||||
link: "n/a"
|
||||
},
|
||||
{
|
||||
name: "react-native-privacy-snapshot",
|
||||
licenseType: "MIT",
|
||||
author: "Roger Chapman",
|
||||
link: "n/a"
|
||||
},
|
||||
{
|
||||
name: "react-native-reanimated",
|
||||
licenseType: "MIT",
|
||||
author: "Krzysztof Magiera krzys.magiera@gmail.com",
|
||||
link: "https://github.com/software-mansion/react-native-reanimated"
|
||||
},
|
||||
{
|
||||
name: "react-native-safe-area-context",
|
||||
licenseType: "MIT",
|
||||
author: "Janic Duplessis <janicduplessis@gmail.com>",
|
||||
link: "https://github.com/th3rdwave/react-native-safe-area-context"
|
||||
},
|
||||
{
|
||||
name: "react-native-scoped-storage",
|
||||
licenseType: "MIT",
|
||||
author: "Ammar Ahmed @ammarahm-ed",
|
||||
link: "https://github.com/ammarahm-ed/react-native-scoped-storage"
|
||||
},
|
||||
{
|
||||
name: "react-native-screens",
|
||||
licenseType: "MIT",
|
||||
author: "Krzysztof Magiera krzys.magiera@gmail.com",
|
||||
link: "https://github.com/software-mansion/react-native-screens"
|
||||
},
|
||||
{
|
||||
name: "react-native-securerandom",
|
||||
licenseType: "MIT",
|
||||
author: "Rob Hogan npm@rhogan.net https://github.com/robhogan",
|
||||
link: "https://github.com/robhogan/react-native-securerandom"
|
||||
},
|
||||
{
|
||||
name: "react-native-share",
|
||||
licenseType: "MIT",
|
||||
author: "Esteban Fuentealba efuentealba@json.cl",
|
||||
link: "https://github.com/react-native-community/react-native-share"
|
||||
},
|
||||
{
|
||||
name: "@ammarahmed/react-native-sodium",
|
||||
licenseType: "n/a",
|
||||
author: "n/a",
|
||||
link: "https://github.com/lyubo/react-native-sodium"
|
||||
},
|
||||
{
|
||||
name: "react-native-svg",
|
||||
licenseType: "MIT",
|
||||
author: "n/a",
|
||||
link: "https://github.com/react-native-community/react-native-svg"
|
||||
},
|
||||
{
|
||||
name: "react-native-tooltips",
|
||||
licenseType: "Apache License",
|
||||
author: "Pranav Raj Singh Chauhan",
|
||||
link: "https://github.com/prscX/react-native-tooltips"
|
||||
},
|
||||
{
|
||||
name: "react-native-vector-icons",
|
||||
licenseType: "MIT",
|
||||
author: "Joel Arvidsson joel@oblador.se",
|
||||
link: "git://github.com/oblador/react-native-vector-icons"
|
||||
},
|
||||
{
|
||||
name: "react-native-webview",
|
||||
licenseType: "MIT",
|
||||
author: "Jamon Holmgren <jamon@infinite.red>",
|
||||
link: "https://github.com/react-native-webview/react-native-webview"
|
||||
},
|
||||
{
|
||||
name: "rn-extensions-share",
|
||||
licenseType: "MIT",
|
||||
author:
|
||||
"Djorkaeff Alexandre djorkaeffalexandre@gmail.com http://github.com/djorkaeffalexandre",
|
||||
link: "https://github.com/RocketChat/rn-extensions-share"
|
||||
},
|
||||
{
|
||||
name: "rn-fetch-blob",
|
||||
licenseType: "MIT",
|
||||
author: "Joltup",
|
||||
link: "https://github.com/joltup/rn-fetch-blob"
|
||||
},
|
||||
{
|
||||
name: "react-native-gzip",
|
||||
licenseType: "MIT",
|
||||
author:
|
||||
"ammarahm-ed <ammarahmed6506@gmail.com> (https://github.com/ammarahm-ed)",
|
||||
link: "https://github.com/ammarahm-ed/react-native-gzip"
|
||||
},
|
||||
{
|
||||
name: "@shopify/flash-list",
|
||||
licenseType: "MIT",
|
||||
author: "shopify",
|
||||
link: "https://github.com/Shopify/flash-list"
|
||||
},
|
||||
{
|
||||
name: "@notifee/react-native",
|
||||
licenseType: "Apache-2.0",
|
||||
author: "Invertase <oss@invertase.io> (http://invertase.io)",
|
||||
link: "https://github.com/invertase/notifee"
|
||||
},
|
||||
{
|
||||
name: "react-native-modal-datetime-picker",
|
||||
licenseType: "MIT",
|
||||
author: "Mazzarolo Matteo",
|
||||
link: "https://github.com/mmazzarolo/react-native-modal-datetime-picker"
|
||||
},
|
||||
{
|
||||
name: "@react-native-community/datetimepicker",
|
||||
licenseType: "MIT",
|
||||
author:
|
||||
"Martijn Swaagman <mswaagman@godaddy.com> (https://github.com/swaagie)",
|
||||
link: "https://github.com/react-native-community/datetimepicker"
|
||||
},
|
||||
{
|
||||
name: "react-native-date-picker",
|
||||
licenseType: "MIT",
|
||||
author: "henninghall",
|
||||
link: "ssh://git@github.com/henninghall/react-native-date-picker"
|
||||
},
|
||||
{
|
||||
name: "react-native-notification-sounds",
|
||||
licenseType: "MIT",
|
||||
author: "Asad Iqbal asad.dsoft@gmail.com",
|
||||
link: "https://github.com/saadqbal/react-native-notification-sounds"
|
||||
},
|
||||
{
|
||||
name: "@flyerhq/react-native-link-preview",
|
||||
licenseType: "MIT",
|
||||
author: "Oleksandr Demchenko <alexdemchenko@yahoo.com>",
|
||||
link: "https://github.com/flyerhq/react-native-link-preview#readme"
|
||||
},
|
||||
{
|
||||
name: "@mdi/js",
|
||||
licenseType: "Apache-2.0",
|
||||
author: "Austin Andrews",
|
||||
link: "https://github.com/Templarian/MaterialDesign-JS"
|
||||
},
|
||||
{
|
||||
name: "absolutify",
|
||||
licenseType: "MIT",
|
||||
author:
|
||||
"Beau Sorensen <mail@beausorensen.com> (http://github.com/sorensen)",
|
||||
link: "git://github.com/sorensen/absolutify"
|
||||
},
|
||||
{
|
||||
name: "buffer",
|
||||
licenseType: "MIT",
|
||||
author: "Feross Aboukhadijeh feross@feross.org https://feross.org",
|
||||
link: "git://github.com/feross/buffer"
|
||||
},
|
||||
{
|
||||
name: "dayjs",
|
||||
licenseType: "MIT",
|
||||
author: "iamkun",
|
||||
link: "https://github.com/iamkun/dayjs"
|
||||
},
|
||||
{
|
||||
name: "entities",
|
||||
licenseType: "BSD-2-Clause",
|
||||
author: "Felix Boehm <me@feedic.com>",
|
||||
link: "git://github.com/fb55/entities"
|
||||
},
|
||||
{
|
||||
name: "html-to-text",
|
||||
licenseType: "MIT",
|
||||
author: "Malte Legenhausen legenhausen@werk85.de",
|
||||
link: "git://github.com/html-to-text/node-html-to-text"
|
||||
},
|
||||
{
|
||||
name: "phone",
|
||||
licenseType: "MIT",
|
||||
author: "AfterShip",
|
||||
link: "https://github.com/aftership/phone"
|
||||
},
|
||||
{
|
||||
name: "qclone",
|
||||
licenseType: "MIT",
|
||||
author: "Yuriy Khomenko",
|
||||
link: "https://github.com/Yuriy-Khomenko/qclone"
|
||||
},
|
||||
{
|
||||
name: "react-native-actions-sheet",
|
||||
licenseType: "MIT",
|
||||
author: "ammarahm-ed",
|
||||
link: "https://github.com/ammarahm-ed/react-native-actions-sheet"
|
||||
},
|
||||
{
|
||||
name: "react-native-check-version",
|
||||
licenseType: "MIT",
|
||||
author: "Flexible Agency <info@flexible.agency>",
|
||||
link: "n/a"
|
||||
},
|
||||
{
|
||||
name: "react-native-drax",
|
||||
licenseType: "MIT",
|
||||
author: "Joe Lafiosca <joe@proxi.gg>",
|
||||
link: "ssh://git@github.com/nuclearpasta/react-native-drax"
|
||||
},
|
||||
{
|
||||
name: "react-native-image-zoom-viewer",
|
||||
licenseType: "MIT",
|
||||
author: "ascoders",
|
||||
link: "https://github.com/ascoders/react-native-image-viewer"
|
||||
},
|
||||
{
|
||||
name: "react-native-progress",
|
||||
licenseType: "MIT",
|
||||
author: "Joel Arvidsson joel@oblador.se",
|
||||
link: "git://github.com/oblador/react-native-progress"
|
||||
},
|
||||
{
|
||||
name: "react-native-qrcode-svg",
|
||||
licenseType: "MIT",
|
||||
author: "awesomejerry",
|
||||
link: "ssh://git@github.com/awesomejerry/react-native-qrcode-svg"
|
||||
},
|
||||
{
|
||||
name: "react-native-reanimated-material-menu",
|
||||
licenseType: "MIT",
|
||||
author:
|
||||
"Maksim Milyutin <mmxckk@gmail.com> (https://github.com/mxck) & Ammar Ahmed <ammarahmed6506@gmail.com> (https://github.com/ammarahm-ed/)",
|
||||
link: "n/a"
|
||||
},
|
||||
{
|
||||
name: "react-native-reanimated-progress-bar",
|
||||
licenseType: "MIT",
|
||||
author: "Serhii Tanskiy",
|
||||
link: "https://github.com/serzmerz/react-native-progress-bar"
|
||||
},
|
||||
{
|
||||
name: "@streetwriters/showdown",
|
||||
licenseType: "MIT",
|
||||
author: "Estevão Santos",
|
||||
link: "https://github.com/showdownjs/showdown"
|
||||
},
|
||||
{
|
||||
name: "toggle-switch-react-native",
|
||||
licenseType: "MIT",
|
||||
author: "aminebenkeroum",
|
||||
link: "https://github.com/aminebenkeroum/toggle-switch-react-native"
|
||||
},
|
||||
{
|
||||
name: "url",
|
||||
licenseType: "MIT",
|
||||
author: "n/a",
|
||||
link: "https://github.com/defunctzombie/node-url"
|
||||
},
|
||||
{
|
||||
name: "validator",
|
||||
licenseType: "MIT",
|
||||
author: "Chris O'Hara <cohara87@gmail.com>",
|
||||
link: "https://github.com/validatorjs/validator.js"
|
||||
},
|
||||
{
|
||||
name: "zustand",
|
||||
licenseType: "MIT",
|
||||
author: "Paul Henschel",
|
||||
link: "https://github.com/pmndrs/zustand"
|
||||
},
|
||||
{
|
||||
name: "fflate",
|
||||
licenseType: "MIT",
|
||||
author: "Arjun Barrett <arjunbarrett@gmail.com>",
|
||||
link: "https://github.com/101arrowz/fflate"
|
||||
},
|
||||
{
|
||||
name: "timeago.js",
|
||||
licenseType: "MIT",
|
||||
author: "hustcc https://timeago.org",
|
||||
link: "https://github.com/hustcc/timeago.js"
|
||||
}
|
||||
];
|
||||
@@ -1,71 +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 from "react";
|
||||
import { LICENSES } from "./license-data";
|
||||
import { FlatList, Linking } from "react-native";
|
||||
import { PressableButton } from "../../components/ui/pressable";
|
||||
import Heading from "../../components/ui/typography/heading";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import Paragraph from "../../components/ui/typography/paragraph";
|
||||
|
||||
type LicenseEntry = {
|
||||
name: string;
|
||||
licenseType: string;
|
||||
author: string;
|
||||
link: string;
|
||||
};
|
||||
|
||||
export const Licenses = () => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const renderItem = React.useCallback(
|
||||
({ item }: { item: LicenseEntry }) => (
|
||||
<PressableButton
|
||||
key={item.name}
|
||||
customStyle={{
|
||||
alignItems: "flex-start",
|
||||
justifyContent: "flex-start",
|
||||
alignSelf: "flex-start",
|
||||
padding: 12,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.nav,
|
||||
borderRadius: 0
|
||||
}}
|
||||
onPress={() => {
|
||||
Linking.openURL(item.link).catch(console.log);
|
||||
}}
|
||||
>
|
||||
<Heading size={SIZE.sm}>{item.name}</Heading>
|
||||
<Paragraph>
|
||||
{item.licenseType} | {item.author?.split("<")[0]}
|
||||
</Paragraph>
|
||||
</PressableButton>
|
||||
),
|
||||
[colors.nav]
|
||||
);
|
||||
return (
|
||||
<FlatList
|
||||
data={LICENSES}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -474,14 +474,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
description:
|
||||
"Automatically switch to dark mode when system theme changes",
|
||||
property: "useSystemTheme",
|
||||
icon: "circle-half",
|
||||
modifer: () => {
|
||||
const current = SettingsService.get().useSystemTheme;
|
||||
SettingsService.set({
|
||||
useSystemTheme: !current
|
||||
});
|
||||
getColorScheme();
|
||||
}
|
||||
icon: "circle-half"
|
||||
},
|
||||
{
|
||||
id: "enable-dark-mode",
|
||||
@@ -1095,14 +1088,6 @@ export const settingsGroups: SettingSection[] = [
|
||||
}
|
||||
},
|
||||
description: "Read our privacy policy"
|
||||
},
|
||||
{
|
||||
id: "licenses",
|
||||
name: "Open source Licenses",
|
||||
type: "screen",
|
||||
component: "licenses",
|
||||
description: "Open source libraries used in Notesnook",
|
||||
icon: "open-source-initiative"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -17,107 +17,106 @@ 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 BackgroundFetch from "react-native-background-fetch";
|
||||
// import { DatabaseLogger, db } from "../common/database";
|
||||
// import { AppState, AppRegistry } from "react-native";
|
||||
// import Notifications from "./notifications";
|
||||
// import SettingsService from "./settings";
|
||||
import BackgroundFetch from "react-native-background-fetch";
|
||||
import { DatabaseLogger, db } from "../common/database";
|
||||
import { AppState, AppRegistry } from "react-native";
|
||||
import Notifications from "./notifications";
|
||||
import SettingsService from "./settings";
|
||||
|
||||
// let backgroundFetchStarted = false;
|
||||
// async function start() {
|
||||
// if (backgroundFetchStarted) return;
|
||||
// backgroundFetchStarted = true;
|
||||
// // BackgroundFetch event handler.
|
||||
// const onEvent = async (taskId: string) => {
|
||||
// console.log("[BackgroundFetch] task: ", taskId, AppState.currentState);
|
||||
// // Do your background work...
|
||||
// await onBackgroundSyncStarted();
|
||||
// // IMPORTANT: You must signal to the OS that your task is complete.
|
||||
// BackgroundFetch.finish(taskId);
|
||||
// };
|
||||
let backgroundFetchStarted = false;
|
||||
async function start() {
|
||||
if (backgroundFetchStarted) return;
|
||||
backgroundFetchStarted = true;
|
||||
// BackgroundFetch event handler.
|
||||
const onEvent = async (taskId: string) => {
|
||||
console.log("[BackgroundFetch] task: ", taskId, AppState.currentState);
|
||||
// Do your background work...
|
||||
await onBackgroundSyncStarted();
|
||||
// IMPORTANT: You must signal to the OS that your task is complete.
|
||||
BackgroundFetch.finish(taskId);
|
||||
};
|
||||
|
||||
// // Timeout callback is executed when your Task has exceeded its allowed running-time.
|
||||
// // You must stop what you're doing immediately BackgroundFetch.finish(taskId)
|
||||
// const onTimeout = async (taskId: string) => {
|
||||
// console.warn("[BackgroundFetch] TIMEOUT: ", taskId);
|
||||
// BackgroundFetch.finish(taskId);
|
||||
// };
|
||||
// Timeout callback is executed when your Task has exceeded its allowed running-time.
|
||||
// You must stop what you're doing immediately BackgroundFetch.finish(taskId)
|
||||
const onTimeout = async (taskId: string) => {
|
||||
console.warn("[BackgroundFetch] TIMEOUT: ", taskId);
|
||||
BackgroundFetch.finish(taskId);
|
||||
};
|
||||
|
||||
// // Initialize BackgroundFetch only once when component mounts.
|
||||
// const status = await BackgroundFetch.configure(
|
||||
// {
|
||||
// minimumFetchInterval: 15,
|
||||
// enableHeadless: true,
|
||||
// startOnBoot: true,
|
||||
// stopOnTerminate: false,
|
||||
// requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY
|
||||
// },
|
||||
// onEvent,
|
||||
// onTimeout
|
||||
// );
|
||||
// DatabaseLogger.info(`[BackgroundFetch] configure status: ${status}`);
|
||||
// console.log(`[BackgroundFetch] configure status: ${status}`);
|
||||
// }
|
||||
// Initialize BackgroundFetch only once when component mounts.
|
||||
const status = await BackgroundFetch.configure(
|
||||
{
|
||||
minimumFetchInterval: 15,
|
||||
enableHeadless: true,
|
||||
startOnBoot: true,
|
||||
stopOnTerminate: false,
|
||||
requiredNetworkType: BackgroundFetch.NETWORK_TYPE_ANY
|
||||
},
|
||||
onEvent,
|
||||
onTimeout
|
||||
);
|
||||
DatabaseLogger.info(`[BackgroundFetch] configure status: ${status}`);
|
||||
console.log(`[BackgroundFetch] configure status: ${status}`);
|
||||
}
|
||||
|
||||
// const task = async (event: { taskId: string; timeout: boolean }) => {
|
||||
// // Get task id from event {}:
|
||||
// const taskId = event.taskId;
|
||||
// const isTimeout = event.timeout; // <-- true when your background-time has expired.
|
||||
// if (isTimeout) {
|
||||
// console.log("[BackgroundFetch] Headless TIMEOUT:", taskId);
|
||||
// BackgroundFetch.finish(taskId);
|
||||
// return;
|
||||
// }
|
||||
// DatabaseLogger.info(
|
||||
// "[BackgroundFetch HeadlessTask] start: " + taskId + AppState.currentState
|
||||
// );
|
||||
// await onBackgroundSyncStarted();
|
||||
// BackgroundFetch.finish(taskId);
|
||||
// };
|
||||
const task = async (event: { taskId: string; timeout: boolean }) => {
|
||||
// Get task id from event {}:
|
||||
const taskId = event.taskId;
|
||||
const isTimeout = event.timeout; // <-- true when your background-time has expired.
|
||||
if (isTimeout) {
|
||||
console.log("[BackgroundFetch] Headless TIMEOUT:", taskId);
|
||||
BackgroundFetch.finish(taskId);
|
||||
return;
|
||||
}
|
||||
DatabaseLogger.info(
|
||||
"[BackgroundFetch HeadlessTask] start: " + taskId + AppState.currentState
|
||||
);
|
||||
await onBackgroundSyncStarted();
|
||||
BackgroundFetch.finish(taskId);
|
||||
};
|
||||
|
||||
// BackgroundFetch.registerHeadlessTask(task);
|
||||
BackgroundFetch.registerHeadlessTask(task);
|
||||
|
||||
// async function onBackgroundSyncStarted() {
|
||||
// try {
|
||||
// DatabaseLogger.info("Background Sync" + "start");
|
||||
// await db.init();
|
||||
// const user = await db.user?.getUser();
|
||||
// if (user) {
|
||||
// await db.sync(true, false);
|
||||
// }
|
||||
// await Notifications.setupReminders();
|
||||
// DatabaseLogger.info("Background Sync" + "end");
|
||||
// } catch (e) {
|
||||
// DatabaseLogger.error(e as Error);
|
||||
// console.log("Background Sync Error", (e as Error).message);
|
||||
// }
|
||||
// }
|
||||
async function onBackgroundSyncStarted() {
|
||||
try {
|
||||
DatabaseLogger.info("Background Sync" + "start");
|
||||
await db.init();
|
||||
const user = await db.user?.getUser();
|
||||
if (user) {
|
||||
await db.sync(true, false);
|
||||
}
|
||||
await Notifications.setupReminders();
|
||||
DatabaseLogger.info("Background Sync" + "end");
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(e as Error);
|
||||
console.log("Background Sync Error", (e as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
// const onBoot = async () => {
|
||||
// try {
|
||||
// DatabaseLogger.info("BOOT TASK STARTED");
|
||||
// await db.init();
|
||||
// await Notifications.setupReminders();
|
||||
// SettingsService.init();
|
||||
// if (SettingsService.get().notifNotes) {
|
||||
// Notifications.pinQuickNote(false);
|
||||
// }
|
||||
// DatabaseLogger.info("BOOT TASK COMPLETE");
|
||||
// } catch (e) {
|
||||
// console.log(e);
|
||||
// }
|
||||
// };
|
||||
const onBoot = async () => {
|
||||
try {
|
||||
DatabaseLogger.info("BOOT TASK STARTED");
|
||||
await db.init();
|
||||
await Notifications.setupReminders();
|
||||
SettingsService.init();
|
||||
if (SettingsService.get().notifNotes) {
|
||||
Notifications.pinQuickNote(false);
|
||||
}
|
||||
DatabaseLogger.info("BOOT TASK COMPLETE");
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
// const registerHeadlessTask = () =>
|
||||
// AppRegistry.registerHeadlessTask(
|
||||
// "com.streetwriters.notesnook.BOOT_TASK",
|
||||
// () => {
|
||||
// return onBoot;
|
||||
// }
|
||||
// );
|
||||
const registerHeadlessTask = () =>
|
||||
AppRegistry.registerHeadlessTask(
|
||||
"com.streetwriters.notesnook.BOOT_TASK",
|
||||
() => {
|
||||
return onBoot;
|
||||
}
|
||||
);
|
||||
|
||||
// export const BackgroundSync = {
|
||||
// start,
|
||||
// registerHeadlessTask
|
||||
// };
|
||||
export {};
|
||||
export const BackgroundSync = {
|
||||
start,
|
||||
registerHeadlessTask
|
||||
};
|
||||
|
||||
@@ -17,8 +17,6 @@ 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 { Platform } from "react-native";
|
||||
import { verifyUser } from "../screens/settings/functions";
|
||||
import { useMessageStore } from "../stores/use-message-store";
|
||||
import {
|
||||
eOpenLoginDialog,
|
||||
@@ -27,6 +25,9 @@ import {
|
||||
} from "../utils/events";
|
||||
import { eSendEvent } from "./event-manager";
|
||||
import PremiumService from "./premium";
|
||||
import { verifyUser } from "../screens/settings/functions";
|
||||
import { Platform } from "react-native";
|
||||
import umami from "../common/analytics";
|
||||
import SettingsService from "./settings";
|
||||
|
||||
const rateAppMessage = {
|
||||
@@ -81,6 +82,7 @@ const loginMessage = {
|
||||
message: "You are not logged in",
|
||||
actionText: "Login to encrypt and sync notes",
|
||||
onPress: () => {
|
||||
umami.pageView("/signup", "/welcome/home");
|
||||
eSendEvent(eOpenLoginDialog);
|
||||
},
|
||||
data: {},
|
||||
|
||||
@@ -41,6 +41,7 @@ import { DDS } from "./device-detection";
|
||||
import { eSendEvent } from "./event-manager";
|
||||
import SettingsService from "./settings";
|
||||
import { useSettingStore } from "../stores/use-setting-store";
|
||||
import ReminderNotify from "../components/sheets/reminder-notify";
|
||||
import { sleep } from "../utils/time";
|
||||
import { useRelationStore } from "../stores/use-relation-store";
|
||||
import { useReminderStore } from "../stores/use-reminder-store";
|
||||
@@ -106,8 +107,6 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
if (notification?.data?.type === "reminder") {
|
||||
const reminder = db.reminders?.reminder(notification.id?.split("_")[0]);
|
||||
await sleep(1000);
|
||||
const ReminderNotify =
|
||||
require("../components/sheets/reminder-notify").default;
|
||||
ReminderNotify.present(reminder);
|
||||
}
|
||||
|
||||
@@ -452,10 +451,6 @@ async function displayNotification({
|
||||
localOnly: true,
|
||||
channelId: await getChannelId("default"),
|
||||
autoCancel: false,
|
||||
pressAction: {
|
||||
id: "default",
|
||||
mainComponent: "notesnook"
|
||||
},
|
||||
actions: actions?.map((action) => ({
|
||||
pressAction: {
|
||||
id: action
|
||||
@@ -521,7 +516,6 @@ async function checkAndRequestPermissions(
|
||||
await notifee.openAlarmPermissionSettings();
|
||||
}
|
||||
permissionStatus = await notifee.getNotificationSettings();
|
||||
|
||||
if (
|
||||
permissionStatus.authorizationStatus === AuthorizationStatus.AUTHORIZED &&
|
||||
permissionStatus.android.alarm === 1
|
||||
|
||||
@@ -19,12 +19,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { Dimensions, PlatformOSType } from "react-native";
|
||||
import Config from "react-native-config";
|
||||
import { Sound } from "react-native-notification-sounds";
|
||||
import { initialWindowMetrics } from "react-native-safe-area-context";
|
||||
import { FileType } from "react-native-scoped-storage";
|
||||
import create, { State } from "zustand";
|
||||
import { Reminder } from "../services/notifications";
|
||||
import { ACCENT } from "../utils/color-scheme";
|
||||
import { initialWindowMetrics } from "react-native-safe-area-context";
|
||||
import { Reminder } from "../services/notifications";
|
||||
import { Sound } from "react-native-notification-sounds";
|
||||
|
||||
export type Settings = {
|
||||
showToolbarOnTop?: boolean;
|
||||
@@ -123,7 +123,7 @@ export const useSettingStore = create<SettingStore>((set) => ({
|
||||
screenshotMode: true,
|
||||
privacyScreen: false,
|
||||
appLockMode: "none",
|
||||
telemetry: false,
|
||||
telemetry: true,
|
||||
notebooksListMode: "normal",
|
||||
notesListMode: "normal",
|
||||
devMode: false,
|
||||
|
||||
@@ -25,18 +25,17 @@ import {
|
||||
tapByText,
|
||||
createNote,
|
||||
prepare,
|
||||
visibleById,
|
||||
notVisibleById,
|
||||
navigate,
|
||||
elementByText,
|
||||
sleep,
|
||||
notVisibleByText
|
||||
sleep
|
||||
} from "./utils";
|
||||
|
||||
async function createNotebook(
|
||||
title = "Notebook 1",
|
||||
description = true,
|
||||
topic = true,
|
||||
topicCount = 1
|
||||
topic = true
|
||||
) {
|
||||
await tapByText("Add your first notebook");
|
||||
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
|
||||
@@ -48,12 +47,10 @@ async function createNotebook(
|
||||
).typeText(`Description of ${title}`);
|
||||
}
|
||||
if (topic) {
|
||||
for (let i = 1; i <= topicCount; i++) {
|
||||
await elementById(notesnook.ids.dialogs.notebook.inputs.topic).typeText(
|
||||
i === 1 ? "Topic" : "Topic " + i
|
||||
);
|
||||
await tapById("topic-add-button");
|
||||
}
|
||||
await elementById(notesnook.ids.dialogs.notebook.inputs.topic).typeText(
|
||||
"Topic"
|
||||
);
|
||||
await tapById("topic-add-button");
|
||||
}
|
||||
await tapByText("Create notebook");
|
||||
await sleep(500);
|
||||
@@ -70,7 +67,7 @@ describe("NOTEBOOKS", () => {
|
||||
it("Create a notebook with title only", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", false, false);
|
||||
await sleep(500);
|
||||
@@ -82,7 +79,7 @@ describe("NOTEBOOKS", () => {
|
||||
it("Create a notebook title & description", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", true, false);
|
||||
await sleep(500);
|
||||
@@ -94,6 +91,7 @@ describe("NOTEBOOKS", () => {
|
||||
it("Create a notebook with description and topics", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", false, false);
|
||||
await sleep(500);
|
||||
@@ -106,6 +104,7 @@ describe("NOTEBOOKS", () => {
|
||||
await prepare();
|
||||
let note = await createNote();
|
||||
await navigate("Notebooks");
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", true, true);
|
||||
await sleep(500);
|
||||
@@ -122,6 +121,7 @@ describe("NOTEBOOKS", () => {
|
||||
it("Add new topic to notebook", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", true, false);
|
||||
await sleep(500);
|
||||
@@ -138,6 +138,7 @@ describe("NOTEBOOKS", () => {
|
||||
it("Edit topic", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", true, true);
|
||||
await sleep(500);
|
||||
@@ -156,6 +157,7 @@ describe("NOTEBOOKS", () => {
|
||||
it("Add new note to topic", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", true, true);
|
||||
await sleep(500);
|
||||
@@ -168,6 +170,8 @@ describe("NOTEBOOKS", () => {
|
||||
it("Remove note from topic", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", true, true);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
@@ -180,59 +184,31 @@ describe("NOTEBOOKS", () => {
|
||||
await notVisibleById(note.title);
|
||||
});
|
||||
|
||||
it.only("Add/Remove note to notebook from home", async () => {
|
||||
it("Add/Remove note to notebook from home", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", true, true, 3);
|
||||
await createNotebook("Notebook 1", true, true);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await navigate("Notes");
|
||||
await createNote();
|
||||
console.log("ADD TO A SINGLE TOPIC");
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-Add to notebook");
|
||||
await sleep(500);
|
||||
await tapByText("Notebook 1");
|
||||
await tapByText("Topic");
|
||||
await tapByText("Save");
|
||||
await sleep(300);
|
||||
await visibleByText("Notebook 1 › Topic");
|
||||
console.log("MOVE FROM ONE TOPIC TO ANOTHER");
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-Add to notebook");
|
||||
await tapByText("Notebook 1");
|
||||
await tapByText("Topic 2");
|
||||
await tapByText("Save");
|
||||
await visibleByText("Notebook 1 › Topic 2");
|
||||
console.log("REMOVE FROM TOPIC");
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-Add to notebook");
|
||||
await tapByText("Notebook 1");
|
||||
await tapByText("Topic 2");
|
||||
await tapByText("Save");
|
||||
await sleep(300);
|
||||
await notVisibleByText("Notebook 1 › Topic 2");
|
||||
console.log("MOVE TO MULTIPLE TOPICS");
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-Add to notebook");
|
||||
await tapByText("Notebook 1");
|
||||
await elementByText("Topic").longPress();
|
||||
await visibleByText("Reset selection");
|
||||
await tapByText("Topic 2");
|
||||
await tapByText("Topic 3");
|
||||
await tapByText("Save");
|
||||
await sleep(300);
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await visibleByText("Topic");
|
||||
await visibleByText("Topic 2");
|
||||
await visibleByText("Topic 3");
|
||||
await visibleById("icon-check");
|
||||
await tapByText("Topic");
|
||||
await notVisibleById("icon-check");
|
||||
});
|
||||
|
||||
it("Edit notebook title, description and add a topic", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook();
|
||||
await sleep(500);
|
||||
@@ -265,7 +241,7 @@ describe("NOTEBOOKS", () => {
|
||||
it("Move notebook to trash", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", false, false);
|
||||
await sleep(500);
|
||||
@@ -285,7 +261,7 @@ describe("NOTEBOOKS", () => {
|
||||
await prepare();
|
||||
let note = await createNote();
|
||||
await navigate("Notebooks");
|
||||
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", false, true);
|
||||
await sleep(500);
|
||||
@@ -308,7 +284,7 @@ describe("NOTEBOOKS", () => {
|
||||
await prepare();
|
||||
let note = await createNote();
|
||||
await navigate("Notebooks");
|
||||
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", false, true);
|
||||
await sleep(500);
|
||||
@@ -330,7 +306,7 @@ describe("NOTEBOOKS", () => {
|
||||
it("Pin notebook to side menu", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", false, false);
|
||||
await sleep(500);
|
||||
@@ -347,7 +323,7 @@ describe("NOTEBOOKS", () => {
|
||||
it("Pin topic to side menu", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
|
||||
await tapByText("Skip introduction");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1");
|
||||
await sleep(500);
|
||||
|
||||
@@ -139,11 +139,6 @@ def reactNativeArchitectures() {
|
||||
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
||||
}
|
||||
|
||||
def fdroidBuild() {
|
||||
def value = project.getProperties().get("fdroidBuild")
|
||||
return value ? value : false
|
||||
}
|
||||
|
||||
def getNpmVersion() {
|
||||
def inputFile = file("$rootDir/../../package.json")
|
||||
def jsonPackage = new JsonSlurper().parseText(inputFile.text)
|
||||
@@ -165,7 +160,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 2027
|
||||
versionCode 2025
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
@@ -285,21 +280,18 @@ android {
|
||||
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
|
||||
def abi = output.getFilter(OutputFile.ABI)
|
||||
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||
if (fdroidBuild()) {
|
||||
output.versionCodeOverride =
|
||||
defaultConfig.versionCode * 5 + versionCodes.get(abi)
|
||||
println("Fdroid Version code: ${output.versionCodeOverride} for abi ${versionCodes.get(abi)}");
|
||||
} else {
|
||||
println("Version code: ${output.versionCodeOverride} for abi ${versionCodes.get(abi)}");
|
||||
output.versionCodeOverride =
|
||||
output.versionCodeOverride =
|
||||
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -69,8 +69,8 @@ allprojects {
|
||||
|
||||
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
// maven {
|
||||
// url("${project(':react-native-background-fetch').projectDir}/libs")
|
||||
// }
|
||||
maven {
|
||||
url("${project(':react-native-background-fetch').projectDir}/libs")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,4 @@ newArchEnabled=false
|
||||
|
||||
# V8 Cache Mode Config
|
||||
# v8.cacheMode=none
|
||||
# v8.android.tools.dir=/home/ammarahm-ed/Repos/notesnook-mobile/node_modules/v8-android-jit-nointl/dist/tools/android
|
||||
|
||||
# fdroid
|
||||
fdroidBuild=false
|
||||
# v8.android.tools.dir=/home/ammarahm-ed/Repos/notesnook-mobile/node_modules/v8-android-jit-nointl/dist/tools/android
|
||||
@@ -1,9 +1,3 @@
|
||||
- Improved UX for linking Notebooks
|
||||
- Redesign properties sheet to be simple & handy
|
||||
- Added support for RTL languages in editor
|
||||
- Telemetery is now opt-in
|
||||
- Get default reminder title/description from note
|
||||
- Fix unable to input recovery code on 2FA sheet
|
||||
- Bug fixes & small improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -1,6 +1,60 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
global.Buffer = require('buffer').Buffer;
|
||||
//import { ScriptManager, Script } from '@callstack/repack/client';
|
||||
import '../app/common/logger/index';
|
||||
import { DOMParser } from './worker.js';
|
||||
|
||||
// class DOM {
|
||||
// static domparser;
|
||||
|
||||
// parseFromString(markupLanguage, mimeType, globals) {
|
||||
// return DOM.domparser?.parseFromString(markupLanguage, mimeType, globals);
|
||||
// }
|
||||
|
||||
// static async prepare() {
|
||||
// if (!DOM.domparser) {
|
||||
// let module = await import('./worker.js');
|
||||
// DOM.domparser = new module.DOMParser();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
global.DOMParser = DOMParser;
|
||||
|
||||
// try {
|
||||
// const shared = ScriptManager.shared;
|
||||
// } catch (e) {
|
||||
// new ScriptManager({
|
||||
// resolve: async (scriptId, caller) => {
|
||||
// if (__DEV__) {
|
||||
// return {
|
||||
// url: Script.getDevServerURL(scriptId),
|
||||
// cache: false
|
||||
// };
|
||||
// }
|
||||
|
||||
// return {
|
||||
// url: Script.getFileSystemURL(scriptId)
|
||||
// };
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
if (__DEV__) {
|
||||
// ScriptManager.shared.on('resolving', (...args) => {
|
||||
// console.log('DEBUG/resolving', ...args);
|
||||
// });
|
||||
// ScriptManager.shared.on('resolved', (...args) => {
|
||||
// console.log('DEBUG/resolved', ...args);
|
||||
// });
|
||||
// ScriptManager.shared.on('prefetching', (...args) => {
|
||||
// console.log('DEBUG/prefetching', ...args);
|
||||
// });
|
||||
// ScriptManager.shared.on('loading', (...args) => {
|
||||
// console.log('DEBUG/loading', ...args);
|
||||
// });
|
||||
// ScriptManager.shared.on('loaded', (...args) => {
|
||||
// console.log('DEBUG/loaded', ...args);
|
||||
// });
|
||||
// ScriptManager.shared.on('error', (...args) => {
|
||||
// console.log('DEBUG/error', ...args);
|
||||
// });
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import './globals.js';
|
||||
import 'react-native-get-random-values';
|
||||
import 'react-native-gesture-handler';
|
||||
import React from 'react';
|
||||
import { AppRegistry, LogBox, Platform, UIManager } from 'react-native';
|
||||
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";
|
||||
Notifications.init();
|
||||
import Notifications from '../app/services/notifications';
|
||||
//import {BackgroundSync} from '../app/services/background-sync';
|
||||
|
||||
const appName = appJson.name;
|
||||
if (Config.isTesting) {
|
||||
@@ -21,7 +22,8 @@ if (__DEV__) {
|
||||
LogBox.ignoreAllLogs();
|
||||
}
|
||||
let NotesnookShare;
|
||||
|
||||
Notifications.init();
|
||||
//BackgroundSync.registerHeadlessTask();
|
||||
let QuickNoteIOS;
|
||||
|
||||
const AppProvider = () => {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
65AA857E25E6DDEC00772A01 /* NotesWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AA857D25E6DDEC00772A01 /* NotesWidget.swift */; };
|
||||
65AA858025E6DDEE00772A01 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 65AA857F25E6DDEE00772A01 /* Assets.xcassets */; };
|
||||
65AA858425E6DDEE00772A01 /* NotesWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 65AA857725E6DDEC00772A01 /* NotesWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
65B450AD2976AF0C00EA090B /* RNBackgroundFetch+AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 65B450AC2976AF0B00EA090B /* RNBackgroundFetch+AppDelegate.m */; };
|
||||
65B5014425A672B200E2D264 /* ShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 65B5014325A672B200E2D264 /* ShareViewController.m */; };
|
||||
65B5014725A672B200E2D264 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 65B5014525A672B200E2D264 /* MainInterface.storyboard */; };
|
||||
65B5014B25A672B200E2D264 /* Make Note.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 65B5014025A672B200E2D264 /* Make Note.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
@@ -122,6 +123,7 @@
|
||||
65AA857D25E6DDEC00772A01 /* NotesWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesWidget.swift; sourceTree = "<group>"; };
|
||||
65AA857F25E6DDEE00772A01 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
65AA858125E6DDEE00772A01 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
65B450AC2976AF0B00EA090B /* RNBackgroundFetch+AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "RNBackgroundFetch+AppDelegate.m"; path = "../../node_modules/react-native-background-fetch/ios/RNBackgroundFetch/RNBackgroundFetch+AppDelegate.m"; sourceTree = "<group>"; };
|
||||
65B5014025A672B200E2D264 /* Make Note.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Make Note.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
65B5014325A672B200E2D264 /* ShareViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShareViewController.m; sourceTree = "<group>"; };
|
||||
65B5014625A672B200E2D264 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||
@@ -221,6 +223,7 @@
|
||||
13B07FAE1A68108700A75B9A /* Notesnook */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
65B450AC2976AF0B00EA090B /* RNBackgroundFetch+AppDelegate.m */,
|
||||
6593E4A2281C345400492C50 /* AppDelegate.mm */,
|
||||
6529A13D279BC4C70048D4A8 /* BootSplash.storyboard */,
|
||||
659BE46625E11A5100E05671 /* notesnook-text.png */,
|
||||
@@ -941,6 +944,7 @@
|
||||
65E0340B257B9FF100793428 /* File.swift in Sources */,
|
||||
6593E4A3281C345400492C50 /* AppDelegate.mm in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
65B450AD2976AF0C00EA090B /* RNBackgroundFetch+AppDelegate.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -1083,7 +1087,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2027;
|
||||
CURRENT_PROJECT_VERSION = 2025;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1157,7 +1161,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.4;
|
||||
MARKETING_VERSION = 2.4.2;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1187,7 +1191,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Notesnook/Notesnook.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2027;
|
||||
CURRENT_PROJECT_VERSION = 2025;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
@@ -1260,7 +1264,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.4;
|
||||
MARKETING_VERSION = 2.4.2;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1418,7 +1422,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2027;
|
||||
CURRENT_PROJECT_VERSION = 2025;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1430,7 +1434,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.4;
|
||||
MARKETING_VERSION = 2.4.2;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1460,7 +1464,7 @@
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2027;
|
||||
CURRENT_PROJECT_VERSION = 2025;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1472,7 +1476,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.4;
|
||||
MARKETING_VERSION = 2.4.2;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1501,7 +1505,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2027;
|
||||
CURRENT_PROJECT_VERSION = 2025;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1575,7 +1579,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.4;
|
||||
MARKETING_VERSION = 2.4.2;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1605,7 +1609,7 @@
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2027;
|
||||
CURRENT_PROJECT_VERSION = 2025;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1679,7 +1683,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.4;
|
||||
MARKETING_VERSION = 2.4.2;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#import <React/RCTAppSetupUtils.h>
|
||||
#import <React/RCTLinkingManager.h>
|
||||
#import "RNShortcuts.h"
|
||||
//#import <TSBackgroundFetch/TSBackgroundFetch.h>
|
||||
#import <TSBackgroundFetch/TSBackgroundFetch.h>
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
#import <React/CoreModulesPlugins.h>
|
||||
@@ -63,7 +63,7 @@ RCTBridge *bridge;
|
||||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
self.window.rootViewController = rootViewController;
|
||||
[self.window makeKeyAndVisible];
|
||||
//[[TSBackgroundFetch sharedInstance] didFinishLaunching];
|
||||
[[TSBackgroundFetch sharedInstance] didFinishLaunching];
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
@@ -345,6 +345,8 @@ PODS:
|
||||
- React-Core
|
||||
- rn-fetch-blob (0.12.0):
|
||||
- React-Core
|
||||
- RNBackgroundFetch (4.1.7):
|
||||
- React-Core
|
||||
- RNBootSplash (4.3.2):
|
||||
- React-Core
|
||||
- RNCCheckbox (0.5.12):
|
||||
@@ -369,10 +371,10 @@ PODS:
|
||||
- React-Core
|
||||
- RNKeychain (4.0.5):
|
||||
- React
|
||||
- RNNotifee (7.4.3):
|
||||
- RNNotifee (7.3.1):
|
||||
- React-Core
|
||||
- RNNotifee/NotifeeCore (= 7.4.3)
|
||||
- RNNotifee/NotifeeCore (7.4.3):
|
||||
- RNNotifee/NotifeeCore (= 7.3.1)
|
||||
- RNNotifee/NotifeeCore (7.3.1):
|
||||
- React-Core
|
||||
- RNPrivacySnapshot (1.0.0):
|
||||
- React-Core
|
||||
@@ -481,6 +483,7 @@ DEPENDENCIES:
|
||||
- ReactCommon/turbomodule/core (from `../../node_modules/react-native/ReactCommon`)
|
||||
- rn-extensions-share (from `../../node_modules/rn-extensions-share`)
|
||||
- rn-fetch-blob (from `../../node_modules/rn-fetch-blob`)
|
||||
- RNBackgroundFetch (from `../../node_modules/react-native-background-fetch`)
|
||||
- RNBootSplash (from `../../node_modules/react-native-bootsplash`)
|
||||
- "RNCCheckbox (from `../../node_modules/@react-native-community/checkbox`)"
|
||||
- "RNCClipboard (from `../../node_modules/@react-native-clipboard/clipboard`)"
|
||||
@@ -622,6 +625,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../../node_modules/rn-extensions-share"
|
||||
rn-fetch-blob:
|
||||
:path: "../../node_modules/rn-fetch-blob"
|
||||
RNBackgroundFetch:
|
||||
:path: "../../node_modules/react-native-background-fetch"
|
||||
RNBootSplash:
|
||||
:path: "../../node_modules/react-native-bootsplash"
|
||||
RNCCheckbox:
|
||||
@@ -736,6 +741,7 @@ SPEC CHECKSUMS:
|
||||
ReactCommon: 1e783348b9aa73ae68236271df972ba898560a95
|
||||
rn-extensions-share: 3f0ecce20dfbca1f0358deb4ebfb9ee121a6d92a
|
||||
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
|
||||
RNBackgroundFetch: 13fbe7b23d4a082eac8caa7b08c5bcbeabd92f79
|
||||
RNBootSplash: 5f346163977573d6b2aeba1b25df9d2245c0d73c
|
||||
RNCCheckbox: ed1b4ca295475b41e7251ebae046360a703b6eb5
|
||||
RNCClipboard: 2834e1c4af68697089cdd455ee4a4cdd198fa7dd
|
||||
@@ -748,7 +754,7 @@ SPEC CHECKSUMS:
|
||||
RNGestureHandler: b7a872907ee289ada902127f2554fa1d2c076122
|
||||
RNIap: d248609d1b8937e63bd904e865c318e9b1457eff
|
||||
RNKeychain: 840f8e6f13be0576202aefcdffd26a4f54bfe7b5
|
||||
RNNotifee: 5dfb0c5783ddb3da47b39e75d06cc7e748d6ca39
|
||||
RNNotifee: 2fab820e17613db15a1f836053ee3b87af7e96b9
|
||||
RNPrivacySnapshot: 8eaf571478a353f2e5184f5c803164f22428b023
|
||||
RNReanimated: f1b109fb8341505ace9d7d2eedd150da1686716b
|
||||
RNScreens: 34cc502acf1b916c582c60003dc3089fa01dc66d
|
||||
|
||||
@@ -52,11 +52,12 @@
|
||||
"rn-fetch-blob": "^0.12.0",
|
||||
"react-native-gzip":"1.0.0",
|
||||
"@shopify/flash-list":"1.4.0",
|
||||
"@ammarahmed/notifee-react-native": "7.4.3",
|
||||
"@ammarahmed/notifee-react-native": "7.3.1",
|
||||
"react-native-modal-datetime-picker":"14.0.0",
|
||||
"@react-native-community/datetimepicker":"6.6.0",
|
||||
"react-native-date-picker": "4.2.6",
|
||||
"react-native-notification-sounds": "0.5.5"
|
||||
"react-native-notification-sounds": "0.5.5",
|
||||
"react-native-background-fetch": "4.1.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.9",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
const isGithubRelease = false;
|
||||
const isGithubRelease = process.env.GITHUB_RELEASE;
|
||||
const config = {
|
||||
commands: require('@callstack/repack/commands'),
|
||||
project: {
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
- Improved UX for linking Notebooks
|
||||
- Redesign properties sheet to be simple & handy
|
||||
- Added support for RTL languages in editor
|
||||
- Telemetery is now opt-in
|
||||
- Get default reminder title/description from note
|
||||
- Fix unable to input recovery code on 2FA sheet
|
||||
- Bug fixes & small improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
387
apps/mobile/package-lock.json
generated
387
apps/mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "2.4.4",
|
||||
"version": "2.4.2",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
diff --git a/node_modules/react-native-actions-sheet/dist/src/index.js b/node_modules/react-native-actions-sheet/dist/src/index.js
|
||||
index 7eb4357..18bf26d 100644
|
||||
--- a/node_modules/react-native-actions-sheet/dist/src/index.js
|
||||
+++ b/node_modules/react-native-actions-sheet/dist/src/index.js
|
||||
@@ -747,7 +747,7 @@ var ActionSheet = /** @class */ (function (_super) {
|
||||
width: "100%"
|
||||
},
|
||||
]}>
|
||||
- {this.props.ExtraOverlayComponent}
|
||||
+
|
||||
|
||||
<FlatList testID={(_a = this.props.testIDs) === null || _a === void 0 ? void 0 : _a.scrollview} bounces={false} keyboardShouldPersistTaps={keyboardShouldPersistTaps} keyboardDismissMode={keyboardDismissMode} ref={this.scrollViewRef} scrollEventThrottle={16} overScrollMode="never" showsVerticalScrollIndicator={false} onMomentumScrollBegin={this._onScrollBegin} onScrollEndDrag={this._onScrollEnd} onMomentumScrollEnd={this._onScrollEnd} scrollEnabled={scrollable} onScrollBeginDrag={this._onScrollBeginDrag} onTouchEnd={this._onTouchEnd} onScroll={this._onScroll} scrollsToTop={false} style={[
|
||||
styles.scrollView,
|
||||
@@ -821,6 +821,7 @@ var ActionSheet = /** @class */ (function (_super) {
|
||||
</Animated.View>
|
||||
</View>);
|
||||
}}/>
|
||||
+ {this.props.ExtraOverlayComponent}
|
||||
</Animated.View>
|
||||
</Root>
|
||||
</>);
|
||||
19
apps/web/.vscode/launch.json
vendored
19
apps/web/.vscode/launch.json
vendored
@@ -7,21 +7,14 @@
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "Start Notesnook (web)",
|
||||
"preLaunchTask": "StartReactApp",
|
||||
"postDebugTask": "Terminate All Tasks",
|
||||
"name": "Debug Chrome",
|
||||
"name": "Launch Chrome against localhost",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "firefox",
|
||||
"request": "launch",
|
||||
"reAttach": true,
|
||||
"preLaunchTask": "Start Notesnook (web)",
|
||||
"postDebugTask": "Terminate All Tasks",
|
||||
"name": "Debug Firefox",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
"webRoot": "${workspaceFolder}",
|
||||
"env": { "DISPLAY": ":0" },
|
||||
"runtimeExecutable": "/usr/bin/brave-browser",
|
||||
"runtimeArgs": ["--remote-debugging-port=9222"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
12
apps/web/.vscode/tasks.json
vendored
12
apps/web/.vscode/tasks.json
vendored
@@ -5,26 +5,24 @@
|
||||
"tasks": [
|
||||
{
|
||||
"type": "shell",
|
||||
"command": "npm run debug",
|
||||
"command": "yarn debug",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
},
|
||||
"dependsOn": "Terminate All Tasks",
|
||||
"isBackground": true, //This prevents the launch.json to wait for the completion of the task
|
||||
"problemMatcher": {
|
||||
"fileLocation": "relative",
|
||||
"owner": "custom", //This is not needed but, required by the problemMatcher Object
|
||||
"pattern": {
|
||||
"regexp": "^$" //This is not needed but, required by the problemMatcher Object
|
||||
},
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": "^$",
|
||||
"endsPattern": "^You can now view (.*) in the browser.$"
|
||||
"beginsPattern": "Compiling...", //Signals the begin of the Task
|
||||
"endsPattern": "Compiled .*" //Signals that now the initialization of the task is complete
|
||||
}
|
||||
},
|
||||
"label": "Start Notesnook (web)"
|
||||
"label": "StartReactApp"
|
||||
},
|
||||
{
|
||||
"label": "Terminate All Tasks",
|
||||
|
||||
@@ -40,7 +40,7 @@ test.afterAll(async () => {
|
||||
});
|
||||
|
||||
test.afterEach(async () => {
|
||||
await app.goto(true);
|
||||
await app.goto();
|
||||
});
|
||||
|
||||
function roundOffPrices(prices: PriceItem[]) {
|
||||
@@ -142,7 +142,7 @@ test("apply coupon through url", async () => {
|
||||
|
||||
planPrices[plan] = roundOffPrices(await pricing.getPrices());
|
||||
|
||||
await app.goto(true);
|
||||
await app.goto();
|
||||
}
|
||||
|
||||
for (const key in planPrices) {
|
||||
|
||||
@@ -289,7 +289,9 @@ test("Ctrl+Click to select/unselect notes", async ({ page }, info) => {
|
||||
}
|
||||
|
||||
await selectedNote.contextMenu.open();
|
||||
expect(await selectedNote.contextMenu.title()).toBeFalsy();
|
||||
expect(
|
||||
(await selectedNote.contextMenu.title()) === (await selectedNote.getTitle())
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test("opening a note & right clicking on another note should show note properties menu", async ({
|
||||
@@ -304,5 +306,7 @@ test("opening a note & right clicking on another note should show note propertie
|
||||
|
||||
await notesList[0].click();
|
||||
await selectedNote.contextMenu.open();
|
||||
expect(await selectedNote.contextMenu.title()).toBeFalsy();
|
||||
expect(
|
||||
(await selectedNote.contextMenu.title()) === (await selectedNote.getTitle())
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -48,10 +48,9 @@ export class AppModel {
|
||||
this.routeHeader = this.page.locator(getTestId("routeHeader"));
|
||||
}
|
||||
|
||||
async goto(isLoggedIn = false) {
|
||||
async goto() {
|
||||
await this.page.goto("/");
|
||||
await this.routeHeader.waitFor({ state: "visible" });
|
||||
if (!isLoggedIn) await this.navigation.waitForItem("Login");
|
||||
}
|
||||
|
||||
goBack() {
|
||||
@@ -102,9 +101,7 @@ export class AppModel {
|
||||
async navigateTo(title: string) {
|
||||
if ((await this.getRouteHeader()) === title) return;
|
||||
const item = await this.navigation.findItem(title);
|
||||
if (!item) throw new Error(`Could not find item to navigate to: ${title}`);
|
||||
|
||||
await item.click();
|
||||
await item?.click();
|
||||
await this.page.waitForTimeout(1000);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,15 +21,12 @@ import { Page, Locator } from "@playwright/test";
|
||||
import { getTestId } from "../utils";
|
||||
|
||||
export class ContextMenuModel {
|
||||
readonly menuContainer: Locator;
|
||||
readonly titleText: Locator;
|
||||
constructor(private readonly page: Page) {
|
||||
this.menuContainer = this.page.locator(getTestId(`menu-container`));
|
||||
this.titleText = this.page.locator(getTestId(`menu-title`));
|
||||
}
|
||||
|
||||
async title() {
|
||||
if (!(await this.titleText.isVisible())) return null;
|
||||
return await this.titleText.textContent();
|
||||
}
|
||||
|
||||
@@ -38,7 +35,7 @@ export class ContextMenuModel {
|
||||
button: "left" | "right" | "middle" | undefined = "right"
|
||||
) {
|
||||
await locator.click({ button });
|
||||
await this.menuContainer.waitFor();
|
||||
await this.titleText.waitFor();
|
||||
}
|
||||
|
||||
async clickOnItem(id: string) {
|
||||
|
||||
@@ -38,12 +38,6 @@ export class NavigationMenuModel {
|
||||
}
|
||||
}
|
||||
|
||||
waitForItem(title: string) {
|
||||
return this.menu
|
||||
.locator(getTestId(`navigation-item`), { hasText: title })
|
||||
.waitFor();
|
||||
}
|
||||
|
||||
async getShortcuts() {
|
||||
const shortcuts: string[] = [];
|
||||
for await (const item of this.iterateList()) {
|
||||
|
||||
@@ -250,9 +250,7 @@ export class NoteContextMenuModel extends BaseProperties {
|
||||
|
||||
async addToNotebook(notebook: Notebook) {
|
||||
await this.open();
|
||||
|
||||
await this.menu.clickOnItem("notebooks");
|
||||
await this.menu.clickOnItem("link-notebooks");
|
||||
await this.menu.clickOnItem("addtonotebook");
|
||||
|
||||
const filterInput = this.page.locator(getTestId("filter-input"));
|
||||
await filterInput.type(notebook.title);
|
||||
@@ -265,15 +263,14 @@ export class NoteContextMenuModel extends BaseProperties {
|
||||
|
||||
const notebookItems = this.page.locator(getTestId("notebook"));
|
||||
for await (const item of iterateList(notebookItems)) {
|
||||
await item.locator(getTestId("notebook-tools")).click();
|
||||
const title = item.locator(getTestId("notebook-title"));
|
||||
const createTopicButton = item.locator(getTestId("create-topic"));
|
||||
const notebookTitle = await title.textContent();
|
||||
const treeItem = item.locator(getTestId(`tree-item`));
|
||||
const title = treeItem.locator(getTestId("title"));
|
||||
const newItemButton = treeItem.locator(getTestId("tree-item-new"));
|
||||
|
||||
if (notebookTitle?.includes(notebook.title)) {
|
||||
if ((await title.textContent()) === notebook.title) {
|
||||
for (const topic of notebook.topics) {
|
||||
await createTopicButton.click();
|
||||
const newItemInput = item.locator(getTestId("new-topic-input"));
|
||||
await newItemButton.click();
|
||||
const newItemInput = item.locator(getTestId("new-tree-item-input"));
|
||||
|
||||
await newItemInput.waitFor({ state: "visible" });
|
||||
await newItemInput.fill(topic);
|
||||
@@ -284,9 +281,8 @@ export class NoteContextMenuModel extends BaseProperties {
|
||||
|
||||
const topicItems = item.locator(getTestId("topic"));
|
||||
for await (const topicItem of iterateList(topicItems)) {
|
||||
await this.page.keyboard.down("Control");
|
||||
await topicItem.click();
|
||||
await this.page.keyboard.up("Control");
|
||||
const treeItem = topicItem.locator(getTestId(`tree-item`));
|
||||
await treeItem.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import { getTestId } from "../utils";
|
||||
|
||||
export class ToastsModel {
|
||||
private readonly toasts: Locator;
|
||||
private readonly toastMessage: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.toasts = page.locator(".toasts-container > div");
|
||||
|
||||
@@ -95,11 +95,7 @@ test("add a note to notebook", async ({ page }) => {
|
||||
topics: ["Hello", "World", "Did", "what"]
|
||||
});
|
||||
|
||||
expect(
|
||||
await app.toasts.waitForToast(
|
||||
"1 note added to 4 topics & removed from 0 topics."
|
||||
)
|
||||
).toBe(true);
|
||||
expect(await app.toasts.waitForToast("Added 1 note to 4 topics")).toBe(true);
|
||||
});
|
||||
|
||||
const actors = ["contextMenu", "properties"] as const;
|
||||
|
||||
@@ -182,6 +182,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test 1</h1>
|
||||
<p data-spacing="double">This is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1</p>
|
||||
<p data-spacing="double">This is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -43,7 +43,7 @@ export class WindowState {
|
||||
private readonly config: WindowStateOptions;
|
||||
private state: SerializableWindowState;
|
||||
private windowRef: BrowserWindow | undefined;
|
||||
private readonly eventHandlingDelay = 1000;
|
||||
private readonly eventHandlingDelay = 100;
|
||||
private stateChangeTimer: NodeJS.Timeout | undefined;
|
||||
|
||||
constructor(options?: Partial<WindowStateOptions>) {
|
||||
@@ -176,7 +176,7 @@ export class WindowState {
|
||||
// Handles both 'resize' and 'move'
|
||||
if (this.stateChangeTimer) clearTimeout(this.stateChangeTimer);
|
||||
this.stateChangeTimer = setTimeout(
|
||||
() => this.saveState(this.windowRef),
|
||||
() => this.updateState(),
|
||||
this.eventHandlingDelay
|
||||
);
|
||||
};
|
||||
@@ -199,8 +199,8 @@ export class WindowState {
|
||||
if (this.config.fullScreen && this.state.isFullScreen) {
|
||||
win.setFullScreen(true);
|
||||
}
|
||||
win.on("resized", this.stateChangeHandler);
|
||||
win.on("moved", this.stateChangeHandler);
|
||||
win.on("resize", this.stateChangeHandler);
|
||||
win.on("move", this.stateChangeHandler);
|
||||
win.on("close", this.closeHandler);
|
||||
win.on("closed", this.closedHandler);
|
||||
this.windowRef = win;
|
||||
@@ -208,8 +208,8 @@ export class WindowState {
|
||||
|
||||
unmanage() {
|
||||
if (this.windowRef) {
|
||||
this.windowRef.removeListener("resized", this.stateChangeHandler);
|
||||
this.windowRef.removeListener("moved", this.stateChangeHandler);
|
||||
this.windowRef.removeListener("resize", this.stateChangeHandler);
|
||||
this.windowRef.removeListener("move", this.stateChangeHandler);
|
||||
if (this.stateChangeTimer) clearTimeout(this.stateChangeTimer);
|
||||
this.windowRef.removeListener("close", this.closeHandler);
|
||||
this.windowRef.removeListener("closed", this.closedHandler);
|
||||
|
||||
@@ -68,6 +68,7 @@ async function createWindow() {
|
||||
y: mainWindowState.y,
|
||||
width: mainWindowState.width,
|
||||
height: mainWindowState.height,
|
||||
fullscreen: mainWindowState.isFullScreen,
|
||||
darkTheme: getTheme() === "dark",
|
||||
backgroundColor: getBackgroundColor(),
|
||||
|
||||
@@ -92,6 +93,7 @@ async function createWindow() {
|
||||
setupMenu();
|
||||
setupJumplist();
|
||||
setupDesktopIntegration();
|
||||
setupTray();
|
||||
|
||||
mainWindow.webContents.session.setSpellCheckerDictionaryDownloadURL(
|
||||
"http://dictionaries.notesnook.com/"
|
||||
@@ -172,27 +174,15 @@ function createURL(options) {
|
||||
function setupDesktopIntegration() {
|
||||
const desktopIntegration = getDesktopIntegration();
|
||||
|
||||
if (
|
||||
desktopIntegration.closeToSystemTray ||
|
||||
desktopIntegration.minimizeToSystemTray
|
||||
) {
|
||||
setupTray();
|
||||
}
|
||||
|
||||
// when close to system tray is enabled, it becomes nigh impossible
|
||||
// to "quit" the app. This is necessary in order to fix that.
|
||||
if (desktopIntegration.closeToSystemTray) {
|
||||
app.on("before-quit", () => app.exit(0));
|
||||
if (desktopIntegration.autoStart) {
|
||||
AutoLaunch.enable(desktopIntegration.startMinimized);
|
||||
}
|
||||
|
||||
globalThis.window.on("close", (e) => {
|
||||
if (getDesktopIntegration().closeToSystemTray) {
|
||||
e.preventDefault();
|
||||
if (process.platform == "darwin") {
|
||||
// on macOS window cannot be minimized/hidden if it is already fullscreen
|
||||
// so we just close it.
|
||||
if (globalThis.window.isFullScreen()) app.exit(0);
|
||||
else app.hide();
|
||||
app.hide();
|
||||
} else {
|
||||
globalThis.window.minimize();
|
||||
globalThis.window.hide();
|
||||
@@ -209,12 +199,4 @@ function setupDesktopIntegration() {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (desktopIntegration.autoStart) {
|
||||
AutoLaunch.enable(desktopIntegration.startMinimized);
|
||||
|
||||
if (process.platform === "win32" && desktopIntegration.startMinimized) {
|
||||
globalThis.window.minimize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,19 +20,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { setPrivacyMode } from "../../config/privacyMode";
|
||||
|
||||
export default (args) => {
|
||||
if (!globalThis.window || !["win32", "darwin"].includes(process.platform))
|
||||
return;
|
||||
|
||||
if (!globalThis.window) return;
|
||||
const { privacyMode } = args;
|
||||
globalThis.window.setContentProtection(privacyMode);
|
||||
|
||||
if (process.platform === "win32") {
|
||||
globalThis.window.setThumbnailClip(
|
||||
privacyMode
|
||||
? { x: 0, y: 0, width: 1, height: 1 }
|
||||
: { x: 0, y: 0, width: 0, height: 0 }
|
||||
);
|
||||
}
|
||||
|
||||
globalThis.window.setThumbnailClip(
|
||||
privacyMode
|
||||
? { x: 0, y: 0, width: 1, height: 1 }
|
||||
: { x: 0, y: 0, width: 0, height: 0 }
|
||||
);
|
||||
setPrivacyMode(privacyMode);
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Menu, MenuItem, clipboard, shell } from "electron";
|
||||
import { Menu, MenuItem, clipboard } from "electron";
|
||||
|
||||
function setupMenu() {
|
||||
globalThis.window.webContents.on("context-menu", (_event, params) => {
|
||||
@@ -54,15 +54,6 @@ function setupMenu() {
|
||||
})
|
||||
);
|
||||
|
||||
if (params.linkURL.length) {
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label: "Open in browser",
|
||||
click: () => shell.openExternal(params.linkURL)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (params.isEditable) {
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
@@ -99,15 +90,14 @@ function setupMenu() {
|
||||
})
|
||||
);
|
||||
|
||||
if (params.selectionText.length) {
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label: "Copy",
|
||||
role: "copy",
|
||||
accelerator: "CommandOrControl+C"
|
||||
})
|
||||
);
|
||||
}
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label: "Copy",
|
||||
role: "copy",
|
||||
enabled: params.selectionText.length > 0,
|
||||
accelerator: "CommandOrControl+C"
|
||||
})
|
||||
);
|
||||
|
||||
if (params.isEditable)
|
||||
menu.append(
|
||||
@@ -119,7 +109,7 @@ function setupMenu() {
|
||||
})
|
||||
);
|
||||
|
||||
if (menu.items.length > 0) menu.popup();
|
||||
menu.popup();
|
||||
});
|
||||
}
|
||||
export { setupMenu };
|
||||
|
||||
8
apps/web/desktop/package-lock.json
generated
8
apps/web/desktop/package-lock.json
generated
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.4.3",
|
||||
"version": "2.4.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.4.3",
|
||||
"version": "2.4.2",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"diary": "^0.3.1",
|
||||
"electron-updater": "^5.3.0",
|
||||
@@ -4519,7 +4520,8 @@
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.4.3",
|
||||
"version": "2.4.2",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./build/electron.js",
|
||||
|
||||
@@ -27,8 +27,6 @@ let tray: Tray | undefined = undefined;
|
||||
export function setupTray() {
|
||||
if (tray) tray.destroy();
|
||||
|
||||
const trayIconSize = process.platform === "win32" || process.platform === "darwin" ? 16 : 32;
|
||||
|
||||
tray = new Tray(
|
||||
AssetManager.icon("tray-icon", {
|
||||
size: process.platform === "darwin" ? 22 : 32
|
||||
@@ -39,14 +37,14 @@ export function setupTray() {
|
||||
{
|
||||
label: "Show app",
|
||||
type: "normal",
|
||||
icon: AssetManager.icon("tray-icon", { size: trayIconSize }),
|
||||
icon: AssetManager.icon("tray-icon", { size: 16 }),
|
||||
click: bringToFront
|
||||
},
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: "New note",
|
||||
type: "normal",
|
||||
icon: AssetManager.icon("note-add", { size: trayIconSize }),
|
||||
icon: AssetManager.icon("note-add", { size: 16 }),
|
||||
click: () => {
|
||||
bringToFront();
|
||||
sendMessageToRenderer(EVENTS.createItem, { itemType: "note" });
|
||||
@@ -55,7 +53,7 @@ export function setupTray() {
|
||||
{
|
||||
label: "New notebook",
|
||||
type: "normal",
|
||||
icon: AssetManager.icon("notebook-add", { size: trayIconSize }),
|
||||
icon: AssetManager.icon("notebook-add", { size: 16 }),
|
||||
click: () => {
|
||||
bringToFront();
|
||||
sendMessageToRenderer(EVENTS.createItem, { itemType: "notebook" });
|
||||
@@ -64,7 +62,7 @@ export function setupTray() {
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: "Quit",
|
||||
icon: AssetManager.icon("quit", { size: trayIconSize }),
|
||||
icon: AssetManager.icon("quit", { size: 16 }),
|
||||
type: "normal",
|
||||
click: () => {
|
||||
app.exit(0);
|
||||
|
||||
15161
apps/web/package-lock.json
generated
15161
apps/web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.4.3",
|
||||
"version": "2.4.2",
|
||||
"private": true,
|
||||
"main": "./src/app.js",
|
||||
"homepage": "https://notesnook.com/",
|
||||
@@ -14,8 +14,8 @@
|
||||
"@dnd-kit/modifiers": "^6.0.0",
|
||||
"@dnd-kit/sortable": "^7.0.1",
|
||||
"@emotion/react": "^11.10.0",
|
||||
"@mdi/js": "^6.9.96",
|
||||
"@mdi/react": "^1.6.0",
|
||||
"@mdi/js": "^6.5.95",
|
||||
"@mdi/react": "^1.4.0",
|
||||
"@notesnook-importer/core": "^1.5.2",
|
||||
"@notesnook/core": "*",
|
||||
"@notesnook/crypto": "*",
|
||||
@@ -40,7 +40,7 @@
|
||||
"event-source-polyfill": "^1.0.25",
|
||||
"fflate": "^0.7.4",
|
||||
"file-saver": "^2.0.5",
|
||||
"framer-motion": "^6.5.1",
|
||||
"framer-motion": "^4.1.17",
|
||||
"hash-wasm": "^4.9.0",
|
||||
"hotkeys-js": "^3.8.3",
|
||||
"immer": "^9.0.6",
|
||||
@@ -64,7 +64,7 @@
|
||||
"timeago.js": "^4.0.2",
|
||||
"w3c-keyname": "^2.2.6",
|
||||
"web-streams-polyfill": "^3.1.1",
|
||||
"wouter": "2.7.3",
|
||||
"wouter": "^2.7.3",
|
||||
"zustand": "^3.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -94,7 +94,7 @@
|
||||
"react-dom": "17.0.2",
|
||||
"source-map-explorer": "^2.5.2",
|
||||
"typescript": "^4.8.2",
|
||||
"webpack-bundle-analyzer": "^4.8.0",
|
||||
"webpack-bundle-analyzer": "^4.5.0",
|
||||
"worker-loader": "^3.0.8"
|
||||
},
|
||||
"overrides": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/node_modules/react-scripts/config/webpack.config.js b/node_modules/react-scripts/config/webpack.config.js
|
||||
index e465d8e..5236a94 100644
|
||||
index e465d8e..9d1d3f3 100644
|
||||
--- a/node_modules/react-scripts/config/webpack.config.js
|
||||
+++ b/node_modules/react-scripts/config/webpack.config.js
|
||||
@@ -106,6 +106,9 @@ module.exports = function (webpackEnv) {
|
||||
@@ -35,14 +35,12 @@ index e465d8e..5236a94 100644
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebook/create-react-app/issues/253
|
||||
@@ -320,13 +330,17 @@ module.exports = function (webpackEnv) {
|
||||
@@ -320,13 +330,15 @@ module.exports = function (webpackEnv) {
|
||||
.map(ext => `.${ext}`)
|
||||
.filter(ext => useTypeScript || !ext.includes('ts')),
|
||||
alias: {
|
||||
+ "react": path.resolve(path.join(__dirname,'../../react')),
|
||||
+ "react-dom": path.resolve(path.join(__dirname,'../../react-dom')),
|
||||
+ "@mdi/js": path.resolve(path.join(__dirname,'../../@mdi/js')),
|
||||
+ "@mdi/react": path.resolve(path.join(__dirname,'../../@mdi/react')),
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
'react-native': 'react-native-web',
|
||||
@@ -54,7 +52,7 @@ index e465d8e..5236a94 100644
|
||||
}),
|
||||
...(modules.webpackAliases || {}),
|
||||
},
|
||||
@@ -336,14 +350,14 @@ module.exports = function (webpackEnv) {
|
||||
@@ -336,14 +348,14 @@ module.exports = function (webpackEnv) {
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
@@ -77,7 +75,7 @@ index e465d8e..5236a94 100644
|
||||
],
|
||||
},
|
||||
module: {
|
||||
@@ -353,7 +367,7 @@ module.exports = function (webpackEnv) {
|
||||
@@ -353,7 +365,7 @@ module.exports = function (webpackEnv) {
|
||||
shouldUseSourceMap && {
|
||||
enforce: 'pre',
|
||||
exclude: /@babel(?:\/|\\{1,2})runtime/,
|
||||
@@ -86,7 +84,7 @@ index e465d8e..5236a94 100644
|
||||
loader: require.resolve('source-map-loader'),
|
||||
},
|
||||
{
|
||||
@@ -408,13 +422,13 @@ module.exports = function (webpackEnv) {
|
||||
@@ -408,13 +420,13 @@ module.exports = function (webpackEnv) {
|
||||
},
|
||||
],
|
||||
issuer: {
|
||||
@@ -102,7 +100,7 @@ index e465d8e..5236a94 100644
|
||||
include: paths.appSrc,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
@@ -453,6 +467,9 @@ module.exports = function (webpackEnv) {
|
||||
@@ -453,6 +465,9 @@ module.exports = function (webpackEnv) {
|
||||
isEnvDevelopment &&
|
||||
shouldUseReactRefresh &&
|
||||
require.resolve('react-refresh/babel'),
|
||||
@@ -112,7 +110,7 @@ index e465d8e..5236a94 100644
|
||||
].filter(Boolean),
|
||||
// This is a feature of `babel-loader` for webpack (not Babel itself).
|
||||
// It enables caching results in ./node_modules/.cache/babel-loader/
|
||||
@@ -460,13 +477,13 @@ module.exports = function (webpackEnv) {
|
||||
@@ -460,13 +475,13 @@ module.exports = function (webpackEnv) {
|
||||
cacheDirectory: true,
|
||||
// See #6846 for context on why cacheCompression is disabled
|
||||
cacheCompression: false,
|
||||
@@ -128,7 +126,7 @@ index e465d8e..5236a94 100644
|
||||
exclude: /@babel(?:\/|\\{1,2})runtime/,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
@@ -594,7 +611,7 @@ module.exports = function (webpackEnv) {
|
||||
@@ -594,7 +609,7 @@ module.exports = function (webpackEnv) {
|
||||
// its runtime that would otherwise be processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
|
||||
@@ -1,45 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="script-src 'self' 'nonce-7WIq8hRwApoXhctoGZZthMLYQLRNiprTwcPi6Azdf' 'unsafe-eval';"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="%PUBLIC_URL%/apple-touch-icon.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAHUUExURUxpcf////////////////////////////////////////////////////////////////////////////////////39/f39/f////////////////////////////////z8/O3s7P7+/vTz8wAAAPz7++/v7/Py8u/u7vDv7+7t7dfX1/79/ezr66Kiovv6+vf29vX09OTj4w0NDfv7++Ti4vr6+vT09Ovq6vn5+f39/fb19dnX1/b29tvb29/f3xMTE/j4+Pn4+O7s7MnJyerq6uno6M/Pz8jIyN3d3dnZ2d7e3kZGRvj39/38/Pf39+zs7PDw8OPi4ubl5cvLy+rp6czMzPLy8vPz887Ozujo6MrKyuDf3+fn59PT0+bm5uXl5c3NzfX19evr6wsLC+/t7ePj4/Ty8u3r6/Lx8eXk5OPh4dDQ0Nza2tjX19vZ2fHw8Nva2tLR0djW1tbW1tra2tLS0uHh4evp6eLi4tXV1djY2L+/vysrK7q6utHR0aGhoa6urrm5uQICAigoKCEhIZCQkFxcXBgYGFRUVBISEm1tbQ4ODklJSbu7uyAgID09PZqamkVFRXh4eLGxsTg4OOTk5FFRUQEBAQoKCnx8fBcXF7/+rP8AAAAfdFJOUwBOqO2h6KT9Mtyr5+w3Str+6TTZOjXb31M24O8BOd0PapSsAAABxElEQVQ4y2NgYOBgYWWXxwLYBdmEGICAjx9Z1NJJVQfB42QE6hdG0RaoaaCqhKSCl4EF1VxNVAXyTAzcaArUUBXwMLBjKDBBViDAII+uwBZFgTy6AjU1awIKNKz1aa0g1VBfB58CDS08CuIb40AKzLAqsJKXN2tXacChwKS+rTtB3tJHRU9ewxmbFZ6tKiqTrSAKdIO1LDEUaPeoqMzsUwAp0NIt8Uj0ijJXQFVgnDJVZcK0XrACXcXY0sjoHM8AfyUkBbOn9KvMmgRSYFEW4hUVqJagHeaShqTAwX7ijJQusAkW5VXRHu6eruFqaigKkqarqAAVKFbYxWiYq1XGedijuMHBWz6xo7m2Rl7RwtvdJaw6NjzCFl2BVVNdshJQgbqBsqtdpIu7nasjXIFXS2cyRL2CW6i6JTCgFCLitUPs4QrM7cDmKZi7FRYVm6o5QeLbDGICLNEqJAVk59rk2djY5Me4FThawQOKFSzrrxmaZWpqBAamQUGmwRaakNTNzMAm7+ekrKmlqOjsrIgAGcAA1ci0NVPiYhAV8zVURgbp6nBg7isuwsDAKKfkp4AEVOHASklKApS9JZmY5bECaS5ZGQYA3vqpy6NoYh0AAABXelRYdFJhdyBwcm9maWxlIHR5cGUgaXB0YwAAeJzj8gwIcVYoKMpPy8xJ5VIAAyMLLmMLEyMTS5MUAxMgRIA0w2QDI7NUIMvY1MjEzMQcxAfLgEigSi4A6hcRdPJCNZUAAAAASUVORK5CYII="
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="%PUBLIC_URL%/favicon-16x16.png"
|
||||
/>
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<!-- <link rel="manifest" href="%PUBLIC_URL%/site.webmanifest" /> -->
|
||||
<link
|
||||
rel="mask-icon"
|
||||
href="%PUBLIC_URL%/safari-pinned-tab.svg"
|
||||
color="#01c352"
|
||||
/>
|
||||
<meta name="msapplication-TileColor" content="#01c352" />
|
||||
<meta name="theme-color" content="#01c352" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<!--
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Security-Policy"
|
||||
content="script-src 'self' 'nonce-7WIq8hRwApoXhctoGZZthMLYQLRNiprTwcPi6Azdf' 'unsafe-eval';" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="%PUBLIC_URL%/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32"
|
||||
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAHUUExURUxpcf////////////////////////////////////////////////////////////////////////////////////39/f39/f////////////////////////////////z8/O3s7P7+/vTz8wAAAPz7++/v7/Py8u/u7vDv7+7t7dfX1/79/ezr66Kiovv6+vf29vX09OTj4w0NDfv7++Ti4vr6+vT09Ovq6vn5+f39/fb19dnX1/b29tvb29/f3xMTE/j4+Pn4+O7s7MnJyerq6uno6M/Pz8jIyN3d3dnZ2d7e3kZGRvj39/38/Pf39+zs7PDw8OPi4ubl5cvLy+rp6czMzPLy8vPz887Ozujo6MrKyuDf3+fn59PT0+bm5uXl5c3NzfX19evr6wsLC+/t7ePj4/Ty8u3r6/Lx8eXk5OPh4dDQ0Nza2tjX19vZ2fHw8Nva2tLR0djW1tbW1tra2tLS0uHh4evp6eLi4tXV1djY2L+/vysrK7q6utHR0aGhoa6urrm5uQICAigoKCEhIZCQkFxcXBgYGFRUVBISEm1tbQ4ODklJSbu7uyAgID09PZqamkVFRXh4eLGxsTg4OOTk5FFRUQEBAQoKCnx8fBcXF7/+rP8AAAAfdFJOUwBOqO2h6KT9Mtyr5+w3Str+6TTZOjXb31M24O8BOd0PapSsAAABxElEQVQ4y2NgYOBgYWWXxwLYBdmEGICAjx9Z1NJJVQfB42QE6hdG0RaoaaCqhKSCl4EF1VxNVAXyTAzcaArUUBXwMLBjKDBBViDAII+uwBZFgTy6AjU1awIKNKz1aa0g1VBfB58CDS08CuIb40AKzLAqsJKXN2tXacChwKS+rTtB3tJHRU9ewxmbFZ6tKiqTrSAKdIO1LDEUaPeoqMzsUwAp0NIt8Uj0ijJXQFVgnDJVZcK0XrACXcXY0sjoHM8AfyUkBbOn9KvMmgRSYFEW4hUVqJagHeaShqTAwX7ijJQusAkW5VXRHu6eruFqaigKkqarqAAVKFbYxWiYq1XGedijuMHBWz6xo7m2Rl7RwtvdJaw6NjzCFl2BVVNdshJQgbqBsqtdpIu7nasjXIFXS2cyRL2CW6i6JTCgFCLitUPs4QrM7cDmKZi7FRYVm6o5QeLbDGICLNEqJAVk59rk2djY5Me4FThawQOKFSzrrxmaZWpqBAamQUGmwRaakNTNzMAm7+ekrKmlqOjsrIgAGcAA1ci0NVPiYhAV8zVURgbp6nBg7isuwsDAKKfkp4AEVOHASklKApS9JZmY5bECaS5ZGQYA3vqpy6NoYh0AAABXelRYdFJhdyBwcm9maWxlIHR5cGUgaXB0YwAAeJzj8gwIcVYoKMpPy8xJ5VIAAyMLLmMLEyMTS5MUAxMgRIA0w2QDI7NUIMvY1MjEzMQcxAfLgEigSi4A6hcRdPJCNZUAAAAASUVORK5CYII=" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="%PUBLIC_URL%/favicon-16x16.png" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<!-- <link rel="manifest" href="%PUBLIC_URL%/site.webmanifest" /> -->
|
||||
<link rel="mask-icon" href="%PUBLIC_URL%/safari-pinned-tab.svg" color="#01c352" />
|
||||
<meta name="msapplication-TileColor" content="#01c352" />
|
||||
<meta name="theme-color" content="#01c352" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<!-- <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> -->
|
||||
<!--
|
||||
<!-- <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> -->
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
@@ -48,152 +30,147 @@
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<meta name="description" content="Your private note taking space" />
|
||||
<meta property="og:title" content="Notesnook" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="A fully open source & end-to-end encrypted note taking alternative to Evernote."
|
||||
/>
|
||||
<meta property="og:image" content="https://app.notesnook.com/banner.jpg" />
|
||||
<meta
|
||||
property="og:image:alt"
|
||||
content="A fully open source & end-to-end encrypted note taking alternative to Evernote."
|
||||
/>
|
||||
<meta property="og:url" content="https://app.notesnook.com/" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="Notesnook by Streetwriters LLC" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<title>Notesnook</title>
|
||||
<meta name="description" content="Your private note taking space" />
|
||||
<meta property="og:title" content="Notesnook" />
|
||||
<meta property="og:description"
|
||||
content="A fully open source & end-to-end encrypted note taking alternative to Evernote." />
|
||||
<meta property="og:image" content="https://app.notesnook.com/banner.jpg" />
|
||||
<meta property="og:image:alt"
|
||||
content="A fully open source & end-to-end encrypted note taking alternative to Evernote." />
|
||||
<meta property="og:url" content="https://app.notesnook.com/" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="Notesnook by Streetwriters LLC" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<title>Notesnook</title>
|
||||
|
||||
<script nonce="7WIq8hRwApoXhctoGZZthMLYQLRNiprTwcPi6Azdf">
|
||||
const theme = JSON.parse(
|
||||
window.localStorage.getItem("theme") || '"light"'
|
||||
);
|
||||
const root = document.querySelector("html");
|
||||
if (root) root.setAttribute("data-theme", theme);
|
||||
</script>
|
||||
<style>
|
||||
html[data-theme="dark"] {
|
||||
--bg: #1b1b1b;
|
||||
--fg: #fff;
|
||||
--three-bars-bg: #494949;
|
||||
--gradient-stop-color: #111111;
|
||||
--n-letter-color: #e1e1e1;
|
||||
<script nonce="7WIq8hRwApoXhctoGZZthMLYQLRNiprTwcPi6Azdf">
|
||||
const theme = JSON.parse(
|
||||
window.localStorage.getItem("theme") || '"light"'
|
||||
);
|
||||
const root = document.querySelector("html");
|
||||
if (root) root.setAttribute("data-theme", theme);
|
||||
</script>
|
||||
<style>
|
||||
html[data-theme="dark"] {
|
||||
--bg: #1b1b1b;
|
||||
--fg: #fff;
|
||||
--three-bars-bg: #494949;
|
||||
--gradient-stop-color: #111111;
|
||||
--n-letter-color: #e1e1e1;
|
||||
}
|
||||
|
||||
html[data-theme="light"] {
|
||||
--bg: #fff;
|
||||
--three-bars-bg: #bebebe;
|
||||
--gradient-stop-color: #fff9f9;
|
||||
--n-letter-color: #000;
|
||||
}
|
||||
|
||||
html[data-theme="light"] .react-loading-skeleton {
|
||||
--base-color: #e5e5e5;
|
||||
--highlight-color: #dbdbdb;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .react-loading-skeleton {
|
||||
--base-color: #2b2b2b;
|
||||
--highlight-color: #353535;
|
||||
}
|
||||
|
||||
#splash svg {
|
||||
transform: scale(1);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
html[data-theme="light"] {
|
||||
--bg: #fff;
|
||||
--three-bars-bg: #bebebe;
|
||||
--gradient-stop-color: #fff9f9;
|
||||
--n-letter-color: #000;
|
||||
}
|
||||
|
||||
html[data-theme="light"] .react-loading-skeleton {
|
||||
--base-color: #f7f7f7;
|
||||
--highlight-color: #f7f7f7;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .react-loading-skeleton {
|
||||
--base-color: #2b2b2b;
|
||||
--highlight-color: #2b2b2b;
|
||||
}
|
||||
|
||||
#splash svg {
|
||||
70% {
|
||||
transform: scale(1);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 480px) {
|
||||
|
||||
html,
|
||||
body {
|
||||
font-size: 16px;
|
||||
font-size: 18px;
|
||||
background-color: var(--background);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 480px) {
|
||||
html,
|
||||
body {
|
||||
font-size: 18px;
|
||||
background-color: var(--background);
|
||||
}
|
||||
}
|
||||
:root,
|
||||
body,
|
||||
#root {
|
||||
margin: 0 !important;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:root,
|
||||
body,
|
||||
#root {
|
||||
margin: 0 !important;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
* {
|
||||
font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
@keyframes fadeUp {
|
||||
0% {
|
||||
transform: translateY(500px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
80% {
|
||||
transform: translateY(0px);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* svg {
|
||||
width: 1.5rem;
|
||||
} */
|
||||
.ReactModal__Overlay {
|
||||
@keyframes fadeUp {
|
||||
0% {
|
||||
transform: translateY(500px);
|
||||
opacity: 0;
|
||||
transition: opacity 200ms ease-in-out;
|
||||
}
|
||||
|
||||
.ReactModal__Overlay--after-open {
|
||||
80% {
|
||||
transform: translateY(0px);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.ReactModal__Overlay--before-close {
|
||||
opacity: 0;
|
||||
}
|
||||
/* svg {
|
||||
width: 1.5rem;
|
||||
} */
|
||||
.ReactModal__Overlay {
|
||||
opacity: 0;
|
||||
transition: opacity 200ms ease-in-out;
|
||||
}
|
||||
|
||||
.slide {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.ReactModal__Overlay--after-open {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.carousel.carousel-slider {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
.ReactModal__Overlay--before-close {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/highlightjs@9.16.2/highlight.pack.min.js"></script>
|
||||
.slide {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.carousel.carousel-slider {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/highlightjs@9.16.2/highlight.pack.min.js"></script>
|
||||
-->
|
||||
<div id="root"></div>
|
||||
<div
|
||||
id="splash"
|
||||
style="
|
||||
<div id="root"></div>
|
||||
<div id="splash" style="
|
||||
background-color: var(--bg);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@@ -204,72 +181,34 @@
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<svg viewBox="0 0 339 339" style="width: 150px">
|
||||
<defs />
|
||||
<defs>
|
||||
<linearGradient
|
||||
xlink:href="#a"
|
||||
id="b"
|
||||
x1="188.61227"
|
||||
x2="193.54405"
|
||||
y1="165.2058"
|
||||
y2="216.81519"
|
||||
gradientTransform="rotate(5 4448 -4204) scale(2.93671)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
/>
|
||||
|
||||
<linearGradient id="a">
|
||||
<stop offset="0" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="var(--gradient-stop-color)"
|
||||
stop-opacity="0"
|
||||
/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="c"
|
||||
x1="167.8"
|
||||
x2="270.6"
|
||||
y1="76.9"
|
||||
y2="64.2"
|
||||
gradientTransform="rotate(5 465 -2050) scale(1.50082)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
xlink:href="#a"
|
||||
/>
|
||||
</defs>
|
||||
<g transform="translate(0 42)">
|
||||
<path fill="url(#b)" d="M160 205l154 42-141 44-155-42z" />
|
||||
<path fill="url(#c)" d="M160-35v240l154 42 1-253z" />
|
||||
<path
|
||||
fill="none"
|
||||
stroke-width="1.2"
|
||||
d="M160 205V-35m0 240L18 249m142-44l154 41"
|
||||
/>
|
||||
<path
|
||||
fill="var(--n-letter-color)"
|
||||
d="M84 109l35 54V98l21-7v91l-27 9-35-54v65l-21 6v-91z"
|
||||
/>
|
||||
<rect
|
||||
width="86.1"
|
||||
height="12.6"
|
||||
x="185"
|
||||
y="97"
|
||||
fill="var(--three-bars-bg)"
|
||||
ry="2.3"
|
||||
transform="skewY(15) scale(.9669 1)"
|
||||
/>
|
||||
<path
|
||||
fill="var(--three-bars-bg)"
|
||||
d="M181 169l99 26 2 3v8c0 1-1 2-2 1l-99-26-2-3v-7c0-2 1-2 2-2zm0-47l99 27 2 2v8l-2 2-99-27c-1 0-2-1-2-3v-7l2-2z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="dialogContainer"></div>
|
||||
<div id="floatingViewContainer"></div>
|
||||
<!--
|
||||
">
|
||||
<svg viewBox="0 0 339 339" style="width: 150px">
|
||||
<defs />
|
||||
<defs>
|
||||
<linearGradient id="b" x1="193.9" x2="198.7" y1="166.8" y2="223.3"
|
||||
gradientTransform="rotate(5 4448 -4204) scale(2.93671)" gradientUnits="userSpaceOnUse" xlink:href="#a" />
|
||||
<linearGradient id="a">
|
||||
<stop offset="0" />
|
||||
<stop offset="1" stop-color="var(--gradient-stop-color)" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient id="c" x1="167.8" x2="270.6" y1="76.9" y2="64.2"
|
||||
gradientTransform="rotate(5 465 -2050) scale(1.50082)" gradientUnits="userSpaceOnUse" xlink:href="#a" />
|
||||
</defs>
|
||||
<g transform="translate(0 42)">
|
||||
<path fill="url(#b)" d="M160 205l154 42-141 44-155-42z" />
|
||||
<path fill="url(#c)" d="M160-35v240l154 42 1-253z" />
|
||||
<path fill="none" stroke-width="1.2" d="M160 205V-35m0 240L18 249m142-44l154 41" />
|
||||
<path fill="var(--n-letter-color)" d="M84 109l35 54V98l21-7v91l-27 9-35-54v65l-21 6v-91z" />
|
||||
<rect width="86.1" height="12.6" x="185" y="97" fill="var(--three-bars-bg)" ry="2.3"
|
||||
transform="skewY(15) scale(.9669 1)" />
|
||||
<path fill="var(--three-bars-bg)"
|
||||
d="M181 169l99 26 2 3v8c0 1-1 2-2 1l-99-26-2-3v-7c0-2 1-2 2-2zm0-47l99 27 2 2v8l-2 2-99-27c-1 0-2-1-2-3v-7l2-2z" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="dialogContainer"></div>
|
||||
<div id="floatingViewContainer"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
@@ -279,5 +218,6 @@
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -93,8 +93,6 @@ export default function AppEffects({ setShow }) {
|
||||
initEditorStore();
|
||||
|
||||
(async function () {
|
||||
await resetReminders();
|
||||
|
||||
await updateLastSynced();
|
||||
if (await initUser()) {
|
||||
showUpgradeReminderDialogs();
|
||||
|
||||
@@ -34,13 +34,13 @@ import NavigationMenu from "./components/navigation-menu";
|
||||
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";
|
||||
|
||||
const GlobalMenuWrapper = React.lazy(() =>
|
||||
import("./components/global-menu-wrapper")
|
||||
);
|
||||
const AppEffects = React.lazy(() => import("./app-effects"));
|
||||
const MobileAppEffects = React.lazy(() => import("./app-effects.mobile"));
|
||||
const CachedRouter = React.lazy(() => import("./components/cached-router"));
|
||||
const HashRouter = React.lazy(() => import("./components/hash-router"));
|
||||
|
||||
function App() {
|
||||
@@ -136,12 +136,14 @@ function DesktopAppContents({ isAppLoaded, show, setShow }) {
|
||||
visible={!isFocusMode}
|
||||
priority={LayoutPriority.Low}
|
||||
>
|
||||
<NavigationMenu
|
||||
toggleNavigationContainer={(state) => {
|
||||
setShow(state || !show);
|
||||
}}
|
||||
isTablet={isNarrow}
|
||||
/>
|
||||
<Flex sx={{ overflow: "hidden", flex: 1 }}>
|
||||
<NavigationMenu
|
||||
toggleNavigationContainer={(state) => {
|
||||
setShow(state || !show);
|
||||
}}
|
||||
isTablet={isNarrow}
|
||||
/>
|
||||
</Flex>
|
||||
</Allotment.Pane>
|
||||
<Allotment.Pane
|
||||
className="pane middle-pane"
|
||||
@@ -151,7 +153,11 @@ function DesktopAppContents({ isAppLoaded, show, setShow }) {
|
||||
priority={LayoutPriority.Normal}
|
||||
>
|
||||
<Flex className="listMenu" variant="columnFill">
|
||||
{isAppLoaded && <CachedRouter />}
|
||||
<SuspenseLoader
|
||||
condition={isAppLoaded}
|
||||
component={CachedRouter}
|
||||
fallback={<ViewLoader />}
|
||||
/>
|
||||
</Flex>
|
||||
</Allotment.Pane>
|
||||
<Allotment.Pane
|
||||
@@ -165,13 +171,11 @@ function DesktopAppContents({ isAppLoaded, show, setShow }) {
|
||||
flexDirection: "column"
|
||||
}}
|
||||
>
|
||||
{isAppLoaded && (
|
||||
<SuspenseLoader
|
||||
fallback={<EditorLoader />}
|
||||
component={HashRouter}
|
||||
condition={isAppLoaded}
|
||||
/>
|
||||
)}
|
||||
<SuspenseLoader
|
||||
fallback={<EditorLoader />}
|
||||
component={HashRouter}
|
||||
condition={isAppLoaded}
|
||||
/>
|
||||
</Flex>
|
||||
</Allotment.Pane>
|
||||
</Allotment>
|
||||
|
||||
@@ -137,8 +137,8 @@ class AddNotebookDialog extends React.Component {
|
||||
this.createNotebook();
|
||||
}
|
||||
}}
|
||||
onClose={() => props.onClose(false)}
|
||||
negativeButton={{ text: "Cancel", onClick: () => props.onClose(false) }}
|
||||
onClose={props.onClose}
|
||||
negativeButton={{ text: "Cancel", onClick: props.onClose }}
|
||||
>
|
||||
<Flex sx={{ overflowY: "auto", flexDirection: "column" }}>
|
||||
<Field
|
||||
|
||||
@@ -152,7 +152,7 @@ export default function AddReminderDialog(props: AddReminderDialogProps) {
|
||||
isOpen={true}
|
||||
title={reminderId ? "Edit reminder" : "Add a reminder"}
|
||||
description={""}
|
||||
onClose={() => props.onClose(false)}
|
||||
onClose={props.onClose}
|
||||
positiveButton={{
|
||||
text: reminderId ? "Save" : "Add",
|
||||
disabled:
|
||||
@@ -210,7 +210,7 @@ export default function AddReminderDialog(props: AddReminderDialogProps) {
|
||||
props.onClose(true);
|
||||
}
|
||||
}}
|
||||
negativeButton={{ text: "Cancel", onClick: () => props.onClose(false) }}
|
||||
negativeButton={{ text: "Cancel", onClick: props.onClose }}
|
||||
>
|
||||
<Field
|
||||
id="title"
|
||||
|
||||
@@ -28,16 +28,16 @@ function Confirm(props) {
|
||||
icon={props.icon}
|
||||
width={props.width}
|
||||
description={props.subtitle}
|
||||
onClose={() => props.onNo(false)}
|
||||
onClose={props.onNo}
|
||||
positiveButton={
|
||||
props.yesText && {
|
||||
text: props.yesText,
|
||||
onClick: () => props.onYes(true),
|
||||
onClick: props.onYes,
|
||||
autoFocus: !!props.yesText
|
||||
}
|
||||
}
|
||||
negativeButton={
|
||||
props.noText && { text: props.noText, onClick: () => props.onNo(false) }
|
||||
props.noText && { text: props.noText, onClick: props.onNo }
|
||||
}
|
||||
>
|
||||
<Box pb={!props.noText && !props.yesText ? 2 : 0}>
|
||||
|
||||
@@ -17,17 +17,29 @@ 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 { createContext, useContext } from "react";
|
||||
import Dialog from "./dialog";
|
||||
import Skeleton from "react-loading-skeleton";
|
||||
import "react-loading-skeleton/dist/skeleton.css";
|
||||
import { Flex } from "@theme-ui/components";
|
||||
|
||||
export const SelectionContext = createContext({
|
||||
enabled: false,
|
||||
selected: [],
|
||||
toggleSelection: (item) => null,
|
||||
deselect: (item) => null,
|
||||
select: (item) => null,
|
||||
isSelected: (item) => null,
|
||||
setMultiSelect: () => null,
|
||||
deselectAll: () => null
|
||||
});
|
||||
export const SelectionProvider = SelectionContext.Provider;
|
||||
export const useSelectionContext = () => useContext(SelectionContext);
|
||||
export default function DialogLoader() {
|
||||
return (
|
||||
<Dialog
|
||||
isOpen={true}
|
||||
title={<Skeleton height={30} width={150} />}
|
||||
description={<Skeleton height={15} width={100} />}
|
||||
>
|
||||
<Skeleton height={200} />
|
||||
<Flex
|
||||
sx={{
|
||||
mb: 3,
|
||||
mt: 1,
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "flex-end"
|
||||
}}
|
||||
>
|
||||
<Skeleton height={25} width={70} />
|
||||
</Flex>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user