mobile: refactor some sheets into screens

This commit is contained in:
Ammar Ahmed
2025-02-24 10:02:36 +05:00
committed by Ammar Ahmed
parent 47c959374c
commit ef2a4f5126
16 changed files with 3829 additions and 3746 deletions

View File

@@ -41,12 +41,16 @@ interface FloatingButtonProps {
color?: string;
shouldShow?: boolean;
alwaysVisible?: boolean;
icon?: string;
testID?: string;
}
const FloatingButton = ({
onPress,
color,
alwaysVisible = false
alwaysVisible = false,
icon,
testID
}: FloatingButtonProps) => {
const { colors } = useThemeColors();
const deviceMode = useSettingStore((state) => state.deviceMode);
@@ -116,7 +120,7 @@ const FloatingButton = ({
]}
>
<TouchableOpacity
testID={notesnook.buttons.add}
testID={testID || notesnook.buttons.add}
activeOpacity={0.95}
style={{
...getElevationStyle(10),
@@ -141,7 +145,9 @@ const FloatingButton = ({
>
<Icon
name={
route.name === "Notebooks"
icon
? icon
: route.name === "Notebooks"
? "notebook-plus"
: route.name === "Trash"
? "delete"

View File

@@ -41,10 +41,9 @@ import { AppFontSize } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import { sleep } from "../../utils/time";
import { presentDialog } from "../dialog/functions";
import MoveNoteSheet from "../sheets/add-to";
import ExportNotesSheet from "../sheets/export-notes";
import ManageTagsSheet from "../sheets/manage-tags";
import { MoveNotebookSheet } from "../sheets/move-notebook";
import { MoveNotebook } from "../../screens/move-notebook";
import { IconButton } from "../ui/icon-button";
import NativeTooltip from "../../utils/tooltip";
@@ -203,7 +202,7 @@ export const SelectionHeader = React.memo(
onPress: async () => {
const ids = selectedItemsList;
const notebooks = await db.notebooks.all.items(ids);
MoveNotebookSheet.present(notebooks);
MoveNotebook.present(notebooks);
},
visible: renderedInRoute === "Notebooks",
icon: "arrow-right-bold-box-outline"
@@ -230,7 +229,7 @@ export const SelectionHeader = React.memo(
title: strings.linkNotebook(),
onPress: async () => {
await sleep(100);
MoveNoteSheet.present();
// MoveNoteSheet.present();
},
visible: type === "note",
icon: "plus"

View File

@@ -40,7 +40,6 @@ import { Button } from "../../ui/button";
import Input from "../../ui/input";
import Seperator from "../../ui/seperator";
import Heading from "../../ui/typography/heading";
import { MoveNotes } from "../move-notes/movenote";
export const AddNotebookSheet = ({
notebook,
@@ -103,11 +102,22 @@ export const AddNotebookSheet = ({
useNotebookStore.getState().refresh();
}
if (!notebook && showMoveNotesOnComplete && id) {
MoveNotes.present(await db.notebooks.notebook(id));
} else {
close?.(true);
if (showMoveNotesOnComplete && id) {
ToastManager.show({
heading: "Notebook added",
type: "success",
context: "global",
actionText: "Link notes",
duration: 8000,
func: async () => {
Navigation.navigate("MoveNotes", {
notebook: (await db.notebooks.notebook(id)) as Notebook
});
ToastManager.hide();
}
});
}
close?.(true);
};
return (

View File

@@ -17,6 +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 { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { View } from "react-native";
@@ -34,12 +35,13 @@ import { db } from "../../common/database";
import { useGroupOptions } from "../../hooks/use-group-options";
import { presentSheet, ToastManager } from "../../services/event-manager";
import Navigation from "../../services/navigation";
import { useTagStore } from "../../stores/use-tag-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { deleteItems } from "../../utils/functions";
import { AppFontSize } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import { presentDialog } from "../dialog/functions";
import { AddNotebookSheet } from "../sheets/add-notebook";
import { MoveNotebookSheet } from "../sheets/move-notebook";
import Sort from "../sheets/sort";
import { IconButton } from "../ui/icon-button";
import { Pressable } from "../ui/pressable";
@@ -51,9 +53,6 @@ import {
useSideMenuNotebookSelectionStore,
useSideMenuTagsSelectionStore
} from "./stores";
import { presentDialog } from "../dialog/functions";
import { strings } from "@notesnook/intl";
import { useTagStore } from "../../stores/use-tag-store";
const renderScene = SceneMap({
home: SideMenuHome,
notebooks: SideMenuNotebooks,
@@ -206,7 +205,9 @@ const TabBar = (
.getState()
.getSelectedItemIds();
const notebooks = await db.notebooks.all.items(ids);
MoveNotebookSheet.present(notebooks);
Navigation.navigate("MoveNotebook", {
selectedNotebooks: notebooks
});
break;
}
case "Close": {

View File

@@ -37,10 +37,11 @@ import {
} from "../../services/event-manager";
import { getElevationStyle } from "../../utils/elevation";
import { eHideToast, eShowToast } from "../../utils/events";
import { AppFontSize } from "../../utils/size";
import { AppFontSize, defaultBorderRadius } from "../../utils/size";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { DefaultAppStyles } from "../../utils/styles";
export const Toast = ({ context = "global" }) => {
const { colors, isDark } = useThemeColors();
@@ -133,14 +134,14 @@ export const Toast = ({ context = "global" }) => {
...getElevationStyle(5),
backgroundColor: isDark ? colors.static.black : colors.static.white,
alignSelf: "center",
borderRadius: 100,
borderRadius: defaultBorderRadius * 2,
paddingVertical: 12,
paddingHorizontal: 12,
justifyContent: "space-between",
flexDirection: "row",
alignItems: "center",
maxWidth: "95%",
gap: 10,
maxWidth: "90%",
gap: DefaultAppStyles.GAP_SMALL,
flexShrink: 1
}}
>
@@ -148,7 +149,7 @@ export const Toast = ({ context = "global" }) => {
style={{
flexDirection: "row",
alignItems: "center",
gap: 10,
gap: DefaultAppStyles.GAP_SMALL,
flexShrink: 1
}}
>
@@ -178,7 +179,6 @@ export const Toast = ({ context = "global" }) => {
<View
style={{
paddingRight: toastOptions?.func ? 0 : 12,
flexShrink: 1
}}
>
@@ -194,7 +194,7 @@ export const Toast = ({ context = "global" }) => {
{toastOptions.message || toastOptions.heading ? (
<Paragraph
color={!isDark ? colors.static.black : colors.static.white}
size={AppFontSize.xs}
size={AppFontSize.sm}
>
{toastOptions.message || toastOptions.heading}
</Paragraph>
@@ -205,10 +205,12 @@ export const Toast = ({ context = "global" }) => {
{toastOptions.func ? (
<Button
testID={notesnook.toast.button}
fontSize={AppFontSize.md}
type={toastOptions.type === "error" ? "errorShade" : "transparent"}
fontSize={AppFontSize.xs}
type={
toastOptions.type === "error" ? "errorShade" : "secondaryAccented"
}
onPress={toastOptions.func}
title={toastOptions.actionText}
title={"Link notes"}
height={35}
style={{
zIndex: 10

View File

@@ -17,12 +17,13 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from "react";
import { getContainerBorder } from "../../../utils/colors";
import { View, ViewStyle } from "react-native";
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { View, ViewStyle } from "react-native";
import { getContainerBorder } from "../../../utils/colors";
import { AppFontSize } from "../../../utils/size";
import { IconButton } from "../icon-button";
import { DefaultAppStyles } from "../../../utils/styles";
import AppIcon from "../AppIcon";
import Paragraph from "../typography/paragraph";
export interface NoticeProps {
@@ -46,28 +47,25 @@ export const Notice = ({
return (
<View
style={{
padding: 12,
paddingHorizontal: DefaultAppStyles.GAP,
paddingLeft: 5,
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
flexDirection: "row",
backgroundColor: colors.secondary.background,
borderRadius: isSmall ? 5 : 10,
alignItems: "flex-start",
gap: 5,
...getContainerBorder(colors.secondary.background),
...style
}}
>
<IconButton
size={isSmall ? AppFontSize.lg + 1 : AppFontSize.xxl}
<AppIcon
size={isSmall ? AppFontSize.md + 2 : AppFontSize.xxl}
name={type}
style={{
width: isSmall ? undefined : 40,
height: isSmall ? undefined : 40,
alignSelf: "flex-start"
}}
color={type === "alert" ? colors.error.icon : colors.primary.accent}
/>
<Paragraph
style={{
marginLeft: 10,
flexShrink: 1
}}
selectable={selectable}

View File

@@ -37,10 +37,7 @@ import { AuthMode } from "../components/auth/common";
import { presentDialog } from "../components/dialog/functions";
import NoteHistory from "../components/note-history";
import { AddNotebookSheet } from "../components/sheets/add-notebook";
import MoveNoteSheet from "../components/sheets/add-to";
import ExportNotesSheet from "../components/sheets/export-notes";
import { MoveNotebookSheet } from "../components/sheets/move-notebook";
import { MoveNotes } from "../components/sheets/move-notes/movenote";
import PublishNoteSheet from "../components/sheets/publish-note";
import { ReferencesList } from "../components/sheets/references";
import { RelationsList } from "../components/sheets/relations-list/index";
@@ -536,11 +533,14 @@ export const useActions = ({
},
{
id: "move-notes",
title: strings.moveNotes(),
title: strings.linkNotes(),
hidden: item.type !== "notebook",
icon: "text",
onPress: () => {
MoveNotes.present(item);
close();
Navigation.navigate("MoveNotes", {
notebook: item
});
}
},
{
@@ -548,7 +548,10 @@ export const useActions = ({
title: strings.moveNotebookFix(),
icon: "arrow-right-bold-box-outline",
onPress: () => {
MoveNotebookSheet.present([item]);
close();
Navigation.navigate("MoveNotebook", {
selectedNotebooks: [item]
});
}
}
);
@@ -619,7 +622,11 @@ export const useActions = ({
}
function addTo() {
MoveNoteSheet.present(item as Note);
Navigation.navigate("LinkNotebooks", {
note: item as Note
});
close();
//MoveNoteSheet.present(item as Note);
}
async function addToFavorites() {

View File

@@ -44,6 +44,9 @@ import { useSelectionStore } from "../stores/use-selection-store";
import { useSettingStore } from "../stores/use-setting-store";
import { fluidTabsRef, rootNavigatorRef } from "../utils/global-refs";
import { FluidPanelsView } from "./fluid-panels-view";
import LinkNotebooks from "../screens/link-notebooks";
import { MoveNotebook } from "../screens/move-notebook";
import { MoveNotes } from "../screens/move-notes";
const RootStack = createNativeStackNavigator();
const AppStack = createNativeStackNavigator();
@@ -126,6 +129,12 @@ export const RootNavigation = () => {
<RootStack.Screen name="Auth" component={Auth as any} />
<RootStack.Screen name="FluidPanelsView" component={FluidPanelsView} />
<RootStack.Screen name="AppLock" component={AppLockedScreen} />
<RootStack.Screen
name="LinkNotebooks"
component={LinkNotebooks as any}
/>
<RootStack.Screen name="MoveNotebook" component={MoveNotebook as any} />
<RootStack.Screen name="MoveNotes" component={MoveNotes as any} />
<AppStack.Screen name="Settings" component={Settings} />
</RootStack.Navigator>
</NavigationContainer>

View File

@@ -17,35 +17,34 @@ 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 { Note, Notebook } from "@notesnook/core";
import { Notebook } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { RefObject, useCallback, useEffect, useRef } from "react";
import React, { 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 { FlatList } from "react-native-actions-sheet";
import { SafeAreaView } from "react-native-safe-area-context";
import { db } from "../../common/database";
import Navigation, { NavigationProps } 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 { 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";
} 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 { updateNotebook } from "../../utils/notebooks";
import { AppFontSize } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import { FloatingButton } from "../../components/container/floating-button";
import { Header } from "../../components/header";
import { NotebookItem } from "../../components/side-menu/notebook-item";
import Input from "../../components/ui/input";
import Paragraph from "../../components/ui/typography/paragraph";
import { AddNotebookSheet } from "../../components/sheets/add-notebook";
import { Notice } from "../../components/ui/notice";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
const {
useNotebookExpandedStore,
@@ -90,19 +89,15 @@ async function updateInitialSelectionState(items: string[]) {
});
}
const MoveNoteSheet = ({
note,
actionSheetRef
}: {
note: Note | undefined;
actionSheetRef: RefObject<ActionSheetRef>;
}) => {
const LinkNotebooks = (props: NavigationProps<"LinkNotebooks">) => {
const { colors } = useThemeColors();
const [notebooks, loading] = useNotebooks();
const tree = useNotebookTreeStore((state) => state.tree);
const searchQuery = useRef("");
const searchTimer = useRef<NodeJS.Timeout>();
const [filteredNotebooks, setFilteredNotebooks] = React.useState(notebooks);
useNavigationFocus(props.navigation, { focusOnInit: true });
const loadRootNotebooks = React.useCallback(async () => {
if (!filteredNotebooks) return;
const _notebooks: Notebook[] = [];
@@ -145,8 +140,17 @@ const MoveNoteSheet = ({
);
const multiSelect = useNotebookSelectionStore((state) => state.multiSelect);
const hasSelection = useNotebookSelectionStore((state) =>
Object.keys(state.selection).some(
(key) =>
state.selection[key] && state.selection[key] !== state.initialState[key]
)
);
useEffect(() => {
const items = note ? [note.id] : selectedItemsList;
const items = props.route.params.note
? [props.route.params.note.id]
: selectedItemsList;
updateInitialSelectionState(items);
return () => {
useNotebookSelectionStore.setState({
@@ -156,10 +160,12 @@ const MoveNoteSheet = ({
canEnableMultiSelectMode: true
});
};
}, [note, selectedItemsList]);
}, [props.route.params.note, selectedItemsList]);
const onSave = async () => {
const noteIds = note ? [note.id] : selectedItemsList;
const noteIds = props.route.params.note
? [props.route.params.note.id]
: selectedItemsList;
const changedNotebooks = useNotebookSelectionStore.getState().selection;
@@ -181,12 +187,7 @@ const MoveNoteSheet = ({
Navigation.queueRoutesForUpdate();
useRelationStore.getState().update();
actionSheetRef.current?.hide();
};
const hasSelected = () => {
const selection = useNotebookSelectionStore.getState().selection;
return Object.keys(selection).some((key) => selection[key] === "selected");
Navigation.goBack();
};
const renderNotebook = useCallback(
@@ -197,136 +198,106 @@ const MoveNoteSheet = ({
);
return (
<>
<Dialog context="move_note" />
<SheetProvider context="link-notebooks" />
<View
<SafeAreaView
style={{
gap: DefaultAppStyles.GAP_VERTICAL,
flex: 1,
backgroundColor: colors.primary.background
}}
>
<Header
title="Link Notebooks"
canGoBack
rightButton={
hasSelection
? {
name: "restore",
onPress: () => {
const items = props.route.params.note
? [props.route.params.note.id]
: selectedItemsList;
updateInitialSelectionState(items);
}
}
: undefined
}
/>
<FlatList
data={tree}
windowSize={3}
style={{
gap: DefaultAppStyles.GAP_VERTICAL
width: "100%"
}}
>
<View
style={{
paddingHorizontal: DefaultAppStyles.GAP,
justifyContent: "space-between",
flexDirection: "row",
alignItems: "center",
borderBottomWidth: 1,
borderBottomColor: colors.primary.border,
paddingBottom: DefaultAppStyles.GAP_VERTICAL
}}
>
<DialogHeader
style={{
flexShrink: 1
}}
title={strings.selectNotebooks()}
paragraph={
!multiSelect
? strings.enableMultiSelect()
: strings.selectNotebooksDesc()
}
/>
keyboardShouldPersistTaps="handled"
ListHeaderComponent={
<View
style={{
flexDirection: "row",
columnGap: DefaultAppStyles.GAP_SMALL
paddingHorizontal: DefaultAppStyles.GAP,
width: "100%",
paddingVertical: DefaultAppStyles.GAP_VERTICAL
}}
>
{hasSelected() ? (
<IconButton
name="restore"
color={colors.primary.icon}
onPress={() => {
const items = note ? [note.id] : selectedItemsList;
updateInitialSelectionState(items);
}}
style={{
width: 40,
height: 40
}}
<Input
placeholder={strings.searchNotebooks()}
onChangeText={(value) => {
searchQuery.current = value;
searchTimer.current = setTimeout(() => {
updateNotebooks();
}, 300);
}}
button={{
icon: "plus",
onPress: () => {
AddNotebookSheet.present(
undefined,
undefined,
"link-notebooks",
undefined,
false,
searchQuery.current
);
},
color: colors.primary.icon
}}
/>
{!multiSelect ? (
<Notice
text={strings.enableMultiSelect()}
type="information"
size="small"
/>
) : null}
<Button
height={40}
style={{
paddingHorizontal: 24,
alignSelf: "flex-start"
}}
title={strings.save()}
type="accent"
onPress={onSave}
/>
</View>
</View>
<FlatList
data={tree}
windowSize={3}
style={{
width: "100%"
}}
keyboardShouldPersistTaps="handled"
ListHeaderComponent={
<View
style={{
paddingHorizontal: DefaultAppStyles.GAP,
width: "100%",
paddingTop: DefaultAppStyles.GAP_VERTICAL
}}
>
<Input
placeholder={strings.searchNotebooks()}
button={{
icon: "plus",
onPress: () => {
AddNotebookSheet.present(
undefined,
undefined,
"link-notebooks",
undefined,
false,
searchQuery.current
);
},
color: colors.primary.icon
}}
onChangeText={(value) => {
searchQuery.current = value;
searchTimer.current = setTimeout(() => {
updateNotebooks();
}, 300);
}}
}
renderItem={renderNotebook}
ListEmptyComponent={
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
height: 200
}}
>
{loading ? (
<ActivityIndicator
size={AppFontSize.lg}
color={colors.primary.accent}
/>
</View>
}
renderItem={renderNotebook}
ListEmptyComponent={
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
height: 200
}}
>
{loading ? (
<ActivityIndicator
size={AppFontSize.lg}
color={colors.primary.accent}
/>
) : (
<Paragraph color={colors.primary.icon}>
{strings.emptyPlaceholders("notebook")}
</Paragraph>
)}
</View>
}
/>
</View>
</>
) : (
<Paragraph color={colors.primary.icon}>
{strings.emptyPlaceholders("notebook")}
</Paragraph>
)}
</View>
}
/>
{hasSelection ? (
<FloatingButton icon="check" onPress={() => onSave()} />
) : null}
</SafeAreaView>
);
};
@@ -420,7 +391,7 @@ const NotebookItemWrapper = React.memo(
AddNotebookSheet.present(
undefined,
item.notebook,
"link-notebooks",
"global",
undefined,
false
);
@@ -441,10 +412,4 @@ const NotebookItemWrapper = React.memo(
}
);
NotebookItemWrapper.displayName = "NotebookItemWrapper";
MoveNoteSheet.present = (note?: Note) => {
presentSheet({
component: (ref) => <MoveNoteSheet actionSheetRef={ref} note={note} />
});
};
export default MoveNoteSheet;
export default LinkNotebooks;

View File

@@ -19,36 +19,37 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Notebook } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useCallback, useEffect, useState } from "react";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { Text, View } from "react-native";
import { FlatList } from "react-native-actions-sheet";
import { db } from "../../../common/database";
import { presentSheet } from "../../../services/event-manager";
import { SafeAreaView } from "react-native-safe-area-context";
import { db } from "../../common/database";
import { presentDialog } from "../../components/dialog/functions";
import { Header } from "../../components/header";
import { AddNotebookSheet } from "../../components/sheets/add-notebook";
import { NotebookItem } from "../../components/side-menu/notebook-item";
import {
useSideMenuNotebookExpandedStore,
useSideMenuNotebookSelectionStore
} from "../../components/side-menu/stores";
import { Button } from "../../components/ui/button";
import Input from "../../components/ui/input";
import Navigation, { NavigationProps } from "../../services/navigation";
import {
createNotebookTreeStores,
TreeItem
} from "../../../stores/create-notebook-tree-stores";
} from "../../stores/create-notebook-tree-stores";
import {
useNotebooks,
useNotebookStore
} from "../../../stores/use-notebook-store";
} from "../../stores/use-notebook-store";
import {
checkParentSelected,
findRootNotebookId,
getParentNotebookId
} from "../../../utils/notebooks";
import { DefaultAppStyles } from "../../../utils/styles";
import { Dialog } from "../../dialog";
import DialogHeader from "../../dialog/dialog-header";
import { presentDialog } from "../../dialog/functions";
import SheetProvider from "../../sheet-provider";
import { NotebookItem } from "../../side-menu/notebook-item";
import {
useSideMenuNotebookExpandedStore,
useSideMenuNotebookSelectionStore
} from "../../side-menu/stores";
import { Button } from "../../ui/button";
import { AddNotebookSheet } from "../add-notebook";
} from "../../utils/notebooks";
import { DefaultAppStyles } from "../../utils/styles";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
const {
useNotebookExpandedStore,
@@ -56,15 +57,12 @@ const {
useNotebookSelectionStore
} = createNotebookTreeStores(false, false, "move-notebook-tree-expanded");
export const MoveNotebookSheet = ({
selectedNotebooks,
close
}: {
selectedNotebooks: Notebook[];
close?: () => void;
}) => {
export const MoveNotebook = (props: NavigationProps<"MoveNotebook">) => {
const selectedNotebooks = props.route.params.selectedNotebooks;
const searchTimer = useRef<NodeJS.Timeout>();
const [notebooks, loading] = useNotebooks();
const { colors } = useThemeColors();
useNavigationFocus(props.navigation, { focusOnInit: true });
const tree = useNotebookTreeStore((state) => state.tree);
const lastQuery = React.useRef<string>();
const [filteredNotebooks, setFilteredNotebooks] = React.useState(notebooks);
@@ -171,14 +169,14 @@ export const MoveNotebookSheet = ({
enabled: false,
selection: {}
});
close?.();
Navigation.goBack();
}
});
}}
/>
);
},
[close, selectedNotebooks]
[selectedNotebooks]
);
const filteredTree = React.useMemo(
() =>
@@ -190,24 +188,20 @@ export const MoveNotebookSheet = ({
);
return (
<View>
<SheetProvider context="move-notebook" />
<Dialog context="move-notebook" />
<View
style={{
paddingHorizontal: DefaultAppStyles.GAP,
borderBottomWidth: 1,
borderBottomColor: colors.primary.border
}}
>
<DialogHeader
title={strings.moveNotebook(
selectedNotebooks.length,
selectedNotebooks[0].title
)}
/>
</View>
<SafeAreaView
style={{
gap: DefaultAppStyles.GAP_VERTICAL,
flex: 1,
backgroundColor: colors.primary.background
}}
>
<Header
title={strings.moveNotebook(
selectedNotebooks.length,
selectedNotebooks[0].title
)}
canGoBack
/>
<FlatList
data={filteredTree}
@@ -220,7 +214,30 @@ export const MoveNotebookSheet = ({
paddingHorizontal: DefaultAppStyles.GAP
}}
>
{moveToTopEnabled ? (
<Input
placeholder={strings.searchNotebooks()}
onChangeText={(value) => {
lastQuery.current = value;
searchTimer.current = setTimeout(() => {
updateNotebooks();
}, 300);
}}
button={{
icon: "plus",
onPress: () => {
AddNotebookSheet.present(
undefined,
undefined,
"global",
undefined,
false,
lastQuery.current
);
},
color: colors.primary.icon
}}
/>
{moveToTopEnabled && tree.length > 0 ? (
<Button
title={strings.moveToTop()}
style={{
@@ -254,7 +271,7 @@ export const MoveNotebookSheet = ({
enabled: false,
selection: {}
});
close?.();
Navigation.goBack();
}}
/>
) : null}
@@ -279,7 +296,7 @@ export const MoveNotebookSheet = ({
</View>
}
/>
</View>
</SafeAreaView>
);
};
@@ -378,11 +395,4 @@ const NotebookItemWrapper = React.memo(
);
NotebookItemWrapper.displayName = "NotebookItemWrapper";
MoveNotebookSheet.present = async (notebooks: Notebook[]) => {
presentSheet({
component: (ref, close) => (
<MoveNotebookSheet selectedNotebooks={notebooks} close={close} />
),
keyboardHandlerDisabled: true
});
};
MoveNotebook.present = async (notebooks: Notebook[]) => {};

View File

@@ -17,41 +17,34 @@ 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 { VirtualizedGrouping } from "@notesnook/core";
import { Note, Notebook } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme";
import React, { RefObject, useEffect, useRef, useState } from "react";
import { ActivityIndicator, TextInput, View } from "react-native";
import { ActionSheetRef } from "react-native-actions-sheet";
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
import { db } from "../../../common/database";
import { useDBItem } from "../../../hooks/use-db-item";
import { presentSheet } from "../../../services/event-manager";
import Navigation from "../../../services/navigation";
import { createItemSelectionStore } from "../../../stores/item-selection-store";
import { updateNotebook } from "../../../utils/notebooks";
import { AppFontSize } from "../../../utils/size";
import { Dialog } from "../../dialog";
import DialogHeader from "../../dialog/dialog-header";
import { Button } from "../../ui/button";
import { IconButton } from "../../ui/icon-button";
import Input from "../../ui/input";
import { Pressable } from "../../ui/pressable";
import Seperator from "../../ui/seperator";
import Paragraph from "../../ui/typography/paragraph";
import { Note, Notebook, VirtualizedGrouping } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useRef, useState } from "react";
import { ActivityIndicator, TextInput, View } from "react-native";
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
import { SafeAreaView } from "react-native-safe-area-context";
import { db } from "../../common/database";
import { FloatingButton } from "../../components/container/floating-button";
import { Header } from "../../components/header";
import AppIcon from "../../components/ui/AppIcon";
import Input from "../../components/ui/input";
import { Notice } from "../../components/ui/notice";
import { Pressable } from "../../components/ui/pressable";
import Heading from "../../components/ui/typography/heading";
import Paragraph from "../../components/ui/typography/paragraph";
import { useDBItem } from "../../hooks/use-db-item";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import Navigation, { NavigationProps } from "../../services/navigation";
import { createItemSelectionStore } from "../../stores/item-selection-store";
import { updateNotebook } from "../../utils/notebooks";
import { AppFontSize } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
const useItemSelectionStore = createItemSelectionStore(true);
export const MoveNotes = ({
notebook,
fwdRef
}: {
notebook: Notebook;
fwdRef: RefObject<ActionSheetRef>;
}) => {
export const MoveNotes = (props: NavigationProps<"MoveNotes">) => {
const { colors } = useThemeColors();
const currentNotebook = notebook;
const currentNotebook = props.route.params.notebook;
const inputRef = useRef<TextInput>(null);
const [loading, setLoading] = useState(false);
const selectionCount = useItemSelectionStore(
@@ -63,6 +56,8 @@ export const MoveNotes = ({
)?.length > 0
);
useNavigationFocus(props.navigation, { focusOnInit: true });
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();
const loadNotes = React.useCallback(
@@ -115,80 +110,94 @@ export const MoveNotes = ({
);
return (
<View
<SafeAreaView
style={{
paddingHorizontal: 12,
maxHeight: "100%",
height: "100%"
gap: DefaultAppStyles.GAP_VERTICAL,
flex: 1,
backgroundColor: colors.primary.background
}}
>
<Dialog context="local" />
<Header title={strings.linkNotes()} canGoBack />
<DialogHeader title={strings.addNotesToNotebook(currentNotebook.title)} />
<Seperator />
<Input
button={{
icon: "magnify",
color: colors.primary.accent,
size: AppFontSize.lg,
onPress: () => {}
<View
style={{
paddingHorizontal: DefaultAppStyles.GAP,
flex: 1,
gap: DefaultAppStyles.GAP_VERTICAL
}}
testID="search-input"
fwdRef={inputRef}
autoCapitalize="none"
onChangeText={(v) => {
loadNotes(v && v.trim() === "" ? undefined : v.trim());
}}
placeholder={strings.searchANote()}
/>
<FlashList
ListEmptyComponent={
<View
style={{
minHeight: 100,
justifyContent: "center",
alignItems: "center"
}}
>
{loading ? (
<ActivityIndicator size="large" color={colors.primary.accent} />
) : (
<Paragraph color={colors.secondary.paragraph}>
{strings.emptyPlaceholders("note")}
</Paragraph>
)}
</View>
}
estimatedItemSize={50}
data={loading ? [] : notes?.placeholders}
renderItem={renderItem}
/>
{selectionCount ? (
<Button
onPress={async () => {
await db.notes?.addToNotebook(
currentNotebook.id,
...useItemSelectionStore.getState().getSelectedItemIds()
);
await db.notes?.removeFromNotebook(
currentNotebook.id,
...useItemSelectionStore.getState().getDeselectedItemIds()
);
updateNotebook(currentNotebook.id);
Navigation.queueRoutesForUpdate();
fwdRef?.current?.hide();
>
<Input
button={{
icon: "magnify",
color: colors.primary.icon,
size: AppFontSize.lg,
onPress: () => {}
}}
title={strings.moveSelectedNotes()}
type="accent"
width="100%"
testID="search-input"
fwdRef={inputRef}
wrapperStyle={{
marginBottom: 0
}}
autoCapitalize="none"
onChangeText={(v) => {
loadNotes(v && v.trim() === "" ? undefined : v.trim());
}}
placeholder={strings.searchANote()}
/>
) : null}
</View>
<Notice
text={strings.linkingNotesTo(currentNotebook.title)}
size="small"
type="information"
style={{
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
/>
<FlashList
ListEmptyComponent={
<View
style={{
minHeight: 100,
justifyContent: "center",
alignItems: "center"
}}
>
{loading ? (
<ActivityIndicator size="large" color={colors.primary.accent} />
) : (
<Paragraph color={colors.secondary.paragraph}>
{strings.emptyPlaceholders("note")}
</Paragraph>
)}
</View>
}
estimatedItemSize={50}
data={loading ? [] : notes?.placeholders}
renderItem={renderItem}
/>
{selectionCount ? (
<FloatingButton
icon="check"
onPress={async () => {
await db.notes?.addToNotebook(
currentNotebook.id,
...useItemSelectionStore.getState().getSelectedItemIds()
);
await db.notes?.removeFromNotebook(
currentNotebook.id,
...useItemSelectionStore.getState().getDeselectedItemIds()
);
updateNotebook(currentNotebook.id);
Navigation.queueRoutesForUpdate();
Navigation.goBack();
}}
/>
) : null}
</View>
</SafeAreaView>
);
};
@@ -221,36 +230,40 @@ const SelectableNoteItem = React.memo(
}}
type={"transparent"}
style={{
paddingVertical: 12,
flexDirection: "row",
width: "100%",
justifyContent: "flex-start",
height: 50
height: 50,
paddingHorizontal: DefaultAppStyles.GAP_SMALL
}}
>
{!item ? null : (
<>
<IconButton
<View
style={{
backgroundColor: "transparent",
marginRight: 5
flexGrow: 1
}}
onPress={() => {
if (!item) return;
useItemSelectionStore
.getState()
.markAs(item, selected ? "deselected" : "selected");
>
<Heading size={AppFontSize.sm} numberOfLines={1}>
{item?.title}
</Heading>
{item.type == "note" && item.headline ? (
<Paragraph
numberOfLines={1}
color={colors?.secondary.paragraph}
size={AppFontSize.sm}
>
{item.headline}
</Paragraph>
) : null}
</View>
<AppIcon
style={{
backgroundColor: "transparent"
}}
top={0}
left={0}
right={0}
bottom={0}
name={
selected
? "check-circle-outline"
: "checkbox-blank-circle-outline"
}
type="plain"
size={AppFontSize.lg}
name={selected ? "checkbox-outline" : "checkbox-blank-outline"}
color={
selected
? colors.selected.icon
@@ -259,23 +272,6 @@ const SelectableNoteItem = React.memo(
: colors.primary.icon
}
/>
<View
style={{
flexShrink: 1
}}
>
<Paragraph numberOfLines={1}>{item?.title}</Paragraph>
{item.type == "note" && item.headline ? (
<Paragraph
numberOfLines={1}
color={colors?.secondary.paragraph}
size={AppFontSize.xs}
>
{item.headline}
</Paragraph>
) : null}
</View>
</>
)}
</Pressable>
@@ -285,11 +281,4 @@ const SelectableNoteItem = React.memo(
SelectableNoteItem.displayName = "SelectableNoteItem";
MoveNotes.present = (notebook?: Notebook) => {
if (!notebook) return;
presentSheet({
component: (ref: RefObject<ActionSheetRef>) => (
<MoveNotes fwdRef={ref} notebook={notebook} />
)
});
};
MoveNotes.present = (notebook?: Notebook) => {};

View File

@@ -63,7 +63,10 @@ const routeNames = {
AppLock: "AppLock",
Login: "Login",
Signup: "Signup",
Reminders: "Reminders"
Reminders: "Reminders",
MoveNotebook: "MoveNotebook",
LinkNotebooks: "LinkNotebooks",
MoveNotes: "MoveNotes"
};
export type NavigationProps<T extends RouteName> = NativeStackScreenProps<

View File

@@ -17,9 +17,9 @@ 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 { FilteredSelector } from "@notesnook/core";
import {
Color,
FilteredSelector,
Item,
ItemType,
Note,
@@ -28,8 +28,8 @@ import {
Tag,
TrashItem
} from "@notesnook/core";
import create, { State } from "zustand";
import { ParamListBase } from "@react-navigation/core";
import create, { State } from "zustand";
export type GenericRouteParam = undefined;
@@ -81,6 +81,15 @@ export interface RouteParams extends ParamListBase {
AppLock: GenericRouteParam;
Settings: GenericRouteParam;
Auth: AuthParams;
LinkNotebooks: {
note?: Note;
};
MoveNotebook: {
selectedNotebooks: Notebook[];
};
MoveNotes: {
notebook: Notebook;
};
}
export type RouteName = keyof RouteParams;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -454,7 +454,8 @@ $headline$: Use starting line of the note as title.`,
selectNotebooksDesc: () => t`Select notebooks you want to add note(s) to.`,
selectNotebooksDesktopDesc: (keyboardShortcut: string) =>
t`Use ${keyboardShortcut}+click to select multiple notebooks`,
enableMultiSelect: () => t`Tap and hold to enable multi-select.`,
enableMultiSelect: () =>
t`You can also link a note to multiple Notebooks. Tap and hold any notebook to enable multi-select.`,
changeEmail: () => t`Change email address`,
changeEmailDesc: () =>
t`Your account email will be changed without affecting your subscription or any other settings.`,
@@ -2453,5 +2454,7 @@ Use this if changes from other devices are not appearing on this device. This wi
believerPlan: () => t`Believer plan`,
storage: () => t`Storage`,
used: () => t`used`,
editProfile: () => t`Edit profile`
editProfile: () => t`Edit profile`,
linkNotes: () => t`Link notes`,
linkingNotesTo: (title: string) => t`Select notes to link to "${title}"`
};