mobile: refactor zustand stores

This commit is contained in:
Ammar Ahmed
2023-12-16 14:58:32 +05:00
committed by Abdullah Atta
parent 787c36c754
commit c444f7ddb8
41 changed files with 938 additions and 846 deletions

View File

@@ -31,16 +31,16 @@ import { RNSqliteDriver } from "./sqlite.kysely";
database.host(
__DEV__
? {
API_HOST: "https://api.notesnook.com",
AUTH_HOST: "https://auth.streetwriters.co",
SSE_HOST: "https://events.streetwriters.co",
SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
ISSUES_HOST: "https://issues.streetwriters.co"
// API_HOST: "http://192.168.43.5:5264",
// AUTH_HOST: "http://192.168.43.5:8264",
// SSE_HOST: "http://192.168.43.5:7264",
// SUBSCRIPTIONS_HOST: "http://192.168.43.5:9264",
// ISSUES_HOST: "http://192.168.43.5:2624"
// API_HOST: "https://api.notesnook.com",
// AUTH_HOST: "https://auth.streetwriters.co",
// SSE_HOST: "https://events.streetwriters.co",
// SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
// ISSUES_HOST: "https://issues.streetwriters.co"
API_HOST: "http://192.168.43.5:5264",
AUTH_HOST: "http://192.168.43.5:8264",
SSE_HOST: "http://192.168.43.5:7264",
SUBSCRIPTIONS_HOST: "http://192.168.43.5:9264",
ISSUES_HOST: "http://192.168.43.5:2624"
}
: {
API_HOST: "https://api.notesnook.com",
@@ -59,7 +59,7 @@ database.setup({
compress: Gzip.deflate,
decompress: Gzip.inflate
},
batchSize: 500,
batchSize: 100,
sqliteOptions: {
dialect: (name) => ({
createDriver: () => {

View File

@@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { useNoteStore } from "../../stores/use-notes-store";
import { AnnouncementDialog } from "../announcements";
import AuthModal from "../auth/auth-modal";
import { SessionExpired } from "../auth/session-expired";
@@ -40,7 +39,6 @@ import RestoreDataSheet from "../sheets/restore-data";
const DialogProvider = () => {
const { colors } = useThemeColors();
const loading = useNoteStore((state) => state.loading);
return (
<>
@@ -57,7 +55,7 @@ const DialogProvider = () => {
<VaultDialog colors={colors} />
<RateAppSheet />
<ImagePreview />
{loading ? null : <Expiring />}
<Expiring />
<AnnouncementDialog />
<SessionExpired />
<PDFPreview />

View File

@@ -57,7 +57,7 @@ export const openNotebook = (item: Notebook | BaseTrashItem<Notebook>) => {
},
onClose: async () => {
await db.trash.delete(item.id);
useTrashStore.getState().setTrash();
useTrashStore.getState().refresh();
useSelectionStore.getState().setSelectionMode(undefined);
ToastManager.show({
heading: "Permanently deleted items",

View File

@@ -35,6 +35,12 @@ import React, { useCallback, useEffect, useRef, useState } from "react";
import { View } from "react-native";
import { db } from "../../common/database";
import { eSendEvent } from "../../services/event-manager";
import {
NotebooksWithDateEdited,
TagsWithDateEdited,
isNoteResolvedData,
resolveItems
} from "../../stores/resolve-items";
import { RouteName } from "../../stores/use-navigation-store";
import { eOpenJumpToDialog } from "../../utils/events";
import { SectionHeader } from "../list-items/headers/section-header";
@@ -43,10 +49,6 @@ import { NotebookWrapper } from "../list-items/notebook/wrapper";
import ReminderItem from "../list-items/reminder";
import TagItem from "../list-items/tag";
export type WithDateEdited<T> = { items: T[]; dateEdited: number };
export type NotebooksWithDateEdited = WithDateEdited<Notebook>;
export type TagsWithDateEdited = WithDateEdited<Tag>;
type ListItemWrapperProps<TItem = Item> = {
group?: GroupingKey;
items: VirtualizedGrouping<TItem> | undefined;
@@ -279,137 +281,3 @@ function getDate(item: Item, groupType?: GroupingKey): number {
) || 0
);
}
function withDateEdited<
T extends { dateEdited: number } | { dateModified: number }
>(items: T[]): WithDateEdited<T> {
let latestDateEdited = 0;
items.forEach((item) => {
const date = "dateEdited" in item ? item.dateEdited : item.dateModified;
if (latestDateEdited < date) latestDateEdited = date;
});
return { dateEdited: latestDateEdited, items };
}
export async function resolveItems(ids: string[], items: Item[]) {
if (!ids.length || !items.length) return [];
const { type } = items[0];
if (type === "note") return resolveNotes(ids);
else if (type === "notebook") {
return Promise.all(ids.map((id) => db.notebooks.totalNotes(id)));
} else if (type === "tag") {
return Promise.all(
ids.map((id) => db.relations.from({ id, type: "tag" }, "note").count())
);
}
return [];
}
type NoteResolvedData = {
notebooks?: NotebooksWithDateEdited;
reminder?: Reminder;
color?: Color;
tags?: TagsWithDateEdited;
attachmentsCount?: number;
};
async function resolveNotes(ids: string[]) {
console.time("relations");
const relations = [
...(await db.relations
.to({ type: "note", ids }, ["notebook", "tag", "color"])
.get()),
...(await db.relations.from({ type: "note", ids }, "reminder").get())
];
console.timeEnd("relations");
const relationIds: {
notebooks: Set<string>;
colors: Set<string>;
tags: Set<string>;
reminders: Set<string>;
} = {
colors: new Set(),
notebooks: new Set(),
tags: new Set(),
reminders: new Set()
};
const grouped: Record<
string,
{
notebooks: string[];
color?: string;
tags: string[];
reminder?: string;
}
> = {};
for (const relation of relations) {
const noteId =
relation.toType === "reminder" ? relation.fromId : relation.toId;
const data = grouped[noteId] || {
notebooks: [],
tags: []
};
if (relation.toType === "reminder" && !data.reminder) {
data.reminder = relation.toId;
relationIds.reminders.add(relation.toId);
} else if (relation.fromType === "notebook" && data.notebooks.length < 2) {
data.notebooks.push(relation.fromId);
relationIds.notebooks.add(relation.fromId);
} else if (relation.fromType === "tag" && data.tags.length < 3) {
data.tags.push(relation.fromId);
relationIds.tags.add(relation.fromId);
} else if (relation.fromType === "color" && !data.color) {
data.color = relation.fromId;
relationIds.colors.add(relation.fromId);
}
grouped[noteId] = data;
}
console.time("resolve");
const resolved = {
notebooks: await db.notebooks.all.records(
Array.from(relationIds.notebooks)
),
tags: await db.tags.all.records(Array.from(relationIds.tags)),
colors: await db.colors.all.records(Array.from(relationIds.colors)),
reminders: await db.reminders.all.records(Array.from(relationIds.reminders))
};
console.timeEnd("resolve");
const data: NoteResolvedData[] = [];
for (const noteId of ids) {
const group = grouped[noteId];
if (!group) {
data.push({});
continue;
}
data.push({
color: group.color ? resolved.colors[group.color] : undefined,
reminder: group.reminder ? resolved.reminders[group.reminder] : undefined,
tags: withDateEdited(
group.tags.map((id) => resolved.tags[id]).filter(Boolean)
),
notebooks: withDateEdited(
group.notebooks.map((id) => resolved.notebooks[id]).filter(Boolean)
),
attachmentsCount:
(await db.attachments?.ofNote(noteId, "all").ids())?.length || 0
});
}
return data;
}
export function isNoteResolvedData(data: unknown): data is NoteResolvedData {
return (
typeof data === "object" &&
!!data &&
"notebooks" in data &&
"reminder" in data &&
"color" in data &&
"tags" in data
);
}

View File

@@ -84,7 +84,7 @@ export default function NotePreview({ session, content, note }) {
context: "local",
positivePress: async () => {
await db.trash.delete(note.id);
useTrashStore.getState().setTrash();
useTrashStore.getState().refresh();
useSelectionStore.getState().setSelectionMode(false);
ToastManager.show({
heading: "Permanently deleted items",

View File

@@ -20,13 +20,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Note } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme";
import React, { RefObject, useCallback, useEffect } from "react";
import { Keyboard, TouchableOpacity, View } from "react-native";
import {
ActivityIndicator,
Keyboard,
TouchableOpacity,
View
} from "react-native";
import { ActionSheetRef, FlashList } from "react-native-actions-sheet";
import { db } from "../../../common/database";
import { presentSheet } from "../../../services/event-manager";
import Navigation from "../../../services/navigation";
import { ItemSelection } from "../../../stores/item-selection-store";
import { useNotebookStore } from "../../../stores/use-notebook-store";
import {
useNotebookStore,
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";
@@ -39,6 +47,7 @@ import { IconButton } from "../../ui/icon-button";
import Paragraph from "../../ui/typography/paragraph";
import { NotebookItem } from "./notebook-item";
import { useNotebookItemSelectionStore } from "./store";
import { SIZE } from "../../../utils/size";
async function updateInitialSelectionState(items: string[]) {
const relations = await db.relations
@@ -85,7 +94,7 @@ const MoveNoteSheet = ({
actionSheetRef: RefObject<ActionSheetRef>;
}) => {
const { colors } = useThemeColors();
const notebooks = useNotebookStore((state) => state.notebooks);
const [notebooks, loading] = useNotebooks();
const dimensions = useSettingStore((state) => state.dimensions);
const selectedItemsList = useSelectionStore(
(state) => state.selectedItemsList
@@ -234,7 +243,16 @@ const MoveNoteSheet = ({
height: 200
}}
>
<Paragraph color={colors.primary.icon}>No notebooks</Paragraph>
{loading ? (
<ActivityIndicator
size={SIZE.lg}
color={colors.primary.accent}
/>
) : (
<Paragraph color={colors.primary.icon}>
No notebooks
</Paragraph>
)}
</View>
}
ListFooterComponent={<View style={{ height: 50 }} />}

View File

@@ -177,7 +177,7 @@ const ManageTagsSheet = (props: {
}
useRelationStore.getState().update();
useTagStore.getState().setTags();
useTagStore.getState().refresh();
setQuery(undefined);
} catch (e) {
ToastManager.show({
@@ -225,7 +225,7 @@ const ManageTagsSheet = (props: {
console.error(e);
}
}
useTagStore.getState().setTags();
useTagStore.getState().refresh();
useRelationStore.getState().update();
refreshTags();
setTimeout(() => {

View File

@@ -26,7 +26,7 @@ import { ColoredNotes } from "../../screens/notes/colored";
import Navigation from "../../services/navigation";
import { useMenuStore } from "../../stores/use-menu-store";
import useNavigationStore from "../../stores/use-navigation-store";
import { useNoteStore } from "../../stores/use-notes-store";
import { useSettingStore } from "../../stores/use-setting-store";
import { SIZE, normalize } from "../../utils/size";
import { presentDialog } from "../dialog/functions";
import { PressableButton } from "../ui/pressable";
@@ -35,8 +35,11 @@ import Paragraph from "../ui/typography/paragraph";
export const ColorSection = React.memo(
function ColorSection() {
const colorNotes = useMenuStore((state) => state.colorNotes);
const loading = useNoteStore((state) => state.loading);
const [colorNotes, loadingColors] = useMenuStore((state) => [
state.colorNotes,
state.loadingColors
]);
const loading = useSettingStore((state) => state.isAppLoading);
const setColorNotes = useMenuStore((state) => state.setColorNotes);
useEffect(() => {
@@ -45,9 +48,11 @@ export const ColorSection = React.memo(
}
}, [loading, setColorNotes]);
return colorNotes.map((item) => {
return <ColorItem key={item.id} item={item} />;
});
return !loadingColors
? colorNotes.map((item) => {
return <ColorItem key={item.id} item={item} />;
})
: null;
},
() => true
);

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 { useThemeColors } from "@notesnook/theme";
import React, { useCallback } from "react";
import { FlatList, View } from "react-native";
import { notesnook } from "../../../e2e/test.ids";
@@ -24,18 +25,16 @@ import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { DDS } from "../../services/device-detection";
import { eSendEvent } from "../../services/event-manager";
import Navigation from "../../services/navigation";
import { useNoteStore } from "../../stores/use-notes-store";
import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeColors } from "@notesnook/theme";
import { useThemeStore } from "../../stores/use-theme-store";
import { useUserStore } from "../../stores/use-user-store";
import { MenuItemsList } from "../../utils/menu-items";
import { SUBSCRIPTION_STATUS } from "../../utils/constants";
import { eOpenPremiumDialog } from "../../utils/events";
import { MenuItemsList } from "../../utils/menu-items";
import { ColorSection } from "./color-section";
import { MenuItem } from "./menu-item";
import { TagsSection } from "./pinned-section";
import { UserStatus } from "./user-status";
import { useThemeStore } from "../../stores/use-theme-store";
export const SideMenu = React.memo(
function SideMenu() {
@@ -44,7 +43,8 @@ export const SideMenu = React.memo(
const subscriptionType = useUserStore(
(state) => state.user?.subscription?.type
);
const loading = useNoteStore((state) => state.loading);
const isAppLoading = useSettingStore((state) => state.isAppLoading);
const introCompleted = useSettingStore(
(state) => state.settings.introCompleted
);
@@ -96,7 +96,7 @@ export const SideMenu = React.memo(
[noTextMode]
);
return !loading && introCompleted ? (
return !isAppLoading && introCompleted ? (
<View
style={{
height: "100%",

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Notebook, Tag } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme";
import React, { useCallback, useEffect, useRef, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import { FlatList, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database";
@@ -28,7 +28,7 @@ import { TaggedNotes } from "../../screens/notes/tagged";
import Navigation from "../../services/navigation";
import { useMenuStore } from "../../stores/use-menu-store";
import useNavigationStore from "../../stores/use-navigation-store";
import { useNoteStore } from "../../stores/use-notes-store";
import { useSettingStore } from "../../stores/use-setting-store";
import { SIZE, normalize } from "../../utils/size";
import { Properties } from "../properties";
import { Button } from "../ui/button";
@@ -41,8 +41,8 @@ import Paragraph from "../ui/typography/paragraph";
export const TagsSection = React.memo(
function TagsSection() {
const menuPins = useMenuStore((state) => state.menuPins);
const loading = useNoteStore((state) => state.loading);
const [menuPins] = useMenuStore((state) => [state.menuPins]);
const loading = useSettingStore((state) => state.isAppLoading);
const setMenuPins = useMenuStore((state) => state.setMenuPins);
useEffect(() => {

View File

@@ -192,7 +192,7 @@ export const useActions = ({
});
InteractionManager.runAfterInteractions(() => {
useTagStore.getState().setTags();
useTagStore.getState().refresh();
useMenuStore.getState().setMenuPins();
Navigation.queueRoutesForUpdate();
useRelationStore.getState().update();
@@ -223,7 +223,7 @@ export const useActions = ({
await db.tags.remove(item.id);
}
setImmediate(() => {
useTagStore.getState().setTags();
useTagStore.getState().refresh();
Navigation.queueRoutesForUpdate();
useRelationStore.getState().update();
});

View File

@@ -78,11 +78,10 @@ import {
import PremiumService from "../services/premium";
import SettingsService from "../services/settings";
import Sync from "../services/sync";
import { initAfterSync, initialize } from "../stores";
import { initAfterSync } from "../stores";
import { useAttachmentStore } from "../stores/use-attachment-store";
import { useEditorStore } from "../stores/use-editor-store";
import { useMessageStore } from "../stores/use-message-store";
import { useNoteStore } from "../stores/use-notes-store";
import { useSettingStore } from "../stores/use-setting-store";
import { SyncStatus, useUserStore } from "../stores/use-user-store";
import { updateStatusBarColor } from "../utils/colors";
@@ -278,7 +277,7 @@ const onSubscriptionError = async (error: RNIap.PurchaseError) => {
const SodiumEventEmitter = new NativeEventEmitter(NativeModules.Sodium);
export const useAppEvents = () => {
const loading = useNoteStore((state) => state.loading);
const loading = useSettingStore((state) => state.isAppLoading);
const [setLastSynced, setUser, appLocked, syncing] = useUserStore((state) => [
state.setLastSynced,
state.setUser,
@@ -628,8 +627,7 @@ export const useAppEvents = () => {
if (!db.isInitialized) {
await db.init();
}
await initialize();
useNoteStore.getState().setLoading(false);
useSettingStore.getState().setAppLoading(false);
},
disableClosing: true
});
@@ -657,9 +655,8 @@ export const useAppEvents = () => {
}
}
if (IsDatabaseMigrationRequired()) return;
initialize();
setImmediate(() => {
useNoteStore.getState().setLoading(false);
useSettingStore.getState().setAppLoading(false);
});
Walkthrough.init();
} catch (e) {
@@ -675,7 +672,7 @@ export const useAppEvents = () => {
if (
!state.appLocked &&
db.isInitialized &&
useNoteStore.getState().loading
useSettingStore.getState().isAppLoading
) {
initializeDatabase();
sub();

View File

@@ -132,7 +132,7 @@ const Tabs = React.memo(_Tabs, () => true);
const _NavigationStack = () => {
const clearSelection = useSelectionStore((state) => state.clearSelection);
const loading = useNoteStore((state) => state.loading);
const loading = useSettingStore((state) => state.isAppLoading);
const onStateChange = React.useCallback(() => {
if (useSelectionStore.getState().selectionMode) {
clearSelection(true);

View File

@@ -138,7 +138,7 @@ export const useEditorEvents = (
const deviceMode = useSettingStore((state) => state.deviceMode);
const fullscreen = useSettingStore((state) => state.fullscreen);
const corsProxy = useSettingStore((state) => state.settings.corsProxy);
const loading = useNoteStore((state) => state.loading);
const loading = useSettingStore((state) => state.isAppLoading);
const [dateFormat, timeFormat] = useSettingStore((state) => [
state.dateFormat,
state.timeFormat
@@ -405,7 +405,7 @@ export const useEditorEvents = (
db.relations
.unlink(editorMessage.value as ItemReference, editor.note.current)
.then(async () => {
useTagStore.getState().setTags();
useTagStore.getState().refresh();
useRelationStore.getState().update();
await editor.commands.setTags(editor.note.current);
Navigation.queueRoutesForUpdate();

View File

@@ -48,7 +48,7 @@ import Notifications from "../../../services/notifications";
import SettingsService from "../../../services/settings";
import { TipManager } from "../../../services/tip-manager";
import { useEditorStore } from "../../../stores/use-editor-store";
import { useNoteStore } from "../../../stores/use-notes-store";
import { useSettingStore } from "../../../stores/use-setting-store";
import { useTagStore } from "../../../stores/use-tag-store";
import { eClearEditor, eOnLoadNote } from "../../../utils/events";
import { tabBarRef } from "../../../utils/global-refs";
@@ -90,7 +90,7 @@ export const useEditor = (
const sessionHistoryId = useRef<number>();
const state = useRef<Partial<EditorState>>(defaultState);
const placeholderTip = useRef(TipManager.placeholderTip());
const tags = useTagStore((state) => state.tags);
const tags = useTagStore((state) => state.items);
const insets = useGlobalSafeAreaInsets();
const isDefaultEditor = editorId === "";
const saveCount = useRef(0);
@@ -585,9 +585,9 @@ export const useEditor = (
tabBarRef.current?.goToPage(1, false);
}
if (appState.note) {
if (useNoteStore.getState().loading) {
const remove = useNoteStore.subscribe((state) => {
if (!state.loading && appState.note) {
if (useSettingStore.getState().isAppLoading) {
const remove = useSettingStore.subscribe((state) => {
if (!state.isAppLoading && appState.note) {
loadNote({
item: appState.note
});

View File

@@ -21,21 +21,18 @@ import React from "react";
import DelayLayout from "../../components/delay-layout";
import { Header } from "../../components/header";
import List from "../../components/list";
import SelectionHeader from "../../components/selection-header";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import Navigation, { NavigationProps } from "../../services/navigation";
import SettingsService from "../../services/settings";
import { useFavoriteStore } from "../../stores/use-favorite-store";
import { useFavorites } from "../../stores/use-favorite-store";
import useNavigationStore from "../../stores/use-navigation-store";
import { useNoteStore } from "../../stores/use-notes-store";
import SelectionHeader from "../../components/selection-header";
export const Favorites = ({
navigation,
route
}: NavigationProps<"Favorites">) => {
const favorites = useFavoriteStore((state) => state.favorites);
const setFavorites = useFavoriteStore((state) => state.setFavorites);
const loading = useNoteStore((state) => state.loading);
const [favorites, loading, refresh] = useFavorites();
const isFocused = useNavigationFocus(navigation, {
onFocus: (prev) => {
Navigation.routeNeedsUpdate(
@@ -43,7 +40,7 @@ export const Favorites = ({
Navigation.routeUpdateFunctions[route.name]
);
useNavigationStore.getState().setFocusedRouteId(route?.name);
return !prev?.current;
return false;
},
onBlur: () => false,
delay: SettingsService.get().homepage === route.name ? 1 : -1
@@ -75,10 +72,10 @@ export const Favorites = ({
data={favorites}
dataType="note"
onRefresh={() => {
setFavorites();
refresh();
}}
renderedInRoute="Favorites"
loading={loading || !isFocused}
loading={loading}
placeholder={{
title: "Your favorites",
paragraph: "You have not added any notes to favorites yet.",

View File

@@ -22,17 +22,16 @@ import { FloatingButton } from "../../components/container/floating-button";
import DelayLayout from "../../components/delay-layout";
import { Header } from "../../components/header";
import List from "../../components/list";
import SelectionHeader from "../../components/selection-header";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import Navigation, { NavigationProps } from "../../services/navigation";
import SettingsService from "../../services/settings";
import { useNoteStore } from "../../stores/use-notes-store";
import { openEditor } from "../notes/common";
import useNavigationStore from "../../stores/use-navigation-store";
import SelectionHeader from "../../components/selection-header";
import { useNotes } from "../../stores/use-notes-store";
import { openEditor } from "../notes/common";
export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
const notes = useNoteStore((state) => state.notes);
const loading = useNoteStore((state) => state.loading);
const [notes, loading] = useNotes();
const isFocused = useNavigationFocus(navigation, {
onFocus: (prev) => {
Navigation.routeNeedsUpdate(

View File

@@ -39,12 +39,12 @@ import { eUpdateNotebookRoute } from "../../utils/events";
import { findRootNotebookId } from "../../utils/notebooks";
import { openEditor, setOnFirstSave } from "../notes/common";
import SelectionHeader from "../../components/selection-header";
import { resolveItems } from "../../components/list/list-item.wrapper";
import Paragraph from "../../components/ui/typography/paragraph";
import { View } from "react-native";
import { SIZE } from "../../utils/size";
import { IconButton } from "../../components/ui/icon-button";
import { PressableButton } from "../../components/ui/pressable";
import { resolveItems } from "../../stores/resolve-items";
const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();

View File

@@ -23,14 +23,14 @@ import { FloatingButton } from "../../components/container/floating-button";
import DelayLayout from "../../components/delay-layout";
import { Header } from "../../components/header";
import List from "../../components/list";
import SelectionHeader from "../../components/selection-header";
import { AddNotebookSheet } from "../../components/sheets/add-notebook";
import { Walkthrough } from "../../components/walkthroughs";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import Navigation, { NavigationProps } from "../../services/navigation";
import SettingsService from "../../services/settings";
import useNavigationStore from "../../stores/use-navigation-store";
import { useNotebookStore } from "../../stores/use-notebook-store";
import SelectionHeader from "../../components/selection-header";
import { useNotebooks } from "../../stores/use-notebook-store";
const onButtonPress = () => {
AddNotebookSheet.present();
@@ -40,7 +40,8 @@ export const Notebooks = ({
navigation,
route
}: NavigationProps<"Notebooks">) => {
const notebooks = useNotebookStore((state) => state.notebooks);
const [notebooks, loading] = useNotebooks();
const isFocused = useNavigationFocus(navigation, {
onFocus: (prev) => {
Navigation.routeNeedsUpdate(
@@ -48,7 +49,7 @@ export const Notebooks = ({
Navigation.routeUpdateFunctions[route.name]
);
useNavigationStore.getState().setFocusedRouteId(route.name);
return !prev?.current;
return false;
},
onBlur: () => false,
delay: SettingsService.get().homepage === route.name ? 1 : -1
@@ -83,12 +84,11 @@ export const Notebooks = ({
}}
onPressDefaultRightButton={onButtonPress}
/>
<DelayLayout>
<DelayLayout wait={loading}>
<List
data={notebooks}
dataType="notebook"
renderedInRoute="Notebooks"
loading={!isFocused}
placeholder={{
title: "Your notebooks",
paragraph: "You have not added any notebooks yet.",

View File

@@ -107,7 +107,7 @@ export async function onNoteCreated(noteId: string, data: FirstSaveData) {
}
editorState().onNoteCreated = null;
useTagStore.getState().setTags();
useTagStore.getState().refresh();
useRelationStore.getState().update();
break;
}

View File

@@ -38,7 +38,7 @@ import useNavigationStore, {
} from "../../stores/use-navigation-store";
import { useNoteStore } from "../../stores/use-notes-store";
import { setOnFirstSave } from "./common";
import { resolveItems } from "../../components/list/list-item.wrapper";
import { resolveItems } from "../../stores/resolve-items";
export const WARNING_DATA = {
title: "Some notes in this topic are not synced"
};
@@ -75,7 +75,6 @@ const NotesPage = ({
>) => {
const params = useRef<NotesScreenParams>(route?.params);
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();
const loading = useNoteStore((state) => state.loading);
const [loadingNotes, setLoadingNotes] = useState(true);
const isMonograph = route.name === "Monographs";
const title =
@@ -140,7 +139,7 @@ const NotesPage = ({
);
useEffect(() => {
if (loadingNotes && !loading) {
if (loadingNotes) {
get(params.current, true)
.then(async (items) => {
setNotes(items as VirtualizedGrouping<Note>);
@@ -152,7 +151,7 @@ const NotesPage = ({
setLoadingNotes(false);
});
}
}, [loadingNotes, loading, get]);
}, [loadingNotes, get]);
useEffect(() => {
eSubscribeEvent(route.name, onRequestUpdate);
@@ -186,12 +185,12 @@ const NotesPage = ({
headerRightButtons={rightButtons?.(params?.current)}
/>
<DelayLayout color={accentColor} wait={loading || loadingNotes}>
<DelayLayout color={accentColor} wait={loadingNotes}>
<List
data={notes}
dataType="note"
onRefresh={onRequestUpdate}
loading={loading || !isFocused}
loading={!isFocused}
renderedInRoute={route.name}
id={params.current.item?.id}
headerTitle={title}

View File

@@ -22,19 +22,19 @@ import { FloatingButton } from "../../components/container/floating-button";
import DelayLayout from "../../components/delay-layout";
import { Header } from "../../components/header";
import List from "../../components/list";
import SelectionHeader from "../../components/selection-header";
import ReminderSheet from "../../components/sheets/reminder";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import Navigation, { NavigationProps } from "../../services/navigation";
import SettingsService from "../../services/settings";
import useNavigationStore from "../../stores/use-navigation-store";
import { useReminderStore } from "../../stores/use-reminder-store";
import SelectionHeader from "../../components/selection-header";
import { useReminders } from "../../stores/use-reminder-store";
export const Reminders = ({
navigation,
route
}: NavigationProps<"Reminders">) => {
const reminders = useReminderStore((state) => state.reminders);
const [reminders, loading] = useReminders();
const isFocused = useNavigationFocus(navigation, {
onFocus: (prev) => {
Navigation.routeNeedsUpdate(
@@ -43,7 +43,7 @@ export const Reminders = ({
);
useNavigationStore.getState().setFocusedRouteId(route.name);
return !prev?.current;
return false;
},
onBlur: () => false,
delay: SettingsService.get().homepage === route.name ? 1 : -1
@@ -72,13 +72,13 @@ export const Reminders = ({
}}
/>
<DelayLayout>
<DelayLayout wait={loading}>
<List
data={reminders}
dataType="reminder"
headerTitle="Reminders"
renderedInRoute="Reminders"
loading={!isFocused}
loading={loading}
placeholder={{
title: "Your reminders",
paragraph: "You have not set any reminders yet.",

View File

@@ -22,7 +22,6 @@ import create, { State } from "zustand";
import { persist, StateStorage } from "zustand/middleware";
import { db } from "../../../common/database";
import { MMKV } from "../../../common/database/mmkv";
import { useNoteStore } from "../../../stores/use-notes-store";
import { useSettingStore } from "../../../stores/use-setting-store";
import { presets } from "./toolbar-definition";
export type ToolDefinition = string | string[];
@@ -124,7 +123,7 @@ export const useDragState = create<DragState>(
getStorage: () => MMKV as unknown as StateStorage,
onRehydrateStorage: () => {
return () => {
if (!useNoteStore.getState().loading) {
if (!useSettingStore.getState().isAppLoading) {
useDragState.getState().init();
} else {
setTimeout(() => {

View File

@@ -297,6 +297,7 @@ export const settingsGroups: SettingSection[] = [
await BiometicService.resetCredentials();
MMKV.clearStore();
clearAllStores();
Navigation.queueRoutesForUpdate();
SettingsService.resetSettings();
useUserStore.getState().setUser(null);
useUserStore.getState().setSyncing(false);
@@ -304,7 +305,7 @@ export const settingsGroups: SettingSection[] = [
Navigation.popToTop();
setTimeout(() => {
eSendEvent("settings-loading", false);
}, 2000);
}, 3000);
}, 300);
} catch (e) {
ToastManager.error(e as Error, "Error logging out");

View File

@@ -21,16 +21,15 @@ import React from "react";
import DelayLayout from "../../components/delay-layout";
import { Header } from "../../components/header";
import List from "../../components/list";
import SelectionHeader from "../../components/selection-header";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import Navigation, { NavigationProps } from "../../services/navigation";
import SettingsService from "../../services/settings";
import useNavigationStore from "../../stores/use-navigation-store";
import { useTagStore } from "../../stores/use-tag-store";
import { db } from "../../common/database";
import SelectionHeader from "../../components/selection-header";
import { useTags } from "../../stores/use-tag-store";
export const Tags = ({ navigation, route }: NavigationProps<"Tags">) => {
const tags = useTagStore((state) => state.tags);
const [tags, loading] = useTags();
const isFocused = useNavigationFocus(navigation, {
onFocus: (prev) => {
Navigation.routeNeedsUpdate(
@@ -61,7 +60,7 @@ export const Tags = ({ navigation, route }: NavigationProps<"Tags">) => {
});
}}
/>
<DelayLayout>
<DelayLayout wait={loading}>
<List
data={tags}
dataType="tag"

View File

@@ -29,7 +29,7 @@ import { ToastManager } from "../../services/event-manager";
import Navigation, { NavigationProps } from "../../services/navigation";
import useNavigationStore from "../../stores/use-navigation-store";
import { useSelectionStore } from "../../stores/use-selection-store";
import { useTrashStore } from "../../stores/use-trash-store";
import { useTrash, useTrashStore } from "../../stores/use-trash-store";
import SelectionHeader from "../../components/selection-header";
const onPressFloatingButton = () => {
@@ -40,7 +40,7 @@ const onPressFloatingButton = () => {
negativeText: "Cancel",
positivePress: async () => {
await db.trash?.clear();
useTrashStore.getState().setTrash();
useTrashStore.getState().refresh();
useSelectionStore.getState().clearSelection();
ToastManager.show({
heading: "Trash cleared",
@@ -67,7 +67,7 @@ const PLACEHOLDER_DATA = (trashCleanupInterval = 7) => ({
});
export const Trash = ({ navigation, route }: NavigationProps<"Trash">) => {
const trash = useTrashStore((state) => state.trash);
const [trash, loading] = useTrash();
const isFocused = useNavigationFocus(navigation, {
onFocus: () => {
Navigation.routeNeedsUpdate(
@@ -75,12 +75,6 @@ export const Trash = ({ navigation, route }: NavigationProps<"Trash">) => {
Navigation.routeUpdateFunctions[route.name]
);
useNavigationStore.getState().setFocusedRouteId(route.name);
if (
!useTrashStore.getState().trash ||
useTrashStore.getState().trash?.ids?.length === 0
) {
useTrashStore.getState().setTrash();
}
return false;
},
onBlur: () => false
@@ -103,7 +97,7 @@ export const Trash = ({ navigation, route }: NavigationProps<"Trash">) => {
});
}}
/>
<DelayLayout>
<DelayLayout wait={loading}>
<List
data={trash}
dataType="trash"

View File

@@ -73,18 +73,18 @@ export type NavigationProps<T extends RouteName> = NativeStackScreenProps<
const routeUpdateFunctions: {
[name: string]: (...params: GenericRouteParam[]) => void;
} = {
Notes: () => useNoteStore.getState().setNotes(),
Notebooks: () => useNotebookStore.getState().setNotebooks(),
Tags: () => useTagStore.getState().setTags(),
Favorites: () => useFavoriteStore.getState().setFavorites(),
Trash: () => useTrashStore.getState().setTrash(),
Notes: () => useNoteStore.getState().refresh(),
Notebooks: () => useNotebookStore.getState().refresh(),
Tags: () => useTagStore.getState().refresh(),
Favorites: () => useFavoriteStore.getState().refresh(),
Trash: () => useTrashStore.getState().refresh(),
Notebook: (params) => eSendEvent(eUpdateNotebookRoute, params),
NotesPage: (params) => eSendEvent("NotesPage", params),
TaggedNotes: (params) => eSendEvent("TaggedNotes", params),
ColoredNotes: (params) => eSendEvent("ColoredNotes", params),
TopicNotes: (params) => eSendEvent("TopicNotes", params),
Monographs: (params) => eSendEvent("Monographs", params),
Reminders: () => useReminderStore.getState().setReminders(),
Reminders: () => useReminderStore.getState().refresh(),
Search: () => eSendEvent(eOnRefreshSearch)
};

View File

@@ -17,38 +17,38 @@ 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 { Reminder } from "@notesnook/core/dist/types";
import notifee, {
AndroidStyle,
AuthorizationStatus,
DisplayedNotification,
Event,
EventType,
RepeatFrequency,
TimestampTrigger,
Trigger,
TriggerType,
Event,
TriggerNotification,
TimestampTrigger
TriggerType
} from "@notifee/react-native";
import NetInfo from "@react-native-community/netinfo";
import dayjs, { Dayjs } from "dayjs";
import { encodeNonAsciiHTML } from "entities";
import { Platform } from "react-native";
import { db } from "../common/database";
import { MMKV } from "../common/database/mmkv";
import { presentDialog } from "../components/dialog/functions";
import { editorState } from "../screens/editor/tiptap/utils";
import { useNoteStore } from "../stores/use-notes-store";
import { eOnLoadNote } from "../utils/events";
import { tabBarRef } from "../utils/global-refs";
import { DDS } from "./device-detection";
import { eSendEvent } from "./event-manager";
import SettingsService from "./settings";
import { useSettingStore } from "../stores/use-setting-store";
import { sleep } from "../utils/time";
import { useRelationStore } from "../stores/use-relation-store";
import { useReminderStore } from "../stores/use-reminder-store";
import { presentDialog } from "../components/dialog/functions";
import NetInfo from "@react-native-community/netinfo";
import { encodeNonAsciiHTML } from "entities";
import { useSettingStore } from "../stores/use-setting-store";
import { eOnLoadNote } from "../utils/events";
import { tabBarRef } from "../utils/global-refs";
import { convertNoteToText } from "../utils/note-to-text";
import { Reminder } from "@notesnook/core/dist/types";
import { sleep } from "../utils/time";
import { DDS } from "./device-detection";
import { eSendEvent } from "./event-manager";
import Navigation from "./navigation";
import SettingsService from "./settings";
let pinned: DisplayedNotification[] = [];
@@ -136,19 +136,7 @@ const onEvent = async ({ type, detail }: Event) => {
}
editorState().movedAway = false;
const noteId = notification?.id;
if (useNoteStore?.getState()?.loading === false) {
loadNote(noteId as string, false);
return;
}
const unsub = useNoteStore.subscribe(
(loading) => {
if (loading === false) {
loadNote(noteId as string, true);
}
unsub();
},
(state) => state.loading
);
loadNote(noteId as string, true);
}
if (type === EventType.ACTION_PRESS) {
@@ -173,7 +161,7 @@ const onEvent = async ({ type, detail }: Event) => {
await db.reminders?.reminder(reminder?.id)
);
useRelationStore.getState().update();
useReminderStore.getState().setReminders();
useReminderStore.getState().refresh();
break;
}
case "REMINDER_DISABLE": {
@@ -191,7 +179,7 @@ const onEvent = async ({ type, detail }: Event) => {
await db.reminders?.reminder(reminder?.id)
);
useRelationStore.getState().update();
useReminderStore.getState().setReminders();
useReminderStore.getState().refresh();
break;
}
case "UNPIN": {
@@ -207,7 +195,7 @@ const onEvent = async ({ type, detail }: Event) => {
disabled: true
});
useRelationStore.getState().update();
useReminderStore.getState().setReminders();
useReminderStore.getState().refresh();
}
break;
}
@@ -253,7 +241,7 @@ const onEvent = async ({ type, detail }: Event) => {
console.log(e, (e as Error).stack);
}
}
useNoteStore.getState().setNotes();
Navigation.queueRoutesForUpdate();
break;
}
}
@@ -422,14 +410,17 @@ async function loadNote(id: string, jump: boolean) {
tabBarRef.current?.goToPage(1);
}
eSendEvent("loadingNote", note);
setTimeout(() => {
eSendEvent(eOnLoadNote, {
item: note
});
if (!jump && !DDS.isTab) {
tabBarRef.current?.goToPage(1);
}
}, 2000);
setTimeout(
() => {
eSendEvent(eOnLoadNote, {
item: note
});
if (!jump && !DDS.isTab) {
tabBarRef.current?.goToPage(1);
}
},
tabBarRef?.current ? 0 : 2000
);
}
async function getChannelId(id: "silent" | "vibrate" | "urgent" | "default") {

View File

@@ -0,0 +1,93 @@
/*
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 { Item, VirtualizedGrouping } from "@notesnook/core";
import create, { State, StoreApi, UseBoundStore } from "zustand";
import { resolveItems } from "./resolve-items";
import { useSettingStore } from "./use-setting-store";
export interface DBCollectionStore<Type extends Item> extends State {
items: VirtualizedGrouping<Type> | undefined;
loading: boolean;
refresh: () => Promise<void>;
clear: () => void;
}
const ALL_STORES: UseBoundStore<
DBCollectionStore<Item>,
StoreApi<DBCollectionStore<Item>>
>[] = [];
export function clearAllStores() {
ALL_STORES.forEach((store) => {
store?.getState().clear();
});
}
export function refreshAllStores() {
ALL_STORES.forEach((store) => store?.getState().refresh());
}
export default function createDBCollectionStore<Type extends Item>({
getCollection,
eagerlyFetchFirstBatch
}: {
getCollection: () => Promise<VirtualizedGrouping<Type>>;
eagerlyFetchFirstBatch?: boolean;
}) {
const useDBCollectionStore = create<DBCollectionStore<Type>>((set, get) => ({
items: undefined,
loading: true,
refresh: async () => {
const items = await getCollection();
if (get().loading && eagerlyFetchFirstBatch) {
await items.item(0, resolveItems);
}
set({
items,
loading: false
});
},
clear: () => set({ items: undefined, loading: true })
}));
const useStoreHook = (): [
VirtualizedGrouping<Type> | undefined,
boolean,
() => Promise<void>
] => {
const isAppLoading = useSettingStore((state) => state.isAppLoading);
const [items, loading] = useDBCollectionStore((state) => [
state.items,
state.loading
]);
if (!items && !isAppLoading) {
useDBCollectionStore.getState().refresh();
}
return [items, loading, useDBCollectionStore.getState().refresh];
};
ALL_STORES.push(useDBCollectionStore as any);
return {
useStore: useDBCollectionStore,
useCollection: useStoreHook
};
}

View File

@@ -17,7 +17,6 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { db } from "../common/database";
import Navigation from "../services/navigation";
import Notifications from "../services/notifications";
import { useFavoriteStore } from "./use-favorite-store";
@@ -28,36 +27,26 @@ import { useRelationStore } from "./use-relation-store";
import { useReminderStore } from "./use-reminder-store";
import { useTagStore } from "./use-tag-store";
import { useTrashStore } from "./use-trash-store";
export function initAfterSync() {
useMenuStore.getState().setColorNotes();
useMenuStore.getState().setMenuPins();
Navigation.queueRoutesForUpdate();
// Whenever sync completes, try to reschedule
// any new/updated reminders.
Notifications.setupReminders(true);
useRelationStore.getState().update();
}
export async function initialize() {
if (!db) return;
useMenuStore.getState().setColorNotes();
useMenuStore.getState().setMenuPins();
useNotebookStore.getState().setNotebooks();
useTrashStore.getState().setTrash();
useTagStore.getState().setTags();
useFavoriteStore.getState().setFavorites();
await useNoteStore.getState().setNotes();
useReminderStore.getState().setReminders();
Notifications.setupReminders();
}
export async function initialize() {}
export function clearAllStores() {
useNotebookStore.getState().clearNotebooks();
useTagStore.getState().clearTags();
useFavoriteStore.getState().clearFavorites();
useNotebookStore.getState().clear();
useTagStore.getState().clear();
useFavoriteStore.getState().clear();
useMenuStore.getState().clearAll();
useNoteStore.getState().clearNotes();
useNoteStore.getState().clear();
useMenuStore.getState().clearAll();
useTrashStore.getState().clearTrash();
useReminderStore.getState().cleareReminders();
useTrashStore.getState().clear();
useReminderStore.getState().clear();
}

View File

@@ -0,0 +1,158 @@
/*
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 { Color, Item, Notebook, Reminder, Tag } from "@notesnook/core";
import { db } from "../common/database";
export type WithDateEdited<T> = { items: T[]; dateEdited: number };
export type NotebooksWithDateEdited = WithDateEdited<Notebook>;
export type TagsWithDateEdited = WithDateEdited<Tag>;
function withDateEdited<
T extends { dateEdited: number } | { dateModified: number }
>(items: T[]): WithDateEdited<T> {
let latestDateEdited = 0;
items.forEach((item) => {
const date = "dateEdited" in item ? item.dateEdited : item.dateModified;
if (latestDateEdited < date) latestDateEdited = date;
});
return { dateEdited: latestDateEdited, items };
}
export async function resolveItems(ids: string[], items: Item[]) {
if (!ids.length || !items.length) return [];
const { type } = items[0];
if (type === "note") return resolveNotes(ids);
else if (type === "notebook") {
return Promise.all(ids.map((id) => db.notebooks.totalNotes(id)));
} else if (type === "tag") {
return Promise.all(
ids.map((id) => db.relations.from({ id, type: "tag" }, "note").count())
);
}
return [];
}
type NoteResolvedData = {
notebooks?: NotebooksWithDateEdited;
reminder?: Reminder;
color?: Color;
tags?: TagsWithDateEdited;
attachmentsCount?: number;
};
async function resolveNotes(ids: string[]) {
console.time("relations");
const relations = [
...(await db.relations
.to({ type: "note", ids }, ["notebook", "tag", "color"])
.get()),
...(await db.relations.from({ type: "note", ids }, "reminder").get())
];
console.timeEnd("relations");
const relationIds: {
notebooks: Set<string>;
colors: Set<string>;
tags: Set<string>;
reminders: Set<string>;
} = {
colors: new Set(),
notebooks: new Set(),
tags: new Set(),
reminders: new Set()
};
const grouped: Record<
string,
{
notebooks: string[];
color?: string;
tags: string[];
reminder?: string;
}
> = {};
for (const relation of relations) {
const noteId =
relation.toType === "reminder" ? relation.fromId : relation.toId;
const data = grouped[noteId] || {
notebooks: [],
tags: []
};
if (relation.toType === "reminder" && !data.reminder) {
data.reminder = relation.toId;
relationIds.reminders.add(relation.toId);
} else if (relation.fromType === "notebook" && data.notebooks.length < 2) {
data.notebooks.push(relation.fromId);
relationIds.notebooks.add(relation.fromId);
} else if (relation.fromType === "tag" && data.tags.length < 3) {
data.tags.push(relation.fromId);
relationIds.tags.add(relation.fromId);
} else if (relation.fromType === "color" && !data.color) {
data.color = relation.fromId;
relationIds.colors.add(relation.fromId);
}
grouped[noteId] = data;
}
console.time("resolve");
const resolved = {
notebooks: await db.notebooks.all.records(
Array.from(relationIds.notebooks)
),
tags: await db.tags.all.records(Array.from(relationIds.tags)),
colors: await db.colors.all.records(Array.from(relationIds.colors)),
reminders: await db.reminders.all.records(Array.from(relationIds.reminders))
};
console.timeEnd("resolve");
const data: NoteResolvedData[] = [];
for (const noteId of ids) {
const group = grouped[noteId];
if (!group) {
data.push({});
continue;
}
data.push({
color: group.color ? resolved.colors[group.color] : undefined,
reminder: group.reminder ? resolved.reminders[group.reminder] : undefined,
tags: withDateEdited(
group.tags.map((id) => resolved.tags[id]).filter(Boolean)
),
notebooks: withDateEdited(
group.notebooks.map((id) => resolved.notebooks[id]).filter(Boolean)
),
attachmentsCount:
(await db.attachments?.ofNote(noteId, "all").ids())?.length || 0
});
}
return data;
}
export function isNoteResolvedData(data: unknown): data is NoteResolvedData {
return (
typeof data === "object" &&
!!data &&
"notebooks" in data &&
"reminder" in data &&
"color" in data &&
"tags" in data
);
}

View File

@@ -17,26 +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 create, { State } from "zustand";
import { db } from "../common/database";
import { Note, VirtualizedGrouping } from "@notesnook/core";
import createDBCollectionStore from "./create-db-collection-store";
export interface FavoriteStore extends State {
favorites: VirtualizedGrouping<Note> | undefined;
setFavorites: (items?: Note[]) => void;
clearFavorites: () => void;
}
const { useStore: useFavoriteStore, useCollection: useFavorites } =
createDBCollectionStore({
getCollection: () =>
db.notes.favorites.grouped(db.settings.getGroupOptions("favorites")),
eagerlyFetchFirstBatch: true
});
export const useFavoriteStore = create<FavoriteStore>((set) => ({
favorites: undefined,
setFavorites: () => {
db.notes.favorites
.grouped(db.settings.getGroupOptions("favorites"))
.then((notes) => {
set({
favorites: notes
});
});
},
clearFavorites: () => set({ favorites: undefined })
}));
export { useFavoriteStore, useFavorites };

View File

@@ -27,20 +27,25 @@ export interface MenuStore extends State {
setMenuPins: () => void;
setColorNotes: () => void;
clearAll: () => void;
loadingShortcuts: boolean;
loadingColors: boolean;
}
export const useMenuStore = create<MenuStore>((set) => ({
menuPins: [],
colorNotes: [],
loadingShortcuts: true,
loadingColors: true,
setMenuPins: () => {
db.shortcuts.resolved().then((shortcuts) => {
set({ menuPins: [...(shortcuts as [])] });
set({ menuPins: [...(shortcuts as [])], loadingShortcuts: false });
});
},
setColorNotes: () => {
db.colors?.all.items().then((colors) => {
set({
colorNotes: colors
colorNotes: colors,
loadingColors: false
});
});
},

View File

@@ -17,26 +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 create, { State } from "zustand";
import { db } from "../common/database";
import { VirtualizedGrouping, Notebook } from "@notesnook/core";
import createDBCollectionStore from "./create-db-collection-store";
export interface NotebookStore extends State {
notebooks: VirtualizedGrouping<Notebook> | undefined;
setNotebooks: (items?: Notebook[]) => void;
clearNotebooks: () => void;
}
const { useStore: useNotebookStore, useCollection: useNotebooks } =
createDBCollectionStore({
getCollection: () =>
db.notebooks.roots.grouped(db.settings.getGroupOptions("notebooks")),
eagerlyFetchFirstBatch: true
});
export const useNotebookStore = create<NotebookStore>((set) => ({
notebooks: undefined,
setNotebooks: () => {
db.notebooks.roots
.grouped(db.settings.getGroupOptions("notebooks"))
.then((notebooks) => {
set({
notebooks: notebooks
});
});
},
clearNotebooks: () => set({ notebooks: undefined })
}));
export { useNotebookStore, useNotebooks };

View File

@@ -17,30 +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 { Note, VirtualizedGrouping } from "@notesnook/core";
import create, { State } from "zustand";
import { db } from "../common/database";
import createDBCollectionStore from "./create-db-collection-store";
export interface NoteStore extends State {
notes: VirtualizedGrouping<Note> | undefined;
loading: boolean;
setLoading: (loading: boolean) => void;
setNotes: () => void;
clearNotes: () => void;
}
const { useStore: useNoteStore, useCollection: useNotes } =
createDBCollectionStore({
getCollection: () =>
db.notes.all.grouped(db.settings.getGroupOptions("home")),
eagerlyFetchFirstBatch: true
});
export const useNoteStore = create<NoteStore>((set) => ({
notes: undefined,
loading: true,
setLoading: (loading) => set({ loading: loading }),
setNotes: async () => {
const notes = await db.notes.all.grouped(
db.settings.getGroupOptions("home")
);
await notes.item(0);
set({
notes: notes
});
},
clearNotes: () => set({ notes: undefined })
}));
export { useNoteStore, useNotes };

View File

@@ -17,26 +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 create, { State } from "zustand";
import { db } from "../common/database";
import { Reminder, VirtualizedGrouping } from "@notesnook/core";
import createDBCollectionStore from "./create-db-collection-store";
export interface ReminderStore extends State {
reminders: VirtualizedGrouping<Reminder> | undefined;
setReminders: (items?: Reminder[]) => void;
cleareReminders: () => void;
}
const { useStore: useReminderStore, useCollection: useReminders } =
createDBCollectionStore({
getCollection: () =>
db.reminders.all.grouped(db.settings.getGroupOptions("reminders")),
eagerlyFetchFirstBatch: true
});
export const useReminderStore = create<ReminderStore>((set) => ({
reminders: undefined,
setReminders: () => {
db.reminders.all
.grouped(db.settings.getGroupOptions("reminders"))
.then((reminders) => {
set({
reminders: reminders
});
});
},
cleareReminders: () => set({ reminders: undefined })
}));
export { useReminderStore, useReminders };

View File

@@ -98,8 +98,8 @@ export interface SettingStore extends State {
setFullscreen: (fullscreen: boolean) => void;
setDeviceMode: (mode: string) => void;
setDimensions: (dimensions: DimensionsType) => void;
appLoading: boolean;
setAppLoading: (appLoading: boolean) => void;
isAppLoading: boolean;
setAppLoading: (isAppLoading: boolean) => void;
setSheetKeyboardHandler: (sheetKeyboardHandler: boolean) => void;
sheetKeyboardHandler: boolean;
requestBiometrics: boolean;
@@ -165,12 +165,12 @@ export const useSettingStore = create<SettingStore>((set, get) => ({
fullscreen: false,
deviceMode: null,
dimensions: { width, height },
appLoading: true,
isAppLoading: true,
setSettings: (settings) => set({ settings }),
setFullscreen: (fullscreen) => set({ fullscreen }),
setDeviceMode: (mode) => set({ deviceMode: mode }),
setDimensions: (dimensions) => set({ dimensions: dimensions }),
setAppLoading: (appLoading) => set({ appLoading }),
setAppLoading: (isAppLoading) => set({ isAppLoading }),
setSheetKeyboardHandler: (sheetKeyboardHandler) =>
set({ sheetKeyboardHandler }),
requestBiometrics: false,

View File

@@ -17,24 +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 create, { State } from "zustand";
import { db } from "../common/database";
import { Tag, VirtualizedGrouping } from "@notesnook/core";
import createDBCollectionStore from "./create-db-collection-store";
export interface TagStore extends State {
tags: VirtualizedGrouping<Tag> | undefined;
setTags: (items?: Tag[]) => void;
clearTags: () => void;
}
const { useStore: useTagStore, useCollection: useTags } =
createDBCollectionStore({
getCollection: () =>
db.tags.all.grouped(db.settings.getGroupOptions("tags")),
eagerlyFetchFirstBatch: true
});
export const useTagStore = create<TagStore>((set) => ({
tags: undefined,
setTags: () => {
db.tags.all.grouped(db.settings.getGroupOptions("tags")).then((tags) => {
set({
tags: tags
});
});
},
clearTags: () => set({ tags: undefined })
}));
export { useTagStore, useTags };

View File

@@ -17,25 +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 create, { State } from "zustand";
import { db } from "../common/database";
import { GroupedItems, TrashItem } from "@notesnook/core/dist/types";
import { VirtualizedGrouping } from "@notesnook/core";
import createDBCollectionStore from "./create-db-collection-store";
export interface TrashStore extends State {
trash: VirtualizedGrouping<TrashItem> | undefined;
setTrash: (items?: GroupedItems<TrashItem>) => void;
clearTrash: () => void;
}
const { useStore: useTrashStore, useCollection: useTrash } =
createDBCollectionStore({
getCollection: () => db.trash.grouped(db.settings.getGroupOptions("trash")),
eagerlyFetchFirstBatch: true
});
export const useTrashStore = create<TrashStore>((set, get) => ({
trash: undefined,
setTrash: () => {
db.trash.grouped(db.settings.getGroupOptions("trash")).then((trash) => {
set({
trash: trash
});
});
},
clearTrash: () => set({ trash: undefined })
}));
export { useTrashStore, useTrash };

File diff suppressed because it is too large Load Diff

View File

@@ -45,6 +45,7 @@
"kysely": "^0.26.3",
"react": "18.2.0",
"react-native": "0.72.0",
"react-native-quick-sqlite": "^8.0.6"
"react-native-quick-sqlite": "^8.0.6",
"tinycolor2": "1.6.0"
}
}