diff --git a/apps/mobile/app/components/list-items/headers/section-header.tsx b/apps/mobile/app/components/list-items/headers/section-header.tsx
index 17ccb9049..25a2b71c7 100644
--- a/apps/mobile/app/components/list-items/headers/section-header.tsx
+++ b/apps/mobile/app/components/list-items/headers/section-header.tsx
@@ -64,7 +64,8 @@ export const SectionHeader = React.memo<
.
*/
-import { Note } from "@notesnook/core";
+import { Note, Notebook } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
-import React, {
- RefObject,
- useCallback,
- useEffect,
- useRef,
- useState
-} from "react";
-import {
- ActivityIndicator,
- Keyboard,
- TouchableOpacity,
- View
-} from "react-native";
-import { ActionSheetRef } from "react-native-actions-sheet";
-import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
+import React, { RefObject, useCallback, useEffect, useRef } from "react";
+import { ActivityIndicator, View } from "react-native";
+import { ActionSheetRef, FlatList } from "react-native-actions-sheet";
import { db } from "../../../common/database";
import { presentSheet } from "../../../services/event-manager";
import Navigation from "../../../services/navigation";
+import {
+ createNotebookTreeStores,
+ TreeItem
+} from "../../../stores/create-notebook-tree-stores";
import { ItemSelection } from "../../../stores/item-selection-store";
import { useNotebooks } from "../../../stores/use-notebook-store";
import { useRelationStore } from "../../../stores/use-relation-store";
import { useSelectionStore } from "../../../stores/use-selection-store";
-import { useSettingStore } from "../../../stores/use-setting-store";
import { updateNotebook } from "../../../utils/notebooks";
import { AppFontSize } from "../../../utils/size";
+import { DefaultAppStyles } from "../../../utils/styles";
import { Dialog } from "../../dialog";
import DialogHeader from "../../dialog/dialog-header";
import SheetProvider from "../../sheet-provider";
+import { NotebookItem } from "../../side-menu/notebook-item";
import { Button } from "../../ui/button";
import { IconButton } from "../../ui/icon-button";
import Input from "../../ui/input";
import Paragraph from "../../ui/typography/paragraph";
import { AddNotebookSheet } from "../add-notebook";
-import { NotebookItem } from "./notebook-item";
-import { useNotebookItemSelectionStore } from "./store";
+
+const {
+ useNotebookExpandedStore,
+ useNotebookSelectionStore,
+ useNotebookTreeStore
+} = createNotebookTreeStores(true, true, "link-notebooks-expanded-store");
async function updateInitialSelectionState(items: string[]) {
const relations = await db.relations
@@ -86,7 +83,7 @@ async function updateInitialSelectionState(items: string[]) {
initialSelectionState[id] = "intermediate";
}
}
- useNotebookItemSelectionStore.setState({
+ useNotebookSelectionStore.setState({
initialState: initialSelectionState,
selection: { ...initialSelectionState },
multiSelect: relations.length > 1
@@ -101,31 +98,58 @@ const MoveNoteSheet = ({
actionSheetRef: RefObject;
}) => {
const { colors } = useThemeColors();
- const [rootNotebooks, loading] = useNotebooks();
+ const [notebooks, loading] = useNotebooks();
+ const tree = useNotebookTreeStore((state) => state.tree);
const searchQuery = useRef("");
const searchTimer = useRef();
- const [notebooks, setNotebooks] = useState(rootNotebooks);
+ const [filteredNotebooks, setFilteredNotebooks] = React.useState(notebooks);
+ const loadRootNotebooks = React.useCallback(async () => {
+ if (!filteredNotebooks) return;
+ const _notebooks: Notebook[] = [];
+ for (let i = 0; i < filteredNotebooks.placeholders.length; i++) {
+ _notebooks[i] = (await filteredNotebooks?.item(i))?.item as Notebook;
+ }
+ const items = await useNotebookTreeStore
+ .getState()
+ .addNotebooks("root", _notebooks, 0);
+ useNotebookTreeStore.getState().setTree(items);
+ }, [filteredNotebooks]);
+
+ const updateNotebooks = React.useCallback(() => {
+ if (searchQuery.current) {
+ db.lookup
+ .notebooks(searchQuery.current)
+ .sorted()
+ .then((filtered) => {
+ setFilteredNotebooks(filtered);
+ });
+ } else {
+ setFilteredNotebooks(notebooks);
+ }
+ }, [notebooks]);
+
+ useEffect(() => {
+ updateNotebooks();
+ }, [updateNotebooks]);
+
+ useEffect(() => {
+ (async () => {
+ if (!loading) {
+ loadRootNotebooks();
+ }
+ })();
+ }, [loadRootNotebooks, loading]);
- const dimensions = useSettingStore((state) => state.dimensions);
const selectedItemsList = useSelectionStore(
(state) => state.selectedItemsList
);
-
- const multiSelect = useNotebookItemSelectionStore(
- (state) => state.multiSelect
- );
-
- useEffect(() => {
- if (!loading) {
- setNotebooks(rootNotebooks);
- }
- }, [loading, rootNotebooks]);
+ const multiSelect = useNotebookSelectionStore((state) => state.multiSelect);
useEffect(() => {
const items = note ? [note.id] : selectedItemsList;
updateInitialSelectionState(items);
return () => {
- useNotebookItemSelectionStore.setState({
+ useNotebookSelectionStore.setState({
initialState: {},
selection: {},
multiSelect: false,
@@ -137,7 +161,7 @@ const MoveNoteSheet = ({
const onSave = async () => {
const noteIds = note ? [note.id] : selectedItemsList;
- const changedNotebooks = useNotebookItemSelectionStore.getState().selection;
+ const changedNotebooks = useNotebookSelectionStore.getState().selection;
for (const id in changedNotebooks) {
const item = await db.notebooks.notebook(id);
@@ -161,43 +185,39 @@ const MoveNoteSheet = ({
};
const hasSelected = () => {
- const selection = useNotebookItemSelectionStore.getState().selection;
+ const selection = useNotebookSelectionStore.getState().selection;
return Object.keys(selection).some((key) => selection[key] === "selected");
};
const renderNotebook = useCallback(
- ({ index }: { item: boolean; index: number }) => (
-
+ ({ item, index }: { item: TreeItem; index: number }) => (
+
),
- [notebooks]
+ []
);
return (
<>
-
- {
- Keyboard.dismiss();
- }}
- />
+
{hasSelected() ? (
@@ -232,112 +252,199 @@ const MoveNoteSheet = ({
-
-
+ {
+ AddNotebookSheet.present(
+ undefined,
+ undefined,
+ "link-notebooks",
+ undefined,
+ false,
+ searchQuery.current
+ );
+ },
+ color: colors.primary.icon
}}
- >
- {
- AddNotebookSheet.present(
- undefined,
- undefined,
- "link-notebooks",
- undefined,
- false,
- searchQuery.current
- );
- },
- color: colors.primary.icon
- }}
- onChangeText={(value) => {
- searchQuery.current = value;
- if (!searchQuery.current) {
- setNotebooks(rootNotebooks);
- return;
- }
- searchTimer.current = setTimeout(() => {
- db.lookup
- .notebooks(searchQuery.current)
- .sorted()
- .then((result) => {
- if (searchQuery.current === value) {
- setNotebooks(result);
- }
- });
- }, 300);
- }}
+ onChangeText={(value) => {
+ searchQuery.current = value;
+ searchTimer.current = setTimeout(() => {
+ updateNotebooks();
+ }, 300);
+ }}
+ />
+
+ }
+ renderItem={renderNotebook}
+ ListEmptyComponent={
+
+ {loading ? (
+
-
- }
- estimatedItemSize={50}
- renderItem={renderNotebook}
- ListEmptyComponent={
-
- {loading ? (
-
- ) : (
-
- {strings.emptyPlaceholders("notebook")}
-
- )}
-
- }
- ListFooterComponent={}
- />
-
+ ) : (
+
+ {strings.emptyPlaceholders("notebook")}
+
+ )}
+
+ }
+ />
>
);
};
+const NotebookItemWrapper = React.memo(
+ ({
+ item,
+ index,
+ onPress
+ }: {
+ item: TreeItem;
+ index: number;
+ onPress?: () => void;
+ }) => {
+ const expanded = useNotebookExpandedStore(
+ (state) => state.expanded[item.notebook.id]
+ );
+ const selectionEnabled = useNotebookSelectionStore(
+ (state) => state.enabled
+ );
+ const selected = useNotebookSelectionStore(
+ (state) => state.selection[item.notebook.id] === "selected"
+ );
+
+ const onItemUpdate = React.useCallback(async () => {
+ const notebook = await db.notebooks.notebook(item.notebook.id);
+ if (notebook) {
+ useNotebookTreeStore.getState().updateItem(item.notebook.id, notebook);
+ if (expanded) {
+ useNotebookTreeStore
+ .getState()
+ .setTree(
+ await useNotebookTreeStore
+ .getState()
+ .fetchAndAdd(item.notebook.id, item.depth + 1)
+ );
+ }
+ } else {
+ useNotebookTreeStore.getState().removeItem(item.notebook.id);
+ }
+ }, [expanded, item.depth, item.notebook.id]);
+
+ return (
+
+ {
+ useNotebookExpandedStore.getState().setExpanded(item.notebook.id);
+ if (!expanded) {
+ useNotebookTreeStore
+ .getState()
+ .setTree(
+ await useNotebookTreeStore
+ .getState()
+ .fetchAndAdd(item.notebook.id, item.depth + 1)
+ );
+ } else {
+ useNotebookTreeStore.getState().removeChildren(item.notebook.id);
+ }
+ }}
+ onLongPress={() => {
+ useNotebookSelectionStore.setState({
+ multiSelect: !useNotebookSelectionStore.getState().multiSelect
+ });
+
+ const state = useNotebookSelectionStore.getState();
+ useNotebookSelectionStore
+ .getState()
+ .markAs(
+ item.notebook,
+ !selected
+ ? "selected"
+ : !state.initialState[item.notebook.id]
+ ? undefined
+ : "deselected"
+ );
+ }}
+ canDisableSelectionMode={false}
+ selected={selected}
+ selectionEnabled={selectionEnabled}
+ selectionStore={useNotebookSelectionStore}
+ onItemUpdate={onItemUpdate}
+ focused={false}
+ onPress={onPress}
+ onAddNotebook={() => {
+ AddNotebookSheet.present(
+ undefined,
+ item.notebook,
+ "link-notebooks",
+ undefined,
+ false
+ );
+ }}
+ />
+
+ );
+ },
+ (prev, next) => {
+ return (
+ prev.item.notebook.id === next.item.notebook.id &&
+ prev.item.notebook.dateModified === next.item.notebook.dateModified &&
+ prev.item.notebook.dateEdited === next.item.notebook.dateEdited &&
+ prev.item.hasChildren === next.item.hasChildren &&
+ prev.index === next.index &&
+ prev.item.parentId === next.item.parentId
+ );
+ }
+);
+NotebookItemWrapper.displayName = "NotebookItemWrapper";
+
MoveNoteSheet.present = (note?: Note) => {
presentSheet({
- component: (ref) => ,
- enableGesturesInScrollView: false,
- noBottomPadding: true,
- keyboardHandlerDisabled: true
+ component: (ref) =>
});
};
export default MoveNoteSheet;
diff --git a/apps/mobile/app/components/sheets/add-to/notebook-item.tsx b/apps/mobile/app/components/sheets/add-to/notebook-item.tsx
deleted file mode 100644
index 6c751677d..000000000
--- a/apps/mobile/app/components/sheets/add-to/notebook-item.tsx
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
-This file is part of the Notesnook project (https://notesnook.com/)
-
-Copyright (C) 2023 Streetwriters (Private) Limited
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see .
-*/
-import { Notebook, VirtualizedGrouping } from "@notesnook/core";
-import { useThemeColors } from "@notesnook/theme";
-import React, { useEffect } from "react";
-import { View, useWindowDimensions } from "react-native";
-import { notesnook } from "../../../../e2e/test.ids";
-import { useTotalNotes } from "../../../hooks/use-db-item";
-import { useNotebook } from "../../../hooks/use-notebook";
-import useNavigationStore from "../../../stores/use-navigation-store";
-import { AppFontSize } from "../../../utils/size";
-import { IconButton } from "../../ui/icon-button";
-import { Pressable } from "../../ui/pressable";
-import Paragraph from "../../ui/typography/paragraph";
-import { AddNotebookSheet } from "../add-notebook";
-import {
- useNotebookExpandedStore,
- useNotebookItemSelectionStore
-} from "./store";
-
-type NotebookParentProp = {
- parent?: NotebookParentProp;
- item?: Notebook;
-};
-
-export const NotebookItem = ({
- id,
- currentLevel = 0,
- index,
- parent,
- items
-}: {
- id: string | number;
- currentLevel?: number;
- index: number;
- parent?: NotebookParentProp;
- items?: VirtualizedGrouping;
-}) => {
- const { nestedNotebooks, notebook: item } = useNotebook(id, items, true);
- const expanded = useNotebookExpandedStore((state) =>
- item?.id ? state.expanded[item?.id] : false
- );
- const { totalNotes: totalNotes, getTotalNotes } = useTotalNotes("notebook");
- const focusedRouteId = useNavigationStore((state) => state.focusedRouteId);
- const { colors } = useThemeColors("sheet");
- const selection = useNotebookItemSelectionStore((state) =>
- item?.id ? state.selection[item?.id] : undefined
- );
- const isSelected = selection === "selected";
- const isFocused = focusedRouteId === id;
- const { fontScale } = useWindowDimensions();
-
- useEffect(() => {
- if (item?.id) {
- getTotalNotes([item?.id]);
- }
- }, [getTotalNotes, item?.id]);
-
- const onPress = () => {
- if (!item) return;
- const state = useNotebookItemSelectionStore.getState();
-
- if (isSelected) {
- state.markAs(
- item,
- !state.initialState[item?.id] ? undefined : "deselected"
- );
- return;
- }
-
- if (!state.multiSelect) {
- const keys = Object.keys(state.selection);
- const nextState: any = {};
- for (const key in keys) {
- nextState[key] = !state.initialState[key] ? undefined : "deselected";
- }
-
- state.setSelection({
- [item.id]: "selected",
- ...nextState
- });
- } else {
- state.markAs(item, "selected");
- }
- };
-
- return (
- 0 && currentLevel < 6 ? 15 : undefined,
- width: "100%"
- }}
- >
- {
- if (!item) return;
- useNotebookItemSelectionStore.setState({
- multiSelect: true
- });
- const state = useNotebookItemSelectionStore.getState();
- useNotebookItemSelectionStore
- .getState()
- .markAs(
- item,
- !isSelected
- ? "selected"
- : !state.initialState[item?.id]
- ? undefined
- : "deselected"
- );
- }}
- testID={`add-to-notebook-item-${currentLevel}-${index}`}
- onPress={onPress}
- style={{
- justifyContent: "space-between",
- width: "100%",
- alignItems: "center",
- flexDirection: "row",
- paddingLeft: 12,
- paddingRight: 12,
- borderRadius: 0,
- height: 45
- }}
- >
-
- {nestedNotebooks?.placeholders.length ? (
- {
- if (!item?.id) return;
- useNotebookExpandedStore.getState().setExpanded(item?.id);
- }}
- top={0}
- left={0}
- bottom={0}
- right={0}
- style={{
- width: 35,
- height: 35
- }}
- name={expanded ? "chevron-down" : "chevron-right"}
- />
- ) : null}
-
-
-
-
- {item?.title}
-
-
-
-
- {item?.id && totalNotes?.(item?.id) ? (
-
- {totalNotes(item?.id)}
-
- ) : null}
- {
- if (!item) return;
- AddNotebookSheet.present(
- undefined,
- item,
- "link-notebooks",
- undefined,
- false
- );
- }}
- left={0}
- right={0}
- bottom={0}
- top={0}
- color={colors.primary.icon}
- size={AppFontSize.xl}
- />
-
-
-
- {!expanded
- ? null
- : nestedNotebooks?.placeholders.map((id, index) => (
-
- ))}
-
- );
-};
diff --git a/apps/mobile/app/components/sheets/add-to/store.ts b/apps/mobile/app/components/sheets/add-to/store.ts
deleted file mode 100644
index 729afcbb7..000000000
--- a/apps/mobile/app/components/sheets/add-to/store.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-This file is part of the Notesnook project (https://notesnook.com/)
-
-Copyright (C) 2023 Streetwriters (Private) Limited
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see .
-*/
-import create from "zustand";
-import { createItemSelectionStore } from "../../../stores/item-selection-store";
-
-export const useNotebookExpandedStore = create<{
- expanded: {
- [id: string]: boolean;
- };
- setExpanded: (id: string) => void;
-}>((set, get) => ({
- expanded: {},
- setExpanded(id: string) {
- set({
- expanded: {
- ...get().expanded,
- [id]: !get().expanded[id]
- }
- });
- }
-}));
-
-export const useNotebookItemSelectionStore = createItemSelectionStore(true);
diff --git a/apps/mobile/app/components/sheets/move-notebook/index.tsx b/apps/mobile/app/components/sheets/move-notebook/index.tsx
index d1d968801..958056c74 100644
--- a/apps/mobile/app/components/sheets/move-notebook/index.tsx
+++ b/apps/mobile/app/components/sheets/move-notebook/index.tsx
@@ -63,9 +63,9 @@ export const MoveNotebookSheet = ({
selectedNotebooks: Notebook[];
close?: () => void;
}) => {
- const tree = useNotebookTreeStore((state) => state.tree);
const [notebooks, loading] = useNotebooks();
const { colors } = useThemeColors();
+ const tree = useNotebookTreeStore((state) => state.tree);
const lastQuery = React.useRef();
const [filteredNotebooks, setFilteredNotebooks] = React.useState(notebooks);
const [moveToTopEnabled, setMoveToTopEnabled] = useState(false);
diff --git a/apps/mobile/app/components/side-menu/notebook-item.tsx b/apps/mobile/app/components/side-menu/notebook-item.tsx
index 9c19dd7b6..74a423aed 100644
--- a/apps/mobile/app/components/side-menu/notebook-item.tsx
+++ b/apps/mobile/app/components/side-menu/notebook-item.tsx
@@ -48,7 +48,8 @@ export const NotebookItem = ({
onItemUpdate,
onPress,
onLongPress,
- onAddNotebook
+ onAddNotebook,
+ canDisableSelectionMode
}: {
index: number;
item: TreeItem;
@@ -62,6 +63,7 @@ export const NotebookItem = ({
onPress?: () => void;
onLongPress?: () => void;
onAddNotebook?: () => void;
+ canDisableSelectionMode?: boolean;
}) => {
const notebook = item.notebook;
const isFocused = focused;
@@ -101,10 +103,34 @@ export const NotebookItem = ({
testID={`notebook-item-${item.depth}-${index}`}
onPress={async () => {
if (selectionEnabled) {
- selectionStore
- .getState()
- .markAs(item.notebook, selected ? "deselected" : "selected");
- if (selectionStore.getState().getSelectedItemIds().length === 0) {
+ const state = selectionStore.getState();
+
+ if (selected) {
+ state.markAs(item.notebook, "deselected");
+ return;
+ }
+
+ if (!state.multiSelect) {
+ const keys = Object.keys(state.selection);
+ const nextState: any = {};
+ for (const key in keys) {
+ nextState[key] = !state.initialState[key]
+ ? undefined
+ : "deselected";
+ }
+
+ state.setSelection({
+ [item.notebook.id]: "selected",
+ ...nextState
+ });
+ } else {
+ state.markAs(item.notebook, "selected");
+ }
+
+ if (
+ selectionStore.getState().getSelectedItemIds().length === 0 &&
+ canDisableSelectionMode
+ ) {
selectionStore.setState({
enabled: false
});
@@ -167,48 +193,59 @@ export const NotebookItem = ({
- {selectionEnabled ? (
-
-
-
- ) : onAddNotebook ? (
- {
- onAddNotebook();
- }}
- />
- ) : (
- <>
-
+ {selectionEnabled ? (
+
- {totalNotes?.(notebook?.id) || 0}
-
- >
- )}
+
+
+ ) : (
+ <>
+
+ {totalNotes?.(notebook?.id) || 0}
+
+ >
+ )}
+
+ {onAddNotebook ? (
+ {
+ onAddNotebook();
+ }}
+ />
+ ) : null}
+
);
diff --git a/apps/mobile/app/components/side-menu/side-menu-notebooks.tsx b/apps/mobile/app/components/side-menu/side-menu-notebooks.tsx
index 1d34924d2..21b733365 100644
--- a/apps/mobile/app/components/side-menu/side-menu-notebooks.tsx
+++ b/apps/mobile/app/components/side-menu/side-menu-notebooks.tsx
@@ -39,7 +39,9 @@ import {
useSideMenuNotebookSelectionStore,
useSideMenuNotebookTreeStore
} from "./stores";
-
+useSideMenuNotebookSelectionStore.setState({
+ multiSelect: true
+});
export const SideMenuNotebooks = () => {
const tree = useSideMenuNotebookTreeStore((state) => state.tree);
const [notebooks, loading] = useNotebooks();