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( database.host(
__DEV__ __DEV__
? { ? {
API_HOST: "https://api.notesnook.com", // API_HOST: "https://api.notesnook.com",
AUTH_HOST: "https://auth.streetwriters.co", // AUTH_HOST: "https://auth.streetwriters.co",
SSE_HOST: "https://events.streetwriters.co", // SSE_HOST: "https://events.streetwriters.co",
SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co", // SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
ISSUES_HOST: "https://issues.streetwriters.co" // ISSUES_HOST: "https://issues.streetwriters.co"
// API_HOST: "http://192.168.43.5:5264", API_HOST: "http://192.168.43.5:5264",
// AUTH_HOST: "http://192.168.43.5:8264", AUTH_HOST: "http://192.168.43.5:8264",
// SSE_HOST: "http://192.168.43.5:7264", SSE_HOST: "http://192.168.43.5:7264",
// SUBSCRIPTIONS_HOST: "http://192.168.43.5:9264", SUBSCRIPTIONS_HOST: "http://192.168.43.5:9264",
// ISSUES_HOST: "http://192.168.43.5:2624" ISSUES_HOST: "http://192.168.43.5:2624"
} }
: { : {
API_HOST: "https://api.notesnook.com", API_HOST: "https://api.notesnook.com",
@@ -59,7 +59,7 @@ database.setup({
compress: Gzip.deflate, compress: Gzip.deflate,
decompress: Gzip.inflate decompress: Gzip.inflate
}, },
batchSize: 500, batchSize: 100,
sqliteOptions: { sqliteOptions: {
dialect: (name) => ({ dialect: (name) => ({
createDriver: () => { 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 { useThemeColors } from "@notesnook/theme";
import React from "react"; import React from "react";
import { useNoteStore } from "../../stores/use-notes-store";
import { AnnouncementDialog } from "../announcements"; import { AnnouncementDialog } from "../announcements";
import AuthModal from "../auth/auth-modal"; import AuthModal from "../auth/auth-modal";
import { SessionExpired } from "../auth/session-expired"; import { SessionExpired } from "../auth/session-expired";
@@ -40,7 +39,6 @@ import RestoreDataSheet from "../sheets/restore-data";
const DialogProvider = () => { const DialogProvider = () => {
const { colors } = useThemeColors(); const { colors } = useThemeColors();
const loading = useNoteStore((state) => state.loading);
return ( return (
<> <>
@@ -57,7 +55,7 @@ const DialogProvider = () => {
<VaultDialog colors={colors} /> <VaultDialog colors={colors} />
<RateAppSheet /> <RateAppSheet />
<ImagePreview /> <ImagePreview />
{loading ? null : <Expiring />} <Expiring />
<AnnouncementDialog /> <AnnouncementDialog />
<SessionExpired /> <SessionExpired />
<PDFPreview /> <PDFPreview />

View File

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

View File

@@ -35,6 +35,12 @@ import React, { useCallback, useEffect, useRef, useState } from "react";
import { View } from "react-native"; import { View } from "react-native";
import { db } from "../../common/database"; import { db } from "../../common/database";
import { eSendEvent } from "../../services/event-manager"; import { eSendEvent } from "../../services/event-manager";
import {
NotebooksWithDateEdited,
TagsWithDateEdited,
isNoteResolvedData,
resolveItems
} from "../../stores/resolve-items";
import { RouteName } from "../../stores/use-navigation-store"; import { RouteName } from "../../stores/use-navigation-store";
import { eOpenJumpToDialog } from "../../utils/events"; import { eOpenJumpToDialog } from "../../utils/events";
import { SectionHeader } from "../list-items/headers/section-header"; 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 ReminderItem from "../list-items/reminder";
import TagItem from "../list-items/tag"; 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> = { type ListItemWrapperProps<TItem = Item> = {
group?: GroupingKey; group?: GroupingKey;
items: VirtualizedGrouping<TItem> | undefined; items: VirtualizedGrouping<TItem> | undefined;
@@ -279,137 +281,3 @@ function getDate(item: Item, groupType?: GroupingKey): number {
) || 0 ) || 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", context: "local",
positivePress: async () => { positivePress: async () => {
await db.trash.delete(note.id); await db.trash.delete(note.id);
useTrashStore.getState().setTrash(); useTrashStore.getState().refresh();
useSelectionStore.getState().setSelectionMode(false); useSelectionStore.getState().setSelectionMode(false);
ToastManager.show({ ToastManager.show({
heading: "Permanently deleted items", 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 { Note } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme"; import { useThemeColors } from "@notesnook/theme";
import React, { RefObject, useCallback, useEffect } from "react"; 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 { ActionSheetRef, FlashList } from "react-native-actions-sheet";
import { db } from "../../../common/database"; import { db } from "../../../common/database";
import { presentSheet } from "../../../services/event-manager"; import { presentSheet } from "../../../services/event-manager";
import Navigation from "../../../services/navigation"; import Navigation from "../../../services/navigation";
import { ItemSelection } from "../../../stores/item-selection-store"; 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 { useRelationStore } from "../../../stores/use-relation-store";
import { useSelectionStore } from "../../../stores/use-selection-store"; import { useSelectionStore } from "../../../stores/use-selection-store";
import { useSettingStore } from "../../../stores/use-setting-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 Paragraph from "../../ui/typography/paragraph";
import { NotebookItem } from "./notebook-item"; import { NotebookItem } from "./notebook-item";
import { useNotebookItemSelectionStore } from "./store"; import { useNotebookItemSelectionStore } from "./store";
import { SIZE } from "../../../utils/size";
async function updateInitialSelectionState(items: string[]) { async function updateInitialSelectionState(items: string[]) {
const relations = await db.relations const relations = await db.relations
@@ -85,7 +94,7 @@ const MoveNoteSheet = ({
actionSheetRef: RefObject<ActionSheetRef>; actionSheetRef: RefObject<ActionSheetRef>;
}) => { }) => {
const { colors } = useThemeColors(); const { colors } = useThemeColors();
const notebooks = useNotebookStore((state) => state.notebooks); const [notebooks, loading] = useNotebooks();
const dimensions = useSettingStore((state) => state.dimensions); const dimensions = useSettingStore((state) => state.dimensions);
const selectedItemsList = useSelectionStore( const selectedItemsList = useSelectionStore(
(state) => state.selectedItemsList (state) => state.selectedItemsList
@@ -234,7 +243,16 @@ const MoveNoteSheet = ({
height: 200 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> </View>
} }
ListFooterComponent={<View style={{ height: 50 }} />} ListFooterComponent={<View style={{ height: 50 }} />}

View File

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

View File

@@ -26,7 +26,7 @@ import { ColoredNotes } from "../../screens/notes/colored";
import Navigation from "../../services/navigation"; import Navigation from "../../services/navigation";
import { useMenuStore } from "../../stores/use-menu-store"; import { useMenuStore } from "../../stores/use-menu-store";
import useNavigationStore from "../../stores/use-navigation-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 { SIZE, normalize } from "../../utils/size";
import { presentDialog } from "../dialog/functions"; import { presentDialog } from "../dialog/functions";
import { PressableButton } from "../ui/pressable"; import { PressableButton } from "../ui/pressable";
@@ -35,8 +35,11 @@ import Paragraph from "../ui/typography/paragraph";
export const ColorSection = React.memo( export const ColorSection = React.memo(
function ColorSection() { function ColorSection() {
const colorNotes = useMenuStore((state) => state.colorNotes); const [colorNotes, loadingColors] = useMenuStore((state) => [
const loading = useNoteStore((state) => state.loading); state.colorNotes,
state.loadingColors
]);
const loading = useSettingStore((state) => state.isAppLoading);
const setColorNotes = useMenuStore((state) => state.setColorNotes); const setColorNotes = useMenuStore((state) => state.setColorNotes);
useEffect(() => { useEffect(() => {
@@ -45,9 +48,11 @@ export const ColorSection = React.memo(
} }
}, [loading, setColorNotes]); }, [loading, setColorNotes]);
return colorNotes.map((item) => { return !loadingColors
? colorNotes.map((item) => {
return <ColorItem key={item.id} item={item} />; return <ColorItem key={item.id} item={item} />;
}); })
: null;
}, },
() => true () => 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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { useThemeColors } from "@notesnook/theme";
import React, { useCallback } from "react"; import React, { useCallback } from "react";
import { FlatList, View } from "react-native"; import { FlatList, View } from "react-native";
import { notesnook } from "../../../e2e/test.ids"; 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 { DDS } from "../../services/device-detection";
import { eSendEvent } from "../../services/event-manager"; import { eSendEvent } from "../../services/event-manager";
import Navigation from "../../services/navigation"; import Navigation from "../../services/navigation";
import { useNoteStore } from "../../stores/use-notes-store";
import { useSettingStore } from "../../stores/use-setting-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 { useUserStore } from "../../stores/use-user-store";
import { MenuItemsList } from "../../utils/menu-items";
import { SUBSCRIPTION_STATUS } from "../../utils/constants"; import { SUBSCRIPTION_STATUS } from "../../utils/constants";
import { eOpenPremiumDialog } from "../../utils/events"; import { eOpenPremiumDialog } from "../../utils/events";
import { MenuItemsList } from "../../utils/menu-items";
import { ColorSection } from "./color-section"; import { ColorSection } from "./color-section";
import { MenuItem } from "./menu-item"; import { MenuItem } from "./menu-item";
import { TagsSection } from "./pinned-section"; import { TagsSection } from "./pinned-section";
import { UserStatus } from "./user-status"; import { UserStatus } from "./user-status";
import { useThemeStore } from "../../stores/use-theme-store";
export const SideMenu = React.memo( export const SideMenu = React.memo(
function SideMenu() { function SideMenu() {
@@ -44,7 +43,8 @@ export const SideMenu = React.memo(
const subscriptionType = useUserStore( const subscriptionType = useUserStore(
(state) => state.user?.subscription?.type (state) => state.user?.subscription?.type
); );
const loading = useNoteStore((state) => state.loading); const isAppLoading = useSettingStore((state) => state.isAppLoading);
const introCompleted = useSettingStore( const introCompleted = useSettingStore(
(state) => state.settings.introCompleted (state) => state.settings.introCompleted
); );
@@ -96,7 +96,7 @@ export const SideMenu = React.memo(
[noTextMode] [noTextMode]
); );
return !loading && introCompleted ? ( return !isAppLoading && introCompleted ? (
<View <View
style={{ style={{
height: "100%", 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 { Notebook, Tag } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme"; 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 { FlatList, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons"; import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database"; import { db } from "../../common/database";
@@ -28,7 +28,7 @@ import { TaggedNotes } from "../../screens/notes/tagged";
import Navigation from "../../services/navigation"; import Navigation from "../../services/navigation";
import { useMenuStore } from "../../stores/use-menu-store"; import { useMenuStore } from "../../stores/use-menu-store";
import useNavigationStore from "../../stores/use-navigation-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 { SIZE, normalize } from "../../utils/size";
import { Properties } from "../properties"; import { Properties } from "../properties";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
@@ -41,8 +41,8 @@ import Paragraph from "../ui/typography/paragraph";
export const TagsSection = React.memo( export const TagsSection = React.memo(
function TagsSection() { function TagsSection() {
const menuPins = useMenuStore((state) => state.menuPins); const [menuPins] = useMenuStore((state) => [state.menuPins]);
const loading = useNoteStore((state) => state.loading); const loading = useSettingStore((state) => state.isAppLoading);
const setMenuPins = useMenuStore((state) => state.setMenuPins); const setMenuPins = useMenuStore((state) => state.setMenuPins);
useEffect(() => { useEffect(() => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -39,12 +39,12 @@ import { eUpdateNotebookRoute } from "../../utils/events";
import { findRootNotebookId } from "../../utils/notebooks"; import { findRootNotebookId } from "../../utils/notebooks";
import { openEditor, setOnFirstSave } from "../notes/common"; import { openEditor, setOnFirstSave } from "../notes/common";
import SelectionHeader from "../../components/selection-header"; import SelectionHeader from "../../components/selection-header";
import { resolveItems } from "../../components/list/list-item.wrapper";
import Paragraph from "../../components/ui/typography/paragraph"; import Paragraph from "../../components/ui/typography/paragraph";
import { View } from "react-native"; import { View } from "react-native";
import { SIZE } from "../../utils/size"; import { SIZE } from "../../utils/size";
import { IconButton } from "../../components/ui/icon-button"; import { IconButton } from "../../components/ui/icon-button";
import { PressableButton } from "../../components/ui/pressable"; import { PressableButton } from "../../components/ui/pressable";
import { resolveItems } from "../../stores/resolve-items";
const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => { const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>(); 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 DelayLayout from "../../components/delay-layout";
import { Header } from "../../components/header"; import { Header } from "../../components/header";
import List from "../../components/list"; import List from "../../components/list";
import SelectionHeader from "../../components/selection-header";
import { AddNotebookSheet } from "../../components/sheets/add-notebook"; import { AddNotebookSheet } from "../../components/sheets/add-notebook";
import { Walkthrough } from "../../components/walkthroughs"; import { Walkthrough } from "../../components/walkthroughs";
import { useNavigationFocus } from "../../hooks/use-navigation-focus"; import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import Navigation, { NavigationProps } from "../../services/navigation"; import Navigation, { NavigationProps } from "../../services/navigation";
import SettingsService from "../../services/settings"; import SettingsService from "../../services/settings";
import useNavigationStore from "../../stores/use-navigation-store"; import useNavigationStore from "../../stores/use-navigation-store";
import { useNotebookStore } from "../../stores/use-notebook-store"; import { useNotebooks } from "../../stores/use-notebook-store";
import SelectionHeader from "../../components/selection-header";
const onButtonPress = () => { const onButtonPress = () => {
AddNotebookSheet.present(); AddNotebookSheet.present();
@@ -40,7 +40,8 @@ export const Notebooks = ({
navigation, navigation,
route route
}: NavigationProps<"Notebooks">) => { }: NavigationProps<"Notebooks">) => {
const notebooks = useNotebookStore((state) => state.notebooks); const [notebooks, loading] = useNotebooks();
const isFocused = useNavigationFocus(navigation, { const isFocused = useNavigationFocus(navigation, {
onFocus: (prev) => { onFocus: (prev) => {
Navigation.routeNeedsUpdate( Navigation.routeNeedsUpdate(
@@ -48,7 +49,7 @@ export const Notebooks = ({
Navigation.routeUpdateFunctions[route.name] Navigation.routeUpdateFunctions[route.name]
); );
useNavigationStore.getState().setFocusedRouteId(route.name); useNavigationStore.getState().setFocusedRouteId(route.name);
return !prev?.current; return false;
}, },
onBlur: () => false, onBlur: () => false,
delay: SettingsService.get().homepage === route.name ? 1 : -1 delay: SettingsService.get().homepage === route.name ? 1 : -1
@@ -83,12 +84,11 @@ export const Notebooks = ({
}} }}
onPressDefaultRightButton={onButtonPress} onPressDefaultRightButton={onButtonPress}
/> />
<DelayLayout> <DelayLayout wait={loading}>
<List <List
data={notebooks} data={notebooks}
dataType="notebook" dataType="notebook"
renderedInRoute="Notebooks" renderedInRoute="Notebooks"
loading={!isFocused}
placeholder={{ placeholder={{
title: "Your notebooks", title: "Your notebooks",
paragraph: "You have not added any notebooks yet.", 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; editorState().onNoteCreated = null;
useTagStore.getState().setTags(); useTagStore.getState().refresh();
useRelationStore.getState().update(); useRelationStore.getState().update();
break; break;
} }

View File

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

View File

@@ -22,19 +22,19 @@ import { FloatingButton } from "../../components/container/floating-button";
import DelayLayout from "../../components/delay-layout"; import DelayLayout from "../../components/delay-layout";
import { Header } from "../../components/header"; import { Header } from "../../components/header";
import List from "../../components/list"; import List from "../../components/list";
import SelectionHeader from "../../components/selection-header";
import ReminderSheet from "../../components/sheets/reminder"; import ReminderSheet from "../../components/sheets/reminder";
import { useNavigationFocus } from "../../hooks/use-navigation-focus"; import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import Navigation, { NavigationProps } from "../../services/navigation"; import Navigation, { NavigationProps } from "../../services/navigation";
import SettingsService from "../../services/settings"; import SettingsService from "../../services/settings";
import useNavigationStore from "../../stores/use-navigation-store"; import useNavigationStore from "../../stores/use-navigation-store";
import { useReminderStore } from "../../stores/use-reminder-store"; import { useReminders } from "../../stores/use-reminder-store";
import SelectionHeader from "../../components/selection-header";
export const Reminders = ({ export const Reminders = ({
navigation, navigation,
route route
}: NavigationProps<"Reminders">) => { }: NavigationProps<"Reminders">) => {
const reminders = useReminderStore((state) => state.reminders); const [reminders, loading] = useReminders();
const isFocused = useNavigationFocus(navigation, { const isFocused = useNavigationFocus(navigation, {
onFocus: (prev) => { onFocus: (prev) => {
Navigation.routeNeedsUpdate( Navigation.routeNeedsUpdate(
@@ -43,7 +43,7 @@ export const Reminders = ({
); );
useNavigationStore.getState().setFocusedRouteId(route.name); useNavigationStore.getState().setFocusedRouteId(route.name);
return !prev?.current; return false;
}, },
onBlur: () => false, onBlur: () => false,
delay: SettingsService.get().homepage === route.name ? 1 : -1 delay: SettingsService.get().homepage === route.name ? 1 : -1
@@ -72,13 +72,13 @@ export const Reminders = ({
}} }}
/> />
<DelayLayout> <DelayLayout wait={loading}>
<List <List
data={reminders} data={reminders}
dataType="reminder" dataType="reminder"
headerTitle="Reminders" headerTitle="Reminders"
renderedInRoute="Reminders" renderedInRoute="Reminders"
loading={!isFocused} loading={loading}
placeholder={{ placeholder={{
title: "Your reminders", title: "Your reminders",
paragraph: "You have not set any reminders yet.", 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 { persist, StateStorage } from "zustand/middleware";
import { db } from "../../../common/database"; import { db } from "../../../common/database";
import { MMKV } from "../../../common/database/mmkv"; import { MMKV } from "../../../common/database/mmkv";
import { useNoteStore } from "../../../stores/use-notes-store";
import { useSettingStore } from "../../../stores/use-setting-store"; import { useSettingStore } from "../../../stores/use-setting-store";
import { presets } from "./toolbar-definition"; import { presets } from "./toolbar-definition";
export type ToolDefinition = string | string[]; export type ToolDefinition = string | string[];
@@ -124,7 +123,7 @@ export const useDragState = create<DragState>(
getStorage: () => MMKV as unknown as StateStorage, getStorage: () => MMKV as unknown as StateStorage,
onRehydrateStorage: () => { onRehydrateStorage: () => {
return () => { return () => {
if (!useNoteStore.getState().loading) { if (!useSettingStore.getState().isAppLoading) {
useDragState.getState().init(); useDragState.getState().init();
} else { } else {
setTimeout(() => { setTimeout(() => {

View File

@@ -297,6 +297,7 @@ export const settingsGroups: SettingSection[] = [
await BiometicService.resetCredentials(); await BiometicService.resetCredentials();
MMKV.clearStore(); MMKV.clearStore();
clearAllStores(); clearAllStores();
Navigation.queueRoutesForUpdate();
SettingsService.resetSettings(); SettingsService.resetSettings();
useUserStore.getState().setUser(null); useUserStore.getState().setUser(null);
useUserStore.getState().setSyncing(false); useUserStore.getState().setSyncing(false);
@@ -304,7 +305,7 @@ export const settingsGroups: SettingSection[] = [
Navigation.popToTop(); Navigation.popToTop();
setTimeout(() => { setTimeout(() => {
eSendEvent("settings-loading", false); eSendEvent("settings-loading", false);
}, 2000); }, 3000);
}, 300); }, 300);
} catch (e) { } catch (e) {
ToastManager.error(e as Error, "Error logging out"); 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 DelayLayout from "../../components/delay-layout";
import { Header } from "../../components/header"; import { Header } from "../../components/header";
import List from "../../components/list"; import List from "../../components/list";
import SelectionHeader from "../../components/selection-header";
import { useNavigationFocus } from "../../hooks/use-navigation-focus"; import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import Navigation, { NavigationProps } from "../../services/navigation"; import Navigation, { NavigationProps } from "../../services/navigation";
import SettingsService from "../../services/settings"; import SettingsService from "../../services/settings";
import useNavigationStore from "../../stores/use-navigation-store"; import useNavigationStore from "../../stores/use-navigation-store";
import { useTagStore } from "../../stores/use-tag-store"; import { useTags } from "../../stores/use-tag-store";
import { db } from "../../common/database";
import SelectionHeader from "../../components/selection-header";
export const Tags = ({ navigation, route }: NavigationProps<"Tags">) => { export const Tags = ({ navigation, route }: NavigationProps<"Tags">) => {
const tags = useTagStore((state) => state.tags); const [tags, loading] = useTags();
const isFocused = useNavigationFocus(navigation, { const isFocused = useNavigationFocus(navigation, {
onFocus: (prev) => { onFocus: (prev) => {
Navigation.routeNeedsUpdate( Navigation.routeNeedsUpdate(
@@ -61,7 +60,7 @@ export const Tags = ({ navigation, route }: NavigationProps<"Tags">) => {
}); });
}} }}
/> />
<DelayLayout> <DelayLayout wait={loading}>
<List <List
data={tags} data={tags}
dataType="tag" dataType="tag"

View File

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

View File

@@ -73,18 +73,18 @@ export type NavigationProps<T extends RouteName> = NativeStackScreenProps<
const routeUpdateFunctions: { const routeUpdateFunctions: {
[name: string]: (...params: GenericRouteParam[]) => void; [name: string]: (...params: GenericRouteParam[]) => void;
} = { } = {
Notes: () => useNoteStore.getState().setNotes(), Notes: () => useNoteStore.getState().refresh(),
Notebooks: () => useNotebookStore.getState().setNotebooks(), Notebooks: () => useNotebookStore.getState().refresh(),
Tags: () => useTagStore.getState().setTags(), Tags: () => useTagStore.getState().refresh(),
Favorites: () => useFavoriteStore.getState().setFavorites(), Favorites: () => useFavoriteStore.getState().refresh(),
Trash: () => useTrashStore.getState().setTrash(), Trash: () => useTrashStore.getState().refresh(),
Notebook: (params) => eSendEvent(eUpdateNotebookRoute, params), Notebook: (params) => eSendEvent(eUpdateNotebookRoute, params),
NotesPage: (params) => eSendEvent("NotesPage", params), NotesPage: (params) => eSendEvent("NotesPage", params),
TaggedNotes: (params) => eSendEvent("TaggedNotes", params), TaggedNotes: (params) => eSendEvent("TaggedNotes", params),
ColoredNotes: (params) => eSendEvent("ColoredNotes", params), ColoredNotes: (params) => eSendEvent("ColoredNotes", params),
TopicNotes: (params) => eSendEvent("TopicNotes", params), TopicNotes: (params) => eSendEvent("TopicNotes", params),
Monographs: (params) => eSendEvent("Monographs", params), Monographs: (params) => eSendEvent("Monographs", params),
Reminders: () => useReminderStore.getState().setReminders(), Reminders: () => useReminderStore.getState().refresh(),
Search: () => eSendEvent(eOnRefreshSearch) 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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { Reminder } from "@notesnook/core/dist/types";
import notifee, { import notifee, {
AndroidStyle, AndroidStyle,
AuthorizationStatus, AuthorizationStatus,
DisplayedNotification, DisplayedNotification,
Event,
EventType, EventType,
RepeatFrequency, RepeatFrequency,
TimestampTrigger,
Trigger, Trigger,
TriggerType,
Event,
TriggerNotification, TriggerNotification,
TimestampTrigger TriggerType
} from "@notifee/react-native"; } from "@notifee/react-native";
import NetInfo from "@react-native-community/netinfo";
import dayjs, { Dayjs } from "dayjs"; import dayjs, { Dayjs } from "dayjs";
import { encodeNonAsciiHTML } from "entities";
import { Platform } from "react-native"; import { Platform } from "react-native";
import { db } from "../common/database"; import { db } from "../common/database";
import { MMKV } from "../common/database/mmkv"; import { MMKV } from "../common/database/mmkv";
import { presentDialog } from "../components/dialog/functions";
import { editorState } from "../screens/editor/tiptap/utils"; 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 { useRelationStore } from "../stores/use-relation-store";
import { useReminderStore } from "../stores/use-reminder-store"; import { useReminderStore } from "../stores/use-reminder-store";
import { presentDialog } from "../components/dialog/functions"; import { useSettingStore } from "../stores/use-setting-store";
import NetInfo from "@react-native-community/netinfo"; import { eOnLoadNote } from "../utils/events";
import { encodeNonAsciiHTML } from "entities"; import { tabBarRef } from "../utils/global-refs";
import { convertNoteToText } from "../utils/note-to-text"; 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[] = []; let pinned: DisplayedNotification[] = [];
@@ -136,20 +136,8 @@ const onEvent = async ({ type, detail }: Event) => {
} }
editorState().movedAway = false; editorState().movedAway = false;
const noteId = notification?.id; 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); loadNote(noteId as string, true);
} }
unsub();
},
(state) => state.loading
);
}
if (type === EventType.ACTION_PRESS) { if (type === EventType.ACTION_PRESS) {
notifee.decrementBadgeCount(); notifee.decrementBadgeCount();
@@ -173,7 +161,7 @@ const onEvent = async ({ type, detail }: Event) => {
await db.reminders?.reminder(reminder?.id) await db.reminders?.reminder(reminder?.id)
); );
useRelationStore.getState().update(); useRelationStore.getState().update();
useReminderStore.getState().setReminders(); useReminderStore.getState().refresh();
break; break;
} }
case "REMINDER_DISABLE": { case "REMINDER_DISABLE": {
@@ -191,7 +179,7 @@ const onEvent = async ({ type, detail }: Event) => {
await db.reminders?.reminder(reminder?.id) await db.reminders?.reminder(reminder?.id)
); );
useRelationStore.getState().update(); useRelationStore.getState().update();
useReminderStore.getState().setReminders(); useReminderStore.getState().refresh();
break; break;
} }
case "UNPIN": { case "UNPIN": {
@@ -207,7 +195,7 @@ const onEvent = async ({ type, detail }: Event) => {
disabled: true disabled: true
}); });
useRelationStore.getState().update(); useRelationStore.getState().update();
useReminderStore.getState().setReminders(); useReminderStore.getState().refresh();
} }
break; break;
} }
@@ -253,7 +241,7 @@ const onEvent = async ({ type, detail }: Event) => {
console.log(e, (e as Error).stack); console.log(e, (e as Error).stack);
} }
} }
useNoteStore.getState().setNotes(); Navigation.queueRoutesForUpdate();
break; break;
} }
} }
@@ -422,14 +410,17 @@ async function loadNote(id: string, jump: boolean) {
tabBarRef.current?.goToPage(1); tabBarRef.current?.goToPage(1);
} }
eSendEvent("loadingNote", note); eSendEvent("loadingNote", note);
setTimeout(() => { setTimeout(
() => {
eSendEvent(eOnLoadNote, { eSendEvent(eOnLoadNote, {
item: note item: note
}); });
if (!jump && !DDS.isTab) { if (!jump && !DDS.isTab) {
tabBarRef.current?.goToPage(1); tabBarRef.current?.goToPage(1);
} }
}, 2000); },
tabBarRef?.current ? 0 : 2000
);
} }
async function getChannelId(id: "silent" | "vibrate" | "urgent" | "default") { 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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { db } from "../common/database";
import Navigation from "../services/navigation"; import Navigation from "../services/navigation";
import Notifications from "../services/notifications"; import Notifications from "../services/notifications";
import { useFavoriteStore } from "./use-favorite-store"; import { useFavoriteStore } from "./use-favorite-store";
@@ -28,36 +27,26 @@ import { useRelationStore } from "./use-relation-store";
import { useReminderStore } from "./use-reminder-store"; import { useReminderStore } from "./use-reminder-store";
import { useTagStore } from "./use-tag-store"; import { useTagStore } from "./use-tag-store";
import { useTrashStore } from "./use-trash-store"; import { useTrashStore } from "./use-trash-store";
export function initAfterSync() { export function initAfterSync() {
useMenuStore.getState().setColorNotes();
useMenuStore.getState().setMenuPins();
Navigation.queueRoutesForUpdate(); Navigation.queueRoutesForUpdate();
// Whenever sync completes, try to reschedule // Whenever sync completes, try to reschedule
// any new/updated reminders. // any new/updated reminders.
Notifications.setupReminders(true); Notifications.setupReminders(true);
useRelationStore.getState().update(); useRelationStore.getState().update();
}
export async function initialize() {
if (!db) return;
useMenuStore.getState().setColorNotes(); useMenuStore.getState().setColorNotes();
useMenuStore.getState().setMenuPins(); 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() { export function clearAllStores() {
useNotebookStore.getState().clearNotebooks(); useNotebookStore.getState().clear();
useTagStore.getState().clearTags(); useTagStore.getState().clear();
useFavoriteStore.getState().clearFavorites(); useFavoriteStore.getState().clear();
useMenuStore.getState().clearAll(); useMenuStore.getState().clearAll();
useNoteStore.getState().clearNotes(); useNoteStore.getState().clear();
useMenuStore.getState().clearAll(); useMenuStore.getState().clearAll();
useTrashStore.getState().clearTrash(); useTrashStore.getState().clear();
useReminderStore.getState().cleareReminders(); 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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import create, { State } from "zustand";
import { db } from "../common/database"; import { db } from "../common/database";
import { Note, VirtualizedGrouping } from "@notesnook/core"; import createDBCollectionStore from "./create-db-collection-store";
export interface FavoriteStore extends State { const { useStore: useFavoriteStore, useCollection: useFavorites } =
favorites: VirtualizedGrouping<Note> | undefined; createDBCollectionStore({
setFavorites: (items?: Note[]) => void; getCollection: () =>
clearFavorites: () => void; db.notes.favorites.grouped(db.settings.getGroupOptions("favorites")),
} eagerlyFetchFirstBatch: true
});
export const useFavoriteStore = create<FavoriteStore>((set) => ({ export { useFavoriteStore, useFavorites };
favorites: undefined,
setFavorites: () => {
db.notes.favorites
.grouped(db.settings.getGroupOptions("favorites"))
.then((notes) => {
set({
favorites: notes
});
});
},
clearFavorites: () => set({ favorites: undefined })
}));

View File

@@ -27,20 +27,25 @@ export interface MenuStore extends State {
setMenuPins: () => void; setMenuPins: () => void;
setColorNotes: () => void; setColorNotes: () => void;
clearAll: () => void; clearAll: () => void;
loadingShortcuts: boolean;
loadingColors: boolean;
} }
export const useMenuStore = create<MenuStore>((set) => ({ export const useMenuStore = create<MenuStore>((set) => ({
menuPins: [], menuPins: [],
colorNotes: [], colorNotes: [],
loadingShortcuts: true,
loadingColors: true,
setMenuPins: () => { setMenuPins: () => {
db.shortcuts.resolved().then((shortcuts) => { db.shortcuts.resolved().then((shortcuts) => {
set({ menuPins: [...(shortcuts as [])] }); set({ menuPins: [...(shortcuts as [])], loadingShortcuts: false });
}); });
}, },
setColorNotes: () => { setColorNotes: () => {
db.colors?.all.items().then((colors) => { db.colors?.all.items().then((colors) => {
set({ 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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import create, { State } from "zustand";
import { db } from "../common/database"; import { db } from "../common/database";
import { VirtualizedGrouping, Notebook } from "@notesnook/core"; import createDBCollectionStore from "./create-db-collection-store";
export interface NotebookStore extends State { const { useStore: useNotebookStore, useCollection: useNotebooks } =
notebooks: VirtualizedGrouping<Notebook> | undefined; createDBCollectionStore({
setNotebooks: (items?: Notebook[]) => void; getCollection: () =>
clearNotebooks: () => void; db.notebooks.roots.grouped(db.settings.getGroupOptions("notebooks")),
} eagerlyFetchFirstBatch: true
});
export const useNotebookStore = create<NotebookStore>((set) => ({ export { useNotebookStore, useNotebooks };
notebooks: undefined,
setNotebooks: () => {
db.notebooks.roots
.grouped(db.settings.getGroupOptions("notebooks"))
.then((notebooks) => {
set({
notebooks: notebooks
});
});
},
clearNotebooks: () => set({ notebooks: undefined })
}));

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/>. 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 { db } from "../common/database";
import createDBCollectionStore from "./create-db-collection-store";
export interface NoteStore extends State { const { useStore: useNoteStore, useCollection: useNotes } =
notes: VirtualizedGrouping<Note> | undefined; createDBCollectionStore({
loading: boolean; getCollection: () =>
setLoading: (loading: boolean) => void; db.notes.all.grouped(db.settings.getGroupOptions("home")),
setNotes: () => void; eagerlyFetchFirstBatch: true
clearNotes: () => void;
}
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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import create, { State } from "zustand";
import { db } from "../common/database"; import { db } from "../common/database";
import { Reminder, VirtualizedGrouping } from "@notesnook/core"; import createDBCollectionStore from "./create-db-collection-store";
export interface ReminderStore extends State { const { useStore: useReminderStore, useCollection: useReminders } =
reminders: VirtualizedGrouping<Reminder> | undefined; createDBCollectionStore({
setReminders: (items?: Reminder[]) => void; getCollection: () =>
cleareReminders: () => void; db.reminders.all.grouped(db.settings.getGroupOptions("reminders")),
} eagerlyFetchFirstBatch: true
});
export const useReminderStore = create<ReminderStore>((set) => ({ export { useReminderStore, useReminders };
reminders: undefined,
setReminders: () => {
db.reminders.all
.grouped(db.settings.getGroupOptions("reminders"))
.then((reminders) => {
set({
reminders: reminders
});
});
},
cleareReminders: () => set({ reminders: undefined })
}));

View File

@@ -98,8 +98,8 @@ export interface SettingStore extends State {
setFullscreen: (fullscreen: boolean) => void; setFullscreen: (fullscreen: boolean) => void;
setDeviceMode: (mode: string) => void; setDeviceMode: (mode: string) => void;
setDimensions: (dimensions: DimensionsType) => void; setDimensions: (dimensions: DimensionsType) => void;
appLoading: boolean; isAppLoading: boolean;
setAppLoading: (appLoading: boolean) => void; setAppLoading: (isAppLoading: boolean) => void;
setSheetKeyboardHandler: (sheetKeyboardHandler: boolean) => void; setSheetKeyboardHandler: (sheetKeyboardHandler: boolean) => void;
sheetKeyboardHandler: boolean; sheetKeyboardHandler: boolean;
requestBiometrics: boolean; requestBiometrics: boolean;
@@ -165,12 +165,12 @@ export const useSettingStore = create<SettingStore>((set, get) => ({
fullscreen: false, fullscreen: false,
deviceMode: null, deviceMode: null,
dimensions: { width, height }, dimensions: { width, height },
appLoading: true, isAppLoading: true,
setSettings: (settings) => set({ settings }), setSettings: (settings) => set({ settings }),
setFullscreen: (fullscreen) => set({ fullscreen }), setFullscreen: (fullscreen) => set({ fullscreen }),
setDeviceMode: (mode) => set({ deviceMode: mode }), setDeviceMode: (mode) => set({ deviceMode: mode }),
setDimensions: (dimensions) => set({ dimensions: dimensions }), setDimensions: (dimensions) => set({ dimensions: dimensions }),
setAppLoading: (appLoading) => set({ appLoading }), setAppLoading: (isAppLoading) => set({ isAppLoading }),
setSheetKeyboardHandler: (sheetKeyboardHandler) => setSheetKeyboardHandler: (sheetKeyboardHandler) =>
set({ sheetKeyboardHandler }), set({ sheetKeyboardHandler }),
requestBiometrics: false, 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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import create, { State } from "zustand";
import { db } from "../common/database"; import { db } from "../common/database";
import { Tag, VirtualizedGrouping } from "@notesnook/core"; import createDBCollectionStore from "./create-db-collection-store";
export interface TagStore extends State { const { useStore: useTagStore, useCollection: useTags } =
tags: VirtualizedGrouping<Tag> | undefined; createDBCollectionStore({
setTags: (items?: Tag[]) => void; getCollection: () =>
clearTags: () => void; db.tags.all.grouped(db.settings.getGroupOptions("tags")),
} eagerlyFetchFirstBatch: true
});
export const useTagStore = create<TagStore>((set) => ({ export { useTagStore, useTags };
tags: undefined,
setTags: () => {
db.tags.all.grouped(db.settings.getGroupOptions("tags")).then((tags) => {
set({
tags: tags
});
});
},
clearTags: () => set({ tags: undefined })
}));

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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import create, { State } from "zustand";
import { db } from "../common/database"; import { db } from "../common/database";
import { GroupedItems, TrashItem } from "@notesnook/core/dist/types"; import createDBCollectionStore from "./create-db-collection-store";
import { VirtualizedGrouping } from "@notesnook/core";
export interface TrashStore extends State { const { useStore: useTrashStore, useCollection: useTrash } =
trash: VirtualizedGrouping<TrashItem> | undefined; createDBCollectionStore({
setTrash: (items?: GroupedItems<TrashItem>) => void; getCollection: () => db.trash.grouped(db.settings.getGroupOptions("trash")),
clearTrash: () => void; eagerlyFetchFirstBatch: true
} });
export const useTrashStore = create<TrashStore>((set, get) => ({ export { useTrashStore, useTrash };
trash: undefined,
setTrash: () => {
db.trash.grouped(db.settings.getGroupOptions("trash")).then((trash) => {
set({
trash: trash
});
});
},
clearTrash: () => set({ trash: undefined })
}));

File diff suppressed because it is too large Load Diff

View File

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