Files
notesnook/apps/mobile/app/screens/notes/index.tsx

248 lines
7.5 KiB
TypeScript
Raw Normal View History

/*
This file is part of the Notesnook project (https://notesnook.com/)
2023-01-16 13:44:52 +05:00
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/>.
*/
2022-08-30 16:13:11 +05:00
2024-04-09 15:35:07 +05:00
import { resolveItems } from "@notesnook/common";
2023-11-22 11:16:15 +05:00
import { VirtualizedGrouping } from "@notesnook/core";
2024-09-23 15:18:44 +05:00
import { Color, Note } from "@notesnook/core";
2022-08-29 16:19:17 +05:00
import React, { useEffect, useRef, useState } from "react";
2024-04-09 15:35:07 +05:00
import { db } from "../../common/database";
import { FloatingButton } from "../../components/container/floating-button";
import DelayLayout from "../../components/delay-layout";
2023-11-22 11:16:15 +05:00
import { Header } from "../../components/header";
import List from "../../components/list";
2023-11-22 11:16:15 +05:00
import { PlaceholderData } from "../../components/list/empty";
2024-04-09 15:35:07 +05:00
import SelectionHeader from "../../components/selection-header";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import {
eSubscribeEvent,
eUnSubscribeEvent
} from "../../services/event-manager";
2022-08-30 18:27:09 +05:00
import Navigation, { NavigationProps } from "../../services/navigation";
import useNavigationStore, {
HeaderRightButton,
2022-08-30 18:27:09 +05:00
NotesScreenParams,
RouteName
} from "../../stores/use-navigation-store";
2023-11-22 11:16:15 +05:00
import { setOnFirstSave } from "./common";
2024-08-13 15:13:46 +05:00
import { strings } from "@notesnook/intl";
2022-04-24 05:59:14 +05:00
2022-04-25 00:37:09 +05:00
export interface RouteProps<T extends RouteName> extends NavigationProps<T> {
2023-11-16 08:54:37 +05:00
get: (
params: NotesScreenParams,
grouped?: boolean
) => Promise<VirtualizedGrouping<Note> | Note[]>;
placeholder: PlaceholderData;
2022-04-24 05:59:14 +05:00
onPressFloatingButton: () => void;
focusControl?: boolean;
canGoBack?: boolean;
rightButtons?: (params: NotesScreenParams) => HeaderRightButton[];
}
2022-07-08 20:54:55 +05:00
function getItemType(routeName: RouteName) {
if (routeName === "TaggedNotes") return "tag";
if (routeName === "ColoredNotes") return "color";
if (routeName === "Monographs") return "monograph";
return "note";
2022-07-08 20:54:55 +05:00
}
2022-04-24 05:59:14 +05:00
const NotesPage = ({
route,
navigation,
get,
2023-11-16 08:54:37 +05:00
placeholder,
2022-04-24 05:59:14 +05:00
onPressFloatingButton,
2022-06-13 10:55:34 +05:00
focusControl = true,
2022-04-24 05:59:14 +05:00
rightButtons
2024-04-09 15:35:07 +05:00
}: RouteProps<"NotesPage" | "TaggedNotes" | "Monographs" | "ColoredNotes">) => {
2022-04-24 05:59:14 +05:00
const params = useRef<NotesScreenParams>(route?.params);
2023-11-16 08:54:37 +05:00
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();
const [loadingNotes, setLoadingNotes] = useState(true);
const isMonograph = route.name === "Monographs";
2023-11-22 11:16:15 +05:00
const title =
params.current?.item.type === "tag"
? "#" + params.current?.item.title
: params.current?.item.title;
const accentColor =
route.name === "ColoredNotes"
? (params.current?.item as Color)?.colorCode
: undefined;
2024-04-30 20:15:21 +05:00
const updateOnFocus = useRef(false);
2022-04-24 05:59:14 +05:00
const isFocused = useNavigationFocus(navigation, {
onFocus: (prev) => {
2024-04-30 20:15:21 +05:00
if (updateOnFocus.current) {
onRequestUpdate();
updateOnFocus.current = false;
} else {
Navigation.routeNeedsUpdate(route.name, onRequestUpdate);
}
2022-04-24 05:59:14 +05:00
syncWithNavigation();
2022-06-11 15:12:50 +05:00
if (focusControl) return !prev.current;
2022-04-24 05:59:14 +05:00
return false;
},
onBlur: () => {
2024-04-30 20:15:21 +05:00
updateOnFocus.current = false;
2022-04-24 05:59:14 +05:00
setOnFirstSave(null);
return false;
},
focusOnInit: !focusControl
});
2022-08-30 18:27:09 +05:00
const syncWithNavigation = React.useCallback(() => {
2023-11-22 11:16:15 +05:00
const { item } = params.current;
useNavigationStore
.getState()
.setFocusedRouteId(params?.current?.item?.id || route.name);
2022-04-24 05:59:14 +05:00
!isMonograph &&
setOnFirstSave({
2022-07-08 20:54:55 +05:00
type: getItemType(route.name),
2023-11-22 11:16:15 +05:00
id: item.id
2022-04-24 05:59:14 +05:00
});
2023-11-24 15:11:38 +05:00
}, [isMonograph, route.name]);
2022-08-30 18:27:09 +05:00
const onRequestUpdate = React.useCallback(
2023-11-16 08:54:37 +05:00
async (data?: NotesScreenParams) => {
2024-04-30 20:15:21 +05:00
if (
useNavigationStore.getState().focusedRouteId !==
params.current.item.id &&
!data
) {
updateOnFocus.current = false;
return;
}
2022-08-30 18:27:09 +05:00
const isNew = data && data?.item?.id !== params.current?.item?.id;
if (data) params.current = data;
2024-02-15 10:30:07 +05:00
2022-08-30 18:27:09 +05:00
try {
if (isNew) setLoadingNotes(true);
2023-11-16 08:54:37 +05:00
const notes = (await get(
params.current,
true
)) as VirtualizedGrouping<Note>;
2024-02-15 10:30:07 +05:00
if (route.name === "TaggedNotes" || route.name === "ColoredNotes") {
const item = await (db as any)[params.current.item.type + "s"][
params.current.item.type
](params.current.item.id);
if (!item) {
Navigation.goBack();
return;
}
params.current.item = item;
params.current.title = item.title;
}
2023-12-27 09:40:15 +05:00
if (notes.placeholders.length === 0) setLoadingNotes(false);
2022-08-30 18:27:09 +05:00
setNotes(notes);
2023-12-16 11:30:41 +05:00
await notes.item(0, resolveItems);
setLoadingNotes(false);
2022-08-30 18:27:09 +05:00
syncWithNavigation();
} catch (e) {
console.error(e);
2022-04-24 05:59:14 +05:00
}
2022-08-30 18:27:09 +05:00
},
2024-02-15 10:30:07 +05:00
[get, route.name, syncWithNavigation]
2022-08-30 18:27:09 +05:00
);
2022-04-24 05:59:14 +05:00
2022-06-13 10:55:34 +05:00
useEffect(() => {
2023-12-16 14:58:32 +05:00
if (loadingNotes) {
2023-11-16 08:54:37 +05:00
get(params.current, true)
2023-12-16 11:30:41 +05:00
.then(async (items) => {
2023-11-16 08:54:37 +05:00
setNotes(items as VirtualizedGrouping<Note>);
2023-12-16 11:30:41 +05:00
await (items as VirtualizedGrouping<Note>).item(0, resolveItems);
2023-11-16 08:54:37 +05:00
setLoadingNotes(false);
})
.catch((e) => {
setLoadingNotes(false);
});
2022-07-09 17:30:14 +05:00
}
2023-12-16 14:58:32 +05:00
}, [loadingNotes, get]);
2022-06-13 10:55:34 +05:00
2022-04-24 05:59:14 +05:00
useEffect(() => {
eSubscribeEvent(route.name, onRequestUpdate);
return () => {
setOnFirstSave(null);
2022-04-24 05:59:14 +05:00
eUnSubscribeEvent(route.name, onRequestUpdate);
};
2022-08-30 18:27:09 +05:00
}, [onRequestUpdate, route.name]);
2022-04-24 05:59:14 +05:00
return (
2023-11-22 11:16:15 +05:00
<>
2024-02-15 10:30:07 +05:00
<SelectionHeader
id={route.params?.item?.id}
items={notes}
type="note"
renderedInRoute={route.name}
/>
2023-11-22 11:16:15 +05:00
<Header
renderedInRoute={route.name}
2024-08-13 15:13:46 +05:00
title={
2024-11-27 13:55:41 +05:00
route.name === "Monographs" ? strings.routes[route.name]() : title
2024-08-13 15:13:46 +05:00
}
2023-11-22 11:16:15 +05:00
canGoBack={params?.current?.canGoBack}
hasSearch={true}
id={
route.name === "Monographs" ? "Monographs" : params?.current.item?.id
2023-11-16 08:54:37 +05:00
}
2023-11-22 11:16:15 +05:00
onSearch={() => {
const selector =
route.name === "Monographs"
? db.monographs.all
: db.relations.from(params.current.item, "note").selector;
2023-11-22 11:16:15 +05:00
Navigation.push("Search", {
2024-08-13 15:13:46 +05:00
placeholder: strings.searchInRoute(title || route.name),
2023-11-22 11:16:15 +05:00
type: "note",
title: title,
route: route.name,
items: selector
2023-11-22 11:16:15 +05:00
});
}}
accentColor={accentColor}
onPressDefaultRightButton={onPressFloatingButton}
headerRightButtons={rightButtons?.(params?.current)}
2022-04-24 05:59:14 +05:00
/>
2023-12-16 14:58:32 +05:00
<DelayLayout color={accentColor} wait={loadingNotes}>
2023-11-22 11:16:15 +05:00
<List
data={notes}
dataType="note"
onRefresh={onRequestUpdate}
2023-12-16 14:58:32 +05:00
loading={!isFocused}
2023-11-24 15:11:38 +05:00
renderedInRoute={route.name}
2023-12-16 11:30:41 +05:00
id={params.current.item?.id}
2024-08-13 15:13:46 +05:00
headerTitle={title || "Monographs"}
2023-11-22 11:16:15 +05:00
customAccentColor={accentColor}
placeholder={placeholder}
2023-11-16 08:54:37 +05:00
/>
2023-11-22 11:16:15 +05:00
{!isMonograph &&
2023-12-27 09:40:15 +05:00
((notes?.placeholders && (notes?.placeholders?.length || 0) > 0) ||
isFocused) ? (
2024-08-13 15:13:46 +05:00
<FloatingButton color={accentColor} onPress={onPressFloatingButton} />
2023-11-22 11:16:15 +05:00
) : null}
</DelayLayout>
</>
2022-04-24 05:59:14 +05:00
);
};
export default NotesPage;