Compare commits
122 Commits
2.4.7-andr
...
fix-db-ini
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8006914cd8 | ||
|
|
d99ccbc243 | ||
|
|
0f191eba5c | ||
|
|
26657c7ee3 | ||
|
|
a12749eb82 | ||
|
|
1a56d38116 | ||
|
|
0b9025af11 | ||
|
|
6f5a44cd11 | ||
|
|
5e1cc307b1 | ||
|
|
2197f1ec5e | ||
|
|
1da5fd3661 | ||
|
|
49e79b52b2 | ||
|
|
c9a657298c | ||
|
|
cfd7caf16c | ||
|
|
cc2706a39e | ||
|
|
ea3c480edb | ||
|
|
abbcc1263a | ||
|
|
c77e29d7aa | ||
|
|
ba68adbeb1 | ||
|
|
efad6520a2 | ||
|
|
cc58351731 | ||
|
|
cf2f4c4bbb | ||
|
|
cc161b844c | ||
|
|
45ebf0670f | ||
|
|
9cfaa90232 | ||
|
|
edf4a248ed | ||
|
|
14d7ec6b4e | ||
|
|
cd2ca94c3d | ||
|
|
f1609aaa46 | ||
|
|
851364c0bf | ||
|
|
913a1743b5 | ||
|
|
dc5572d4b9 | ||
|
|
554560903c | ||
|
|
885e4a325c | ||
|
|
156ce7e711 | ||
|
|
71ecc05bd6 | ||
|
|
98b93d42c1 | ||
|
|
2e7935cacc | ||
|
|
50d64af7f6 | ||
|
|
e452fd3d2f | ||
|
|
64dd481402 | ||
|
|
28f647fa06 | ||
|
|
9eb1061cd1 | ||
|
|
3cab264213 | ||
|
|
fb56e7c866 | ||
|
|
234e368c4d | ||
|
|
4de4693505 | ||
|
|
5563c7d5f8 | ||
|
|
93225e5a30 | ||
|
|
0ab9846732 | ||
|
|
f6f1cf0ae7 | ||
|
|
69615d54db | ||
|
|
a990d0f404 | ||
|
|
4aeff48e74 | ||
|
|
78c1a75ec9 | ||
|
|
9e6664a80e | ||
|
|
b306a09747 | ||
|
|
fab52012ad | ||
|
|
fc39c714b6 | ||
|
|
5fff4bbb0e | ||
|
|
0a29571ad8 | ||
|
|
275fa1b3ca | ||
|
|
79889665e5 | ||
|
|
9888ad327c | ||
|
|
77ab2d7740 | ||
|
|
509dc4b541 | ||
|
|
ff84ee2d42 | ||
|
|
d838c2e8ad | ||
|
|
28d718acf6 | ||
|
|
adf546f29c | ||
|
|
b18cf277f6 | ||
|
|
8ab26eb852 | ||
|
|
aac43274a0 | ||
|
|
2fb2c4247a | ||
|
|
cb1d32d177 | ||
|
|
75e1985a6f | ||
|
|
0093b39f64 | ||
|
|
d19c2ef735 | ||
|
|
db527cfbf3 | ||
|
|
7da8ddb24f | ||
|
|
5d401ac9ac | ||
|
|
9605eafa00 | ||
|
|
783460c685 | ||
|
|
af906d8ace | ||
|
|
fca26a8793 | ||
|
|
27863def9c | ||
|
|
f1d4fd1d5d | ||
|
|
61b7e43d08 | ||
|
|
87ebe1918c | ||
|
|
4c5253c267 | ||
|
|
f5be8a0c86 | ||
|
|
5392582819 | ||
|
|
651ae4cdaa | ||
|
|
d87b5c5386 | ||
|
|
d5331b1997 | ||
|
|
0a6f8fa18e | ||
|
|
c1ba16eb80 | ||
|
|
0cd01b25bd | ||
|
|
f0c1300ec8 | ||
|
|
70eee2a224 | ||
|
|
42470d1d67 | ||
|
|
7fc58a3fa5 | ||
|
|
e186037c03 | ||
|
|
732a0ffea4 | ||
|
|
89f2e9fde1 | ||
|
|
c369541a7f | ||
|
|
0b7a157ab1 | ||
|
|
9b86c6ebb7 | ||
|
|
22cd07e414 | ||
|
|
47f919cb61 | ||
|
|
5b3c889b7c | ||
|
|
77ac53215c | ||
|
|
167c7125d2 | ||
|
|
ba56fd0279 | ||
|
|
8e795b729a | ||
|
|
a4eb597f35 | ||
|
|
0aeade69da | ||
|
|
90a3bd3307 | ||
|
|
7a61e3558e | ||
|
|
dd9c555bff | ||
|
|
50f84780bb | ||
|
|
e12372364d |
@@ -10,11 +10,11 @@
|
||||
|
||||
## Overview
|
||||
|
||||
Notesnook is a free (as in speech) & open source note taking app focused on user privacy & ease of use. To ensure zero knowledge principles, Notesnook encrypts everything on your device using `XChaCha20-Poly1305` & `Argon2`.
|
||||
Notesnook is a free (as in speech) & open-source note-taking app focused on user privacy & ease of use. To ensure zero knowledge principles, Notesnook encrypts everything on your device using `XChaCha20-Poly1305` & `Argon2`.
|
||||
|
||||
Notesnook is our **proof** that privacy does _not_ (always) have to come at the cost of convenience. Our goal is to provide users peace of mind & 100% confidence that their notes are safe and secure. The decision to go fully open source is one of the most crucial steps towards that.
|
||||
Notesnook is our **proof** that privacy does _not_ (always) have to come at the cost of convenience. We aim to provide users peace of mind & 100% confidence that their notes are safe and secure. The decision to go fully open source is one of the most crucial steps towards that.
|
||||
|
||||
This repository contains all the code required to build & use the Notesnook web, desktop & mobile clients. If you are looking for a full features list or screenshots, please check the [website](https://notesnook.com/).
|
||||
This repository contains all the code required to build & use the Notesnook web, desktop & mobile clients. If you are looking for a full feature list or screenshots, please check the [website](https://notesnook.com/).
|
||||
|
||||
## Developer guide
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
|
||||
## Build instructions
|
||||
|
||||
> **Before you start it is recommended that you read [the contributing guidelines](/CONTRIBUTING.md).**
|
||||
> **Before you start, it is recommended that you read [the contributing guidelines](/CONTRIBUTING.md).**
|
||||
|
||||
### Setting up development environment
|
||||
### Setting up the development environment
|
||||
|
||||
Requirements:
|
||||
|
||||
@@ -57,7 +57,7 @@ npm install
|
||||
|
||||
### Running the app on Android
|
||||
|
||||
[Setup an Android emulator from Android Studio](https://developer.android.com/studio/run/managing-avds) if you haven't already and then run the following command to start the app in the Emulator:
|
||||
[Setup an Android emulator from Android Studio](https://developer.android.com/studio/run/managing-avds) if you haven't already, and then run the following command to start the app in the Emulator:
|
||||
|
||||
```bash
|
||||
npm run start:android
|
||||
@@ -78,11 +78,11 @@ npm run start:ios
|
||||
|
||||
## Developer guide
|
||||
|
||||
> This project is in a transition state between Javascript & Typescript. We are gradually porting everything over to Typescript so if you can help with that, it'd be great!
|
||||
> This project is in a transition state between Javascript & Typescript. We are gradually porting everything over to Typescript, so if you can help with that, it'd be great!
|
||||
|
||||
### The tech stack
|
||||
|
||||
We try to keep the stack as lean as possible
|
||||
We try to keep the stack as lean as possible:
|
||||
|
||||
1. React Native
|
||||
2. Typescript/Javascript
|
||||
@@ -95,17 +95,17 @@ We try to keep the stack as lean as possible
|
||||
|
||||
The app codebase is distributed over two primary directories. `native/` and `app/`.
|
||||
|
||||
- `native/`: Includes `android/` and `ios/` folders and everything related to react native core functionality like bundling, development and packaging. Any react-native dependency that has native code i.e android & ios folders, is installed here.
|
||||
- `native/`: Includes `android/` and `ios/` folders and everything related to react native core functionality like bundling, development, and packaging. Any react-native dependency with native code, i.e., android & ios folders, is installed here.
|
||||
|
||||
- `app/`: Includes all the app code other than the native part. All JS only dependencies are installed here.
|
||||
- `components/`: Each component serves a specific purpose in the app UI, for example the `Paragraph` component is used to render paragraphs in the app and a `Header` component is used to render a `header` on all screens.
|
||||
- `common/`: Features that have integral role in app functionality, for example, notesnook core is initialized here.
|
||||
- `app/`: Includes all the app code other than the native part. All JS-only dependencies are installed here.
|
||||
- `components/`: Each component serves a specific purpose in the app UI. For example, the `Paragraph` component is used to render paragraphs in the app, and a `Header` component is used to render a `header` on all screens.
|
||||
- `common/`: Features that are integral to the app's functionality. For example, the notesnook core is initialized here.
|
||||
- `hooks/`: Hooks for different app logic
|
||||
- `navigation/`: Includes app navigation specific code. Here the app navigation, editor & side menu are rendered side by side in fluid tabs.
|
||||
- `navigation/`: Includes app navigation-specific code. Here the app navigation, editor & side menu are rendered side by side in fluid tabs.
|
||||
- `screens`: Navigator screens.
|
||||
- `services`: Parts of code that do a specific function, for example, the `sync` service is responsibe for running Sync from anywhere in the app.
|
||||
- `stores`: We use `zustand` for global state management in the app. There are multiple stores that provide the state for different parts of the app.
|
||||
- `utils`: General purpose stuff such as constant values, utility functions etc.
|
||||
- `services`: Parts of code that do a specific function. For example, the `sync` service runs Sync from anywhere in the app.
|
||||
- `stores`: We use `zustand` for global state management in the app. Multiple stores provide the state for different parts of the app.
|
||||
- `utils`: General purpose stuff such as constant values, utility functions, etc.
|
||||
|
||||
There are several other folders at the root:
|
||||
|
||||
@@ -115,11 +115,11 @@ There are several other folders at the root:
|
||||
|
||||
### Running the tests
|
||||
|
||||
When you are done making the required changes, you will need to run the tests to make sure you didn't break anything. We use Detox as the testing framework & the tests can be started as follows:
|
||||
When you are done making the required changes, you must run the tests to ensure you didn't break anything. We use Detox as the testing framework & the tests can be started as follows:
|
||||
|
||||
### Android
|
||||
|
||||
To run the tests on android, you will need to create an emulator device on your system:
|
||||
To run the tests on Android, you will need to create an emulator device on your system:
|
||||
|
||||
```
|
||||
$ANDROID_HOME/tools/bin/avdmanager create avd -n Pixel_5_API_31 -d pixel --package "system-images;android-31;default;x86_64"
|
||||
@@ -127,13 +127,13 @@ $ANDROID_HOME/tools/bin/avdmanager create avd -n Pixel_5_API_31 -d pixel --packa
|
||||
|
||||
If you face problems, follow the detailed guide in [Detox documentation](https://wix.github.io/Detox/docs/introduction/android-dev-env). Keep the emulator name set to `Pixel_5_API_31`.
|
||||
|
||||
Once you have created an emulator device, build the android apks
|
||||
Once you have created an emulator device, build the Android apks:
|
||||
|
||||
```
|
||||
npm run build:android
|
||||
```
|
||||
|
||||
Finally run the tests
|
||||
Finally, run the tests:
|
||||
|
||||
```
|
||||
npm run test:android
|
||||
@@ -141,9 +141,9 @@ npm run test:android
|
||||
|
||||
### iOS
|
||||
|
||||
To run e2e tests on iOS simulator, you must be on a Mac with XCode installed.
|
||||
To run e2e tests on the iOS simulator, you must be on a Mac with XCode installed.
|
||||
|
||||
First install [AppleSimulatorUtils](https://github.com/wix/AppleSimulatorUtils)
|
||||
First, install [AppleSimulatorUtils](https://github.com/wix/AppleSimulatorUtils):
|
||||
|
||||
```
|
||||
brew tap wix/brew
|
||||
@@ -156,7 +156,7 @@ Now build the iOS app for testing:
|
||||
npm run build:ios
|
||||
```
|
||||
|
||||
Finally run the tests:
|
||||
Finally, run the tests:
|
||||
|
||||
```
|
||||
npm run test:ios
|
||||
|
||||
BIN
apps/mobile/app/assets/images/notesnook-dark.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
@@ -68,14 +68,3 @@ db.host(
|
||||
ISSUES_HOST: "https://issues.streetwriters.co"
|
||||
}
|
||||
);
|
||||
|
||||
export async function loadDatabase() {
|
||||
// if (!DB) {
|
||||
// let module = await import(/* webpackChunkName: "notes-core" */ 'notes-core/api/index');
|
||||
// DB = module.default;
|
||||
// }
|
||||
// db = new DB(Storage, Platform.OS === 'ios' ? EventSource : AndroidEventSource, filesystem);
|
||||
// if (DOMParser) {
|
||||
// await DOMParser.prepare();
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import { View } from "react-native";
|
||||
|
||||
import { useMenuStore } from "../../../stores/use-menu-store";
|
||||
import {
|
||||
eSendEvent,
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent,
|
||||
ToastEvent
|
||||
@@ -30,6 +31,7 @@ import Navigation from "../../../services/navigation";
|
||||
import { db } from "../../../common/database";
|
||||
import {
|
||||
eCloseAddTopicDialog,
|
||||
eOnTopicSheetUpdate,
|
||||
eOpenAddTopicDialog
|
||||
} from "../../../utils/events";
|
||||
import { sleep } from "../../../utils/time";
|
||||
@@ -40,6 +42,7 @@ import DialogHeader from "../../dialog/dialog-header";
|
||||
import Input from "../../ui/input";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import { Toast } from "../../toast";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
|
||||
export class AddTopicDialog extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -78,9 +81,11 @@ export class AddTopicDialog extends React.Component {
|
||||
}
|
||||
this.close();
|
||||
setTimeout(() => {
|
||||
Navigation.queueRoutesForUpdate("Notebooks", "Notebook", "TopicNotes");
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useMenuStore.getState().setMenuPins();
|
||||
});
|
||||
eSendEvent(eOnTopicSheetUpdate);
|
||||
useRelationStore.getState().update();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
@@ -135,7 +135,6 @@ const ResultDialog = () => {
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
onPress={close}
|
||||
height={50}
|
||||
fontSize={SIZE.md + 2}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -207,13 +207,7 @@ export class VaultDialog extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"TopicNotes",
|
||||
"TaggedNotes",
|
||||
"ColoredNotes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
|
||||
this.password = null;
|
||||
this.confirmPassword = null;
|
||||
|
||||
@@ -109,7 +109,7 @@ export const Title = () => {
|
||||
{!hide && !isHidden ? (
|
||||
<Heading
|
||||
onPress={navigateToNotebook}
|
||||
numberOfLines={isTopic ? 2 : 1}
|
||||
numberOfLines={1}
|
||||
size={SIZE.xl}
|
||||
style={{
|
||||
flexWrap: "wrap",
|
||||
|
||||
@@ -17,12 +17,14 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import notifee from "@notifee/react-native";
|
||||
import React, { useCallback, useEffect, useRef } from "react";
|
||||
import { Platform, View } from "react-native";
|
||||
import RNBootSplash from "react-native-bootsplash";
|
||||
import { checkVersion } from "react-native-check-version";
|
||||
import Config from "react-native-config";
|
||||
import { enabled } from "react-native-privacy-snapshot";
|
||||
import { DatabaseLogger, db, loadDatabase } from "../../common/database";
|
||||
import { DatabaseLogger, db } from "../../common/database";
|
||||
import { useAppState } from "../../hooks/use-app-state";
|
||||
import BiometricService from "../../services/biometrics";
|
||||
import { eSendEvent, presentSheet } from "../../services/event-manager";
|
||||
@@ -36,6 +38,7 @@ import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { eOpenAnnouncementDialog } from "../../utils/events";
|
||||
import { getGithubVersion } from "../../utils/github-version";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { SVG } from "../auth/background";
|
||||
@@ -50,9 +53,6 @@ import { SvgView } from "../ui/svg";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { Walkthrough } from "../walkthroughs";
|
||||
import Config from "react-native-config";
|
||||
import { getGithubVersion } from "../../utils/github-version";
|
||||
import notifee from "@notifee/react-native";
|
||||
|
||||
const Launcher = React.memo(
|
||||
function Launcher() {
|
||||
@@ -69,7 +69,7 @@ const Launcher = React.memo(
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
);
|
||||
const dbInitCompleted = useRef(false);
|
||||
|
||||
const loadNotes = useCallback(async () => {
|
||||
if (verifyUser) {
|
||||
return;
|
||||
@@ -80,28 +80,25 @@ const Launcher = React.memo(
|
||||
initialize();
|
||||
setImmediate(() => {
|
||||
setLoading(false);
|
||||
if (!dbInitCompleted.current) {
|
||||
setImmediate(() => doAppLoadActions());
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}, [doAppLoadActions, setLoading, verifyUser]);
|
||||
}, [setLoading, verifyUser]);
|
||||
|
||||
const init = useCallback(async () => {
|
||||
if (!dbInitCompleted.current) {
|
||||
if (!db.isInitialized) {
|
||||
await RNBootSplash.hide({ fade: true });
|
||||
await loadDatabase();
|
||||
DatabaseLogger.info("Initializing database");
|
||||
await db.init();
|
||||
dbInitCompleted.current = true;
|
||||
}
|
||||
|
||||
if (db.migrations.required() && !verifyUser) {
|
||||
presentSheet({
|
||||
component: <Migrate />,
|
||||
onClose: async () => {
|
||||
await db.init();
|
||||
if (!db.isInitialized) {
|
||||
await db.init();
|
||||
}
|
||||
loadNotes();
|
||||
},
|
||||
disableClosing: true
|
||||
@@ -120,9 +117,6 @@ const Launcher = React.memo(
|
||||
if (!loading) {
|
||||
doAppLoadActions();
|
||||
}
|
||||
return () => {
|
||||
dbInitCompleted.current = false;
|
||||
};
|
||||
}, [doAppLoadActions, loading]);
|
||||
|
||||
const doAppLoadActions = useCallback(async () => {
|
||||
|
||||
@@ -27,6 +27,7 @@ import { db } from "../../../common/database";
|
||||
import Notebook from "../../../screens/notebook";
|
||||
import { TaggedNotes } from "../../../screens/notes/tagged";
|
||||
import { TopicNotes } from "../../../screens/notes/topic-notes";
|
||||
import useNavigationStore from "../../../stores/use-navigation-store";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
@@ -52,12 +53,14 @@ const showActionSheet = (item) => {
|
||||
|
||||
function getNotebook(item) {
|
||||
const isTrash = item.type === "trash";
|
||||
const currentId = useNavigationStore.getState().currentScreen.id;
|
||||
if (isTrash) return [];
|
||||
const items = [];
|
||||
const notebooks = db.relations.to(item, "notebook") || [];
|
||||
|
||||
for (let notebook of notebooks) {
|
||||
if (items.length > 1) break;
|
||||
if (notebook.id === currentId) continue;
|
||||
items.push(notebook);
|
||||
}
|
||||
|
||||
@@ -68,6 +71,7 @@ function getNotebook(item) {
|
||||
if (!notebook) continue;
|
||||
for (let topicId of nb.topics) {
|
||||
if (items.length > 1) break;
|
||||
if (topicId === currentId) continue;
|
||||
const topic = notebook.topics.find((t) => t.id === topicId);
|
||||
if (!topic) continue;
|
||||
items.push(topic);
|
||||
@@ -77,10 +81,20 @@ function getNotebook(item) {
|
||||
return items;
|
||||
}
|
||||
|
||||
function getTags(item) {
|
||||
const noteTags = item.tags?.slice(0, 3) || [];
|
||||
const tags = [];
|
||||
for (const tagName of noteTags) {
|
||||
const tag = db.tags.tag(tagName);
|
||||
if (!tag) continue;
|
||||
tags.push(tag);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
const NoteItem = ({
|
||||
item,
|
||||
isTrash,
|
||||
tags,
|
||||
dateBy = "dateCreated",
|
||||
noOpen = false
|
||||
}) => {
|
||||
@@ -96,6 +110,7 @@ const NoteItem = ({
|
||||
const reminders = db.relations.from(item, "reminder");
|
||||
const reminder = getUpcomingReminder(reminders);
|
||||
const noteColor = COLORS_NOTE[item.color?.toLowerCase()];
|
||||
const tags = getTags(item);
|
||||
return (
|
||||
<>
|
||||
<View
|
||||
@@ -117,8 +132,13 @@ const NoteItem = ({
|
||||
>
|
||||
{notebooks?.map((item) => (
|
||||
<Button
|
||||
title={item.title}
|
||||
key={item}
|
||||
title={
|
||||
item.title.length > 25
|
||||
? item.title.slice(0, 25) + "..."
|
||||
: item.title
|
||||
}
|
||||
tooltipText={item.title}
|
||||
key={item.id}
|
||||
height={25}
|
||||
icon={item.type === "topic" ? "bookmark" : "book-outline"}
|
||||
type="grayBg"
|
||||
|
||||
@@ -100,7 +100,7 @@ export const openNote = async (item, isTrash, setSelectedItem, isSheet) => {
|
||||
};
|
||||
|
||||
export const NoteWrapper = React.memo(
|
||||
function NoteWrapper({ item, index, tags, dateBy, isSheet }) {
|
||||
function NoteWrapper({ item, index, dateBy, isSheet }) {
|
||||
const isTrash = item.type === "trash";
|
||||
const setSelectedItem = useSelectionStore((state) => state.setSelectedItem);
|
||||
|
||||
@@ -113,7 +113,7 @@ export const NoteWrapper = React.memo(
|
||||
isSheet={isSheet}
|
||||
item={item}
|
||||
>
|
||||
<NoteItem item={item} dateBy={dateBy} tags={tags} isTrash={isTrash} />
|
||||
<NoteItem item={item} dateBy={dateBy} isTrash={isTrash} />
|
||||
</SelectionWrapper>
|
||||
);
|
||||
},
|
||||
@@ -125,10 +125,6 @@ export const NoteWrapper = React.memo(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (JSON.stringify(prev.tags) !== JSON.stringify(next.tags)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prev.item !== next.item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -64,16 +64,7 @@ export const openNotebookTopic = (item) => {
|
||||
negativeText: "Delete",
|
||||
positivePress: async () => {
|
||||
await db.trash.restore(item.id);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Tags",
|
||||
"Notes",
|
||||
"Notebooks",
|
||||
"Favorites",
|
||||
"Trash",
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useSelectionStore.getState().setSelectionMode(false);
|
||||
ToastEvent.show({
|
||||
heading: "Restore successful",
|
||||
|
||||
@@ -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, { useRef } from "react";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { RefreshControl, View } from "react-native";
|
||||
import { FlashList } from "@shopify/flash-list";
|
||||
import Animated, { FadeInDown } from "react-native-reanimated";
|
||||
@@ -57,25 +57,10 @@ const RenderItem = ({ item, index, type, ...restArgs }) => {
|
||||
const dateBy =
|
||||
groupOptions.sortBy !== "title" ? groupOptions.sortBy : "dateEdited";
|
||||
const totalNotes = getTotalNotes(item);
|
||||
const tags =
|
||||
item.tags
|
||||
?.slice(0, 3)
|
||||
?.map((item) => {
|
||||
let tag = db.tags.tag(item);
|
||||
|
||||
if (!tag) return null;
|
||||
return {
|
||||
title: tag.title,
|
||||
id: tag.id,
|
||||
alias: tag.alias
|
||||
};
|
||||
})
|
||||
.filter((t) => t !== null) || [];
|
||||
|
||||
return (
|
||||
<Item
|
||||
item={item}
|
||||
tags={tags}
|
||||
dateBy={dateBy}
|
||||
index={index}
|
||||
type={type}
|
||||
@@ -154,13 +139,20 @@ const List = ({
|
||||
[screen]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
eSendEvent(eScrollEvent, {
|
||||
y: 0,
|
||||
screen
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
let styles = {
|
||||
width: "100%",
|
||||
minHeight: 1,
|
||||
minWidth: 1
|
||||
};
|
||||
|
||||
const _keyExtractor = (item) => item.id || item.title;
|
||||
const ListView = ScrollComponent ? ScrollComponent : FlashList;
|
||||
return (
|
||||
<>
|
||||
@@ -226,12 +218,14 @@ const List = ({
|
||||
}
|
||||
/>
|
||||
</Animated.View>
|
||||
<JumpToSectionDialog
|
||||
screen={screen}
|
||||
data={listData}
|
||||
type={screen === "Notes" ? "home" : type}
|
||||
scrollRef={scrollRef}
|
||||
/>
|
||||
{listData ? (
|
||||
<JumpToSectionDialog
|
||||
screen={screen}
|
||||
data={listData}
|
||||
type={screen === "Notes" ? "home" : type}
|
||||
scrollRef={scrollRef}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -85,13 +85,7 @@ const MergeConflicts = () => {
|
||||
}
|
||||
});
|
||||
}
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
if (editorController.current?.note?.id === note.id) {
|
||||
// reload the note in editor
|
||||
eSendEvent(eOnLoadNote, {
|
||||
|
||||
@@ -44,16 +44,7 @@ export default function NotePreview({ session, content, note }) {
|
||||
async function restore() {
|
||||
if (note && note.type === "trash") {
|
||||
await db.trash.restore(note.id);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Tags",
|
||||
"Notes",
|
||||
"Notebooks",
|
||||
"Favorites",
|
||||
"Trash",
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useSelectionStore.getState().setSelectionMode(false);
|
||||
ToastEvent.show({
|
||||
heading: "Restore successful",
|
||||
@@ -73,13 +64,7 @@ export default function NotePreview({ session, content, note }) {
|
||||
}
|
||||
eSendEvent(eCloseSheet, "note_history");
|
||||
eSendEvent(eCloseSheet);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
|
||||
ToastEvent.show({
|
||||
heading: "Note restored successfully",
|
||||
|
||||
@@ -43,13 +43,7 @@ export const ColorTags = ({ item }) => {
|
||||
let _note = db.notes.note(note.id).data;
|
||||
setNote({ ..._note });
|
||||
setColorNotes();
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
eSendEvent(refreshNotesPage);
|
||||
};
|
||||
|
||||
|
||||
@@ -212,7 +212,6 @@ Properties.present = (item, buttons = [], isSheet) => {
|
||||
if (!props[0]) return;
|
||||
presentSheet({
|
||||
context: isSheet ? "local" : undefined,
|
||||
enableGesturesInScrollView: true,
|
||||
component: (ref, close) => (
|
||||
<Properties
|
||||
close={() => {
|
||||
|
||||
@@ -145,13 +145,7 @@ export default function Notebooks({ note, close, full }) {
|
||||
note
|
||||
);
|
||||
useNotebookStore.getState().setNotebooks();
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
ToastEvent.show({
|
||||
heading: "Note removed from topic",
|
||||
context: "local",
|
||||
|
||||
@@ -94,7 +94,7 @@ export const Synced = ({ item, close }) => {
|
||||
fontSize={SIZE.xs + 1}
|
||||
title="Learn more"
|
||||
height={30}
|
||||
type="transparent"
|
||||
type="grayAccent"
|
||||
/>
|
||||
</View>
|
||||
) : null;
|
||||
|
||||
@@ -21,18 +21,18 @@ import React, { useCallback, useEffect } from "react";
|
||||
import { BackHandler, Platform, View } from "react-native";
|
||||
import { db } from "../../common/database";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { eSendEvent, ToastEvent } from "../../services/event-manager";
|
||||
import { ToastEvent } from "../../services/event-manager";
|
||||
import Navigation from "../../services/navigation";
|
||||
import SearchService from "../../services/search";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { eOpenMoveNoteDialog } from "../../utils/events";
|
||||
import { deleteItems } from "../../utils/functions";
|
||||
import { tabBarRef } from "../../utils/global-refs";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { presentDialog } from "../dialog/functions";
|
||||
import MoveNoteSheet from "../sheets/add-to";
|
||||
import ExportNotesSheet from "../sheets/export-notes";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import Heading from "../ui/typography/heading";
|
||||
@@ -64,13 +64,7 @@ export const SelectionHeader = React.memo(() => {
|
||||
selectedItemsList.forEach((item) => {
|
||||
db.notes.note(item.id).favorite();
|
||||
});
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
clearSelection();
|
||||
}
|
||||
};
|
||||
@@ -82,16 +76,7 @@ export const SelectionHeader = React.memo(() => {
|
||||
noteIds.push(item.id);
|
||||
});
|
||||
await db.trash.restore(...noteIds);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes",
|
||||
"Trash",
|
||||
"Notebooks",
|
||||
"Tags"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
|
||||
clearSelection();
|
||||
ToastEvent.show({
|
||||
@@ -118,16 +103,7 @@ export const SelectionHeader = React.memo(() => {
|
||||
noteIds.push(item.id);
|
||||
});
|
||||
await db.trash.delete(...noteIds);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes",
|
||||
"Trash",
|
||||
"Notebooks",
|
||||
"Tags"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
clearSelection();
|
||||
}
|
||||
},
|
||||
@@ -237,7 +213,7 @@ export const SelectionHeader = React.memo(() => {
|
||||
onPress={async () => {
|
||||
//setSelectionMode(false);
|
||||
await sleep(100);
|
||||
eSendEvent(eOpenMoveNoteDialog);
|
||||
MoveNoteSheet.present();
|
||||
}}
|
||||
customStyle={{
|
||||
marginLeft: 10
|
||||
@@ -288,15 +264,7 @@ export const SelectionHeader = React.memo(() => {
|
||||
);
|
||||
}
|
||||
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes",
|
||||
"Notebooks",
|
||||
"Notebook"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
clearSelection();
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -101,6 +101,7 @@ const SheetProvider = ({ context = "global" }) => {
|
||||
setVisible(false);
|
||||
setData(null);
|
||||
}}
|
||||
bottomPadding={!data.noBottomPadding}
|
||||
enableGesturesInScrollView={data.enableGesturesInScrollView}
|
||||
>
|
||||
<View
|
||||
@@ -212,7 +213,6 @@ const SheetProvider = ({ context = "global" }) => {
|
||||
title={item.actionText}
|
||||
icon={item.icon && item.icon}
|
||||
type={item.type || "accent"}
|
||||
height={50}
|
||||
style={{
|
||||
marginBottom: 10
|
||||
}}
|
||||
|
||||
@@ -25,22 +25,22 @@ import {
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from "react-native";
|
||||
import { FlatList } from "react-native-actions-sheet";
|
||||
import { notesnook } from "../../../../e2e/test.ids";
|
||||
import { db } from "../../../common/database";
|
||||
import { DDS } from "../../../services/device-detection";
|
||||
import { presentSheet, ToastEvent } from "../../../services/event-manager";
|
||||
import { ToastEvent, presentSheet } from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { useMenuStore } from "../../../stores/use-menu-store";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { ph, pv, SIZE } from "../../../utils/size";
|
||||
import { SIZE, ph, pv } from "../../../utils/size";
|
||||
import { sleep } from "../../../utils/time";
|
||||
import DialogHeader from "../../dialog/dialog-header";
|
||||
import { Button } from "../../ui/button";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import Input from "../../ui/input";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import { MoveNotes } from "../move-notes/movenote";
|
||||
import { FlatList } from "react-native-actions-sheet";
|
||||
|
||||
let refs = [];
|
||||
export class AddNotebookSheet extends React.Component {
|
||||
@@ -92,7 +92,7 @@ export class AddNotebookSheet extends React.Component {
|
||||
|
||||
close = () => {
|
||||
refs = [];
|
||||
this.props.close();
|
||||
this.props.close(true);
|
||||
};
|
||||
|
||||
onDelete = (index) => {
|
||||
@@ -182,6 +182,7 @@ export class AddNotebookSheet extends React.Component {
|
||||
});
|
||||
|
||||
await db.notebooks.notebook(toEdit.id).topics.add(...nextTopics);
|
||||
this.close();
|
||||
} else {
|
||||
newNotebookId = await db.notebooks.add({
|
||||
title: this.title,
|
||||
@@ -191,14 +192,7 @@ export class AddNotebookSheet extends React.Component {
|
||||
});
|
||||
}
|
||||
useMenuStore.getState().setMenuPins();
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes",
|
||||
"Notebooks",
|
||||
"Notebook"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useRelationStore.getState().update();
|
||||
MoveNotes.present(db.notebooks.notebook(newNotebookId).data);
|
||||
};
|
||||
@@ -251,13 +245,34 @@ export class AddNotebookSheet extends React.Component {
|
||||
willFocus && this.topicInputRef.current?.focus();
|
||||
};
|
||||
|
||||
renderTopicItem = ({ item, index }) => (
|
||||
<TopicItem
|
||||
item={item}
|
||||
onPress={(item, index) => {
|
||||
this.prevIndex = index;
|
||||
this.prevItem = item;
|
||||
this.topicInputRef.current?.setNativeProps({
|
||||
text: item
|
||||
});
|
||||
this.topicInputRef.current?.focus();
|
||||
this.currentInputValue = item;
|
||||
this.setState({
|
||||
editTopic: true
|
||||
});
|
||||
}}
|
||||
onDelete={this.onDelete}
|
||||
index={index}
|
||||
colors={this.props.colors}
|
||||
/>
|
||||
);
|
||||
|
||||
render() {
|
||||
const { colors } = this.props;
|
||||
const { topics, topicInputFocused, notebook } = this.state;
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
maxHeight: DDS.isTab ? "90%" : "96%",
|
||||
maxHeight: DDS.isTab ? "90%" : "97%",
|
||||
borderRadius: DDS.isTab ? 5 : 0,
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
@@ -272,17 +287,34 @@ export class AddNotebookSheet extends React.Component {
|
||||
}}
|
||||
blurOnSubmit={false}
|
||||
/>
|
||||
<DialogHeader
|
||||
title={
|
||||
notebook && notebook.dateCreated ? "Edit Notebook" : "New Notebook"
|
||||
}
|
||||
paragraph={
|
||||
notebook && notebook.dateCreated
|
||||
? "You are editing " + this.title + " notebook."
|
||||
: "Notebooks are the best way to organize your notes."
|
||||
}
|
||||
/>
|
||||
<Seperator half />
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Heading size={SIZE.lg}>
|
||||
{notebook && notebook.dateCreated
|
||||
? "Edit Notebook"
|
||||
: "New Notebook"}
|
||||
</Heading>
|
||||
<Button
|
||||
title="Save"
|
||||
type="accent"
|
||||
height={40}
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 24
|
||||
}}
|
||||
fontSize={SIZE.md}
|
||||
onPress={this.addNewNotebook}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Seperator />
|
||||
|
||||
<Input
|
||||
fwdRef={(ref) => (this.titleRef = ref)}
|
||||
@@ -350,49 +382,11 @@ export class AddNotebookSheet extends React.Component {
|
||||
keyExtractor={(item, index) => item + index.toString()}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="interactive"
|
||||
ListFooterComponent={<View style={{ height: 50 }} />}
|
||||
renderItem={({ item, index }) => (
|
||||
<TopicItem
|
||||
item={item}
|
||||
onPress={(item, index) => {
|
||||
this.prevIndex = index;
|
||||
this.prevItem = item;
|
||||
this.topicInputRef.current?.setNativeProps({
|
||||
text: item
|
||||
});
|
||||
this.topicInputRef.current?.focus();
|
||||
this.currentInputValue = item;
|
||||
this.setState({
|
||||
editTopic: true
|
||||
});
|
||||
}}
|
||||
onDelete={this.onDelete}
|
||||
index={index}
|
||||
colors={colors}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Seperator />
|
||||
<Button
|
||||
width="100%"
|
||||
height={50}
|
||||
fontSize={SIZE.md}
|
||||
title={
|
||||
notebook && notebook.dateCreated
|
||||
? "Save changes"
|
||||
: "Create notebook"
|
||||
ListFooterComponent={
|
||||
topics.length === 0 ? null : <View style={{ height: 50 }} />
|
||||
}
|
||||
type="accent"
|
||||
onPress={this.addNewNotebook}
|
||||
renderItem={this.renderTopicItem}
|
||||
/>
|
||||
{/*
|
||||
{Platform.OS === 'ios' && (
|
||||
<View
|
||||
style={{
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
)} */}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,13 +20,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { createContext, useContext } from "react";
|
||||
|
||||
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;
|
||||
|
||||
@@ -71,7 +71,6 @@ export const FilteredList = ({
|
||||
}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="none"
|
||||
nestedScrollEnabled
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,16 +17,22 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import React, { useCallback, useEffect, useMemo } from "react";
|
||||
import { Keyboard, TouchableOpacity, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../../common/database";
|
||||
import { presentSheet, ToastEvent } from "../../../services/event-manager";
|
||||
import {
|
||||
eSendEvent,
|
||||
presentSheet,
|
||||
ToastEvent
|
||||
} from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import SearchService from "../../../services/search";
|
||||
import { useNotebookStore } from "../../../stores/use-notebook-store";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { eOnTopicSheetUpdate } from "../../../utils/events";
|
||||
import { Dialog } from "../../dialog";
|
||||
import DialogHeader from "../../dialog/dialog-header";
|
||||
import { presentDialog } from "../../dialog/functions";
|
||||
@@ -35,19 +41,21 @@ import Paragraph from "../../ui/typography/paragraph";
|
||||
import { SelectionProvider } from "./context";
|
||||
import { FilteredList } from "./filtered-list";
|
||||
import { ListItem } from "./list-item";
|
||||
import { useItemSelectionStore } from "./store";
|
||||
|
||||
const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const [multiSelect, setMultiSelect] = useState(false);
|
||||
const notebooks = useNotebookStore((state) =>
|
||||
state.notebooks.filter((n) => n?.type === "notebook")
|
||||
);
|
||||
|
||||
const dimensions = useSettingStore((state) => state.dimensions);
|
||||
const selectedItemsList = useSelectionStore(
|
||||
(state) => state.selectedItemsList
|
||||
);
|
||||
const setNotebooks = useNotebookStore((state) => state.setNotebooks);
|
||||
const [itemState, setItemState] = useState({});
|
||||
|
||||
const multiSelect = useItemSelectionStore((state) => state.multiSelect);
|
||||
|
||||
const onAddNotebook = async (title) => {
|
||||
if (!title || title.trim().length === 0) {
|
||||
ToastEvent.show({
|
||||
@@ -130,41 +138,55 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
|
||||
useEffect(() => {
|
||||
resetItemState();
|
||||
return () => {
|
||||
useItemSelectionStore.getState().setMultiSelect(false);
|
||||
useItemSelectionStore.getState().setItemState({});
|
||||
};
|
||||
// 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
|
||||
const itemState = {};
|
||||
const notebooks = db.notebooks.all;
|
||||
let count = 0;
|
||||
for (let notebook of notebooks) {
|
||||
itemState[notebook.id] = state
|
||||
? state
|
||||
: areAllSelectedItemsInNotebook(notebook, selectedItemsList)
|
||||
? "selected"
|
||||
: getSelectedNotesCountInItem(notebook, selectedItemsList) > 0
|
||||
? "intermediate"
|
||||
: "deselected";
|
||||
if (itemState[notebook.id] === "selected") {
|
||||
count++;
|
||||
contextValue.select(notebook);
|
||||
} else {
|
||||
contextValue.deselect(notebook);
|
||||
}
|
||||
for (let topic of notebook.topics) {
|
||||
itemState[topic.id] = state
|
||||
? state
|
||||
: areAllSelectedItemsInNotebook(notebook, selectedItemsList)
|
||||
: areAllSelectedItemsInTopic(topic, selectedItemsList) &&
|
||||
getSelectedNotesCountInItem(topic, selectedItemsList)
|
||||
? "selected"
|
||||
: getSelectedNotesCountInItem(notebook, selectedItemsList) > 0
|
||||
: getSelectedNotesCountInItem(topic, 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);
|
||||
}
|
||||
if (itemState[topic.id] === "selected") {
|
||||
count++;
|
||||
contextValue.select(topic);
|
||||
} else {
|
||||
contextValue.deselect(topic);
|
||||
}
|
||||
}
|
||||
return itemState;
|
||||
});
|
||||
}
|
||||
if (count > 1) {
|
||||
useItemSelectionStore.getState().setMultiSelect(true);
|
||||
} else {
|
||||
useItemSelectionStore.getState().setMultiSelect(false);
|
||||
}
|
||||
useItemSelectionStore.getState().setItemState(itemState);
|
||||
},
|
||||
[contextValue, getSelectedNotesCountInItem, selectedItemsList]
|
||||
);
|
||||
@@ -191,32 +213,26 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
}
|
||||
|
||||
const updateItemState = useCallback(function (item, state) {
|
||||
setItemState((itemState) => {
|
||||
const mergeState = {
|
||||
[item.id]: state
|
||||
};
|
||||
return {
|
||||
...itemState,
|
||||
...mergeState
|
||||
};
|
||||
const itemState = useItemSelectionStore.getState().itemState;
|
||||
const mergeState = {
|
||||
[item.id]: state
|
||||
};
|
||||
useItemSelectionStore.getState().setItemState({
|
||||
...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;
|
||||
});
|
||||
const itemState = useItemSelectionStore.getState().itemState;
|
||||
if (itemState[item.id] === "selected") {
|
||||
updateItemState(item, "deselected");
|
||||
} else {
|
||||
updateItemState(item, "selected");
|
||||
}
|
||||
},
|
||||
setMultiSelect: setMultiSelect,
|
||||
deselect: (item) => {
|
||||
updateItemState(item, "deselected");
|
||||
},
|
||||
@@ -227,7 +243,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
resetItemState(state);
|
||||
}
|
||||
}),
|
||||
[multiSelect, resetItemState, updateItemState]
|
||||
[resetItemState, updateItemState]
|
||||
);
|
||||
|
||||
const getItemFromId = (id) => {
|
||||
@@ -241,6 +257,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
|
||||
const onSave = async () => {
|
||||
const noteIds = note ? [note.id] : selectedItemsList.map((n) => n.id);
|
||||
const itemState = useItemSelectionStore.getState().itemState;
|
||||
for (const id in itemState) {
|
||||
const item = getItemFromId(id);
|
||||
if (itemState[id] === "selected") {
|
||||
@@ -275,15 +292,9 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes",
|
||||
"Notebook"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
setNotebooks();
|
||||
eSendEvent(eOnTopicSheetUpdate);
|
||||
SearchService.updateAndSearch();
|
||||
actionSheetRef.current?.hide();
|
||||
};
|
||||
@@ -335,31 +346,32 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
/>
|
||||
</View>
|
||||
|
||||
{multiSelect ? (
|
||||
<View
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
title="Reset selection"
|
||||
height={30}
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
alignSelf: "flex-start",
|
||||
paddingHorizontal: 0,
|
||||
width: "100%",
|
||||
marginTop: 6
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
title="Reset selection"
|
||||
height={30}
|
||||
style={{
|
||||
alignSelf: "flex-start",
|
||||
paddingHorizontal: 0
|
||||
}}
|
||||
onPress={() => {
|
||||
resetItemState();
|
||||
setMultiSelect(false);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
type="grayAccent"
|
||||
onPress={() => {
|
||||
resetItemState();
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<SelectionProvider value={contextValue}>
|
||||
<FilteredList
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: 12,
|
||||
maxHeight: dimensions.height * 0.85
|
||||
}}
|
||||
ListEmptyComponent={
|
||||
notebooks.length > 0 ? null : (
|
||||
@@ -385,13 +397,8 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
item={item}
|
||||
key={item.id}
|
||||
index={index}
|
||||
intermediate={itemState[item.id] === "intermediate"}
|
||||
removed={
|
||||
itemState[item.id] === "deselected" &&
|
||||
getSelectedNotesCountInItem(item) > 0
|
||||
}
|
||||
hasNotes={getSelectedNotesCountInItem(item) > 0}
|
||||
sheetRef={actionSheetRef}
|
||||
isSelected={itemState[item.id] === "selected"}
|
||||
infoText={
|
||||
<>
|
||||
{item.topics.length === 1
|
||||
@@ -401,17 +408,14 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
}
|
||||
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,
|
||||
hasNotes: getSelectedNotesCountInItem(topic) > 0,
|
||||
style: {
|
||||
marginBottom: 0,
|
||||
height: 40
|
||||
},
|
||||
onPress: (item) => {
|
||||
const itemState =
|
||||
useItemSelectionStore.getState().itemState;
|
||||
const currentState = itemState[item.id];
|
||||
if (currentState !== "selected") {
|
||||
resetItemState("deselected");
|
||||
@@ -440,19 +444,23 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
onAddSublistItem={(item) => {
|
||||
openAddTopicDialog(item);
|
||||
}}
|
||||
onPress={(item) => {
|
||||
const itemState = useItemSelectionStore.getState().itemState;
|
||||
const currentState = itemState[item.id];
|
||||
if (currentState !== "selected") {
|
||||
resetItemState("deselected");
|
||||
contextValue.select(item);
|
||||
} else {
|
||||
contextValue.deselect(item);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
itemType="notebook"
|
||||
onAddItem={async (title) => {
|
||||
return await onAddNotebook(title);
|
||||
}}
|
||||
// ListFooterComponent={
|
||||
// <View
|
||||
// style={{
|
||||
// height: 200
|
||||
// }}
|
||||
// />
|
||||
// }
|
||||
ListFooterComponent={<View style={{ height: 20 }} />}
|
||||
/>
|
||||
</SelectionProvider>
|
||||
</View>
|
||||
@@ -463,7 +471,8 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
MoveNoteSheet.present = (note) => {
|
||||
presentSheet({
|
||||
component: (ref) => <MoveNoteSheet actionSheetRef={ref} note={note} />,
|
||||
enableGesturesInScrollView: false
|
||||
enableGesturesInScrollView: false,
|
||||
noBottomPadding: true
|
||||
});
|
||||
};
|
||||
export default MoveNoteSheet;
|
||||
|
||||
@@ -17,8 +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, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { db } from "../../../common/database";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
@@ -27,13 +29,78 @@ import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { useSelectionContext } from "./context";
|
||||
import { FilteredList } from "./filtered-list";
|
||||
import { useItemSelectionStore } from "./store";
|
||||
|
||||
const _ListItem = ({
|
||||
const SelectionIndicator = ({
|
||||
item,
|
||||
hasNotes,
|
||||
selectItem,
|
||||
onPress,
|
||||
onChange
|
||||
}) => {
|
||||
const itemState = useItemSelectionStore((state) => state.itemState[item.id]);
|
||||
const multiSelect = useItemSelectionStore((state) => state.multiSelect);
|
||||
|
||||
const isSelected = itemState === "selected";
|
||||
const isIntermediate = itemState === "intermediate";
|
||||
const isRemoved = !isSelected && hasNotes;
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
|
||||
useEffect(() => {
|
||||
onChange?.();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [itemState]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
size={22}
|
||||
customStyle={{
|
||||
marginRight: 5,
|
||||
width: 23,
|
||||
height: 23
|
||||
}}
|
||||
color={
|
||||
isRemoved
|
||||
? colors.red
|
||||
: isIntermediate || isSelected
|
||||
? colors.accent
|
||||
: colors.icon
|
||||
}
|
||||
onPress={() => {
|
||||
if (multiSelect) return selectItem();
|
||||
onPress?.(item);
|
||||
}}
|
||||
onLongPress={() => {
|
||||
useItemSelectionStore.getState().setMultiSelect(true);
|
||||
selectItem();
|
||||
}}
|
||||
testID={
|
||||
isRemoved
|
||||
? "close-circle-outline"
|
||||
: isSelected
|
||||
? "check-circle-outline"
|
||||
: isIntermediate
|
||||
? "minus-circle-outline"
|
||||
: "checkbox-blank-circle-outline"
|
||||
}
|
||||
name={
|
||||
isRemoved
|
||||
? "close-circle-outline"
|
||||
: isSelected
|
||||
? "check-circle-outline"
|
||||
: isIntermediate
|
||||
? "minus-circle-outline"
|
||||
: "checkbox-blank-circle-outline"
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const ListItem = ({
|
||||
item,
|
||||
index,
|
||||
icon,
|
||||
infoText,
|
||||
intermediate,
|
||||
hasSubList,
|
||||
onPress,
|
||||
onScrollEnd,
|
||||
@@ -43,19 +110,49 @@ const _ListItem = ({
|
||||
sublistItemType,
|
||||
onAddItem,
|
||||
getSublistItemProps,
|
||||
removed,
|
||||
isSelected,
|
||||
hasHeaderSearch,
|
||||
onAddSublistItem,
|
||||
hasNotes,
|
||||
onChange,
|
||||
sheetRef
|
||||
}) => {
|
||||
const { enabled, toggleSelection, setMultiSelect } = useSelectionContext();
|
||||
const { toggleSelection } = useSelectionContext();
|
||||
const multiSelect = useItemSelectionStore((state) => state.multiSelect);
|
||||
const [showSelectedIndicator, setShowSelectedIndicator] = useState(false);
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
function selectItem() {
|
||||
toggleSelection(item);
|
||||
}
|
||||
|
||||
const getSelectedNotesCountInNotebookTopics = (item) => {
|
||||
if (item.type === "topic") return;
|
||||
|
||||
let count = 0;
|
||||
const noteIds = [];
|
||||
for (let topic of item.topics) {
|
||||
noteIds.push(...(db.notes?.topicReferences.get(topic.id) || []));
|
||||
if (useItemSelectionStore.getState().itemState[topic.id] === "selected") {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
useSelectionStore.getState().selectedItemsList.forEach((item) => {
|
||||
if (noteIds.indexOf(item.id) > -1) {
|
||||
count++;
|
||||
}
|
||||
});
|
||||
return count;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setShowSelectedIndicator(getSelectedNotesCountInNotebookTopics(item) > 0);
|
||||
}, [item]);
|
||||
|
||||
const onChangeSubItem = () => {
|
||||
setShowSelectedIndicator(getSelectedNotesCountInNotebookTopics(item) > 0);
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
@@ -67,12 +164,12 @@ const _ListItem = ({
|
||||
<PressableButton
|
||||
onPress={() => {
|
||||
if (hasSubList) return setExpanded(!expanded);
|
||||
if (enabled) return selectItem();
|
||||
if (multiSelect) return selectItem();
|
||||
onPress?.(item);
|
||||
}}
|
||||
type={type}
|
||||
onLongPress={() => {
|
||||
setMultiSelect(true);
|
||||
useItemSelectionStore.getState().setMultiSelect(true);
|
||||
selectItem();
|
||||
}}
|
||||
customStyle={{
|
||||
@@ -97,43 +194,12 @@ const _ListItem = ({
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
size={22}
|
||||
customStyle={{
|
||||
marginRight: 5,
|
||||
width: 23,
|
||||
height: 23
|
||||
}}
|
||||
color={
|
||||
removed
|
||||
? colors.red
|
||||
: intermediate || isSelected
|
||||
? colors.accent
|
||||
: colors.icon
|
||||
}
|
||||
onPress={() => {
|
||||
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"
|
||||
}
|
||||
<SelectionIndicator
|
||||
hasNotes={hasNotes}
|
||||
onPress={onPress}
|
||||
item={item}
|
||||
onChange={onChange}
|
||||
selectItem={selectItem}
|
||||
/>
|
||||
<View>
|
||||
{hasSubList && expanded ? (
|
||||
@@ -152,14 +218,27 @@ const _ListItem = ({
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row"
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{showSelectedIndicator ? (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: colors.accent,
|
||||
width: 7,
|
||||
height: 7,
|
||||
borderRadius: 100,
|
||||
marginRight: 12
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{onAddSublistItem ? (
|
||||
<IconButton
|
||||
name={"plus"}
|
||||
testID="add-item-icon"
|
||||
color={colors}
|
||||
color={colors.pri}
|
||||
size={SIZE.xl}
|
||||
onPress={() => {
|
||||
onAddSublistItem(item);
|
||||
@@ -201,6 +280,7 @@ const _ListItem = ({
|
||||
item={item}
|
||||
{...getSublistItemProps(item)}
|
||||
index={index}
|
||||
onChange={onChangeSubItem}
|
||||
onScrollEnd={onScrollEnd}
|
||||
/>
|
||||
)}
|
||||
@@ -210,11 +290,3 @@ const _ListItem = ({
|
||||
</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;
|
||||
});
|
||||
|
||||
42
apps/mobile/app/components/sheets/add-to/store.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
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 create, { State } from "zustand";
|
||||
|
||||
type SelectionItemState = Record<
|
||||
string,
|
||||
"intermediate" | "selected" | "deselected"
|
||||
>;
|
||||
|
||||
export interface SelectionStore extends State {
|
||||
itemState: SelectionItemState;
|
||||
setItemState: (state: SelectionItemState) => void;
|
||||
multiSelect: boolean;
|
||||
setMultiSelect: (multiSelect: boolean) => void;
|
||||
}
|
||||
|
||||
export const useItemSelectionStore = create<SelectionStore>((set) => ({
|
||||
itemState: {},
|
||||
setItemState: (itemState) => {
|
||||
set({
|
||||
itemState
|
||||
});
|
||||
},
|
||||
multiSelect: false,
|
||||
setMultiSelect: (multiSelect) => set({ multiSelect })
|
||||
}));
|
||||
@@ -277,7 +277,6 @@ const ExportNotesSheet = ({ notes, update }) => {
|
||||
});
|
||||
});
|
||||
}}
|
||||
height={50}
|
||||
/>
|
||||
<Button
|
||||
title="Share"
|
||||
@@ -301,7 +300,6 @@ const ExportNotesSheet = ({ notes, update }) => {
|
||||
}).catch(console.log);
|
||||
}
|
||||
}}
|
||||
height={50}
|
||||
/>
|
||||
<Button
|
||||
title="Export in another format"
|
||||
@@ -317,7 +315,6 @@ const ExportNotesSheet = ({ notes, update }) => {
|
||||
setResult(null);
|
||||
setExporting(false);
|
||||
}}
|
||||
height={50}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -48,7 +48,6 @@ export const ShareComponent = ({ uri, name, padding }) => {
|
||||
});
|
||||
});
|
||||
}}
|
||||
height={50}
|
||||
/>
|
||||
<Button
|
||||
title="Share"
|
||||
@@ -65,7 +64,6 @@ export const ShareComponent = ({ uri, name, padding }) => {
|
||||
shareFile: true
|
||||
}).catch(console.log);
|
||||
}}
|
||||
height={50}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -196,7 +196,6 @@ For example:
|
||||
onPress={onPress}
|
||||
title={loading ? null : "Submit"}
|
||||
loading={loading}
|
||||
height={50}
|
||||
width="100%"
|
||||
type="accent"
|
||||
/>
|
||||
|
||||
@@ -103,15 +103,7 @@ const ManageTagsSheet = (props) => {
|
||||
});
|
||||
}
|
||||
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes",
|
||||
"Notebooks",
|
||||
"Notebook"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -142,7 +134,6 @@ const ManageTagsSheet = (props) => {
|
||||
setFocus(false);
|
||||
}}
|
||||
onSubmit={onSubmit}
|
||||
height={50}
|
||||
placeholder="Search or add a tag"
|
||||
/>
|
||||
|
||||
@@ -225,13 +216,7 @@ const TagItem = ({ tag, note, setNote }) => {
|
||||
console.error(e);
|
||||
}
|
||||
setTimeout(() => {
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
|
||||
@@ -122,15 +122,7 @@ export const MoveNotes = ({
|
||||
db.notebooks?.notebook(currentNotebook.id).data as NotebookType
|
||||
);
|
||||
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes",
|
||||
"Notebook",
|
||||
"Notebooks"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -318,15 +310,7 @@ export const MoveNotes = ({
|
||||
},
|
||||
...selectedNoteIds
|
||||
);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes",
|
||||
"Notebook",
|
||||
"Notebooks"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
SearchService.updateAndSearch();
|
||||
eSendEvent(eCloseSheet);
|
||||
}}
|
||||
|
||||
@@ -60,16 +60,10 @@ const PublishNoteSheet = ({ note: item, update }) => {
|
||||
if (isLocked && !passwordValue) return;
|
||||
await db.monographs.publish(note.id, {
|
||||
selfDestruct: selfDestruct,
|
||||
password: isLocked && passwordValue
|
||||
password: isLocked && passwordValue.current
|
||||
});
|
||||
setNote(db.notes.note(note.id)?.data);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
setPublishLoading(false);
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -86,9 +80,6 @@ const PublishNoteSheet = ({ note: item, update }) => {
|
||||
|
||||
const setPublishLoading = (value) => {
|
||||
setPublishing(value);
|
||||
update({
|
||||
progress: value
|
||||
});
|
||||
};
|
||||
|
||||
const deletePublishedNote = async () => {
|
||||
@@ -98,13 +89,7 @@ const PublishNoteSheet = ({ note: item, update }) => {
|
||||
if (note?.id) {
|
||||
await db.monographs.unpublish(note.id);
|
||||
setNote(db.notes.note(note.id)?.data);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"Notes",
|
||||
"Favorites",
|
||||
"ColoredNotes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
setPublishLoading(false);
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -164,7 +149,7 @@ const PublishNoteSheet = ({ note: item, update }) => {
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginTop: 15,
|
||||
marginTop: 10,
|
||||
backgroundColor: colors.nav,
|
||||
padding: 12,
|
||||
borderRadius: 5
|
||||
@@ -176,8 +161,8 @@ const PublishNoteSheet = ({ note: item, update }) => {
|
||||
flexShrink: 1
|
||||
}}
|
||||
>
|
||||
<Heading size={SIZE.sm}>Published at:</Heading>
|
||||
<Paragraph size={SIZE.xs} numberOfLines={1}>
|
||||
<Heading size={SIZE.md}>Published at:</Heading>
|
||||
<Paragraph size={SIZE.sm} numberOfLines={1}>
|
||||
{publishUrl}
|
||||
</Paragraph>
|
||||
<Paragraph
|
||||
@@ -214,7 +199,6 @@ const PublishNoteSheet = ({ note: item, update }) => {
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
<Seperator />
|
||||
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
@@ -225,7 +209,11 @@ const PublishNoteSheet = ({ note: item, update }) => {
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginBottom: 10
|
||||
marginBottom: 10,
|
||||
backgroundColor: colors.nav,
|
||||
paddingVertical: 12,
|
||||
borderRadius: 5,
|
||||
marginTop: 10
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
@@ -234,7 +222,7 @@ const PublishNoteSheet = ({ note: item, update }) => {
|
||||
setIsLocked(!isLocked);
|
||||
}}
|
||||
color={isLocked ? colors.accent : colors.icon}
|
||||
size={SIZE.lg}
|
||||
size={SIZE.xl}
|
||||
name={
|
||||
isLocked
|
||||
? "check-circle-outline"
|
||||
@@ -262,7 +250,10 @@ const PublishNoteSheet = ({ note: item, update }) => {
|
||||
activeOpacity={0.9}
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
alignItems: "center",
|
||||
backgroundColor: colors.nav,
|
||||
paddingVertical: 12,
|
||||
borderRadius: 5
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
@@ -270,7 +261,7 @@ const PublishNoteSheet = ({ note: item, update }) => {
|
||||
setSelfDestruct(!selfDestruct);
|
||||
}}
|
||||
color={selfDestruct ? colors.accent : colors.icon}
|
||||
size={SIZE.lg}
|
||||
size={SIZE.xl}
|
||||
name={
|
||||
selfDestruct
|
||||
? "check-circle-outline"
|
||||
@@ -313,31 +304,38 @@ const PublishNoteSheet = ({ note: item, update }) => {
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<Button
|
||||
onPress={publishNote}
|
||||
fontSize={SIZE.md}
|
||||
width="100%"
|
||||
<View
|
||||
style={{
|
||||
marginTop: 10
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
height={50}
|
||||
type="accent"
|
||||
title={isPublished ? "Update published note" : "Publish note"}
|
||||
/>
|
||||
|
||||
{isPublished && (
|
||||
<>
|
||||
<Seperator half />
|
||||
<Button
|
||||
onPress={deletePublishedNote}
|
||||
fontSize={SIZE.md}
|
||||
width="100%"
|
||||
height={50}
|
||||
type="error"
|
||||
title="Unpublish note"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
>
|
||||
{isPublished && (
|
||||
<>
|
||||
<Button
|
||||
onPress={deletePublishedNote}
|
||||
fontSize={SIZE.md}
|
||||
type="error"
|
||||
title="Unpublish"
|
||||
style={{
|
||||
width: "49%"
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Seperator half />
|
||||
<Button
|
||||
onPress={publishNote}
|
||||
fontSize={SIZE.md}
|
||||
style={{
|
||||
width: isPublished ? "49%" : 250,
|
||||
borderRadius: isPublished ? 5 : 100
|
||||
}}
|
||||
type="accent"
|
||||
title={isPublished ? "Update" : "Publish"}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
@@ -347,7 +345,7 @@ const PublishNoteSheet = ({ note: item, update }) => {
|
||||
size={SIZE.xs}
|
||||
style={{
|
||||
textAlign: "center",
|
||||
marginTop: 5,
|
||||
marginTop: 10,
|
||||
textDecorationLine: "underline"
|
||||
}}
|
||||
onPress={async () => {
|
||||
|
||||
@@ -96,7 +96,6 @@ const RateAppSheet = () => {
|
||||
onPress={rateApp}
|
||||
fontSize={SIZE.md}
|
||||
width="100%"
|
||||
height={50}
|
||||
type="accent"
|
||||
title="Rate now (It takes only a second)"
|
||||
/>
|
||||
@@ -121,14 +120,12 @@ const RateAppSheet = () => {
|
||||
fontSize={SIZE.md}
|
||||
type="error"
|
||||
width="48%"
|
||||
height={50}
|
||||
title="Never"
|
||||
/>
|
||||
<Button
|
||||
onPress={onClose}
|
||||
fontSize={SIZE.md}
|
||||
width="48%"
|
||||
height={50}
|
||||
type="grayBg"
|
||||
title="Later"
|
||||
/>
|
||||
|
||||
@@ -298,7 +298,6 @@ class RecoveryKeySheet extends React.Component {
|
||||
width="100%"
|
||||
type="grayAccent"
|
||||
fontSize={SIZE.md}
|
||||
height={50}
|
||||
/>
|
||||
<Seperator />
|
||||
<Button
|
||||
@@ -308,7 +307,6 @@ class RecoveryKeySheet extends React.Component {
|
||||
type="grayAccent"
|
||||
fontSize={SIZE.md}
|
||||
icon="qrcode"
|
||||
height={50}
|
||||
/>
|
||||
<Seperator />
|
||||
<Button
|
||||
@@ -318,7 +316,6 @@ class RecoveryKeySheet extends React.Component {
|
||||
type="grayAccent"
|
||||
icon="text"
|
||||
fontSize={SIZE.md}
|
||||
height={50}
|
||||
/>
|
||||
<Seperator />
|
||||
|
||||
@@ -329,7 +326,6 @@ class RecoveryKeySheet extends React.Component {
|
||||
type="grayAccent"
|
||||
icon="cloud"
|
||||
fontSize={SIZE.md}
|
||||
height={50}
|
||||
/>
|
||||
<Seperator />
|
||||
|
||||
@@ -349,7 +345,6 @@ class RecoveryKeySheet extends React.Component {
|
||||
<Button
|
||||
title="I have saved the key."
|
||||
width="100%"
|
||||
height={50}
|
||||
type="error"
|
||||
fontSize={SIZE.md}
|
||||
onPress={this.close}
|
||||
|
||||
@@ -21,9 +21,9 @@ import { Platform, TextInput, View } from "react-native";
|
||||
import { ActionSheetRef, ScrollView } from "react-native-actions-sheet";
|
||||
import DateTimePickerModal from "react-native-modal-datetime-picker";
|
||||
import {
|
||||
presentSheet,
|
||||
PresentSheetOptions,
|
||||
ToastEvent
|
||||
ToastEvent,
|
||||
presentSheet
|
||||
} from "../../../services/event-manager";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
@@ -33,6 +33,7 @@ import Input from "../../ui/input";
|
||||
import dayjs from "dayjs";
|
||||
import DatePicker from "react-native-date-picker";
|
||||
import { db } from "../../../common/database";
|
||||
import { DDS } from "../../../services/device-detection";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import Notifications, { Reminder } from "../../../services/notifications";
|
||||
import PremiumService from "../../../services/premium";
|
||||
@@ -41,6 +42,7 @@ import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { NoteType } from "../../../utils/types";
|
||||
import { Dialog } from "../../dialog";
|
||||
import { ReminderTime } from "../../ui/reminder-time";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
|
||||
type ReminderSheetProps = {
|
||||
@@ -208,15 +210,7 @@ export default function ReminderSheet({
|
||||
});
|
||||
}
|
||||
Notifications.scheduleNotification(_reminder as Reminder);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"Notes",
|
||||
"NotesPage",
|
||||
"Reminders",
|
||||
"Favorites",
|
||||
"TopicNotes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useRelationStore.getState().update();
|
||||
close?.();
|
||||
} catch (e) {
|
||||
@@ -227,35 +221,57 @@ export default function ReminderSheet({
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: 12,
|
||||
maxHeight: DDS.isTab ? "90%" : "99.99%"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Heading size={SIZE.lg}>Set reminder</Heading>
|
||||
<Button
|
||||
title="Save"
|
||||
type="accent"
|
||||
height={40}
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: 24
|
||||
}}
|
||||
fontSize={SIZE.md}
|
||||
onPress={saveReminder}
|
||||
/>
|
||||
</View>
|
||||
<Dialog context="local" />
|
||||
<ScrollView keyboardShouldPersistTaps="always">
|
||||
<ScrollView>
|
||||
<Input
|
||||
fwdRef={titleRef}
|
||||
defaultValue={reminder?.title || referencedItem?.title}
|
||||
placeholder="Remind me of..."
|
||||
onChangeText={(text) => (title.current = text)}
|
||||
containerStyle={{ borderWidth: 0, borderBottomWidth: 1 }}
|
||||
wrapperStyle={{
|
||||
marginTop: 10
|
||||
}}
|
||||
/>
|
||||
|
||||
<Input
|
||||
defaultValue={
|
||||
reminder ? reminder?.description : referencedItem?.headline
|
||||
}
|
||||
placeholder="Add a quick note"
|
||||
placeholder="Add a short note"
|
||||
onChangeText={(text) => (details.current = text)}
|
||||
containerStyle={{
|
||||
borderWidth: 0,
|
||||
borderBottomWidth: 1,
|
||||
maxHeight: 80,
|
||||
marginTop: 10
|
||||
maxHeight: 80
|
||||
}}
|
||||
multiline
|
||||
textAlignVertical="top"
|
||||
inputStyle={{
|
||||
height: 80
|
||||
minHeight: 80,
|
||||
paddingVertical: 12
|
||||
}}
|
||||
height={80}
|
||||
wrapperStyle={{
|
||||
@@ -434,17 +450,13 @@ export default function ReminderSheet({
|
||||
|
||||
<DatePicker
|
||||
date={date}
|
||||
minimumDate={
|
||||
dayjs(date).subtract(3, "months").isBefore(dayjs())
|
||||
? dayjs().toDate()
|
||||
: dayjs(date).subtract(3, "months").toDate()
|
||||
}
|
||||
maximumDate={dayjs(date).add(3, "months").toDate()}
|
||||
onDateChange={handleConfirm}
|
||||
textColor={colors.night ? "#ffffff" : "#000000"}
|
||||
fadeToColor={colors.bg}
|
||||
theme={colors.night ? "dark" : "light"}
|
||||
is24hourSource="locale"
|
||||
androidVariant="nativeAndroid"
|
||||
mode={reminderMode === ReminderModes.Repeat ? "time" : "datetime"}
|
||||
/>
|
||||
|
||||
@@ -561,15 +573,6 @@ export default function ReminderSheet({
|
||||
alignSelf: "flex-start"
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
title="Save"
|
||||
type="accent"
|
||||
fontSize={SIZE.md}
|
||||
onPress={saveReminder}
|
||||
/>
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
@@ -583,6 +586,7 @@ ReminderSheet.present = (
|
||||
presentSheet({
|
||||
context: isSheet ? "local" : undefined,
|
||||
enableGesturesInScrollView: true,
|
||||
noBottomPadding: true,
|
||||
component: (ref, close, update) => (
|
||||
<ReminderSheet
|
||||
actionSheetRef={ref}
|
||||
|
||||
@@ -32,6 +32,7 @@ import Seperator from "../../ui/seperator";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
const Sort = ({ type, screen }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const isTopicSheet = screen === "TopicSheet";
|
||||
const [groupOptions, setGroupOptions] = useState(
|
||||
db.settings.getGroupOptions(type)
|
||||
);
|
||||
@@ -41,7 +42,7 @@ const Sort = ({ type, screen }) => {
|
||||
layoutmanager.withSpringAnimation(600);
|
||||
setGroupOptions(_groupOptions);
|
||||
setTimeout(() => {
|
||||
Navigation.queueRoutesForUpdate(screen);
|
||||
if (screen !== "TopicSheet") Navigation.queueRoutesForUpdate(screen);
|
||||
eSendEvent("groupOptionsUpdate");
|
||||
eSendEvent(refreshNotesPage);
|
||||
}, 1);
|
||||
@@ -52,6 +53,9 @@ const Sort = ({ type, screen }) => {
|
||||
...groupOptions,
|
||||
sortDirection: groupOptions.sortDirection === "asc" ? "desc" : "asc"
|
||||
};
|
||||
if (type === "topics") {
|
||||
_groupOptions.groupBy = "none";
|
||||
}
|
||||
await updateGroupOptions(_groupOptions);
|
||||
};
|
||||
|
||||
@@ -119,7 +123,7 @@ const Sort = ({ type, screen }) => {
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-start",
|
||||
flexWrap: "wrap",
|
||||
borderBottomWidth: 1,
|
||||
borderBottomWidth: isTopicSheet ? 0 : 1,
|
||||
borderBottomColor: colors.nav,
|
||||
marginBottom: 12,
|
||||
paddingHorizontal: 12,
|
||||
@@ -166,6 +170,9 @@ const Sort = ({ type, screen }) => {
|
||||
...groupOptions,
|
||||
sortBy: type === "trash" ? "dateDeleted" : item
|
||||
};
|
||||
if (type === "topics") {
|
||||
_groupOptions.groupBy = "none";
|
||||
}
|
||||
await updateGroupOptions(_groupOptions);
|
||||
}}
|
||||
iconSize={SIZE.md}
|
||||
@@ -175,64 +182,70 @@ const Sort = ({ type, screen }) => {
|
||||
)}
|
||||
</View>
|
||||
|
||||
<Heading
|
||||
style={{
|
||||
marginLeft: 12
|
||||
}}
|
||||
size={SIZE.lg}
|
||||
>
|
||||
Group by
|
||||
</Heading>
|
||||
|
||||
<Seperator />
|
||||
|
||||
<View
|
||||
style={{
|
||||
borderRadius: 0,
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
{Object.keys(GROUP).map((item) => (
|
||||
<Button
|
||||
key={item}
|
||||
testID={"btn-" + item}
|
||||
type={groupOptions.groupBy === GROUP[item] ? "grayBg" : "gray"}
|
||||
buttonType={{
|
||||
text:
|
||||
groupOptions.groupBy === GROUP[item]
|
||||
? colors.accent
|
||||
: colors.icon
|
||||
}}
|
||||
onPress={async () => {
|
||||
let _groupOptions = {
|
||||
...groupOptions,
|
||||
groupBy: GROUP[item]
|
||||
};
|
||||
|
||||
if (item === "abc") {
|
||||
_groupOptions.sortBy = "title";
|
||||
_groupOptions.sortDirection = "asc";
|
||||
} else {
|
||||
if (groupOptions.sortBy === "title") {
|
||||
_groupOptions.sortBy = "dateEdited";
|
||||
_groupOptions.sortDirection = "desc";
|
||||
}
|
||||
}
|
||||
updateGroupOptions(_groupOptions);
|
||||
}}
|
||||
height={40}
|
||||
icon={groupOptions.groupBy === GROUP[item] ? "check" : null}
|
||||
title={item.slice(0, 1).toUpperCase() + item.slice(1, item.length)}
|
||||
{isTopicSheet ? null : (
|
||||
<>
|
||||
<Heading
|
||||
style={{
|
||||
paddingHorizontal: 8,
|
||||
marginBottom: 10,
|
||||
marginRight: 10
|
||||
marginLeft: 12
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
size={SIZE.lg}
|
||||
>
|
||||
Group by
|
||||
</Heading>
|
||||
|
||||
<Seperator />
|
||||
|
||||
<View
|
||||
style={{
|
||||
borderRadius: 0,
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
{Object.keys(GROUP).map((item) => (
|
||||
<Button
|
||||
key={item}
|
||||
testID={"btn-" + item}
|
||||
type={groupOptions.groupBy === GROUP[item] ? "grayBg" : "gray"}
|
||||
buttonType={{
|
||||
text:
|
||||
groupOptions.groupBy === GROUP[item]
|
||||
? colors.accent
|
||||
: colors.icon
|
||||
}}
|
||||
onPress={async () => {
|
||||
let _groupOptions = {
|
||||
...groupOptions,
|
||||
groupBy: GROUP[item]
|
||||
};
|
||||
|
||||
if (item === "abc") {
|
||||
_groupOptions.sortBy = "title";
|
||||
_groupOptions.sortDirection = "asc";
|
||||
} else {
|
||||
if (groupOptions.sortBy === "title") {
|
||||
_groupOptions.sortBy = "dateEdited";
|
||||
_groupOptions.sortDirection = "desc";
|
||||
}
|
||||
}
|
||||
updateGroupOptions(_groupOptions);
|
||||
}}
|
||||
height={40}
|
||||
icon={groupOptions.groupBy === GROUP[item] ? "check" : null}
|
||||
title={
|
||||
item.slice(0, 1).toUpperCase() + item.slice(1, item.length)
|
||||
}
|
||||
style={{
|
||||
paddingHorizontal: 8,
|
||||
marginBottom: 10,
|
||||
marginRight: 10
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -19,12 +19,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import qclone from "qclone";
|
||||
import React, {
|
||||
createContext,
|
||||
RefObject,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState
|
||||
} from "react";
|
||||
import { Animated, Dimensions, View, RefreshControl } from "react-native";
|
||||
import { RefreshControl, View } from "react-native";
|
||||
import ActionSheet, {
|
||||
ActionSheetRef,
|
||||
FlatList
|
||||
@@ -37,7 +39,8 @@ import { TopicNotes } from "../../../screens/notes/topic-notes";
|
||||
import {
|
||||
eSendEvent,
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
eUnSubscribeEvent,
|
||||
presentSheet
|
||||
} from "../../../services/event-manager";
|
||||
import useNavigationStore, {
|
||||
NotebookScreenParams
|
||||
@@ -49,18 +52,40 @@ import {
|
||||
eOpenAddTopicDialog
|
||||
} from "../../../utils/events";
|
||||
import { normalize, SIZE } from "../../../utils/size";
|
||||
import { NotebookType, TopicType } from "../../../utils/types";
|
||||
import { GroupHeader, NotebookType, TopicType } from "../../../utils/types";
|
||||
|
||||
import { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import Config from "react-native-config";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { notesnook } from "../../../../e2e/test.ids";
|
||||
import { MMKV } from "../../../common/database/mmkv";
|
||||
import { openEditor } from "../../../screens/notes/common";
|
||||
import { getTotalNotes, history } from "../../../utils";
|
||||
import { Properties } from "../../properties";
|
||||
import { deleteItems } from "../../../utils/functions";
|
||||
import { presentDialog } from "../../dialog/functions";
|
||||
import Config from "react-native-config";
|
||||
import { notesnook } from "../../../../e2e/test.ids";
|
||||
import { Properties } from "../../properties";
|
||||
import Sort from "../sort";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
|
||||
type ConfigItem = { id: string; type: string };
|
||||
class TopicSheetConfig {
|
||||
static storageKey: "$$sp";
|
||||
|
||||
static makeId(item: ConfigItem) {
|
||||
return `${TopicSheetConfig.storageKey}:${item.type}:${item.id}`;
|
||||
}
|
||||
|
||||
static get(item: ConfigItem) {
|
||||
return MMKV.getInt(TopicSheetConfig.makeId(item)) || 0;
|
||||
}
|
||||
|
||||
static set(item: ConfigItem, index = 0) {
|
||||
MMKV.setInt(TopicSheetConfig.makeId(item), index);
|
||||
}
|
||||
}
|
||||
|
||||
export const TopicsSheet = () => {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const currentScreen = useNavigationStore((state) => state.currentScreen);
|
||||
const canShow =
|
||||
currentScreen.name === "Notebook" || currentScreen.name === "TopicNotes";
|
||||
@@ -75,12 +100,19 @@ export const TopicsSheet = () => {
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const ref = useRef<ActionSheetRef>(null);
|
||||
const [topics, setTopics] = useState(notebook ? qclone(notebook.topics) : []);
|
||||
const [animations] = useState({
|
||||
translate: new Animated.Value(0),
|
||||
display: new Animated.Value(-5000),
|
||||
opacity: new Animated.Value(0)
|
||||
});
|
||||
const isTopic = currentScreen.name === "TopicNotes";
|
||||
const [topics, setTopics] = useState(
|
||||
notebook
|
||||
? qclone(
|
||||
groupArray(notebook.topics, db.settings?.getGroupOptions("topics"))
|
||||
)
|
||||
: []
|
||||
);
|
||||
|
||||
const [groupOptions, setGroupOptions] = useState(
|
||||
db.settings?.getGroupOptions("topics")
|
||||
);
|
||||
|
||||
const onRequestUpdate = React.useCallback(
|
||||
(data?: NotebookScreenParams) => {
|
||||
if (!canShow) return;
|
||||
@@ -89,15 +121,34 @@ export const TopicsSheet = () => {
|
||||
?.data as NotebookType;
|
||||
if (_notebook) {
|
||||
setNotebook(_notebook);
|
||||
setTopics(qclone(_notebook.topics));
|
||||
|
||||
setTopics(
|
||||
qclone(
|
||||
groupArray(_notebook.topics, db.settings?.getGroupOptions("topics"))
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
[notebook, canShow]
|
||||
[canShow, notebook]
|
||||
);
|
||||
|
||||
const onUpdate = useCallback(() => {
|
||||
setGroupOptions({ ...(db.settings?.getGroupOptions("topics") as any) });
|
||||
onRequestUpdate();
|
||||
}, [onRequestUpdate]);
|
||||
|
||||
useEffect(() => {
|
||||
eSubscribeEvent("groupOptionsUpdate", onUpdate);
|
||||
return () => {
|
||||
eUnSubscribeEvent("groupOptionsUpdate", onUpdate);
|
||||
};
|
||||
}, [onUpdate]);
|
||||
|
||||
useEffect(() => {
|
||||
const onTopicUpdate = () => {
|
||||
onRequestUpdate();
|
||||
setTimeout(() => {
|
||||
onRequestUpdate();
|
||||
}, 1);
|
||||
};
|
||||
eSubscribeEvent(eOnTopicSheetUpdate, onTopicUpdate);
|
||||
eSubscribeEvent(eOnNewTopicAdded, onRequestUpdate);
|
||||
@@ -117,9 +168,16 @@ export const TopicsSheet = () => {
|
||||
loading: "Loading notebook topics"
|
||||
};
|
||||
|
||||
const renderTopic = ({ item, index }: { item: TopicType; index: number }) => (
|
||||
<TopicItem item={item} index={index} />
|
||||
);
|
||||
const renderTopic = ({
|
||||
item,
|
||||
index
|
||||
}: {
|
||||
item: TopicType | GroupHeader;
|
||||
index: number;
|
||||
}) =>
|
||||
(item as GroupHeader).type === "header" ? null : (
|
||||
<TopicItem sheetRef={ref} item={item as TopicType} index={index} />
|
||||
);
|
||||
|
||||
const selectionContext = {
|
||||
selection: selection,
|
||||
@@ -146,29 +204,37 @@ export const TopicsSheet = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (canShow) {
|
||||
const isTopic = currentScreen.name === "TopicNotes";
|
||||
const id = isTopic ? currentScreen?.notebookId : currentScreen?.id;
|
||||
if (!ref.current?.isOpen()) {
|
||||
animations.display.setValue(5000);
|
||||
animations.opacity.setValue(0);
|
||||
}
|
||||
if (id) {
|
||||
onRequestUpdate({
|
||||
item: db.notebooks?.notebook(id).data
|
||||
} as any);
|
||||
}
|
||||
ref.current?.show();
|
||||
setTimeout(() => {
|
||||
const id = isTopic ? currentScreen?.notebookId : currentScreen?.id;
|
||||
const notebook = db.notebooks?.notebook(id as string)?.data;
|
||||
const snapPoint = isTopic
|
||||
? 0
|
||||
: TopicSheetConfig.get({
|
||||
type: isTopic ? "topic" : "notebook",
|
||||
id: currentScreen.id as string
|
||||
});
|
||||
|
||||
if (ref.current?.isOpen()) {
|
||||
ref.current?.snapToIndex(snapPoint);
|
||||
} else {
|
||||
ref.current?.show(snapPoint);
|
||||
}
|
||||
if (notebook) {
|
||||
onRequestUpdate({
|
||||
item: notebook
|
||||
} as any);
|
||||
}
|
||||
}, 300);
|
||||
} else {
|
||||
ref.current?.hide();
|
||||
}
|
||||
}, [
|
||||
animations.display,
|
||||
animations.opacity,
|
||||
canShow,
|
||||
currentScreen?.id,
|
||||
currentScreen.name,
|
||||
currentScreen?.notebookId,
|
||||
onRequestUpdate
|
||||
onRequestUpdate,
|
||||
isTopic
|
||||
]);
|
||||
|
||||
return (
|
||||
@@ -176,14 +242,27 @@ export const TopicsSheet = () => {
|
||||
ref={ref}
|
||||
isModal={false}
|
||||
containerStyle={{
|
||||
maxHeight: 400,
|
||||
maxHeight: 300,
|
||||
borderTopRightRadius: 15,
|
||||
borderTopLeftRadius: 15,
|
||||
backgroundColor: colors.bg,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
borderColor: colors.nav,
|
||||
borderBottomWidth: 0
|
||||
}}
|
||||
openAnimationConfig={{
|
||||
friction: 10
|
||||
}}
|
||||
onSnapIndexChange={(index) => {
|
||||
setCollapsed(index === 0);
|
||||
TopicSheetConfig.set(
|
||||
{
|
||||
type: isTopic ? "topic" : "notebook",
|
||||
id: currentScreen.id as string
|
||||
},
|
||||
index
|
||||
);
|
||||
}}
|
||||
closable={!canShow}
|
||||
elevation={10}
|
||||
indicatorStyle={{
|
||||
@@ -191,66 +270,44 @@ export const TopicsSheet = () => {
|
||||
backgroundColor: colors.nav
|
||||
}}
|
||||
keyboardHandlerEnabled={false}
|
||||
snapPoints={Config.isTesting === "true" ? [100] : [15, 100]}
|
||||
initialSnapIndex={0}
|
||||
snapPoints={Config.isTesting === "true" ? [100] : [25, 100]}
|
||||
initialSnapIndex={1}
|
||||
backgroundInteractionEnabled
|
||||
onChange={(position, height) => {
|
||||
animations.translate.setValue(position - 60);
|
||||
const h = Dimensions.get("window").height;
|
||||
const minPos = h - height;
|
||||
if (position - 100 < minPos || !canShow) {
|
||||
animations.display.setValue(5000);
|
||||
animations.opacity.setValue(0);
|
||||
} else {
|
||||
animations.display.setValue(0);
|
||||
setTimeout(() => {
|
||||
animations.opacity.setValue(1);
|
||||
}, 300);
|
||||
}
|
||||
}}
|
||||
gestureEnabled
|
||||
ExtraOverlayComponent={
|
||||
<Animated.View
|
||||
style={{
|
||||
top: animations.translate,
|
||||
position: "absolute",
|
||||
right: 12,
|
||||
opacity: animations.opacity,
|
||||
transform: [
|
||||
{
|
||||
translateY: animations.display
|
||||
}
|
||||
]
|
||||
}}
|
||||
>
|
||||
<PressableButton
|
||||
testID={notesnook.buttons.add}
|
||||
type="accent"
|
||||
accentColor={"accent"}
|
||||
accentText="light"
|
||||
onPress={openEditor}
|
||||
customStyle={{
|
||||
borderRadius: 100
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: normalize(60),
|
||||
width: normalize(60)
|
||||
}}
|
||||
>
|
||||
<Icon name="plus" color="white" size={SIZE.xxl} />
|
||||
</View>
|
||||
</PressableButton>
|
||||
</Animated.View>
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
maxHeight: 400,
|
||||
height: 400,
|
||||
position: "absolute",
|
||||
right: 12,
|
||||
marginTop: -80
|
||||
}}
|
||||
>
|
||||
<PressableButton
|
||||
testID={notesnook.buttons.add}
|
||||
type="accent"
|
||||
accentColor={"accent"}
|
||||
accentText="light"
|
||||
onPress={openEditor}
|
||||
customStyle={{
|
||||
borderRadius: 100
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: normalize(60),
|
||||
width: normalize(60)
|
||||
}}
|
||||
>
|
||||
<Icon name="plus" color="white" size={SIZE.xxl} />
|
||||
</View>
|
||||
</PressableButton>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
maxHeight: 300,
|
||||
height: 300,
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
@@ -283,7 +340,7 @@ export const TopicsSheet = () => {
|
||||
selection.length > 1 ? "topics" : "topics"
|
||||
}`,
|
||||
paragraph: `Are you sure you want to delete ${
|
||||
selection.length > 1 ? "these topicss?" : "this topics?"
|
||||
selection.length > 1 ? "these topics?" : "this topic?"
|
||||
}`,
|
||||
positiveText: "Delete",
|
||||
negativeText: "Cancel",
|
||||
@@ -304,17 +361,57 @@ export const TopicsSheet = () => {
|
||||
size={22}
|
||||
/>
|
||||
) : (
|
||||
<IconButton
|
||||
name="plus"
|
||||
onPress={PLACEHOLDER_DATA.action}
|
||||
testID="add-topic-button"
|
||||
color={colors.pri}
|
||||
size={22}
|
||||
customStyle={{
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
<>
|
||||
<IconButton
|
||||
name={
|
||||
groupOptions?.sortDirection === "asc"
|
||||
? "sort-ascending"
|
||||
: "sort-descending"
|
||||
}
|
||||
onPress={() => {
|
||||
presentSheet({
|
||||
component: <Sort screen="TopicSheet" type="topics" />
|
||||
});
|
||||
}}
|
||||
testID="group-topic-button"
|
||||
color={colors.pri}
|
||||
size={22}
|
||||
customStyle={{
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
name="plus"
|
||||
onPress={PLACEHOLDER_DATA.action}
|
||||
testID="add-topic-button"
|
||||
color={colors.pri}
|
||||
size={22}
|
||||
customStyle={{
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
name={collapsed ? "chevron-up" : "chevron-down"}
|
||||
onPress={() => {
|
||||
if (ref.current?.currentSnapIndex() !== 0) {
|
||||
setCollapsed(true);
|
||||
ref.current?.snapToIndex(0);
|
||||
} else {
|
||||
setCollapsed(false);
|
||||
ref.current?.snapToIndex(1);
|
||||
}
|
||||
}}
|
||||
color={colors.pri}
|
||||
size={22}
|
||||
customStyle={{
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
@@ -334,7 +431,7 @@ export const TopicsSheet = () => {
|
||||
progressBackgroundColor={colors.bg}
|
||||
/>
|
||||
}
|
||||
keyExtractor={(item) => item.id}
|
||||
keyExtractor={(item) => (item as TopicType).id}
|
||||
renderItem={renderTopic}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
@@ -342,7 +439,7 @@ export const TopicsSheet = () => {
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 300
|
||||
height: 200
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.icon}>No topics</Paragraph>
|
||||
@@ -364,12 +461,20 @@ const SelectionContext = createContext<{
|
||||
}>({
|
||||
selection: [],
|
||||
enabled: false,
|
||||
setEnabled: (value: boolean) => {},
|
||||
toggleSelection: (item: TopicType) => {}
|
||||
setEnabled: (_value: boolean) => {},
|
||||
toggleSelection: (_item: TopicType) => {}
|
||||
});
|
||||
const useSelection = () => useContext(SelectionContext);
|
||||
|
||||
const TopicItem = ({ item, index }: { item: TopicType; index: number }) => {
|
||||
const TopicItem = ({
|
||||
item,
|
||||
index,
|
||||
sheetRef
|
||||
}: {
|
||||
item: TopicType;
|
||||
index: number;
|
||||
sheetRef: RefObject<ActionSheetRef>;
|
||||
}) => {
|
||||
const screen = useNavigationStore((state) => state.currentScreen);
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const selection = useSelection();
|
||||
|
||||
@@ -164,7 +164,6 @@ export const PinItem = React.memo(
|
||||
}}
|
||||
fontSize={SIZE.md}
|
||||
width="95%"
|
||||
height={50}
|
||||
customStyle={{
|
||||
marginBottom: 30
|
||||
}}
|
||||
|
||||
@@ -92,7 +92,7 @@ const Input = ({
|
||||
button,
|
||||
onBlurInput,
|
||||
onPress,
|
||||
height = 50,
|
||||
height = 45,
|
||||
fontSize = SIZE.md,
|
||||
onFocusInput,
|
||||
buttons,
|
||||
|
||||
@@ -38,7 +38,8 @@ const SheetWrapper = ({
|
||||
keyboardMode,
|
||||
overlay,
|
||||
overlayOpacity = 0.3,
|
||||
enableGesturesInScrollView = false
|
||||
enableGesturesInScrollView = false,
|
||||
bottomPadding = true
|
||||
}) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
@@ -119,14 +120,16 @@ const SheetWrapper = ({
|
||||
>
|
||||
<BouncingView>
|
||||
{children}
|
||||
<View
|
||||
style={{
|
||||
height:
|
||||
Platform.OS === "ios" && insets.bottom !== 0
|
||||
? insets.bottom + 5
|
||||
: 20
|
||||
}}
|
||||
/>
|
||||
{bottomPadding ? (
|
||||
<View
|
||||
style={{
|
||||
height:
|
||||
Platform.OS === "ios" && insets.bottom !== 0
|
||||
? insets.bottom + 5
|
||||
: 20
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</BouncingView>
|
||||
</ActionSheet>
|
||||
);
|
||||
|
||||
@@ -19,13 +19,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { FeatureType } from "./components/sheets/new-feature";
|
||||
|
||||
export const features: FeatureType[] = [
|
||||
{
|
||||
title: "Top level notes in notebooks",
|
||||
body: "Now you can add notes to Notesnooks directly without creating any topics."
|
||||
},
|
||||
{
|
||||
title: "Image preview & download",
|
||||
body: "Now you can preview images in fullscreen & download them from editor"
|
||||
}
|
||||
];
|
||||
export const features: FeatureType[] = [];
|
||||
|
||||
@@ -52,7 +52,11 @@ import { useThemeStore } from "../stores/use-theme-store";
|
||||
import { useUserStore } from "../stores/use-user-store";
|
||||
import { toTXT } from "../utils";
|
||||
import { toggleDarkMode } from "../utils/color-scheme/utils";
|
||||
import { eOpenAddTopicDialog, eOpenLoginDialog } from "../utils/events";
|
||||
import {
|
||||
eOnTopicSheetUpdate,
|
||||
eOpenAddTopicDialog,
|
||||
eOpenLoginDialog
|
||||
} from "../utils/events";
|
||||
import { deleteItems } from "../utils/functions";
|
||||
import { sleep } from "../utils/time";
|
||||
|
||||
@@ -144,13 +148,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
if (!item.id) return;
|
||||
close();
|
||||
await db.notes.note(item.id).favorite();
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
}
|
||||
|
||||
async function pinItem() {
|
||||
@@ -158,14 +156,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
close();
|
||||
let type = item.type;
|
||||
await db[`${type}s`][type](item.id).pin();
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes",
|
||||
"Notebooks"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
}
|
||||
|
||||
async function pinToNotifications() {
|
||||
@@ -179,9 +170,17 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
checkNotifPinned();
|
||||
return;
|
||||
}
|
||||
if (item.locked) return;
|
||||
let html = await db.notes.note(item.id).content();
|
||||
let text = await toTXT(item);
|
||||
if (item.locked) {
|
||||
ToastEvent.show({
|
||||
heading: "Note is locked",
|
||||
type: "error",
|
||||
message: "Locked notes cannot be pinned to notifications",
|
||||
context: "local"
|
||||
});
|
||||
return;
|
||||
}
|
||||
let text = await toTXT(item, false);
|
||||
let html = text.replace(/\n/g, "<br />");
|
||||
Notifications.displayNotification({
|
||||
title: item.title,
|
||||
message: item.headline || text,
|
||||
@@ -200,15 +199,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
if (!checkNoteSynced()) return;
|
||||
close();
|
||||
await db.trash.restore(item.id);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes",
|
||||
"Notebooks",
|
||||
"Trash"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
let type = item.type === "trash" ? item.itemType : item.type;
|
||||
ToastEvent.show({
|
||||
heading:
|
||||
@@ -324,13 +315,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
let note = db.notes.note(item.id).data;
|
||||
if (note.locked) {
|
||||
close();
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
}
|
||||
} catch (e) {
|
||||
close();
|
||||
@@ -396,16 +381,11 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
positivePress: async (value) => {
|
||||
if (!value || value === "" || value.trimStart().length == 0) return;
|
||||
await db.tags.rename(item.id, db.tags.sanitize(value));
|
||||
useTagStore.getState().setTags();
|
||||
useMenuStore.getState().setMenuPins();
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes",
|
||||
"Tags"
|
||||
);
|
||||
setImmediate(() => {
|
||||
useTagStore.getState().setTags();
|
||||
useMenuStore.getState().setMenuPins();
|
||||
Navigation.queueRoutesForUpdate();
|
||||
});
|
||||
},
|
||||
input: true,
|
||||
defaultValue: alias,
|
||||
@@ -448,24 +428,16 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
? "This reminder will be removed"
|
||||
: "This tag will be removed from all notes.",
|
||||
positivePress: async () => {
|
||||
const routes = [];
|
||||
routes.push(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"Notes",
|
||||
"NotesPage",
|
||||
"Reminders",
|
||||
"Favorites"
|
||||
);
|
||||
if (item.type === "reminder") {
|
||||
await db.reminders.remove(item.id);
|
||||
} else {
|
||||
await db.tags.remove(item.id);
|
||||
useTagStore.getState().setTags();
|
||||
routes.push("Tags");
|
||||
}
|
||||
Navigation.queueRoutesForUpdate(...routes);
|
||||
useRelationStore.getState().update();
|
||||
setImmediate(() => {
|
||||
useTagStore.getState().setTags();
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useRelationStore.getState().update();
|
||||
});
|
||||
},
|
||||
positiveText: "Delete",
|
||||
positiveType: "errorShade"
|
||||
@@ -503,15 +475,8 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
},
|
||||
item.id
|
||||
);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes",
|
||||
"Notebook",
|
||||
"Notebooks"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
eSendEvent(eOnTopicSheetUpdate);
|
||||
close();
|
||||
}
|
||||
|
||||
@@ -519,15 +484,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
const currentScreen = useNavigationStore.getState().currentScreen;
|
||||
if (currentScreen.name !== "Notebook") return;
|
||||
await db.relations.unlink({ type: "notebook", id: currentScreen.id }, item);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes",
|
||||
"Notebook",
|
||||
"Notebooks"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
close();
|
||||
}
|
||||
|
||||
@@ -542,12 +499,14 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
negativeText: "Cancel",
|
||||
positivePress: async () => {
|
||||
await db.trash.delete(item.id);
|
||||
Navigation.queueRoutesForUpdate("Trash");
|
||||
useSelectionStore.getState().setSelectionMode(false);
|
||||
ToastEvent.show({
|
||||
heading: "Permanantly deleted items",
|
||||
type: "success",
|
||||
context: "local"
|
||||
setImmediate(() => {
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useSelectionStore.getState().setSelectionMode(false);
|
||||
ToastEvent.show({
|
||||
heading: "Permanantly deleted items",
|
||||
type: "success",
|
||||
context: "local"
|
||||
});
|
||||
});
|
||||
},
|
||||
positiveType: "errorShade"
|
||||
@@ -565,19 +524,22 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
}
|
||||
|
||||
async function exportNote() {
|
||||
if (item.locked) {
|
||||
ToastEvent.show({
|
||||
heading: "Note is locked",
|
||||
type: "error",
|
||||
message: "Locked notes cannot be exported",
|
||||
context: "local"
|
||||
});
|
||||
return;
|
||||
}
|
||||
ExportNotesSheet.present([item]);
|
||||
}
|
||||
|
||||
async function toggleLocalOnly() {
|
||||
if (!checkNoteSynced() || !user) return;
|
||||
db.notes.note(item.id).localOnly();
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
close();
|
||||
}
|
||||
|
||||
@@ -588,26 +550,14 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
useEditorStore.getState().setReadonly(current);
|
||||
// tiny.call(EditorWebView, tiny.toogleReadMode(current ? 'readonly' : 'design'));
|
||||
}
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
close();
|
||||
};
|
||||
|
||||
const duplicateNote = async () => {
|
||||
if (!checkNoteSynced()) return;
|
||||
await db.notes.note(item.id).duplicate();
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
close();
|
||||
};
|
||||
const actions = [
|
||||
@@ -686,7 +636,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
id: "pin-to-notifications",
|
||||
title:
|
||||
notifPinned !== null
|
||||
? "Unpin from Notifications"
|
||||
? "Unpin from notifications"
|
||||
: "Pin to notifications",
|
||||
icon: "message-badge-outline",
|
||||
on: notifPinned !== null,
|
||||
@@ -835,15 +785,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
});
|
||||
Notifications.scheduleNotification(item);
|
||||
useRelationStore.getState().update();
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes",
|
||||
"NotesPage",
|
||||
"Reminders"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -402,11 +402,9 @@ export const useAppEvents = () => {
|
||||
|
||||
const checkAutoBackup = useCallback(async () => {
|
||||
if (verify || syncing) {
|
||||
console.log("backup is waiting");
|
||||
refValues.current.backupDidWait = true;
|
||||
return;
|
||||
}
|
||||
console.log("backup running immediate");
|
||||
const user = await db.user.getUser();
|
||||
if (PremiumService.get() && user) {
|
||||
if (SettingsService.get().reminder === "off") {
|
||||
@@ -427,7 +425,6 @@ export const useAppEvents = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!verify && !syncing && refValues.current.backupDidWait) {
|
||||
console.log("backup run after wait");
|
||||
refValues.current.backupDidWait = false;
|
||||
checkAutoBackup();
|
||||
}
|
||||
@@ -561,7 +558,7 @@ export const useAppEvents = () => {
|
||||
let shareExtensionOpened = MMKV.getString("shareExtensionOpened");
|
||||
if (notesAddedFromIntent) {
|
||||
if (Platform.OS === "ios") {
|
||||
await db.init();
|
||||
await db.initCollections();
|
||||
await db.notes.init();
|
||||
}
|
||||
useNoteStore.getState().setNotes();
|
||||
|
||||
@@ -69,6 +69,7 @@ import {
|
||||
import { editorRef, tabBarRef } from "../utils/global-refs";
|
||||
import { sleep } from "../utils/time";
|
||||
import { NavigationStack } from "./navigation-stack";
|
||||
import changeNavigationBarColor from "react-native-navigation-bar-color";
|
||||
|
||||
const _TabsHolder = () => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
@@ -386,6 +387,10 @@ const _TabsHolder = () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
changeNavigationBarColor(colors.bg, !colors.night, true);
|
||||
}, [colors.night, colors.bg]);
|
||||
|
||||
return (
|
||||
<View
|
||||
onLayout={_onLayout}
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
"main": "./App.js",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"react": "18.0.0",
|
||||
"react-native": "0.69.7",
|
||||
"@flyerhq/react-native-link-preview": "^1.6.0",
|
||||
"@mdi/js": "^6.7.96",
|
||||
"absolutify": "^0.1.0",
|
||||
@@ -15,7 +13,7 @@
|
||||
"html-to-text": "8.1.0",
|
||||
"phone": "^3.1.14",
|
||||
"qclone": "^1.2.0",
|
||||
"react-native-actions-sheet": "^0.9.0-alpha.12",
|
||||
"react-native-actions-sheet": "0.9.0-alpha.18",
|
||||
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
|
||||
"react-native-drax": "^0.10.2",
|
||||
"react-native-image-zoom-viewer": "^3.0.1",
|
||||
|
||||
@@ -28,12 +28,12 @@ import { Button } from "../../components/ui/button";
|
||||
import { IconButton } from "../../components/ui/icon-button";
|
||||
import Paragraph from "../../components/ui/typography/paragraph";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import {
|
||||
eSendEvent,
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { eClearEditor } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
@@ -42,7 +42,9 @@ const EditorOverlay = ({ editorId = "", editor }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const [error, setError] = useState(false);
|
||||
const opacity = useSharedValue(1);
|
||||
const translateValue = useSharedValue(6000);
|
||||
const translateValue = useSharedValue(0);
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const isTablet = deviceMode !== "mobile";
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const isDefaultEditor = editorId === "";
|
||||
const timers = useRef({
|
||||
@@ -50,6 +52,9 @@ const EditorOverlay = ({ editorId = "", editor }) => {
|
||||
error: 0,
|
||||
closing: 0
|
||||
});
|
||||
const loadingState = useRef({
|
||||
startTime: 0
|
||||
});
|
||||
|
||||
const clearTimers = () => {
|
||||
clearTimeout(timers.current.loading);
|
||||
@@ -62,6 +67,7 @@ const EditorOverlay = ({ editorId = "", editor }) => {
|
||||
editorState().overlay = true;
|
||||
clearTimers();
|
||||
if (_loading) {
|
||||
loadingState.current.startTime = Date.now();
|
||||
opacity.value = 1;
|
||||
translateValue.value = 0;
|
||||
timers.current.error = setTimeout(() => {
|
||||
@@ -69,7 +75,14 @@ const EditorOverlay = ({ editorId = "", editor }) => {
|
||||
}, 15 * 1000);
|
||||
} else {
|
||||
clearTimers();
|
||||
setTimeout(() => {
|
||||
const timeDiffSinceLoadStarted =
|
||||
Date.now() - loadingState.current.startTime > 300 ? 300 : 0;
|
||||
if (!timeDiffSinceLoadStarted) {
|
||||
setError(false);
|
||||
editorState().overlay = false;
|
||||
opacity.value = 0;
|
||||
translateValue.value = 6000;
|
||||
} else {
|
||||
setError(false);
|
||||
editorState().overlay = false;
|
||||
opacity.value = withTiming(0, {
|
||||
@@ -78,19 +91,24 @@ const EditorOverlay = ({ editorId = "", editor }) => {
|
||||
setTimeout(() => {
|
||||
translateValue.value = 6000;
|
||||
}, 500);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
[opacity, translateValue]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
if (!loadingState.current.startTime) {
|
||||
translateValue.value = 6000;
|
||||
}
|
||||
}, 1000);
|
||||
eSubscribeEvent("loadingNote" + editorId, load);
|
||||
return () => {
|
||||
clearTimers();
|
||||
eUnSubscribeEvent("loadingNote" + editorId, load);
|
||||
};
|
||||
}, [editorId, load]);
|
||||
}, [editorId, load, translateValue]);
|
||||
|
||||
const animatedStyle = useAnimatedStyle(() => {
|
||||
return {
|
||||
@@ -140,7 +158,7 @@ const EditorOverlay = ({ editorId = "", editor }) => {
|
||||
paddingRight: 12
|
||||
}}
|
||||
>
|
||||
{DDS.isTablet() ? (
|
||||
{isTablet ? (
|
||||
<View />
|
||||
) : (
|
||||
<IconButton
|
||||
@@ -184,7 +202,9 @@ const EditorOverlay = ({ editorId = "", editor }) => {
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 10,
|
||||
marginTop: 10
|
||||
marginTop: 5,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.icon} size={13}>
|
||||
|
||||
@@ -129,7 +129,6 @@ typeof globalThis.statusBar !== "undefined" && statusBar.current.set({date:"",sa
|
||||
};
|
||||
|
||||
setInsets = async (insets: EdgeInsets) => {
|
||||
logger.info("setInsets", insets);
|
||||
await this.doAsync(`
|
||||
if (typeof safeAreaController !== "undefined") {
|
||||
safeAreaController.update(${JSON.stringify(insets)})
|
||||
|
||||
@@ -189,12 +189,7 @@ export const useEditorEvents = (
|
||||
setImmediate(() => {
|
||||
useEditorStore.getState().setCurrentlyEditingNote(null);
|
||||
setTimeout(() => {
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"ColoredNotes",
|
||||
"Notes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
}, 500);
|
||||
});
|
||||
editorState().currentlyEditing = false;
|
||||
@@ -273,12 +268,28 @@ export const useEditorEvents = (
|
||||
(event: WebViewMessageEvent) => {
|
||||
const data = event.nativeEvent.data;
|
||||
const editorMessage = JSON.parse(data) as EditorMessage;
|
||||
|
||||
if (editorMessage.type === EventTypes.content) {
|
||||
editor.saveContent({
|
||||
type: editorMessage.type,
|
||||
content: editorMessage.value as string,
|
||||
forSessionId: editorMessage.sessionId
|
||||
});
|
||||
} else if (editorMessage.type === EventTypes.title) {
|
||||
editor.saveContent({
|
||||
type: editorMessage.type,
|
||||
title: editorMessage.value as string,
|
||||
forSessionId: editorMessage.sessionId
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
editorMessage.sessionId !== editor.sessionId &&
|
||||
editorMessage.type !== EditorEvents.status
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (editorMessage.type) {
|
||||
case EventTypes.logger:
|
||||
logger.info("[WEBVIEW LOG]", editorMessage.value);
|
||||
@@ -286,21 +297,8 @@ export const useEditorEvents = (
|
||||
case EventTypes.contentchange:
|
||||
editor.onContentChanged();
|
||||
break;
|
||||
case EventTypes.content:
|
||||
editor.saveContent({
|
||||
type: editorMessage.type,
|
||||
content: editorMessage.value as string
|
||||
});
|
||||
break;
|
||||
case EventTypes.selection:
|
||||
break;
|
||||
case EventTypes.title:
|
||||
editor.saveContent({
|
||||
type: editorMessage.type,
|
||||
title: editorMessage.value as string
|
||||
});
|
||||
break;
|
||||
|
||||
case EventTypes.reminders:
|
||||
if (!editor.note.current) {
|
||||
ToastEvent.show({
|
||||
@@ -337,13 +335,7 @@ export const useEditorEvents = (
|
||||
.then(async () => {
|
||||
useTagStore.getState().setTags();
|
||||
await editor.commands.setTags(editor.note.current);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"ColoredNotes",
|
||||
"Notes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes",
|
||||
"Tags"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -77,6 +77,7 @@ export const useEditor = (
|
||||
const lastContentChangeTime = useRef<number>(0);
|
||||
const lock = useRef(false);
|
||||
const loadedImages = useRef<{ [name: string]: boolean }>({});
|
||||
const lockedSessionId = useRef<string>();
|
||||
|
||||
const postMessage = useCallback(
|
||||
async <T>(type: string, data: T) =>
|
||||
@@ -152,7 +153,7 @@ export const useEditor = (
|
||||
);
|
||||
|
||||
const reset = useCallback(
|
||||
async (resetState = true) => {
|
||||
async (resetState = true, resetContent = true) => {
|
||||
currentNote.current?.id && db.fs.cancel(currentNote.current.id);
|
||||
currentNote.current = null;
|
||||
loadedImages.current = {};
|
||||
@@ -161,10 +162,10 @@ export const useEditor = (
|
||||
sessionHistoryId.current = undefined;
|
||||
saveCount.current = 0;
|
||||
useEditorStore.getState().setReadonly(false);
|
||||
postMessage(EditorEvents.title, "");
|
||||
resetContent && postMessage(EditorEvents.title, "");
|
||||
lastContentChangeTime.current = 0;
|
||||
await commands.clearContent();
|
||||
await commands.clearTags();
|
||||
resetContent && (await commands.clearContent());
|
||||
resetContent && (await commands.clearTags());
|
||||
if (resetState) {
|
||||
isDefaultEditor &&
|
||||
useEditorStore.getState().setCurrentlyEditingNote(null);
|
||||
@@ -239,9 +240,15 @@ export const useEditor = (
|
||||
|
||||
if (
|
||||
useEditorStore.getState().currentEditingNote !== id &&
|
||||
isDefaultEditor
|
||||
isDefaultEditor &&
|
||||
state.current.currentlyEditing
|
||||
) {
|
||||
setTimeout(() => {
|
||||
if (
|
||||
(currentNote.current?.id && currentNote.current?.id !== id) ||
|
||||
!state.current.currentlyEditing
|
||||
)
|
||||
return;
|
||||
id && useEditorStore.getState().setCurrentlyEditingNote(id);
|
||||
});
|
||||
}
|
||||
@@ -262,12 +269,7 @@ export const useEditor = (
|
||||
currentNote.current?.headline?.slice(0, 200) !==
|
||||
note.headline?.slice(0, 200)
|
||||
) {
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"ColoredNotes",
|
||||
"Notes",
|
||||
"TaggedNotes",
|
||||
"TopicNotes"
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,16 +369,26 @@ export const useEditor = (
|
||||
useEditorStore.getState().setReadonly(false);
|
||||
} else {
|
||||
if (!item.forced && currentNote.current?.id === item.id) return;
|
||||
state.current.movedAway = false;
|
||||
state.current.currentlyEditing = true;
|
||||
isDefaultEditor && editorState.setCurrentlyEditingNote(item.id);
|
||||
overlay(true, item);
|
||||
currentNote.current && (await reset(false));
|
||||
currentNote.current && (await reset(false, false));
|
||||
await loadContent(item as NoteType);
|
||||
if (
|
||||
!currentContent.current?.data ||
|
||||
currentContent.current?.data.length < 50000
|
||||
) {
|
||||
overlay(false);
|
||||
} else {
|
||||
overlay(true);
|
||||
}
|
||||
lastContentChangeTime.current = item.dateEdited;
|
||||
const nextSessionId = makeSessionId(item as NoteType);
|
||||
lockedSessionId.current = nextSessionId;
|
||||
sessionHistoryId.current = Date.now();
|
||||
setSessionId(nextSessionId);
|
||||
commands.setSessionId(nextSessionId);
|
||||
sessionIdRef.current = nextSessionId;
|
||||
await commands.setSessionId(nextSessionId);
|
||||
currentNote.current = item as NoteType;
|
||||
await commands.setStatus(timeConverter(item.dateEdited), "Saved");
|
||||
await postMessage(EditorEvents.title, item.title);
|
||||
@@ -384,6 +396,11 @@ export const useEditor = (
|
||||
useEditorStore.getState().setReadonly(item.readonly);
|
||||
await commands.setTags(currentNote.current);
|
||||
commands.setSettings();
|
||||
setTimeout(() => {
|
||||
if (lockedSessionId.current === nextSessionId) {
|
||||
lockedSessionId.current = undefined;
|
||||
}
|
||||
}, 300);
|
||||
overlay(false);
|
||||
loadImages();
|
||||
}
|
||||
@@ -490,14 +507,16 @@ export const useEditor = (
|
||||
({
|
||||
title,
|
||||
content,
|
||||
type
|
||||
type,
|
||||
forSessionId
|
||||
}: {
|
||||
title?: string;
|
||||
content?: string;
|
||||
type: string;
|
||||
forSessionId: string;
|
||||
}) => {
|
||||
if (lock.current || lockedSessionId.current === forSessionId) return;
|
||||
lastContentChangeTime.current = Date.now();
|
||||
if (lock.current) return;
|
||||
if (type === EditorEvents.content) {
|
||||
currentContent.current = {
|
||||
data: content,
|
||||
@@ -505,22 +524,27 @@ export const useEditor = (
|
||||
noteId: currentNote.current?.id as string
|
||||
};
|
||||
}
|
||||
const noteIdFromSessionId =
|
||||
!forSessionId || forSessionId.startsWith("session")
|
||||
? null
|
||||
: forSessionId.split("_")[0];
|
||||
|
||||
const noteId = noteIdFromSessionId || currentNote.current?.id;
|
||||
const params = {
|
||||
title,
|
||||
data: content,
|
||||
type: "tiptap",
|
||||
sessionId,
|
||||
id: currentNote.current?.id,
|
||||
sessionId: forSessionId,
|
||||
id: noteId,
|
||||
sessionHistoryId: sessionHistoryId.current
|
||||
};
|
||||
|
||||
withTimer(
|
||||
currentNote.current?.id || "newnote",
|
||||
noteId || "newnote",
|
||||
() => {
|
||||
if (
|
||||
currentNote.current &&
|
||||
!params.id &&
|
||||
params.sessionId === sessionId
|
||||
params.sessionId === forSessionId
|
||||
) {
|
||||
params.id = currentNote.current?.id;
|
||||
}
|
||||
@@ -530,10 +554,10 @@ export const useEditor = (
|
||||
}
|
||||
saveNote(params);
|
||||
},
|
||||
500
|
||||
150
|
||||
);
|
||||
},
|
||||
[sessionId, withTimer, onChange, saveNote]
|
||||
[withTimer, onChange, saveNote]
|
||||
);
|
||||
|
||||
const restoreEditorState = useCallback(async () => {
|
||||
|
||||
@@ -22,9 +22,9 @@ import { db } from "../../common/database";
|
||||
import DelayLayout from "../../components/delay-layout";
|
||||
import List from "../../components/list";
|
||||
import { NotebookHeader } from "../../components/list-items/headers/notebook-header";
|
||||
import { AddNotebookSheet } from "../../components/sheets/add-notebook";
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import {
|
||||
eSendEvent,
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
@@ -33,7 +33,7 @@ import SearchService from "../../services/search";
|
||||
import useNavigationStore, {
|
||||
NotebookScreenParams
|
||||
} from "../../stores/use-navigation-store";
|
||||
import { eOnNewTopicAdded, eOpenAddNotebookDialog } from "../../utils/events";
|
||||
import { eOnNewTopicAdded } from "../../utils/events";
|
||||
import { NotebookType } from "../../utils/types";
|
||||
import { openEditor, setOnFirstSave } from "../notes/common";
|
||||
const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
@@ -148,7 +148,7 @@ const Notebook = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
ListHeader={
|
||||
<NotebookHeader
|
||||
onEditNotebook={() => {
|
||||
eSendEvent(eOpenAddNotebookDialog, params.current.item);
|
||||
AddNotebookSheet.present(params.current.item);
|
||||
}}
|
||||
notebook={params.current.item}
|
||||
/>
|
||||
|
||||
@@ -94,15 +94,6 @@ async function onNoteCreated(id: string, params: FirstSaveData) {
|
||||
{ type: "notebook", id: params.id },
|
||||
{ type: "note", id: id }
|
||||
);
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes",
|
||||
"Notebook",
|
||||
"Notebooks"
|
||||
);
|
||||
editorState().onNoteCreated = null;
|
||||
break;
|
||||
}
|
||||
@@ -116,41 +107,18 @@ async function onNoteCreated(id: string, params: FirstSaveData) {
|
||||
id
|
||||
);
|
||||
editorState().onNoteCreated = null;
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes",
|
||||
"Notebook",
|
||||
"Notebooks"
|
||||
);
|
||||
eSendEvent(eOnTopicSheetUpdate);
|
||||
break;
|
||||
}
|
||||
case "tag": {
|
||||
await db.notes?.note(id).tag(params.id);
|
||||
editorState().onNoteCreated = null;
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes"
|
||||
);
|
||||
useTagStore.getState().setTags();
|
||||
break;
|
||||
}
|
||||
case "color": {
|
||||
await db.notes?.note(id).color(params.color);
|
||||
editorState().onNoteCreated = null;
|
||||
Navigation.queueRoutesForUpdate(
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes"
|
||||
);
|
||||
useMenuStore.getState().setColorNotes();
|
||||
break;
|
||||
}
|
||||
@@ -158,4 +126,5 @@ async function onNoteCreated(id: string, params: FirstSaveData) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Navigation.queueRoutesForUpdate();
|
||||
}
|
||||
|
||||
@@ -18,9 +18,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { db } from "../../common/database";
|
||||
import { FloatingButton } from "../../components/container/floating-button";
|
||||
import DelayLayout from "../../components/delay-layout";
|
||||
import List from "../../components/list";
|
||||
import { IconButton } from "../../components/ui/icon-button";
|
||||
import Paragraph from "../../components/ui/typography/paragraph";
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
@@ -34,7 +38,9 @@ import useNavigationStore, {
|
||||
RouteName
|
||||
} from "../../stores/use-navigation-store";
|
||||
import { useNoteStore } from "../../stores/use-notes-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { NoteType, TopicType } from "../../utils/types";
|
||||
import Notebook from "../notebook/index";
|
||||
import {
|
||||
getAlias,
|
||||
openEditor,
|
||||
@@ -42,13 +48,6 @@ import {
|
||||
setOnFirstSave,
|
||||
toCamelCase
|
||||
} from "./common";
|
||||
import { View } from "react-native";
|
||||
import { db } from "../../common/database";
|
||||
import Paragraph from "../../components/ui/typography/paragraph";
|
||||
import { IconButton } from "../../components/ui/icon-button";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import Notebook from "../notebook/index";
|
||||
export const WARNING_DATA = {
|
||||
title: "Some notes in this topic are not synced"
|
||||
};
|
||||
@@ -99,7 +98,6 @@ const NotesPage = ({
|
||||
}: RouteProps<
|
||||
"NotesPage" | "TaggedNotes" | "Monographs" | "ColoredNotes" | "TopicNotes"
|
||||
>) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const params = useRef<NotesScreenParams>(route?.params);
|
||||
const [notes, setNotes] = useState<NoteType[]>(get(route.params, true));
|
||||
const loading = useNoteStore((state) => state.loading);
|
||||
@@ -183,8 +181,9 @@ const NotesPage = ({
|
||||
if (isNew) setLoadingNotes(true);
|
||||
const notes = get(params.current, true) as NoteType[];
|
||||
if (
|
||||
(item.type === "tag" || item.type === "color") &&
|
||||
(!notes || notes.length === 0)
|
||||
((item.type === "tag" || item.type === "color") &&
|
||||
(!notes || notes.length === 0)) ||
|
||||
(item.type === "topic" && !notes)
|
||||
) {
|
||||
return Navigation.goBack();
|
||||
}
|
||||
@@ -245,19 +244,23 @@ const NotesPage = ({
|
||||
>
|
||||
Notebooks
|
||||
</Paragraph>
|
||||
<IconButton
|
||||
name="chevron-right"
|
||||
size={14}
|
||||
customStyle={{ width: 25, height: 25 }}
|
||||
/>
|
||||
<Paragraph
|
||||
onPress={() => {
|
||||
Notebook.navigate(notebook, true);
|
||||
}}
|
||||
size={SIZE.xs}
|
||||
>
|
||||
{notebook.title}
|
||||
</Paragraph>
|
||||
{notebook ? (
|
||||
<>
|
||||
<IconButton
|
||||
name="chevron-right"
|
||||
size={14}
|
||||
customStyle={{ width: 25, height: 25 }}
|
||||
/>
|
||||
<Paragraph
|
||||
onPress={() => {
|
||||
Notebook.navigate(notebook, true);
|
||||
}}
|
||||
size={SIZE.xs}
|
||||
>
|
||||
{notebook.title}
|
||||
</Paragraph>
|
||||
</>
|
||||
) : null}
|
||||
</View>
|
||||
) : null}
|
||||
<List
|
||||
@@ -276,7 +279,9 @@ const NotesPage = ({
|
||||
placeholderData={placeholderData}
|
||||
/>
|
||||
|
||||
{notes?.length > 0 || (isFocused && !isMonograph) ? (
|
||||
{!isMonograph &&
|
||||
route.name !== "TopicNotes" &&
|
||||
(notes?.length > 0 || isFocused) ? (
|
||||
<FloatingButton title="Create a note" onPress={onPressFloatingButton} />
|
||||
) : null}
|
||||
</DelayLayout>
|
||||
|
||||
@@ -75,7 +75,11 @@ export const TopicNotes = ({
|
||||
|
||||
TopicNotes.get = (params: NotesScreenParams, grouped = true) => {
|
||||
const { id, notebookId } = params.item as TopicType;
|
||||
const notes = db.notebooks?.notebook(notebookId)?.topics.topic(id)?.all || [];
|
||||
const topic = db.notebooks?.notebook(notebookId)?.topics.topic(id);
|
||||
if (!topic) {
|
||||
return null;
|
||||
}
|
||||
const notes = topic?.all || [];
|
||||
return grouped
|
||||
? groupArray(notes, db.settings?.getGroupOptions("notes"))
|
||||
: notes;
|
||||
|
||||
@@ -106,12 +106,6 @@ export const useDragState = create<DragState>(
|
||||
return;
|
||||
}
|
||||
const preset = toolbarConfig?.preset as DragState["preset"];
|
||||
logger.info(
|
||||
"DragState",
|
||||
"Init user toolbar config",
|
||||
preset,
|
||||
toolbarConfig?.config
|
||||
);
|
||||
set({
|
||||
preset: preset,
|
||||
data:
|
||||
|
||||
@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { NativeStackScreenProps } from "@react-navigation/native-stack";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import Animated, { FadeInDown, FadeOutDown } from "react-native-reanimated";
|
||||
import Animated, { FadeInDown } from "react-native-reanimated";
|
||||
import DelayLayout from "../../components/delay-layout";
|
||||
import BaseDialog from "../../components/dialog/base-dialog";
|
||||
import { ProgressBarComponent } from "../../components/ui/svg/lazy";
|
||||
@@ -75,57 +75,55 @@ const Home = ({
|
||||
|
||||
return (
|
||||
<DelayLayout delay={300} type="settings">
|
||||
<View>
|
||||
{loading && (
|
||||
//@ts-ignore // Migrate to typescript required.
|
||||
<BaseDialog animated={false} bounce={false} visible={true}>
|
||||
{loading && (
|
||||
//@ts-ignore // Migrate to typescript required.
|
||||
<BaseDialog animated={false} bounce={false} visible={true}>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
backgroundColor: colors.bg,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Heading color={colors.pri} size={SIZE.lg}>
|
||||
Logging out
|
||||
</Heading>
|
||||
<Paragraph color={colors.icon}>
|
||||
Please wait while we log out and clear app data.
|
||||
</Paragraph>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
backgroundColor: colors.bg,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
flexDirection: "row",
|
||||
width: 100,
|
||||
marginTop: 15
|
||||
}}
|
||||
>
|
||||
<Heading color={colors.pri} size={SIZE.lg}>
|
||||
Logging out
|
||||
</Heading>
|
||||
<Paragraph color={colors.icon}>
|
||||
Please wait while we log out and clear app data.
|
||||
</Paragraph>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: 100,
|
||||
marginTop: 15
|
||||
}}
|
||||
>
|
||||
<ProgressBarComponent
|
||||
height={5}
|
||||
width={100}
|
||||
animated={true}
|
||||
useNativeDriver
|
||||
indeterminate
|
||||
indeterminateAnimationDuration={2000}
|
||||
unfilledColor={colors.nav}
|
||||
color={colors.accent}
|
||||
borderWidth={0}
|
||||
/>
|
||||
</View>
|
||||
<ProgressBarComponent
|
||||
height={5}
|
||||
width={100}
|
||||
animated={true}
|
||||
useNativeDriver
|
||||
indeterminate
|
||||
indeterminateAnimationDuration={2000}
|
||||
unfilledColor={colors.nav}
|
||||
color={colors.accent}
|
||||
borderWidth={0}
|
||||
/>
|
||||
</View>
|
||||
</BaseDialog>
|
||||
)}
|
||||
</View>
|
||||
</BaseDialog>
|
||||
)}
|
||||
|
||||
<Animated.FlatList
|
||||
entering={FadeInDown}
|
||||
exiting={FadeOutDown}
|
||||
data={settingsGroups}
|
||||
keyExtractor={keyExtractor}
|
||||
ListFooterComponent={<View style={{ height: 200 }} />}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
</View>
|
||||
<Animated.FlatList
|
||||
entering={FadeInDown}
|
||||
data={settingsGroups}
|
||||
windowSize={1}
|
||||
keyExtractor={keyExtractor}
|
||||
ListFooterComponent={<View style={{ height: 200 }} />}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
</DelayLayout>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -42,12 +42,12 @@ export const SectionGroup = ({ item }: { item: SettingSection }) => {
|
||||
color={colors.accent}
|
||||
size={SIZE.xs}
|
||||
>
|
||||
{item.name.toUpperCase()}
|
||||
{(item.name as string).toUpperCase()}
|
||||
</Heading>
|
||||
) : null}
|
||||
|
||||
{item.sections?.map((item) => (
|
||||
<SectionItem key={item.name} item={item} />
|
||||
<SectionItem key={item.name as string} item={item} />
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -169,6 +169,14 @@ const _SectionItem = ({ item }: { item: SettingSection }) => {
|
||||
}
|
||||
item.inputProperties?.onSubmitEditing?.(e);
|
||||
}}
|
||||
onChangeText={(text) => {
|
||||
if (text) {
|
||||
SettingsService.set({
|
||||
[item.property as string]: text
|
||||
});
|
||||
}
|
||||
item.inputProperties?.onSubmitEditing?.(text as any);
|
||||
}}
|
||||
containerStyle={{ marginTop: 12 }}
|
||||
fwdRef={inputRef}
|
||||
onLayout={() => {
|
||||
|
||||
@@ -317,7 +317,6 @@ export const settingsGroups: SettingSection[] = [
|
||||
await PremiumService.setPremiumStatus();
|
||||
await BiometicService.resetCredentials();
|
||||
MMKV.clearStore();
|
||||
await db.init();
|
||||
await clearAllStores();
|
||||
SettingsService.init();
|
||||
setTimeout(() => {
|
||||
@@ -959,9 +958,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
onChange: () => {
|
||||
ToastEvent.show({
|
||||
heading: "Line spacing changed",
|
||||
type: "success",
|
||||
message:
|
||||
"Close and reopen the current opened note or restart the app for changes to take affect."
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1027,11 +1024,21 @@ export const settingsGroups: SettingSection[] = [
|
||||
id: "join-telegram",
|
||||
name: "Join our Telegram group",
|
||||
description: "We are on telegram, let's talk",
|
||||
// icon: 'telegram',
|
||||
modifer: () => {
|
||||
Linking.openURL("https://t.me/notesnook").catch(console.log);
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "join-mastodom",
|
||||
name: "Follow us on Mastodon",
|
||||
description: "We are on mastodom",
|
||||
icon: "mastodon",
|
||||
modifer: () => {
|
||||
Linking.openURL("https://fosstodon.org/@notesnook").catch(
|
||||
console.log
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "join-twitter",
|
||||
name: "Follow us on twitter",
|
||||
|
||||
@@ -87,7 +87,7 @@ export const TrashIntervalSelector = () => {
|
||||
>
|
||||
{[-1, 7, 30, 365].map((item) => (
|
||||
<MenuItem
|
||||
key={item.name}
|
||||
key={item.toString()}
|
||||
onPress={async () => {
|
||||
if (item === -1) {
|
||||
await PremiumService.verify(() => {
|
||||
|
||||
@@ -114,7 +114,8 @@ export type PresentSheetOptions = {
|
||||
actionsArray: SheetAction[];
|
||||
learnMore: string;
|
||||
learnMorePress: () => void;
|
||||
enableGesturesInScrollView: boolean;
|
||||
enableGesturesInScrollView?: boolean;
|
||||
noBottomPadding?: boolean;
|
||||
};
|
||||
|
||||
export function presentSheet(data: Partial<PresentSheetOptions>) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import { eOnNewTopicAdded } from "../utils/events";
|
||||
import { rootNavigatorRef, tabBarRef } from "../utils/global-refs";
|
||||
import { eSendEvent } from "./event-manager";
|
||||
import SettingsService from "./settings";
|
||||
import SearchService from "./search";
|
||||
|
||||
/**
|
||||
* Routes that should be updated on focus
|
||||
@@ -86,7 +87,8 @@ const routeUpdateFunctions: {
|
||||
ColoredNotes: (params) => eSendEvent("ColoredNotes", params),
|
||||
TopicNotes: (params) => eSendEvent("TopicNotes", params),
|
||||
Monographs: (params) => eSendEvent("Monographs", params),
|
||||
Reminders: () => useReminderStore.getState().setReminders()
|
||||
Reminders: () => useReminderStore.getState().setReminders(),
|
||||
Search: () => SearchService.updateAndSearch()
|
||||
};
|
||||
|
||||
function clearRouteFromQueue(routeName: RouteName) {
|
||||
@@ -106,10 +108,14 @@ function routeNeedsUpdate(routeName: RouteName, callback: () => void) {
|
||||
}
|
||||
}
|
||||
|
||||
function queueRoutesForUpdate(...routes: RouteName[]) {
|
||||
function queueRoutesForUpdate(...routesToUpdate: RouteName[]) {
|
||||
const routes =
|
||||
routesToUpdate?.length > 0
|
||||
? routesToUpdate
|
||||
: (Object.keys(routeNames) as (keyof RouteParams)[]);
|
||||
const currentScreen = useNavigationStore.getState().currentScreen;
|
||||
if (routes.indexOf(currentScreen.name) > -1) {
|
||||
routeUpdateFunctions[currentScreen.name]();
|
||||
routeUpdateFunctions[currentScreen.name]?.();
|
||||
clearRouteFromQueue(currentScreen.name);
|
||||
// Remove focused screen from queue
|
||||
routes.splice(routes.indexOf(currentScreen.name), 1);
|
||||
@@ -125,7 +131,7 @@ function navigate<T extends RouteName>(
|
||||
useNavigationStore.getState().update(screen, !!params?.canGoBack);
|
||||
if (screen.name === "Notebook") routeUpdateFunctions["Notebook"](params);
|
||||
if (screen.name.endsWith("Notes") && screen.name !== "Notes")
|
||||
routeUpdateFunctions[screen.name](params);
|
||||
routeUpdateFunctions[screen.name]?.(params);
|
||||
//@ts-ignore Not sure how to fix this for now ignore it.
|
||||
rootNavigatorRef.current?.navigate<RouteName>(screen.name, params);
|
||||
}
|
||||
|
||||
@@ -85,13 +85,20 @@ async function getNextMonthlyReminderDate(
|
||||
return await getNextMonthlyReminderDate(reminder, dayjs().year() + 1);
|
||||
}
|
||||
|
||||
async function initDatabase(notes = true) {
|
||||
if (db.isInitialized) return;
|
||||
await db.initCollections();
|
||||
if (notes) {
|
||||
await db.notes?.init();
|
||||
}
|
||||
}
|
||||
|
||||
const onEvent = async ({ type, detail }: Event) => {
|
||||
const { notification, pressAction, input } = detail;
|
||||
if (type === EventType.DELIVERED && Platform.OS === "android") {
|
||||
const reminder = db.reminders?.reminder(notification?.id?.split("_")[0]);
|
||||
if (reminder && reminder.recurringMode === "month") {
|
||||
await db.init();
|
||||
await db.notes?.init();
|
||||
await initDatabase();
|
||||
await scheduleNotification(reminder);
|
||||
}
|
||||
return;
|
||||
@@ -100,8 +107,7 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
notifee.decrementBadgeCount();
|
||||
if (notification?.data?.type === "quickNote") return;
|
||||
MMKV.removeItem("appState");
|
||||
await db.init();
|
||||
await db.notes?.init();
|
||||
await initDatabase();
|
||||
if (notification?.data?.type === "reminder") {
|
||||
const reminder = db.reminders?.reminder(notification.id?.split("_")[0]);
|
||||
await sleep(1000);
|
||||
@@ -131,8 +137,7 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
notifee.decrementBadgeCount();
|
||||
switch (pressAction?.id) {
|
||||
case "REMINDER_SNOOZE": {
|
||||
await db.init();
|
||||
await db.notes?.init();
|
||||
await initDatabase();
|
||||
const reminder = db.reminders?.reminder(
|
||||
notification?.id?.split("_")[0]
|
||||
);
|
||||
@@ -151,8 +156,7 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
break;
|
||||
}
|
||||
case "REMINDER_DISABLE": {
|
||||
await db.init();
|
||||
await db.notes?.init();
|
||||
await initDatabase();
|
||||
const reminder = db.reminders?.reminder(
|
||||
notification?.id?.split("_")[0]
|
||||
);
|
||||
@@ -168,8 +172,7 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
break;
|
||||
}
|
||||
case "UNPIN": {
|
||||
await db.init();
|
||||
await db.notes?.init();
|
||||
await initDatabase();
|
||||
remove(notification?.id as string);
|
||||
const reminder = db.reminders?.reminder(
|
||||
notification?.id?.split("_")[0]
|
||||
@@ -198,7 +201,7 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
reply_button_text: "Take note",
|
||||
reply_placeholder_text: "Write something..."
|
||||
});
|
||||
await db.init();
|
||||
await initDatabase(false);
|
||||
await db.notes?.add({
|
||||
content: {
|
||||
type: "tiptap",
|
||||
@@ -367,7 +370,8 @@ async function scheduleNotification(
|
||||
|
||||
function loadNote(id: string, jump: boolean) {
|
||||
if (!id || id === "notesnook_note_input") return;
|
||||
const note = db.notes?.note(id).data;
|
||||
const note = db.notes?.note(id)?.data;
|
||||
if (!note) return;
|
||||
if (!DDS.isTab && jump) {
|
||||
tabBarRef.current?.goToPage(1);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ let searchInformation = {
|
||||
placeholder: "Search in all notes",
|
||||
data: [],
|
||||
type: "notes",
|
||||
get: () => null
|
||||
get: () => []
|
||||
};
|
||||
|
||||
let keyword = null;
|
||||
|
||||
@@ -19,22 +19,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { db } from "../common/database";
|
||||
import Navigation from "../services/navigation";
|
||||
import Notifications from "../services/notifications";
|
||||
import { useFavoriteStore } from "./use-favorite-store";
|
||||
import { useMenuStore } from "./use-menu-store";
|
||||
import { RouteName } from "./use-navigation-store";
|
||||
import { useNotebookStore } from "./use-notebook-store";
|
||||
import { useNoteStore } from "./use-notes-store";
|
||||
import { useRelationStore } from "./use-relation-store";
|
||||
import { useReminderStore } from "./use-reminder-store";
|
||||
import { useTagStore } from "./use-tag-store";
|
||||
import { useTrashStore } from "./use-trash-store";
|
||||
import { useReminderStore } from "./use-reminder-store";
|
||||
import Notifications from "../services/notifications";
|
||||
import { useRelationStore } from "./use-relation-store";
|
||||
export function initAfterSync() {
|
||||
useMenuStore.getState().setColorNotes();
|
||||
useMenuStore.getState().setMenuPins();
|
||||
Navigation.queueRoutesForUpdate(
|
||||
...(Object.keys(Navigation.routeUpdateFunctions) as unknown as RouteName[])
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
// Whenever sync completes, try to reschedule
|
||||
// any new/updated reminders.
|
||||
Notifications.setupReminders(true);
|
||||
|
||||
@@ -104,7 +104,7 @@ export const COLOR_SCHEME_PITCH_BLACK = {
|
||||
light: "#ffffff",
|
||||
transGray: "#ffffff10",
|
||||
border: "#383838",
|
||||
placeholder: "#404040"
|
||||
placeholder: "#606060"
|
||||
};
|
||||
|
||||
export const COLOR_SCHEME_DARK = {
|
||||
|
||||
@@ -25,7 +25,7 @@ import SearchService from "../services/search";
|
||||
import { useSelectionStore } from "../stores/use-selection-store";
|
||||
import { useMenuStore } from "../stores/use-menu-store";
|
||||
import { db } from "../common/database";
|
||||
import { eClearEditor } from "./events";
|
||||
import { eClearEditor, eOnTopicSheetUpdate } from "./events";
|
||||
import { useRelationStore } from "../stores/use-relation-store";
|
||||
import { presentDialog } from "../components/dialog/functions";
|
||||
|
||||
@@ -38,11 +38,14 @@ function deleteConfirmDialog(items, type, context) {
|
||||
positiveText: "Delete",
|
||||
negativeText: "Cancel",
|
||||
positivePress: (value) => {
|
||||
console.log(value);
|
||||
resolve({ delete: true, deleteNotes: value });
|
||||
setTimeout(() => {
|
||||
resolve({ delete: true, deleteNotes: value });
|
||||
});
|
||||
},
|
||||
onClose: () => {
|
||||
resolve({ delete: false });
|
||||
setTimeout(() => {
|
||||
resolve({ delete: false });
|
||||
});
|
||||
},
|
||||
context: context,
|
||||
check: {
|
||||
@@ -158,7 +161,7 @@ export const deleteItems = async (item, context) => {
|
||||
}
|
||||
}
|
||||
|
||||
Navigation.queueRoutesForUpdate(...routesForUpdate);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
|
||||
let msgPart = history.selectedItemsList.length === 1 ? " item" : " items";
|
||||
let message = history.selectedItemsList.length + msgPart + " moved to trash.";
|
||||
@@ -182,7 +185,7 @@ export const deleteItems = async (item, context) => {
|
||||
}
|
||||
await db.trash.restore(...ids);
|
||||
|
||||
Navigation.queueRoutesForUpdate(routesForUpdate);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useMenuStore.getState().setMenuPins();
|
||||
useMenuStore.getState().setColorNotes();
|
||||
ToastEvent.hide();
|
||||
@@ -191,11 +194,12 @@ export const deleteItems = async (item, context) => {
|
||||
});
|
||||
}
|
||||
history.selectedItemsList = [];
|
||||
Navigation.queueRoutesForUpdate("Trash");
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useSelectionStore.getState().clearSelection(true);
|
||||
useMenuStore.getState().setMenuPins();
|
||||
useMenuStore.getState().setColorNotes();
|
||||
SearchService.updateAndSearch();
|
||||
eSendEvent(eOnTopicSheetUpdate);
|
||||
};
|
||||
|
||||
export const openLinkInBrowser = async (link) => {
|
||||
|
||||
@@ -145,19 +145,12 @@ export function getTotalNotes(item) {
|
||||
return db.notebooks.notebook(item.id)?.totalNotes || 0;
|
||||
}
|
||||
|
||||
export async function toTXT(note, notitle) {
|
||||
export async function toTXT(note, template = true) {
|
||||
let text;
|
||||
if (note.locked) {
|
||||
text = note.content.data;
|
||||
text = await db.notes.note(note.id).export("txt", note.content, template);
|
||||
} else {
|
||||
text = await db.notes.note(note.id).content();
|
||||
}
|
||||
htmlToText = htmlToText || require("html-to-text");
|
||||
text = htmlToText.convert(text, {
|
||||
selectors: [{ selector: "img", format: "skip" }]
|
||||
});
|
||||
if (!notitle) {
|
||||
text = `${note.title}\n \n ${text}`;
|
||||
text = await db.notes.note(note.id).export("txt", undefined, template);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
@@ -80,10 +80,10 @@ export const normalize = (size) => {
|
||||
}
|
||||
};
|
||||
export const SIZE = {
|
||||
xxs: normalize(10.5) * scale.fontScale,
|
||||
xs: normalize(12) * scale.fontScale,
|
||||
sm: normalize(14.5) * scale.fontScale,
|
||||
md: normalize(16) * scale.fontScale,
|
||||
xxs: normalize(11) * scale.fontScale,
|
||||
xs: normalize(12.5) * scale.fontScale,
|
||||
sm: normalize(15) * scale.fontScale,
|
||||
md: normalize(16.5) * scale.fontScale,
|
||||
lg: normalize(22) * scale.fontScale,
|
||||
xl: normalize(24) * scale.fontScale,
|
||||
xxl: normalize(28) * scale.fontScale,
|
||||
@@ -91,10 +91,10 @@ export const SIZE = {
|
||||
};
|
||||
|
||||
export function updateSize() {
|
||||
SIZE.xxs = normalize(10.5) * scale.fontScale;
|
||||
SIZE.xs = normalize(12) * scale.fontScale;
|
||||
SIZE.sm = normalize(14.5) * scale.fontScale;
|
||||
SIZE.md = normalize(16) * scale.fontScale;
|
||||
SIZE.xxs = normalize(11) * scale.fontScale;
|
||||
SIZE.xs = normalize(12.5) * scale.fontScale;
|
||||
SIZE.sm = normalize(15) * scale.fontScale;
|
||||
SIZE.md = normalize(16.5) * scale.fontScale;
|
||||
SIZE.lg = normalize(22) * scale.fontScale;
|
||||
SIZE.xl = normalize(24) * scale.fontScale;
|
||||
SIZE.xxl = normalize(28) * scale.fontScale;
|
||||
|
||||
@@ -55,12 +55,13 @@ describe("NOTE TESTS", () => {
|
||||
await prepare();
|
||||
let note = await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-Favorite");
|
||||
await tapById("icon-favorite");
|
||||
await visibleById("icon-star");
|
||||
await navigate("Favorites");
|
||||
await visibleByText(note.body);
|
||||
await sleep(500);
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-Favorite");
|
||||
await tapById("icon-favorite");
|
||||
await expect(element(by.text(note.body))).not.toBeVisible();
|
||||
await navigate("Notes");
|
||||
});
|
||||
@@ -69,11 +70,11 @@ describe("NOTE TESTS", () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-Pin");
|
||||
await tapById("icon-pin");
|
||||
await visibleByText("Pinned");
|
||||
await visibleById("icon-pinned");
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-Pin");
|
||||
await tapById("icon-pin");
|
||||
expect(element(by.id("icon-pinned"))).not.toBeVisible();
|
||||
});
|
||||
|
||||
@@ -81,11 +82,12 @@ describe("NOTE TESTS", () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-PinToNotif");
|
||||
await visibleByText("Unpin from Notifications");
|
||||
await tapById("icon-pin-to-notifications");
|
||||
await visibleByText("Unpin from notifications");
|
||||
await sleep(500);
|
||||
await tapById("icon-PinToNotif");
|
||||
await visibleByText("Pin to Notifications");
|
||||
await tapById("icon-pin-to-notifications");
|
||||
await sleep(500);
|
||||
await visibleByText("Pin to notifications");
|
||||
});
|
||||
|
||||
// it("Copy note", async () => {
|
||||
@@ -100,7 +102,7 @@ describe("NOTE TESTS", () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-Export");
|
||||
await tapById("icon-export");
|
||||
await visibleByText("PDF");
|
||||
});
|
||||
|
||||
@@ -122,7 +124,7 @@ describe("NOTE TESTS", () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-Delete");
|
||||
await tapById("icon-delete");
|
||||
await navigate("Trash");
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapByText("Restore note");
|
||||
|
||||
@@ -17,13 +17,15 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { notesnook } from "../test.ids";
|
||||
import {
|
||||
tapById,
|
||||
visibleByText,
|
||||
createNote,
|
||||
prepare,
|
||||
elementById,
|
||||
sleep
|
||||
sleep,
|
||||
tapByText
|
||||
} from "./utils";
|
||||
|
||||
describe("Search", () => {
|
||||
@@ -34,6 +36,10 @@ describe("Search", () => {
|
||||
await sleep(300);
|
||||
await elementById("search-input").typeText("n");
|
||||
await sleep(1000);
|
||||
await tapByText(note.body);
|
||||
await sleep(1000);
|
||||
await device.pressBack();
|
||||
await device.pressBack();
|
||||
await visibleByText(note.body);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -164,7 +164,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 2038
|
||||
versionCode 2043
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 32 KiB |
@@ -1,6 +1,6 @@
|
||||
- Top level notes in notebooks
|
||||
- Image fullscreen preview & download from editor
|
||||
- Change auto clear trash interval from settings
|
||||
- Focus cursor on end when tapping bottom empty area in editor.
|
||||
- Fixed text exports not following line spacing settings
|
||||
- Fixed tags are not updated in list after renaming
|
||||
- Fixed a sync issue where notes marked as local only would get deleted sometimes
|
||||
- Fixed a bug where opening a note from search then closing it would cause a crash
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -33,6 +33,8 @@
|
||||
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, ); }; };
|
||||
65B5020325A6756700E2D264 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65B5020225A6756700E2D264 /* File.swift */; };
|
||||
65D145C529DC30470056FE7D /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 65D145C429DC30470056FE7D /* MaterialCommunityIcons.ttf */; };
|
||||
65D145C629DC30470056FE7D /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 65D145C429DC30470056FE7D /* MaterialCommunityIcons.ttf */; };
|
||||
65E0340B257B9FF100793428 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E0340A257B9FF100793428 /* File.swift */; };
|
||||
7C72B5EA418785B334F3B5EB /* libPods-Notesnook-Make Note.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35547D7C07E83F889159A73D /* libPods-Notesnook-Make Note.a */; };
|
||||
87CE5B71DC72F3D95B7B1AA5 /* libPods-Notesnook-tvOSTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A03E3F5851D27269540E147C /* libPods-Notesnook-tvOSTests.a */; };
|
||||
@@ -129,6 +131,7 @@
|
||||
65B501AA25A6733400E2D264 /* Make Note.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Make Note.entitlements"; sourceTree = "<group>"; };
|
||||
65B5020125A6756700E2D264 /* Make Note-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Make Note-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
65B5020225A6756700E2D264 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = "<group>"; };
|
||||
65D145C429DC30470056FE7D /* MaterialCommunityIcons.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = MaterialCommunityIcons.ttf; path = "../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf"; sourceTree = "<group>"; };
|
||||
65E03409257B9FF100793428 /* Notesnook-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Notesnook-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
65E0340A257B9FF100793428 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = "<group>"; };
|
||||
65EC5B71272A7EE200FB3748 /* NotesWidgetExtensionDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotesWidgetExtensionDebug.entitlements; sourceTree = "<group>"; };
|
||||
@@ -292,6 +295,7 @@
|
||||
65EC5B71272A7EE200FB3748 /* NotesWidgetExtensionDebug.entitlements */,
|
||||
6552012F27019F7700A43C51 /* OpenSans-Regular.ttf */,
|
||||
6552012E27019F6E00A43C51 /* OpenSans-SemiBold.ttf */,
|
||||
65D145C429DC30470056FE7D /* MaterialCommunityIcons.ttf */,
|
||||
13B07FAE1A68108700A75B9A /* Notesnook */,
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
00E356EF1AD99517003FC87E /* NotesnookTests */,
|
||||
@@ -533,6 +537,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
6510626F27042891009661C3 /* OpenSans-Regular.ttf in Resources */,
|
||||
65D145C529DC30470056FE7D /* MaterialCommunityIcons.ttf in Resources */,
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||
6529A13E279BC4C70048D4A8 /* BootSplash.storyboard in Resources */,
|
||||
6510E6D72877215700DACAA9 /* build.bundle in Resources */,
|
||||
@@ -569,6 +574,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
6510E6D82877215700DACAA9 /* build.bundle in Resources */,
|
||||
65D145C629DC30470056FE7D /* MaterialCommunityIcons.ttf in Resources */,
|
||||
6510627727042896009661C3 /* OpenSans-SemiBold.ttf in Resources */,
|
||||
6510627527042893009661C3 /* OpenSans-Regular.ttf in Resources */,
|
||||
65B5014725A672B200E2D264 /* MainInterface.storyboard in Resources */,
|
||||
@@ -621,42 +627,10 @@
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Notesnook/Pods-Notesnook-resources.sh",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -770,42 +744,10 @@
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Notesnook-NotesnookTests/Pods-Notesnook-NotesnookTests-resources.sh",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -842,42 +784,10 @@
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Notesnook-Make Note/Pods-Notesnook-Make Note-resources.sh",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
|
||||
"${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -1083,7 +993,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2030;
|
||||
CURRENT_PROJECT_VERSION = 2035;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1157,7 +1067,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.7;
|
||||
MARKETING_VERSION = 2.4.12;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1187,7 +1097,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Notesnook/Notesnook.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2030;
|
||||
CURRENT_PROJECT_VERSION = 2035;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
@@ -1260,7 +1170,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.7;
|
||||
MARKETING_VERSION = 2.4.12;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1418,7 +1328,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2030;
|
||||
CURRENT_PROJECT_VERSION = 2035;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1430,7 +1340,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.7;
|
||||
MARKETING_VERSION = 2.4.12;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1460,7 +1370,7 @@
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2030;
|
||||
CURRENT_PROJECT_VERSION = 2035;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1472,7 +1382,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.7;
|
||||
MARKETING_VERSION = 2.4.12;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1501,7 +1411,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2030;
|
||||
CURRENT_PROJECT_VERSION = 2035;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1575,7 +1485,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.7;
|
||||
MARKETING_VERSION = 2.4.12;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1605,7 +1515,7 @@
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2030;
|
||||
CURRENT_PROJECT_VERSION = 2035;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1679,7 +1589,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.4.7;
|
||||
MARKETING_VERSION = 2.4.12;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -418,8 +418,6 @@ PODS:
|
||||
- pop (~> 1.0)
|
||||
- React
|
||||
- SexyTooltip
|
||||
- RNVectorIcons (9.2.0):
|
||||
- React-Core
|
||||
- SexyTooltip (1.2.5):
|
||||
- pop (~> 1.0)
|
||||
- toolbar-android (0.2.1):
|
||||
@@ -504,7 +502,6 @@ DEPENDENCIES:
|
||||
- RNShare (from `../../node_modules/react-native-share`)
|
||||
- RNSVG (from `../../node_modules/react-native-svg`)
|
||||
- RNTooltips (from `../../node_modules/react-native-tooltips`)
|
||||
- RNVectorIcons (from `../../node_modules/react-native-vector-icons`)
|
||||
- SexyTooltip (from `https://github.com/ammarahm-ed/SexyTooltip.git`)
|
||||
- "toolbar-android (from `../../node_modules/@react-native-community/toolbar-android`)"
|
||||
- Yoga (from `../../node_modules/react-native/ReactCommon/yoga`)
|
||||
@@ -667,8 +664,6 @@ EXTERNAL SOURCES:
|
||||
:path: "../../node_modules/react-native-svg"
|
||||
RNTooltips:
|
||||
:path: "../../node_modules/react-native-tooltips"
|
||||
RNVectorIcons:
|
||||
:path: "../../node_modules/react-native-vector-icons"
|
||||
SexyTooltip:
|
||||
:git: https://github.com/ammarahm-ed/SexyTooltip.git
|
||||
toolbar-android:
|
||||
@@ -762,7 +757,6 @@ SPEC CHECKSUMS:
|
||||
RNShare: a5dc3b9c53ddc73e155b8cd9a94c70c91913c43c
|
||||
RNSVG: ecd661f380a07ba690c9c5929c475a44f432d674
|
||||
RNTooltips: 5424d4bf0b3d441104127943b1115cc7f0616b1f
|
||||
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
|
||||
SexyTooltip: 5c9b4dec52bfb317938cb0488efd9da3717bb6fd
|
||||
toolbar-android: 2a73856e98b750d7e71ce4644d3f41cc98211719
|
||||
Yoga: 0b84a956f7393ef1f37f3bb213c516184e4a689d
|
||||
|
||||
@@ -57,7 +57,9 @@
|
||||
"@react-native-community/datetimepicker":"6.6.0",
|
||||
"react-native-date-picker": "4.2.6",
|
||||
"react-native-notification-sounds": "0.5.5",
|
||||
"@bam.tech/react-native-image-resizer": "3.0.5"
|
||||
"@bam.tech/react-native-image-resizer": "3.0.5",
|
||||
"react-native-navigation-bar-color": "2.0.2",
|
||||
"react-native-actions-shortcuts": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.9",
|
||||
|
||||
@@ -9,15 +9,19 @@ const config = {
|
||||
}
|
||||
};
|
||||
|
||||
if (!config.dependencies) config.dependencies = {};
|
||||
config.dependencies['react-native-vector-icons'] = {
|
||||
platforms: {
|
||||
ios: null,
|
||||
},
|
||||
}
|
||||
|
||||
if (isGithubRelease) {
|
||||
config.dependencies = {
|
||||
"react-native-iap": {
|
||||
platforms: {
|
||||
android:null
|
||||
}
|
||||
},
|
||||
config.dependencies["react-native-iap"] = {
|
||||
platforms: {
|
||||
android:null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = config;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
- Top level notes in notebooks
|
||||
- Image fullscreen preview & download from editor
|
||||
- Change auto clear trash interval from settings
|
||||
- Fixed image upload not working since last update
|
||||
- Fixed adding images to notes via copy/paste from other apps
|
||||
- Focus cursor on end when tapping bottom empty area in editor.
|
||||
- Fixed iPad orientation change editor resizing
|
||||
- Fixed text exports not following line spacing settings
|
||||
- Fixed tags are not updated in list after renaming
|
||||
- Fixed a sync issue where notes marked as local only would get deleted sometimes
|
||||
- Fixed a bug where opening a note from search then closing it would cause a crash
|
||||
|
||||
Thank you for using Notesnook!
|
||||
47
apps/mobile/package-lock.json
generated
@@ -1,22 +1,20 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "2.4.7",
|
||||
"version": "2.4.12",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "2.4.7",
|
||||
"version": "2.4.12",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
"native/",
|
||||
"app/"
|
||||
],
|
||||
"dependencies": {
|
||||
"fflate": "^0.7.3",
|
||||
"react-native-actions-shortcuts": "^1.0.1",
|
||||
"react-native-fingerprint-scanner": "https://github.com/ammarahm-ed/react-native-fingerprint-scanner.git",
|
||||
"react-native-iap": "7.5.6"
|
||||
"react": "18.0.0",
|
||||
"react-native": "0.69.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"otplib": "12.0.1",
|
||||
@@ -40,9 +38,7 @@
|
||||
"html-to-text": "8.1.0",
|
||||
"phone": "^3.1.14",
|
||||
"qclone": "^1.2.0",
|
||||
"react": "18.0.0",
|
||||
"react-native": "0.69.7",
|
||||
"react-native-actions-sheet": "^0.9.0-alpha.12",
|
||||
"react-native-actions-sheet": "0.9.0-alpha.18",
|
||||
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
|
||||
"react-native-drax": "^0.10.2",
|
||||
"react-native-image-zoom-viewer": "^3.0.1",
|
||||
@@ -80,6 +76,7 @@
|
||||
"deprecated-react-native-prop-types": "^2.3.0",
|
||||
"react": "18.0.0",
|
||||
"react-native": "0.69.7",
|
||||
"react-native-actions-shortcuts": "^1.0.1",
|
||||
"react-native-background-actions": "^2.6.6",
|
||||
"react-native-begin-background-task": "https://github.com/blockfirm/react-native-begin-background-task.git",
|
||||
"react-native-bootsplash": "^4.1.4",
|
||||
@@ -100,6 +97,7 @@
|
||||
"react-native-keychain": "4.0.5",
|
||||
"react-native-mmkv-storage": "^0.8.0",
|
||||
"react-native-modal-datetime-picker": "14.0.0",
|
||||
"react-native-navigation-bar-color": "2.0.2",
|
||||
"react-native-notification-sounds": "0.5.5",
|
||||
"react-native-orientation": "https://github.com/yamill/react-native-orientation.git",
|
||||
"react-native-privacy-snapshot": "https://github.com/standardnotes/react-native-privacy-snapshot.git",
|
||||
@@ -17957,9 +17955,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-actions-sheet": {
|
||||
"version": "0.9.0-alpha.12",
|
||||
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.12.tgz",
|
||||
"integrity": "sha512-VyXsfOFkKCBWB6jn+IlKUTo0KWhItkRC8f6RMvx/RYc5WRhj4Ka5O7/BZVDs86ELJkUT08D7ufzWPjmOQ8hUKw==",
|
||||
"version": "0.9.0-alpha.18",
|
||||
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.18.tgz",
|
||||
"integrity": "sha512-X3PBVXyI7DQMfa1p3TbEFY2UjAUGqwl9ej6HxZ6ohm4KacqmGoDUZKLNz9lKCzw0vYeK8lwxtCC7qDSuEEgGQg==",
|
||||
"peerDependencies": {
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": "*"
|
||||
@@ -17969,6 +17967,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-actions-shortcuts/-/react-native-actions-shortcuts-1.0.1.tgz",
|
||||
"integrity": "sha512-oAVi15d4Y3YygsObRzPVcV6ZYPbPb25iHTeO4UYNwYVnW8VtpguOj6d4u6kZ7dh9c7fQvyCSf1gEyLwqydlYqg==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
@@ -18418,6 +18417,11 @@
|
||||
"react-native": ">=0.65.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-navigation-bar-color": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-navigation-bar-color/-/react-native-navigation-bar-color-2.0.2.tgz",
|
||||
"integrity": "sha512-ZmpLWRocyme1au11e5ZuecMS/UCi57nlzgnioi03Q6ERMbeUOqqbWgNBaNB7SsCeqBV6fZPjo3+A64zEIpzw4w=="
|
||||
},
|
||||
"node_modules/react-native-notification-sounds": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native-notification-sounds/-/react-native-notification-sounds-0.5.5.tgz",
|
||||
@@ -24310,9 +24314,7 @@
|
||||
"html-to-text": "8.1.0",
|
||||
"phone": "^3.1.14",
|
||||
"qclone": "^1.2.0",
|
||||
"react": "18.0.0",
|
||||
"react-native": "0.69.7",
|
||||
"react-native-actions-sheet": "^0.9.0-alpha.12",
|
||||
"react-native-actions-sheet": "0.9.0-alpha.18",
|
||||
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
|
||||
"react-native-drax": "^0.10.2",
|
||||
"react-native-image-zoom-viewer": "^3.0.1",
|
||||
@@ -24401,6 +24403,7 @@
|
||||
"react-native-keychain": "4.0.5",
|
||||
"react-native-mmkv-storage": "^0.8.0",
|
||||
"react-native-modal-datetime-picker": "14.0.0",
|
||||
"react-native-navigation-bar-color": "2.0.2",
|
||||
"react-native-notification-sounds": "0.5.5",
|
||||
"react-native-orientation": "https://github.com/yamill/react-native-orientation.git",
|
||||
"react-native-privacy-snapshot": "https://github.com/standardnotes/react-native-privacy-snapshot.git",
|
||||
@@ -34856,14 +34859,15 @@
|
||||
}
|
||||
},
|
||||
"react-native-actions-sheet": {
|
||||
"version": "0.9.0-alpha.12",
|
||||
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.12.tgz",
|
||||
"integrity": "sha512-VyXsfOFkKCBWB6jn+IlKUTo0KWhItkRC8f6RMvx/RYc5WRhj4Ka5O7/BZVDs86ELJkUT08D7ufzWPjmOQ8hUKw=="
|
||||
"version": "0.9.0-alpha.18",
|
||||
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.18.tgz",
|
||||
"integrity": "sha512-X3PBVXyI7DQMfa1p3TbEFY2UjAUGqwl9ej6HxZ6ohm4KacqmGoDUZKLNz9lKCzw0vYeK8lwxtCC7qDSuEEgGQg=="
|
||||
},
|
||||
"react-native-actions-shortcuts": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-actions-shortcuts/-/react-native-actions-shortcuts-1.0.1.tgz",
|
||||
"integrity": "sha512-oAVi15d4Y3YygsObRzPVcV6ZYPbPb25iHTeO4UYNwYVnW8VtpguOj6d4u6kZ7dh9c7fQvyCSf1gEyLwqydlYqg=="
|
||||
"integrity": "sha512-oAVi15d4Y3YygsObRzPVcV6ZYPbPb25iHTeO4UYNwYVnW8VtpguOj6d4u6kZ7dh9c7fQvyCSf1gEyLwqydlYqg==",
|
||||
"dev": true
|
||||
},
|
||||
"react-native-background-actions": {
|
||||
"version": "2.6.7",
|
||||
@@ -35172,6 +35176,11 @@
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"react-native-navigation-bar-color": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-navigation-bar-color/-/react-native-navigation-bar-color-2.0.2.tgz",
|
||||
"integrity": "sha512-ZmpLWRocyme1au11e5ZuecMS/UCi57nlzgnioi03Q6ERMbeUOqqbWgNBaNB7SsCeqBV6fZPjo3+A64zEIpzw4w=="
|
||||
},
|
||||
"react-native-notification-sounds": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native-notification-sounds/-/react-native-notification-sounds-0.5.5.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "2.4.7",
|
||||
"version": "2.4.12",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
@@ -28,12 +28,10 @@
|
||||
"otplib": "12.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "18.0.0",
|
||||
"react-native": "0.69.7",
|
||||
"@notesnook/core": "*",
|
||||
"@notesnook/editor": "*",
|
||||
"@notesnook/editor-mobile": "*",
|
||||
"fflate": "^0.7.3",
|
||||
"react-native-actions-shortcuts": "^1.0.1",
|
||||
"react-native-fingerprint-scanner": "https://github.com/ammarahm-ed/react-native-fingerprint-scanner.git",
|
||||
"react-native-iap": "7.5.6"
|
||||
"@notesnook/editor-mobile": "*"
|
||||
}
|
||||
}
|
||||
3748
apps/mobile/patches/react-native-navigation-bar-color+2.0.2.patch
Normal file
@@ -17,53 +17,217 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { FlashList } from "@shopify/flash-list";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
FlatList,
|
||||
Platform,
|
||||
StatusBar,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
useWindowDimensions,
|
||||
View
|
||||
View,
|
||||
useWindowDimensions
|
||||
} from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../app/common/database";
|
||||
import { getElevation } from "../app/utils";
|
||||
import { useShareStore } from "./store";
|
||||
import { initDatabase, useShareStore } from "./store";
|
||||
|
||||
export const Search = ({ close, getKeyboardHeight, quicknote }) => {
|
||||
const ListItem = ({ item, mode, close }) => {
|
||||
const colors = useShareStore((state) => state.colors);
|
||||
const setAppendNote = useShareStore((state) => state.setAppendNote);
|
||||
const [results, setResults] = useState([]);
|
||||
const [searching, setSearching] = useState(false);
|
||||
const searchKeyword = useRef(null);
|
||||
const selectedNotebooks = useShareStore((state) => state.selectedNotebooks);
|
||||
const selectedTags = useShareStore((state) => state.selectedTags);
|
||||
const isSelected =
|
||||
mode === "appendNote"
|
||||
? false
|
||||
: mode === "selectTags"
|
||||
? selectedTags.indexOf(item.alias) > -1
|
||||
: selectedNotebooks.findIndex(
|
||||
(selected) => selected.id === item.id && selected.type === item.type
|
||||
) > -1;
|
||||
|
||||
const SearchSetters = React.useMemo(
|
||||
() => ({
|
||||
appendNote: (item) => {
|
||||
useShareStore.getState().setAppendNote(item);
|
||||
close();
|
||||
},
|
||||
selectNotebooks: (item) => {
|
||||
const selectedNotebooks = [
|
||||
...useShareStore.getState().selectedNotebooks
|
||||
];
|
||||
const currentIndex = selectedNotebooks.findIndex(
|
||||
(selected) => selected.id === item.id && selected.type === item.type
|
||||
);
|
||||
if (currentIndex === -1) {
|
||||
selectedNotebooks.push(item);
|
||||
} else {
|
||||
selectedNotebooks.splice(currentIndex, 1);
|
||||
}
|
||||
useShareStore.getState().setSelectedNotebooks(selectedNotebooks);
|
||||
},
|
||||
selectTags: (item) => {
|
||||
const selectedTags = [...useShareStore.getState().selectedTags];
|
||||
const currentIndex = selectedTags.indexOf(item.alias);
|
||||
if (currentIndex === -1) {
|
||||
selectedTags.push(item.alias);
|
||||
} else {
|
||||
selectedTags.splice(currentIndex, 1);
|
||||
}
|
||||
useShareStore.getState().setSelectedTags(selectedTags);
|
||||
}
|
||||
}),
|
||||
[close]
|
||||
);
|
||||
const set = React.useMemo(() => SearchSetters[mode], [mode, SearchSetters]);
|
||||
|
||||
const onSelectItem = async (item) => {
|
||||
if (item.locked) {
|
||||
return;
|
||||
}
|
||||
set(item);
|
||||
};
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
onPress={() => onSelectItem(item)}
|
||||
style={{
|
||||
flexDirection: "column",
|
||||
borderBottomWidth: item.topics?.length > 0 ? 0 : 1,
|
||||
borderBottomColor: colors.nav,
|
||||
justifyContent: "center",
|
||||
paddingVertical: 12
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 12,
|
||||
paddingLeft: item.type === "topic" ? 0 : 12
|
||||
}}
|
||||
>
|
||||
{item.type !== "note" ? (
|
||||
<Icon
|
||||
name={
|
||||
!isSelected
|
||||
? "checkbox-blank-circle-outline"
|
||||
: "check-circle-outline"
|
||||
}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
size={20}
|
||||
color={isSelected ? colors.accent : colors.icon}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "column"
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={{
|
||||
color: colors.pri,
|
||||
fontFamily:
|
||||
item.type === "topic"
|
||||
? "OpenSans-Regular"
|
||||
: "OpenSans-SemiBold",
|
||||
fontSize: 15
|
||||
}}
|
||||
>
|
||||
{item.type === "tag" ? "#" : ""}
|
||||
{item.alias || item.title}
|
||||
</Text>
|
||||
|
||||
{item.type === "note" ? (
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={{
|
||||
color: colors.icon,
|
||||
fontSize: 12,
|
||||
fontFamily: "OpenSans-Regular"
|
||||
}}
|
||||
>
|
||||
{item.headline}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{item.type === "notebook" && item.topics && item.topics.length > 0 ? (
|
||||
<View
|
||||
style={{
|
||||
paddingLeft: 40,
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
{item.topics.map((topic) => (
|
||||
<ListItem key={topic.id} item={topic} mode={mode} close={close} />
|
||||
))}
|
||||
</View>
|
||||
) : null}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const SearchGetters = {
|
||||
appendNote: () => db.notes.all,
|
||||
selectNotebooks: () => db.notebooks.all,
|
||||
selectTags: () => {
|
||||
const selected = useShareStore.getState().selectedTags;
|
||||
const tags = [];
|
||||
if (selected)
|
||||
tags.push(
|
||||
...selected.map((t) => ({
|
||||
alias: t,
|
||||
type: "tag"
|
||||
}))
|
||||
);
|
||||
|
||||
for (let tag of db.tags.all) {
|
||||
const index = tags.findIndex((t) => t.alias === tag.alias);
|
||||
// Skip selected tags as they are already in the list.
|
||||
if (index > -1) continue;
|
||||
tags.push(tag);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
};
|
||||
|
||||
const SearchLookup = {
|
||||
appendNote: (items, kwd) => db.lookup.notes(items, kwd),
|
||||
selectNotebooks: (items, kwd) => db.lookup.notebooks(items, kwd),
|
||||
selectTags: (items, kwd) => db.lookup.tags(items, kwd)
|
||||
};
|
||||
|
||||
export const Search = ({ close, getKeyboardHeight, quicknote, mode }) => {
|
||||
const colors = useShareStore((state) => state.colors);
|
||||
|
||||
const { height } = useWindowDimensions();
|
||||
const notes = useRef(null);
|
||||
const timer = useRef(null);
|
||||
const inputRef = useRef();
|
||||
|
||||
const [searchResults, setSearchResults] = useState([]);
|
||||
const searchableItems = useRef(null);
|
||||
const searchKeyword = useRef(null);
|
||||
|
||||
const insets =
|
||||
Platform.OS === "android"
|
||||
? { top: StatusBar.currentHeight }
|
||||
: // eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useSafeAreaInsets();
|
||||
|
||||
const onSelectItem = async (item) => {
|
||||
if (item.locked) {
|
||||
return;
|
||||
}
|
||||
setAppendNote(item);
|
||||
close();
|
||||
};
|
||||
const get = SearchGetters[mode];
|
||||
const lookup = SearchLookup[mode];
|
||||
|
||||
const onSearch = async () => {
|
||||
if (!notes.current) {
|
||||
await db.init();
|
||||
await db.notes.init();
|
||||
notes.current = db.notes.all;
|
||||
if (!searchableItems.current) {
|
||||
await initDatabase();
|
||||
searchableItems.current = get();
|
||||
}
|
||||
if (timer.current) {
|
||||
clearTimeout(timer.current);
|
||||
@@ -71,61 +235,26 @@ export const Search = ({ close, getKeyboardHeight, quicknote }) => {
|
||||
}
|
||||
timer.current = setTimeout(async () => {
|
||||
if (!searchKeyword.current) {
|
||||
setResults([]);
|
||||
setResults(db.notes.all);
|
||||
setSearchResults([]);
|
||||
setSearchResults(get());
|
||||
return;
|
||||
}
|
||||
setSearching(true);
|
||||
setResults(await db.lookup.notes(notes.current, searchKeyword.current));
|
||||
setSearching(false);
|
||||
}, 500);
|
||||
setSearchResults(
|
||||
await lookup(searchableItems.current, searchKeyword.current)
|
||||
);
|
||||
}, 150);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await db.init();
|
||||
await db.notes.init();
|
||||
notes.current = db.notes.all;
|
||||
setResults(notes.current);
|
||||
await initDatabase();
|
||||
searchableItems.current = get();
|
||||
setSearchResults(searchableItems.current);
|
||||
})();
|
||||
setTimeout(() => {
|
||||
inputRef.current?.focus();
|
||||
}, 300);
|
||||
}, []);
|
||||
}, [get]);
|
||||
|
||||
const renderItem = ({ item }) =>
|
||||
!item.locked ? (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
onPress={() => onSelectItem(item)}
|
||||
style={{
|
||||
height: 50,
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={{
|
||||
color: colors.pri,
|
||||
fontFamily: "OpenSans-SemiBold",
|
||||
fontSize: 15
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</Text>
|
||||
|
||||
<Text
|
||||
numberOfLines={1}
|
||||
style={{
|
||||
color: colors.icon,
|
||||
fontSize: 12,
|
||||
fontFamily: "OpenSans-Regular"
|
||||
}}
|
||||
>
|
||||
{item.headline}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
) : null;
|
||||
!item.locked ? <ListItem item={item} mode={mode} close={close} /> : null;
|
||||
|
||||
let extra = quicknote
|
||||
? {
|
||||
@@ -133,6 +262,7 @@ export const Search = ({ close, getKeyboardHeight, quicknote }) => {
|
||||
paddingTop: insets.top
|
||||
}
|
||||
: {};
|
||||
const searchHeight = height - getKeyboardHeight();
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
@@ -160,7 +290,15 @@ export const Search = ({ close, getKeyboardHeight, quicknote }) => {
|
||||
height: 50
|
||||
}}
|
||||
>
|
||||
<Icon name="arrow-left" color={colors.pri} size={25} onPress={close} />
|
||||
<Icon
|
||||
name="arrow-left"
|
||||
color={colors.pri}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
size={25}
|
||||
onPress={close}
|
||||
/>
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
placeholder="Search for a note"
|
||||
@@ -168,58 +306,76 @@ export const Search = ({ close, getKeyboardHeight, quicknote }) => {
|
||||
style={{
|
||||
fontSize: 15,
|
||||
fontFamily: "OpenSans-Regular",
|
||||
width: "85%"
|
||||
flex: 1
|
||||
}}
|
||||
onChangeText={(value) => {
|
||||
searchKeyword.current = value;
|
||||
if (mode === "selectTags") {
|
||||
searchKeyword.current = db.tags.sanitize(value);
|
||||
} else {
|
||||
searchKeyword.current = value;
|
||||
}
|
||||
onSearch();
|
||||
}}
|
||||
onSubmitEditing={onSearch}
|
||||
/>
|
||||
{searching ? (
|
||||
<ActivityIndicator size={25} color={colors.icon} />
|
||||
) : (
|
||||
<Icon
|
||||
name="magnify"
|
||||
color={colors.pri}
|
||||
size={25}
|
||||
onPress={onSearch}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Icon name="magnify" color={colors.pri} size={25} onPress={onSearch} />
|
||||
</View>
|
||||
|
||||
<FlatList
|
||||
data={results}
|
||||
<View
|
||||
style={{
|
||||
maxHeight: height - getKeyboardHeight()
|
||||
maxHeight: searchHeight > 550 ? 550 : searchHeight,
|
||||
height: searchHeight > 550 ? 550 : searchHeight
|
||||
}}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="none"
|
||||
keyExtractor={(item) => item.id}
|
||||
renderItem={renderItem}
|
||||
ListFooterComponent={<View style={{ height: 200 }} />}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 200
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
>
|
||||
<FlashList
|
||||
data={searchResults}
|
||||
keyboardShouldPersistTaps="always"
|
||||
keyboardDismissMode="none"
|
||||
renderItem={renderItem}
|
||||
estimatedItemSize={50}
|
||||
ListHeaderComponent={
|
||||
mode === "selectTags" &&
|
||||
(searchResults.length === 0 ||
|
||||
(searchKeyword.current &&
|
||||
searchKeyword.current.length > 0 &&
|
||||
!searchResults.find(
|
||||
(item) => item.title === searchKeyword.current
|
||||
))) ? (
|
||||
<ListItem
|
||||
item={{
|
||||
type: "tag",
|
||||
alias: searchKeyword.current
|
||||
}}
|
||||
mode={mode}
|
||||
close={close}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
ListFooterComponent={<View style={{ height: 200 }} />}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
style={{
|
||||
fontFamily: "OpenSans-Regular",
|
||||
color: colors.icon
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 200
|
||||
}}
|
||||
>
|
||||
{searchKeyword.current
|
||||
? `No results found for "${searchKeyword.current}"`
|
||||
: "Search for a note to append to it."}
|
||||
</Text>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
fontFamily: "OpenSans-Regular",
|
||||
color: colors.icon
|
||||
}}
|
||||
>
|
||||
{searchKeyword.current
|
||||
? `No results found for "${searchKeyword.current}"`
|
||||
: "Search for a note to append to it."}
|
||||
</Text>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -30,7 +30,8 @@ import {
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
useWindowDimensions,
|
||||
View
|
||||
View,
|
||||
ScrollView
|
||||
} from "react-native";
|
||||
import {
|
||||
SafeAreaProvider,
|
||||
@@ -46,7 +47,7 @@ import { getElevation } from "../app/utils";
|
||||
import { eOnLoadNote } from "../app/utils/events";
|
||||
import { sleep } from "../app/utils/time";
|
||||
import { Search } from "./search";
|
||||
import { useShareStore } from "./store";
|
||||
import { initDatabase, useShareStore } from "./store";
|
||||
import { Editor } from "./editor";
|
||||
const getLinkPreview = (url) => {
|
||||
return getPreviewData(url, 5000);
|
||||
@@ -194,7 +195,6 @@ const ShareView = ({ quicknote = false }) => {
|
||||
const appendNote = useShareStore((state) => state.appendNote);
|
||||
const [note, setNote] = useState({ ...defaultNote });
|
||||
const noteContent = useRef();
|
||||
const [loadingIntent, setLoadingIntent] = useState(true);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [loadingExtension, setLoadingExtension] = useState(true);
|
||||
const [rawData, setRawData] = useState({
|
||||
@@ -209,9 +209,9 @@ const ShareView = ({ quicknote = false }) => {
|
||||
? { top: StatusBar.currentHeight }
|
||||
: // eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useSafeAreaInsets();
|
||||
const [showSearch, setShowSearch] = useState(false);
|
||||
const [searchMode, setSearchMode] = useState(null);
|
||||
|
||||
const [kh, setKh] = useState(0);
|
||||
const editorRef = useRef();
|
||||
|
||||
const onKeyboardDidShow = (event) => {
|
||||
let kHeight = event.endCoordinates.height;
|
||||
@@ -261,7 +261,6 @@ const ShareView = ({ quicknote = false }) => {
|
||||
setRawData({
|
||||
value: ""
|
||||
});
|
||||
setLoadingIntent(false);
|
||||
return;
|
||||
}
|
||||
let note = { ...defaultNote };
|
||||
@@ -281,7 +280,6 @@ const ShareView = ({ quicknote = false }) => {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
setLoadingIntent(false);
|
||||
}, [onLoad]);
|
||||
|
||||
const onLoad = useCallback(() => {
|
||||
@@ -300,13 +298,12 @@ const ShareView = ({ quicknote = false }) => {
|
||||
//await loadDatabase();
|
||||
setLoadingExtension(false);
|
||||
loadData();
|
||||
useShareStore.getState().restoreAppendNote();
|
||||
useShareStore.getState().restore();
|
||||
})();
|
||||
}, [loadData]);
|
||||
|
||||
const close = async () => {
|
||||
setNote({ ...defaultNote });
|
||||
setLoadingIntent(true);
|
||||
setLoadingExtension(true);
|
||||
if (quicknote) {
|
||||
ShareExtension.openURL("ShareMedia://MainApp");
|
||||
@@ -317,8 +314,7 @@ const ShareView = ({ quicknote = false }) => {
|
||||
|
||||
const onPress = async () => {
|
||||
setLoading(true);
|
||||
await db.init();
|
||||
await db.notes.init();
|
||||
await initDatabase();
|
||||
await sleep(1500);
|
||||
if (!noteContent.current) return;
|
||||
if (appendNote && !db.notes.note(appendNote.id)) {
|
||||
@@ -332,7 +328,7 @@ const ShareView = ({ quicknote = false }) => {
|
||||
let raw = await db.content.raw(appendNote.contentId);
|
||||
_note = {
|
||||
content: {
|
||||
data: raw.data + "\n" + noteContent.current,
|
||||
data: raw.data + noteContent.current,
|
||||
type: "tiptap"
|
||||
},
|
||||
id: appendNote.id,
|
||||
@@ -340,10 +336,28 @@ const ShareView = ({ quicknote = false }) => {
|
||||
};
|
||||
} else {
|
||||
_note = { ...note };
|
||||
_note.tags = useShareStore.getState().selectedTags || [];
|
||||
|
||||
_note.content.data = noteContent.current;
|
||||
_note.sessionId = Date.now();
|
||||
}
|
||||
await db.notes.add(_note);
|
||||
|
||||
let id = await db.notes.add(_note);
|
||||
if (!appendNote) {
|
||||
for (const item of useShareStore.getState().selectedNotebooks) {
|
||||
if (item.type === "notebook") {
|
||||
db.relations.add(item, { id, type: "note" });
|
||||
} else {
|
||||
db.notes.addToNotebook(
|
||||
{
|
||||
id: item.notebookId,
|
||||
topic: item.id
|
||||
},
|
||||
id
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
await Storage.write("notesAddedFromIntent", "added");
|
||||
close();
|
||||
setLoading(false);
|
||||
@@ -389,6 +403,8 @@ const ShareView = ({ quicknote = false }) => {
|
||||
loadData();
|
||||
}, [loadData]);
|
||||
|
||||
const WrapperView = Platform.OS === "android" ? View : ScrollView;
|
||||
|
||||
return loadingExtension ? null : (
|
||||
<SafeAreaView
|
||||
style={{
|
||||
@@ -398,7 +414,7 @@ const ShareView = ({ quicknote = false }) => {
|
||||
justifyContent: quicknote ? "flex-start" : "flex-end"
|
||||
}}
|
||||
>
|
||||
{quicknote && !showSearch ? (
|
||||
{quicknote && !searchMode ? (
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
@@ -417,8 +433,8 @@ const ShareView = ({ quicknote = false }) => {
|
||||
icon="close"
|
||||
iconColor={colors.pri}
|
||||
onPress={() => {
|
||||
if (showSearch) {
|
||||
setShowSearch(false);
|
||||
if (searchMode) {
|
||||
setSearchMode(null);
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
@@ -458,8 +474,8 @@ const ShareView = ({ quicknote = false }) => {
|
||||
<TouchableOpacity
|
||||
activeOpacity={1}
|
||||
onPress={() => {
|
||||
if (showSearch) {
|
||||
setShowSearch(false);
|
||||
if (searchMode) {
|
||||
setSearchMode(null);
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
@@ -482,28 +498,31 @@ const ShareView = ({ quicknote = false }) => {
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
||||
{showSearch ? (
|
||||
{searchMode ? (
|
||||
<Search
|
||||
quicknote={quicknote}
|
||||
getKeyboardHeight={() => keyboardHeight.current}
|
||||
mode={searchMode}
|
||||
close={() => {
|
||||
setShowSearch(false);
|
||||
setSearchMode(null);
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<View
|
||||
<WrapperView
|
||||
style={{
|
||||
paddingVertical: 25,
|
||||
backgroundColor: "transparent",
|
||||
marginBottom: insets.top,
|
||||
display: showSearch ? "none" : "flex"
|
||||
paddingVertical: 12,
|
||||
backgroundColor: colors.bg,
|
||||
display: searchMode ? "none" : "flex",
|
||||
borderTopRightRadius: Platform.OS === "ios" ? 10 : 15,
|
||||
borderTopLeftRadius: Platform.OS === "ios" ? 10 : 15,
|
||||
maxHeight: Platform.OS === "android" ? undefined : "100%"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
maxHeight: "100%",
|
||||
paddingHorizontal: 12
|
||||
maxHeight: "100%"
|
||||
//paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
<View
|
||||
@@ -513,20 +532,56 @@ const ShareView = ({ quicknote = false }) => {
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
marginTop: 10,
|
||||
minHeight: 100,
|
||||
borderRadius: 10,
|
||||
...getElevation(quicknote ? 1 : 5),
|
||||
backgroundColor: colors.bg,
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
justifyContent: "space-between",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
borderBottomWidth: 1,
|
||||
paddingBottom: 12,
|
||||
borderBottomColor: colors.nav,
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 18,
|
||||
fontFamily: "OpenSans-SemiBold"
|
||||
}}
|
||||
>
|
||||
Save to Notesnook
|
||||
</Text>
|
||||
|
||||
<Button
|
||||
title="Done"
|
||||
style={{
|
||||
backgroundColor: colors.accent,
|
||||
height: Platform.OS === "ios" ? 35 : 40,
|
||||
paddingHorizontal: 15,
|
||||
marginBottom: 0
|
||||
}}
|
||||
loading={loading}
|
||||
iconColor={colors.light}
|
||||
onPress={onPress}
|
||||
textStyle={{
|
||||
fontSize: 16,
|
||||
marginLeft: 0
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
height: height * 0.3,
|
||||
height: 200,
|
||||
paddingBottom: 15,
|
||||
borderRadius: 10
|
||||
marginBottom: 10,
|
||||
borderBottomColor: colors.nav,
|
||||
borderBottomWidth: 1
|
||||
}}
|
||||
>
|
||||
<SafeAreaProvider
|
||||
@@ -574,59 +629,47 @@ const ShareView = ({ quicknote = false }) => {
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 12,
|
||||
alignItems: "flex-end",
|
||||
justifyContent: "space-between",
|
||||
width: "100%"
|
||||
width: "100%",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
<Text
|
||||
style={{
|
||||
flexDirection: "row"
|
||||
color: colors.pri,
|
||||
marginRight: 10
|
||||
}}
|
||||
>
|
||||
{rawData.value && isURL(rawData.value) ? (
|
||||
<Button
|
||||
color={mode == 2 ? colors.shade : colors.nav}
|
||||
icon={modes[2].icon}
|
||||
onPress={() => changeMode(2)}
|
||||
title={modes[2].title}
|
||||
iconSize={18}
|
||||
iconColor={mode == 2 ? accent.color : colors.icon}
|
||||
textColor={mode == 2 ? accent.color : colors.icon}
|
||||
type="rounded"
|
||||
style={{ paddingHorizontal: 12 }}
|
||||
/>
|
||||
) : null}
|
||||
Clip Mode:
|
||||
</Text>
|
||||
{rawData.value && isURL(rawData.value) ? (
|
||||
<Button
|
||||
color={mode == 1 ? colors.shade : colors.nav}
|
||||
icon={modes[1].icon}
|
||||
onPress={() => changeMode(1)}
|
||||
title={modes[1].title}
|
||||
iconSize={18}
|
||||
iconColor={mode == 1 ? accent.color : colors.icon}
|
||||
textColor={mode == 1 ? accent.color : colors.icon}
|
||||
type="rounded"
|
||||
style={{ paddingHorizontal: 12 }}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{!quicknote ? (
|
||||
<Button
|
||||
color={accent.color}
|
||||
onPress={onPress}
|
||||
loading={loading || loadingIntent}
|
||||
icon="check"
|
||||
iconSize={20}
|
||||
iconColor={colors.light}
|
||||
color="transparent"
|
||||
icon={mode === 2 ? "radiobox-marked" : "radiobox-blank"}
|
||||
onPress={() => changeMode(2)}
|
||||
title={modes[2].title}
|
||||
iconSize={16}
|
||||
fontSize={14}
|
||||
iconColor={mode == 2 ? accent.color : colors.icon}
|
||||
textColor={mode == 2 ? accent.color : colors.icon}
|
||||
style={{
|
||||
paddingHorizontal: 0,
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 100,
|
||||
minWidth: 0
|
||||
height: 30,
|
||||
marginRight: 12,
|
||||
marginBottom: 0
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<Button
|
||||
color="transparent"
|
||||
icon={mode === 1 ? "radiobox-marked" : "radiobox-blank"}
|
||||
onPress={() => changeMode(1)}
|
||||
title={modes[1].title}
|
||||
iconSize={16}
|
||||
fontSize={14}
|
||||
iconColor={mode == 1 ? accent.color : colors.icon}
|
||||
textColor={mode == 1 ? accent.color : colors.icon}
|
||||
style={{ paddingHorizontal: 0, height: 30, marginBottom: 0 }}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -638,7 +681,8 @@ const ShareView = ({ quicknote = false }) => {
|
||||
flexDirection: "column",
|
||||
marginTop: 10,
|
||||
alignSelf: "center",
|
||||
alignItems: "center"
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
@@ -656,38 +700,56 @@ const ShareView = ({ quicknote = false }) => {
|
||||
fontSize: 15
|
||||
}}
|
||||
style={{
|
||||
paddingHorizontal: 0,
|
||||
...getElevation(1),
|
||||
paddingHorizontal: 12,
|
||||
height: 45,
|
||||
width: "100%",
|
||||
marginRight: 0
|
||||
marginRight: 0,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.nav,
|
||||
justifyContent: "flex-start"
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
color={colors.bg}
|
||||
onPress={() => {
|
||||
setShowSearch(true);
|
||||
setSearchMode("appendNote");
|
||||
}}
|
||||
icon="text-short"
|
||||
iconSize={18}
|
||||
iconColor={appendNote ? accent.color : colors.icon}
|
||||
title={`${
|
||||
appendNote ? appendNote.title.slice(0, 25) : "Append to note"
|
||||
}`}
|
||||
textColor={appendNote ? accent.color : colors.icon}
|
||||
title={`Append to a note`}
|
||||
textColor={colors.icon}
|
||||
type="button"
|
||||
textStyle={{
|
||||
fontSize: 15
|
||||
}}
|
||||
style={{
|
||||
paddingHorizontal: 0,
|
||||
...getElevation(1),
|
||||
paddingHorizontal: 12,
|
||||
height: 45,
|
||||
width: "100%",
|
||||
marginRight: 0
|
||||
marginRight: 0,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.nav,
|
||||
justifyContent: "flex-start"
|
||||
}}
|
||||
/>
|
||||
|
||||
{!appendNote ? (
|
||||
<AddTags
|
||||
onPress={() => {
|
||||
setSearchMode("selectTags");
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{!appendNote ? (
|
||||
<AddNotebooks
|
||||
onPress={() => {
|
||||
setSearchMode("selectNotebooks");
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
<View
|
||||
@@ -696,11 +758,222 @@ const ShareView = ({ quicknote = false }) => {
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</WrapperView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const AddNotebooks = ({ onPress }) => {
|
||||
const colors = useShareStore((state) => state.colors);
|
||||
const notebooks = useShareStore((state) => state.selectedNotebooks);
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
width: "100%",
|
||||
marginRight: 0,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.nav,
|
||||
justifyContent: "center",
|
||||
borderRadius: 5,
|
||||
marginBottom: 10,
|
||||
paddingVertical: 10,
|
||||
paddingTop: 5
|
||||
}}
|
||||
>
|
||||
{!notebooks || !notebooks.length ? (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
flexDirection: "row",
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="notebook-outline"
|
||||
size={20}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
color={colors.icon}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
color: colors.icon,
|
||||
fontSize: 15
|
||||
}}
|
||||
>
|
||||
Add to notebook
|
||||
</Text>
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
flexWrap: "wrap",
|
||||
width: "100%",
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
{notebooks.map((item) => (
|
||||
<Text
|
||||
style={{
|
||||
color: colors.icon,
|
||||
marginRight: 5,
|
||||
fontSize: 14,
|
||||
borderRadius: 4,
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: colors.nav,
|
||||
paddingVertical: 5,
|
||||
marginTop: 5
|
||||
}}
|
||||
onPress={() => {
|
||||
const index = notebooks.findIndex(
|
||||
(nb) => nb.id === item.id && nb.type === item.type
|
||||
);
|
||||
const selectedNotebooks = [...notebooks];
|
||||
selectedNotebooks.splice(index, 1);
|
||||
useShareStore
|
||||
.getState()
|
||||
.setSelectedNotebooks(selectedNotebooks);
|
||||
}}
|
||||
key={item.id}
|
||||
>
|
||||
<Icon
|
||||
name={item.type === "topic" ? "bookmark" : "notebook-outline"}
|
||||
size={15}
|
||||
/>{" "}
|
||||
{item.title}
|
||||
</Text>
|
||||
))}
|
||||
|
||||
<Text
|
||||
style={{
|
||||
color: colors.accent,
|
||||
marginRight: 5,
|
||||
fontSize: 14,
|
||||
borderRadius: 4,
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: colors.nav,
|
||||
paddingVertical: 5,
|
||||
marginTop: 5
|
||||
}}
|
||||
onPress={() => {
|
||||
onPress();
|
||||
}}
|
||||
key="$add-more"
|
||||
>
|
||||
<Icon name="plus" size={16} /> Add more
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const AddTags = ({ onPress }) => {
|
||||
const colors = useShareStore((state) => state.colors);
|
||||
const tags = useShareStore((state) => state.selectedTags);
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
height: 45,
|
||||
width: "100%",
|
||||
marginRight: 0,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.nav,
|
||||
justifyContent: "center",
|
||||
borderRadius: 5,
|
||||
marginBottom: 10
|
||||
}}
|
||||
>
|
||||
{!tags || !tags.length ? (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="pound"
|
||||
size={20}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
color={colors.icon}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
color: colors.icon,
|
||||
fontSize: 15
|
||||
}}
|
||||
>
|
||||
Add tags
|
||||
</Text>
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
flexWrap: "wrap",
|
||||
width: "100%",
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
{tags.map((tag) => (
|
||||
<Text
|
||||
style={{
|
||||
color: colors.icon,
|
||||
marginRight: 5,
|
||||
fontSize: 14,
|
||||
borderRadius: 4,
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: colors.nav,
|
||||
paddingVertical: 5
|
||||
}}
|
||||
onPress={() => {
|
||||
const index = tags.indexOf(tag);
|
||||
const selectedTags = [...tags];
|
||||
selectedTags.splice(index, 1);
|
||||
useShareStore.getState().setSelectedTags(selectedTags);
|
||||
}}
|
||||
key={tag}
|
||||
>
|
||||
#{tag}
|
||||
</Text>
|
||||
))}
|
||||
|
||||
<Text
|
||||
style={{
|
||||
color: colors.accent,
|
||||
marginRight: 5,
|
||||
fontSize: 14,
|
||||
borderRadius: 4,
|
||||
paddingHorizontal: 8,
|
||||
backgroundColor: colors.nav,
|
||||
paddingVertical: 5
|
||||
}}
|
||||
onPress={() => {
|
||||
onPress();
|
||||
}}
|
||||
key="$add-tag"
|
||||
>
|
||||
<Icon name="plus" size={17} />
|
||||
Add tag
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const Button = ({
|
||||
title,
|
||||
onPress,
|
||||
@@ -734,7 +1007,6 @@ const Button = ({
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
marginBottom: 10,
|
||||
minWidth: 80,
|
||||
paddingHorizontal: 20
|
||||
},
|
||||
textStyle: {}
|
||||
@@ -767,11 +1039,9 @@ const Button = ({
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
marginBottom: 10,
|
||||
minWidth: 80,
|
||||
paddingHorizontal: 20
|
||||
},
|
||||
types[type].style,
|
||||
style
|
||||
{ ...types[type].style, ...style }
|
||||
]}
|
||||
>
|
||||
{loading ? <ActivityIndicator color={iconColor} /> : null}
|
||||
|
||||
@@ -25,6 +25,22 @@ import {
|
||||
COLOR_SCHEME_LIGHT
|
||||
} from "../app/utils/color-scheme";
|
||||
import { MMKV } from "../app/common/database/mmkv";
|
||||
import { db } from "../app/common/database";
|
||||
|
||||
export async function initDatabase() {
|
||||
if (!db.isInitialized) {
|
||||
// Only load collections in database.
|
||||
await db.initCollections();
|
||||
await db.notes.init();
|
||||
}
|
||||
}
|
||||
|
||||
const StorageKeys = {
|
||||
appendNote: "shareMenuAppendNote",
|
||||
selectedNotebooks: "shareMenuSelectedNotebooks",
|
||||
selectedTag: "shareMenuSelectedTag",
|
||||
appSettings: "appSettings"
|
||||
};
|
||||
|
||||
export const useShareStore = create((set) => ({
|
||||
colors:
|
||||
@@ -33,7 +49,7 @@ export const useShareStore = create((set) => ({
|
||||
: COLOR_SCHEME_LIGHT,
|
||||
accent: ACCENT,
|
||||
setAccent: async () => {
|
||||
let appSettings = MMKV.getString("appSettings");
|
||||
let appSettings = MMKV.getString(StorageKeys.appSettings);
|
||||
|
||||
if (appSettings) {
|
||||
appSettings = JSON.parse(appSettings);
|
||||
@@ -56,14 +72,31 @@ export const useShareStore = create((set) => ({
|
||||
},
|
||||
appendNote: null,
|
||||
setAppendNote: (note) => {
|
||||
MMKV.setItem("shareMenuAppendNote", JSON.stringify(note));
|
||||
MMKV.setItem(StorageKeys.appendNote, JSON.stringify(note));
|
||||
set({ appendNote: note });
|
||||
},
|
||||
restoreAppendNote: async () => {
|
||||
let note = MMKV.getString("shareMenuAppendNote");
|
||||
if (note) {
|
||||
note = JSON.parse(note);
|
||||
set({ appendNote: note });
|
||||
}
|
||||
restore: () => {
|
||||
let appendNote = MMKV.getString(StorageKeys.appendNote);
|
||||
let selectedNotebooks = MMKV.getString(StorageKeys.selectedNotebooks);
|
||||
let selectedTags = MMKV.getString(StorageKeys.selectedTag);
|
||||
appendNote = JSON.parse(appendNote);
|
||||
set({
|
||||
appendNote: appendNote ? JSON.parse(appendNote) : null,
|
||||
selectedNotebooks: selectedNotebooks ? JSON.parse(selectedNotebooks) : [],
|
||||
selectedTag: selectedTags ? JSON.parse(selectedTags) : []
|
||||
});
|
||||
},
|
||||
selectedTags: [],
|
||||
selectedNotebooks: [],
|
||||
setSelectedNotebooks: (selectedNotebooks) => {
|
||||
MMKV.setItem(
|
||||
StorageKeys.selectedNotebooks,
|
||||
JSON.stringify(selectedNotebooks)
|
||||
);
|
||||
set({ selectedNotebooks });
|
||||
},
|
||||
setSelectedTags: (selectedTags) => {
|
||||
MMKV.setItem(StorageKeys.selectedTag, JSON.stringify(selectedTags));
|
||||
set({ selectedTags });
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
### The desktop app?
|
||||
|
||||
You can find all the desktop related code in [the `desktop/` directory](./desktop/). Since it uses the web app directly, we are keeping both together. (We should probably move it to it's own project at some point.)
|
||||
You can find all the desktop-related code in [the `desktop/` directory](./desktop/). Since it uses the web app directly, we are keeping both together. (We should probably move it to its own project at some point.)
|
||||
|
||||
### Downloads & releases
|
||||
|
||||
@@ -21,7 +21,7 @@ You can find all the desktop related code in [the `desktop/` directory](./deskto
|
||||
|
||||
## Build instructions
|
||||
|
||||
**Before you start it is recommended that you read [the contributing guidelines](/CONTRIBUTING.md).**
|
||||
> **Before you start, it is recommended that you read [the contributing guidelines](/CONTRIBUTING.md).**
|
||||
|
||||
### Setting up the development environment
|
||||
|
||||
@@ -66,11 +66,11 @@ npx serve apps/web/build
|
||||
|
||||
## Developer guide
|
||||
|
||||
> This project is in a transition state between Javascript & Typescript. We are gradually porting everything over to Typescript so if you can help with that, it'd be great!
|
||||
> This project is in a transition state between Javascript & Typescript. We are gradually porting everything over to Typescript, so if you can help with that, it'd be great!
|
||||
|
||||
### The tech stack
|
||||
|
||||
We try to keep the stack as lean as possible
|
||||
We try to keep the stack as lean as possible:
|
||||
|
||||
1. React v17: UI framework
|
||||
2. Typescript/Javascript: The logical side of the app
|
||||
@@ -82,21 +82,21 @@ We try to keep the stack as lean as possible
|
||||
|
||||
### Project structure
|
||||
|
||||
1. `src/`: 99% of the source code lives here & this is also where you'll be spending most of your time.
|
||||
1. `index.tsx`: **the app entrypoint** responsible for loading the appropriate view based on the current route.
|
||||
2. `app.js`: **the default route** that contains the whole note taking experience (notes list, navigation, editor etc.)
|
||||
3. `views/`: Contains **all the views** including views for login, settings, notes, notebooks & topics.
|
||||
4. `components/`: All the **reusuable UI components** are here (e.g. button, editor, etc.)
|
||||
5. `stores/`: Contains the glue code & **logic for all the UI interaction**. For example, when you pin a note the `src/stores/note-store.js` is responsible for everything including refreshing the list to reflect the changes.
|
||||
6. `navigation/`: All the **routing & navigation** logic lives here. The app uses 2 kinds of routers:
|
||||
1. `routes.js`: This contains all the main routes like `/notes`, `/notebooks` with information on what to render when user goes to a particular route.
|
||||
2. `hash-routes.js`: The hash routes are used for temporary navigation like opening dialogs, opening a note. These look like `#/notes/6307bbd65d5d5d5cb86f6f74/edit`.
|
||||
7. `interfaces/`: This is where the **platform specific storage & encryption logic** lives. These interface implementations are used by the `@notesnook/core` to provide capabilities such as persistence & encryption.
|
||||
8. `hooks/`: Contains all the **general purpose React hooks**
|
||||
9. `utils/`: These are **general-purpose utilities** for performing various tasks such as downloading files, storing configuration etc.
|
||||
1. `src/`: 99% of the source code lives here & this is also where you'll spend most of your time.
|
||||
1. `index.tsx`: **the app entry point** responsible for loading the appropriate view based on the current route.
|
||||
2. `app.js`: **the default route** that contains the whole note-taking experience (notes list, navigation, editor, etc.)
|
||||
3. `views/`: Contains **all the views**, including views for login, settings, notes, notebooks & topics.
|
||||
4. `components/`: All the **reusable UI components** are here (e.g., button, editor, etc.)
|
||||
5. `stores/`: Contains the glue code & **logic for all the UI interactions**. For example, when you pin a note, the `src/stores/note-store.js` is responsible for everything, including refreshing the list to reflect the changes.
|
||||
6. `navigation/`: All the **routing & navigation** logic lives here. The app uses two kinds of routers:
|
||||
1. `routes.js`: This contains all the main routes like `/notes`, `/notebooks` with information on what to render when the user goes to a particular route.
|
||||
2. `hash-routes.js`: The hash routes are used for temporary navigation, like opening dialogs or opening a note. These look like `#/notes/6307bbd65d5d5d5cb86f6f74/edit`.
|
||||
7. `interfaces/`: This is where the **platform-specific storage & encryption logic** lives. These interface implementations are used by the `@notesnook/core` to provide capabilities such as persistence & encryption.
|
||||
8. `hooks/`: Contains all the **general-purpose React hooks**
|
||||
9. `utils/`: These are **general-purpose utilities** for performing various tasks such as downloading files, storing configuration, etc.
|
||||
10. `common/`: This directory contains **the shared logic between the whole app**. For example, this is where the database is instantiated for use throughout the app.
|
||||
11. `commands/`: These are **commands used by the desktop app** for things like checking for updates, storing backups etc.
|
||||
2. `desktop/`: The Electron layer for **the desktop app lives here**. (This should be moved outside into it's own project).
|
||||
11. `commands/`: These are **commands the desktop app uses** for things like checking for updates, storing backups etc.
|
||||
2. `desktop/`: The Electron layer for **the desktop app lives here**. (This should be moved outside into its own project).
|
||||
|
||||
### Running the tests
|
||||
|
||||
|
||||
@@ -33,6 +33,22 @@
|
||||
h6 {
|
||||
color: #212121;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
p[data-spacing="double"] {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
p[data-spacing="single"] {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
p[data-spacing="single"]:empty {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
pre.codeblock {
|
||||
overflow-x: auto;
|
||||
@@ -147,9 +163,9 @@
|
||||
font-family: PT Mono, Consolas, Monaco, "Andale Mono", "Ubuntu Mono",
|
||||
monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-break: break-word;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
|
||||
4
apps/web/desktop/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.4.5",
|
||||
"version": "2.4.7",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.4.5",
|
||||
"version": "2.4.7",
|
||||
"dependencies": {
|
||||
"diary": "^0.3.1",
|
||||
"electron-updater": "^5.3.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.4.5",
|
||||
"version": "2.4.7",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./build/electron.js",
|
||||
|
||||
@@ -22,12 +22,14 @@ import { AssetManager } from "./asset-manager";
|
||||
import { EVENTS } from "./events";
|
||||
import bringToFront from "./ipc/actions/bringToFront";
|
||||
import { sendMessageToRenderer } from "./ipc/utils";
|
||||
import { isFlatpak } from "./utils";
|
||||
|
||||
let tray: Tray | undefined = undefined;
|
||||
export function setupTray() {
|
||||
if (tray) tray.destroy();
|
||||
|
||||
const trayIconSize = process.platform === "win32" || process.platform === "darwin" ? 16 : 32;
|
||||
const trayIconSize =
|
||||
process.platform === "win32" || process.platform === "darwin" ? 16 : 32;
|
||||
|
||||
tray = new Tray(
|
||||
AssetManager.icon("tray-icon", {
|
||||
@@ -39,14 +41,18 @@ export function setupTray() {
|
||||
{
|
||||
label: "Show app",
|
||||
type: "normal",
|
||||
icon: AssetManager.icon("tray-icon", { size: trayIconSize }),
|
||||
icon: isFlatpak()
|
||||
? undefined
|
||||
: AssetManager.icon("tray-icon", { size: trayIconSize }),
|
||||
click: bringToFront
|
||||
},
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: "New note",
|
||||
type: "normal",
|
||||
icon: AssetManager.icon("note-add", { size: trayIconSize }),
|
||||
icon: isFlatpak()
|
||||
? undefined
|
||||
: AssetManager.icon("note-add", { size: trayIconSize }),
|
||||
click: () => {
|
||||
bringToFront();
|
||||
sendMessageToRenderer(EVENTS.createItem, { itemType: "note" });
|
||||
@@ -55,7 +61,9 @@ export function setupTray() {
|
||||
{
|
||||
label: "New notebook",
|
||||
type: "normal",
|
||||
icon: AssetManager.icon("notebook-add", { size: trayIconSize }),
|
||||
icon: isFlatpak()
|
||||
? undefined
|
||||
: AssetManager.icon("notebook-add", { size: trayIconSize }),
|
||||
click: () => {
|
||||
bringToFront();
|
||||
sendMessageToRenderer(EVENTS.createItem, { itemType: "notebook" });
|
||||
@@ -64,7 +72,9 @@ export function setupTray() {
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: "Quit",
|
||||
icon: AssetManager.icon("quit", { size: trayIconSize }),
|
||||
icon: isFlatpak()
|
||||
? undefined
|
||||
: AssetManager.icon("quit", { size: trayIconSize }),
|
||||
type: "normal",
|
||||
click: () => {
|
||||
app.exit(0);
|
||||
|
||||
@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { app } from "electron";
|
||||
import { join } from "path";
|
||||
import { statSync } from "fs";
|
||||
import { statSync, existsSync } from "fs";
|
||||
|
||||
const APP_ICON_PATH = join(
|
||||
__dirname,
|
||||
@@ -52,4 +52,8 @@ function getPath(filePath) {
|
||||
}
|
||||
}
|
||||
|
||||
export { getPath, isDevelopment, APP_ICON_PATH };
|
||||
function isFlatpak() {
|
||||
return existsSync("/.flatpak-info");
|
||||
}
|
||||
|
||||
export { getPath, isDevelopment, isFlatpak, APP_ICON_PATH };
|
||||
|
||||
4
apps/web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"version": "2.4.4",
|
||||
"version": "2.4.7",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/web",
|
||||
"version": "2.4.4",
|
||||
"version": "2.4.7",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@aws-sdk/util-base64-browser": "^3.208.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.4.5",
|
||||
"version": "2.4.7",
|
||||
"private": true,
|
||||
"main": "./src/app.js",
|
||||
"homepage": "https://notesnook.com/",
|
||||
|
||||
4823
apps/web/patches/allotment+1.18.1.patch
Normal file
@@ -93,8 +93,6 @@ export default function AppEffects({ setShow }) {
|
||||
initEditorStore();
|
||||
|
||||
(async function () {
|
||||
await resetReminders();
|
||||
|
||||
await updateLastSynced();
|
||||
if (await initUser()) {
|
||||
showUpgradeReminderDialogs();
|
||||
|
||||
@@ -94,7 +94,9 @@ export function showAddNotebookDialog() {
|
||||
isOpen={true}
|
||||
onDone={async (nb: Record<string, unknown>) => {
|
||||
// add the notebook to db
|
||||
await db.notebooks?.add({ ...nb });
|
||||
const notebook = await db.notebooks?.add({ ...nb });
|
||||
if (!notebook) return perform(false);
|
||||
|
||||
notebookStore.refresh();
|
||||
|
||||
showToast("success", "Notebook added successfully!");
|
||||
@@ -209,9 +211,8 @@ export function showError(title: string, message: string) {
|
||||
export function showMultiDeleteConfirmation(length: number) {
|
||||
return confirm({
|
||||
title: `Delete ${length} items?`,
|
||||
message: `These items will be **kept in your Trash for ${
|
||||
db.settings?.getTrashCleanupInterval() || 7
|
||||
} days** after which they will be permanently deleted.`,
|
||||
message: `These items will be **kept in your Trash for ${db.settings?.getTrashCleanupInterval() || 7
|
||||
} days** after which they will be permanently deleted.`,
|
||||
positiveButtonText: "Yes",
|
||||
negativeButtonText: "No"
|
||||
});
|
||||
@@ -471,9 +472,9 @@ export function showCreateTopicDialog() {
|
||||
}}
|
||||
onAction={async (topic: Record<string, unknown>) => {
|
||||
if (!topic) return;
|
||||
const notebookId = notebookStore.get().selectedNotebookId;
|
||||
await db.notebooks?.notebook(notebookId).topics.add(topic);
|
||||
notebookStore.setSelectedNotebook(notebookId);
|
||||
const notebook = notebookStore.get().selectedNotebook;
|
||||
await db.notebooks?.notebook(notebook.id).topics.add(topic);
|
||||
notebookStore.setSelectedNotebook(notebook.id);
|
||||
showToast("success", "Topic created!");
|
||||
perform(true);
|
||||
}}
|
||||
|
||||