diff --git a/apps/mobile/app/components/attachments/attachment-item.tsx b/apps/mobile/app/components/attachments/attachment-item.tsx
index 315df5b85..ae9388432 100644
--- a/apps/mobile/app/components/attachments/attachment-item.tsx
+++ b/apps/mobile/app/components/attachments/attachment-item.tsx
@@ -18,22 +18,22 @@ along with this program. If not, see .
*/
import { formatBytes } from "@notesnook/common";
-import React, { useEffect, useState } from "react";
+import { Attachment, VirtualizedGrouping } from "@notesnook/core";
+import { useThemeColors } from "@notesnook/theme";
+import React from "react";
import { TouchableOpacity, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database";
import { useAttachmentProgress } from "../../hooks/use-attachment-progress";
-import { useThemeColors } from "@notesnook/theme";
+import { useDBItem } from "../../hooks/use-db-item";
import { SIZE } from "../../utils/size";
import { IconButton } from "../ui/icon-button";
import { ProgressCircleComponent } from "../ui/svg/lazy";
import Paragraph from "../ui/typography/paragraph";
import Actions from "./actions";
-import { Attachment, VirtualizedGrouping } from "@notesnook/core";
-import { useDBItem } from "../../hooks/use-db-item";
function getFileExtension(filename: string) {
- var ext = /^.+\.([^.]+)$/.exec(filename);
+ const ext = /^.+\.([^.]+)$/.exec(filename);
return ext == null ? "" : ext[1];
}
@@ -46,7 +46,7 @@ export const AttachmentItem = ({
hideWhenNotDownloading,
context
}: {
- id: string;
+ id: string | number;
attachments?: VirtualizedGrouping;
encryption?: boolean;
setAttachments: (attachments: any) => void;
@@ -54,7 +54,7 @@ export const AttachmentItem = ({
hideWhenNotDownloading?: boolean;
context?: string;
}) => {
- const [attachment] = useDBItem(id, "attachment", attachments?.item);
+ const [attachment] = useDBItem(id, "attachment", attachments);
const { colors } = useThemeColors();
const [currentProgress, setCurrentProgress] = useAttachmentProgress(
@@ -68,7 +68,7 @@ export const AttachmentItem = ({
};
return (hideWhenNotDownloading &&
- (!currentProgress || !(currentProgress as any).value)) ||
+ (!currentProgress || !currentProgress.value)) ||
!attachment ? null : (
{formatBytes(attachment.size)}{" "}
- {(currentProgress as any)?.type
- ? "(" + (currentProgress as any).type + "ing - tap to cancel)"
+ {currentProgress?.type
+ ? "(" + currentProgress.type + "ing - tap to cancel)"
: ""}
) : null}
diff --git a/apps/mobile/app/components/attachments/download-attachments.tsx b/apps/mobile/app/components/attachments/download-attachments.tsx
index b8b4321fb..2c2a5aa5d 100644
--- a/apps/mobile/app/components/attachments/download-attachments.tsx
+++ b/apps/mobile/app/components/attachments/download-attachments.tsx
@@ -91,7 +91,7 @@ const DownloadAttachments = ({
const successResults = () => {
const results = [];
- for (let [key, value] of result.entries()) {
+ for (const [key, value] of result.entries()) {
if (value.status === 1) results.push(db.attachments.attachment(key));
}
return results;
@@ -99,7 +99,7 @@ const DownloadAttachments = ({
const failedResults = () => {
const results = [];
- for (let [key, value] of result.entries()) {
+ for (const [key, value] of result.entries()) {
if (value.status === 0) results.push(db.attachments.attachment(key));
}
return results;
@@ -207,11 +207,11 @@ const DownloadAttachments = ({
}
- keyExtractor={(item) => item as string}
- renderItem={({ item }) => {
+ keyExtractor={(item, index) => "attachment" + index}
+ renderItem={({ index }) => {
return (
{}}
pressable={false}
hideWhenNotDownloading={true}
diff --git a/apps/mobile/app/components/attachments/index.tsx b/apps/mobile/app/components/attachments/index.tsx
index 625e97f91..08bfc4db0 100644
--- a/apps/mobile/app/components/attachments/index.tsx
+++ b/apps/mobile/app/components/attachments/index.tsx
@@ -83,14 +83,14 @@ export const AttachmentDialog = ({ note }: { note?: Note }) => {
}
clearTimeout(searchTimer.current);
searchTimer.current = setTimeout(async () => {
- let results = await db.lookup.attachments(
- attachmentSearchValue.current as string
- );
+ const results = await db.lookup
+ .attachments(attachmentSearchValue.current as string)
+ .sorted();
setAttachments(results);
}, 300);
};
- const renderItem = ({ item }: { item: string }) => (
+ const renderItem = ({ item }: { item: string | number }) => (
{
const onCheck = async () => {
if (!attachments) return;
setLoading(true);
- for (let id of attachments.ids) {
- const attachment = await attachments.item(id as string);
+ for (const id of attachments.ids) {
+ const attachment = (await attachments.item(id))?.item;
if (!attachment) continue;
- let result = await filesystem.checkAttachment(attachment.hash);
+ const result = await filesystem.checkAttachment(attachment.hash);
if (result.failed) {
await db.attachments.markAsFailed(attachment.hash, result.failed);
} else {
- await db.attachments.markAsFailed(id as string, undefined);
+ await db.attachments.markAsFailed(attachment.id, undefined);
}
}
refresh();
@@ -306,7 +306,7 @@ export const AttachmentDialog = ({ note }: { note?: Note }) => {
/>
}
estimatedItemSize={50}
- data={attachments?.ids as string[]}
+ data={attachments?.ids}
renderItem={renderItem}
/>
diff --git a/apps/mobile/app/components/dialogs/jump-to-section/index.tsx b/apps/mobile/app/components/dialogs/jump-to-section/index.tsx
index 0c1a1fd47..ccbd05b5d 100644
--- a/apps/mobile/app/components/dialogs/jump-to-section/index.tsx
+++ b/apps/mobile/app/components/dialogs/jump-to-section/index.tsx
@@ -51,19 +51,18 @@ const JumpToSectionDialog = () => {
const notes = data;
const [visible, setVisible] = useState(false);
const [currentIndex, setCurrentIndex] = useState(0);
+ const currentScrollPosition = useRef(0);
+ const [groups, setGroups] = useState<
+ {
+ index: number;
+ group: GroupHeader;
+ }[]
+ >();
const offsets = useRef([]);
- const timeout = useRef();
- const onPress = (item: GroupHeader) => {
- const index = notes?.ids?.findIndex((i) => {
- if (typeof i === "object") {
- return i.title === item.title && i.type === "header";
- } else {
- false;
- }
- });
+ const onPress = (item: { index: number; group: GroupHeader }) => {
scrollRef.current?.current?.scrollToIndex({
- index: index as number,
+ index: item.index,
animated: true
});
close();
@@ -97,55 +96,44 @@ const JumpToSectionDialog = () => {
}, [open]);
const onScroll = (data: { x: number; y: number }) => {
- const y = data.y;
- if (timeout) {
- clearTimeout(timeout.current);
- timeout.current = undefined;
- }
- timeout.current = setTimeout(() => {
- setCurrentIndex(
- offsets.current?.findIndex(
- (o, i) => o <= y && offsets.current[i + 1] > y
- ) || 0
- );
- }, 200);
+ currentScrollPosition.current = data.y;
};
const close = () => {
setVisible(false);
};
- const loadOffsets = useCallback(() => {
- notes?.ids
- .filter((i) => typeof i === "object" && i.type === "header")
- .map((item, index) => {
- if (typeof item === "string") return;
-
+ const loadGroupsAndOffsets = useCallback(() => {
+ notes?.groups?.().then((groups) => {
+ setGroups(groups);
+ offsets.current = [];
+ groups.map((item, index) => {
let offset = 35 * index;
- let ind = notes.ids.findIndex(
- (i) =>
- typeof i === "object" &&
- i.title === item.title &&
- i.type === "header"
- );
+ let groupIndex = item.index;
const messageState = useMessageStore.getState().message;
const msgOffset = messageState?.visible ? 60 : 10;
- ind = ind + 1;
- ind = ind - (index + 1);
- offset = offset + ind * 100 + msgOffset;
+ groupIndex = groupIndex + 1;
+ groupIndex = groupIndex - (index + 1);
+ offset = offset + groupIndex * 100 + msgOffset;
offsets.current.push(offset);
});
- }, [notes]);
- useEffect(() => {
- loadOffsets();
- }, [loadOffsets, notes]);
+ const index = offsets.current?.findIndex((o, i) => {
+ return (
+ o <= currentScrollPosition.current + 100 &&
+ offsets.current[i + 1] - 100 > currentScrollPosition.current
+ );
+ });
+
+ setCurrentIndex(index < 0 ? 0 : index);
+ });
+ }, [notes]);
return !visible ? null : (
{
- loadOffsets();
+ loadGroupsAndOffsets();
}}
onRequestClose={close}
visible={true}
@@ -178,40 +166,38 @@ const JumpToSectionDialog = () => {
paddingBottom: 20
}}
>
- {notes?.ids
- .filter((i) => typeof i === "object" && i.type === "header")
- .map((item, index) => {
- return typeof item === "object" && item.title ? (
- onPress(item)}
- type={currentIndex === index ? "selected" : "transparent"}
- customStyle={{
- minWidth: "20%",
- width: null,
- paddingHorizontal: 12,
- margin: 5,
- borderRadius: 100,
- height: 25,
- marginVertical: 10
+ {groups?.map((item, index) => {
+ return (
+ onPress(item)}
+ type={currentIndex === index ? "selected" : "transparent"}
+ customStyle={{
+ minWidth: "20%",
+ width: null,
+ paddingHorizontal: 12,
+ margin: 5,
+ borderRadius: 100,
+ height: 25,
+ marginVertical: 10
+ }}
+ >
+
-
- {item.title}
-
-
- ) : null;
- })}
+ {item.group.title}
+
+
+ );
+ })}
diff --git a/apps/mobile/app/components/list/index.tsx b/apps/mobile/app/components/list/index.tsx
index 4e44caf56..3eb617e81 100644
--- a/apps/mobile/app/components/list/index.tsx
+++ b/apps/mobile/app/components/list/index.tsx
@@ -17,13 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import {
- GroupHeader,
- GroupingKey,
- Item,
- VirtualizedGrouping,
- isGroupHeader
-} from "@notesnook/core";
+import { GroupingKey, Item, VirtualizedGrouping } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme";
import { FlashList } from "@shopify/flash-list";
import React, { useEffect, useRef } from "react";
@@ -39,11 +33,10 @@ import { eSendEvent } from "../../services/event-manager";
import Sync from "../../services/sync";
import { RouteName } from "../../stores/use-navigation-store";
import { useSettingStore } from "../../stores/use-setting-store";
-import { eOpenJumpToDialog, eScrollEvent } from "../../utils/events";
+import { eScrollEvent } from "../../utils/events";
import { tabBarRef } from "../../utils/global-refs";
import { Footer } from "../list-items/footer";
import { Header } from "../list-items/headers/header";
-import { SectionHeader } from "../list-items/headers/section-header";
import { Empty, PlaceholderData } from "./empty";
import { ListItemWrapper } from "./list-item.wrapper";
@@ -92,35 +85,20 @@ export default function List(props: ListProps) {
};
const renderItem = React.useCallback(
- ({ item, index }: { item: string | GroupHeader; index: number }) => {
- if (isGroupHeader(item)) {
- return (
- {
- eSendEvent(eOpenJumpToDialog, {
- ref: scrollRef,
- data: props.data
- });
- }}
- />
- );
- } else {
- return (
-
- );
- }
+ ({ index }: { index: number }) => {
+ return (
+
+ );
},
[
groupOptions,
@@ -181,7 +159,6 @@ export default function List(props: ListProps) {
onMomentumScrollEnd={() => {
tabBarRef.current?.unlock();
}}
- getItemType={(item: any) => (isGroupHeader(item) ? "header" : "item")}
estimatedItemSize={isCompactModeEnabled ? 60 : 100}
directionalLockEnabled={true}
keyboardShouldPersistTaps="always"
diff --git a/apps/mobile/app/components/list/list-item.wrapper.tsx b/apps/mobile/app/components/list/list-item.wrapper.tsx
index 4badb6a4f..3c0908285 100644
--- a/apps/mobile/app/components/list/list-item.wrapper.tsx
+++ b/apps/mobile/app/components/list/list-item.wrapper.tsx
@@ -16,24 +16,29 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import React from "react";
-import { getSortValue } from "@notesnook/core/dist/utils/grouping";
import {
+ Color,
+ GroupHeader,
+ GroupOptions,
GroupingKey,
Item,
- VirtualizedGrouping,
- Color,
- Reminder,
+ ItemType,
+ Note,
Notebook,
+ Reminder,
Tag,
TrashItem,
- ItemType,
- Note
+ VirtualizedGrouping
} from "@notesnook/core";
-import { useEffect, useRef, useState } from "react";
+import { getSortValue } from "@notesnook/core/dist/utils/grouping";
+import React, { useEffect, useRef, useState } from "react";
import { View } from "react-native";
-import { NoteWrapper } from "../list-items/note/wrapper";
import { db } from "../../common/database";
+import { eSendEvent } from "../../services/event-manager";
+import { RouteName } from "../../stores/use-navigation-store";
+import { eOpenJumpToDialog } from "../../utils/events";
+import { SectionHeader } from "../list-items/headers/section-header";
+import { NoteWrapper } from "../list-items/note/wrapper";
import { NotebookWrapper } from "../list-items/notebook/wrapper";
import ReminderItem from "../list-items/reminder";
import TagItem from "../list-items/tag";
@@ -45,13 +50,17 @@ export type TagsWithDateEdited = WithDateEdited;
type ListItemWrapperProps = {
group?: GroupingKey;
items: VirtualizedGrouping | undefined;
- id: string;
isSheet: boolean;
index: number;
+ renderedInRoute?: RouteName;
+ customAccentColor?: string;
+ dataType: string;
+ scrollRef: any;
+ groupOptions: GroupOptions;
};
export function ListItemWrapper(props: ListItemWrapperProps) {
- const { id, items, group, isSheet, index } = props;
+ const { items, group, isSheet, index, groupOptions } = props;
const [item, setItem] = useState- ();
const tags = useRef();
const notebooks = useRef();
@@ -59,25 +68,34 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
const color = useRef();
const totalNotes = useRef(0);
const attachmentsCount = useRef(0);
+ const [groupHeader, setGroupHeader] = useState();
+ const previousIndex = useRef();
useEffect(() => {
(async function () {
- const { item, data } = (await items?.item(id, resolveItems)) || {};
- if (!item) return;
- if (item.type === "note" && isNoteResolvedData(data)) {
- tags.current = data.tags;
- notebooks.current = data.notebooks;
- reminder.current = data.reminder;
- color.current = data.color;
- attachmentsCount.current = data.attachmentsCount;
- } else if (item.type === "notebook" && typeof data === "number") {
- totalNotes.current = data;
- } else if (item.type === "tag" && typeof data === "number") {
- totalNotes.current = data;
+ try {
+ const { item, data, group } =
+ (await items?.item(index, resolveItems)) || {};
+ if (!item) return;
+ if (item.type === "note" && isNoteResolvedData(data)) {
+ tags.current = data.tags;
+ notebooks.current = data.notebooks;
+ reminder.current = data.reminder;
+ color.current = data.color;
+ attachmentsCount.current = data.attachmentsCount;
+ } else if (item.type === "notebook" && typeof data === "number") {
+ totalNotes.current = data;
+ } else if (item.type === "tag" && typeof data === "number") {
+ totalNotes.current = data;
+ }
+ previousIndex.current = index;
+ setItem(item);
+ setGroupHeader(group);
+ } catch (e) {
+ console.log("Error", e);
}
- setItem(item);
})();
- }, [id, items]);
+ }, [index, items]);
if (!item) return ;
@@ -85,40 +103,117 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
switch (type) {
case "note": {
return (
-
+ <>
+ {groupHeader && previousIndex.current === index ? (
+ {
+ eSendEvent(eOpenJumpToDialog, {
+ ref: props.scrollRef,
+ data: items
+ });
+ }}
+ />
+ ) : null}
+
+
+ >
);
}
case "notebook":
return (
-
+ <>
+ {groupHeader && previousIndex.current === index ? (
+ {
+ eSendEvent(eOpenJumpToDialog, {
+ ref: props.scrollRef,
+ data: items
+ });
+ }}
+ />
+ ) : null}
+
+ >
);
case "reminder":
return (
-
+ <>
+ {groupHeader && previousIndex.current === index ? (
+ {
+ eSendEvent(eOpenJumpToDialog, {
+ ref: props.scrollRef,
+ data: items
+ });
+ }}
+ />
+ ) : null}
+
+ >
);
case "tag":
return (
-
+ <>
+ {groupHeader && previousIndex.current === index ? (
+ {
+ eSendEvent(eOpenJumpToDialog, {
+ ref: props.scrollRef,
+ data: items
+ });
+ }}
+ />
+ ) : null}
+
+ >
);
default:
return null;
@@ -136,6 +231,19 @@ function withDateEdited<
return { dateEdited: latestDateEdited, items };
}
+export async function resolveItems(ids: string[], items: Item[]) {
+ 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 [];
+}
+
function getDate(item: Item, groupType?: GroupingKey): number {
return (
getSortValue(
@@ -151,22 +259,6 @@ function getDate(item: Item, groupType?: GroupingKey): number {
);
}
-async function resolveItems(ids: string[], items: Record) {
- const { type } = items[ids[0]];
- if (type === "note") return resolveNotes(ids);
- else if (type === "notebook") {
- const data: Record = {};
- for (const id of ids) data[id] = await db.notebooks.totalNotes(id);
- return data;
- } else if (type === "tag") {
- const data: Record = {};
- for (const id of ids)
- data[id] = await db.relations.from({ id, type: "tag" }, "note").count();
- return data;
- }
- return {};
-}
-
type NoteResolvedData = {
notebooks?: NotebooksWithDateEdited;
reminder?: Reminder;
@@ -183,7 +275,6 @@ async function resolveNotes(ids: string[]) {
...(await db.relations.from({ type: "note", ids }, "reminder").get())
];
console.timeEnd("relations");
-
const relationIds: {
notebooks: Set;
colors: Set;
@@ -207,15 +298,15 @@ async function resolveNotes(ids: string[]) {
> = {};
for (const relation of relations) {
const noteId =
- relation.toType === "relation" ? relation.fromId : relation.toId;
+ relation.toType === "reminder" ? relation.fromId : relation.toId;
const data = grouped[noteId] || {
notebooks: [],
tags: []
};
- if (relation.toType === "relation" && !data.reminder) {
- data.reminder = relation.fromId;
- relationIds.reminders.add(relation.fromId);
+ 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);
@@ -226,7 +317,7 @@ async function resolveNotes(ids: string[]) {
data.color = relation.fromId;
relationIds.colors.add(relation.fromId);
}
- grouped[relation.toId] = data;
+ grouped[noteId] = data;
}
console.time("resolve");
@@ -240,10 +331,10 @@ async function resolveNotes(ids: string[]) {
};
console.timeEnd("resolve");
- const data: Record = {};
+ const data: NoteResolvedData[] = [];
for (const noteId in grouped) {
const group = grouped[noteId];
- data[noteId] = {
+ 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])),
@@ -252,12 +343,12 @@ async function resolveNotes(ids: string[]) {
),
attachmentsCount:
(await db.attachments?.ofNote(noteId, "all").ids())?.length || 0
- };
+ });
}
return data;
}
-function isNoteResolvedData(data: unknown): data is NoteResolvedData {
+export function isNoteResolvedData(data: unknown): data is NoteResolvedData {
return (
typeof data === "object" &&
!!data &&
diff --git a/apps/mobile/app/components/properties/index.js b/apps/mobile/app/components/properties/index.js
index 4f7c32b21..82c560efd 100644
--- a/apps/mobile/app/components/properties/index.js
+++ b/apps/mobile/app/components/properties/index.js
@@ -120,13 +120,15 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
{item.type === "note" ? : null}
-
-
-
+ {item.type === "note" ? (
+
+
+
+ ) : null}
.
*/
-import { GroupHeader, Note } from "@notesnook/core";
+import { Note } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme";
import React, { RefObject, useCallback, useEffect } from "react";
import { Keyboard, TouchableOpacity, View } from "react-native";
@@ -135,10 +135,9 @@ const MoveNoteSheet = ({
};
const renderNotebook = useCallback(
- ({ item, index }: { item: string | GroupHeader; index: number }) =>
- (item as GroupHeader).type === "header" ? null : (
-
- ),
+ ({ item, index }: { item: string | number; index: number }) => (
+
+ ),
[notebooks]
);
@@ -220,12 +219,11 @@ const MoveNoteSheet = ({
}}
>
typeof id === "string")}
+ data={notebooks?.ids}
style={{
width: "100%"
}}
estimatedItemSize={50}
- keyExtractor={(item) => item as string}
renderItem={renderNotebook}
ListEmptyComponent={
.
*/
import { Notebook, VirtualizedGrouping } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme";
-import React, { useMemo } from "react";
+import React, { useEffect } from "react";
import { View, useWindowDimensions } from "react-native";
import { notesnook } from "../../../../e2e/test.ids";
import { useTotalNotes } from "../../../hooks/use-db-item";
@@ -46,31 +46,39 @@ export const NotebookItem = ({
parent,
items
}: {
- id: string;
+ id: string | number;
currentLevel?: number;
index: number;
parent?: NotebookParentProp;
items?: VirtualizedGrouping;
}) => {
- const { nestedNotebooks, notebook: item } = useNotebook(id, items);
- const ids = useMemo(() => (id ? [id] : []), [id]);
- const { totalNotes: totalNotes } = useTotalNotes(ids, "notebook");
+ const expanded = useNotebookExpandedStore((state) => state.expanded[id]);
+ const { nestedNotebooks, notebook: item } = useNotebook(id, items, expanded);
+ const { totalNotes: totalNotes, getTotalNotes } = useTotalNotes("notebook");
const focusedRouteId = useNavigationStore((state) => state.focusedRouteId);
const { colors } = useThemeColors("sheet");
const selection = useNotebookItemSelectionStore((state) =>
- id ? state.selection[id] : undefined
+ item?.id ? state.selection[item?.id] : undefined
);
const isSelected = selection === "selected";
const isFocused = focusedRouteId === id;
const { fontScale } = useWindowDimensions();
- const expanded = useNotebookExpandedStore((state) => state.expanded[id]);
+
+ useEffect(() => {
+ if (item?.id) {
+ getTotalNotes([item?.id]);
+ }
+ }, [getTotalNotes, item?.id]);
const onPress = () => {
if (!item) return;
const state = useNotebookItemSelectionStore.getState();
if (isSelected) {
- state.markAs(item, !state.initialState[id] ? undefined : "deselected");
+ state.markAs(
+ item,
+ !state.initialState[item?.id] ? undefined : "deselected"
+ );
return;
}
@@ -112,7 +120,7 @@ export const NotebookItem = ({
item,
!isSelected
? "selected"
- : !state.initialState[id]
+ : !state.initialState[item?.id]
? undefined
: "deselected"
);
@@ -141,7 +149,8 @@ export const NotebookItem = ({
size={SIZE.xl}
color={isSelected ? colors.selected.icon : colors.primary.icon}
onPress={() => {
- useNotebookExpandedStore.getState().setExpanded(id);
+ if (!item?.id) return;
+ useNotebookExpandedStore.getState().setExpanded(item?.id);
}}
top={0}
left={0}
@@ -201,9 +210,9 @@ export const NotebookItem = ({
alignItems: "center"
}}
>
- {totalNotes?.(id) ? (
+ {item?.id && totalNotes?.(item?.id) ? (
- {totalNotes(id)}
+ {totalNotes(item?.id)}
) : null}
(
{
- console.log("items loaded tags");
+ console.log("items loaded tags", items.ids);
setTags(items);
});
}
@@ -237,11 +237,10 @@ const ManageTagsSheet = (props: {
);
const renderTag = useCallback(
- ({ item }: { item: string; index: number }) => (
+ ({ item, index }: { item: string | number; index: number }) => (
}
- id={item as string}
+ id={index}
onPress={onPress}
/>
),
@@ -303,13 +302,13 @@ const ManageTagsSheet = (props: {
) : null}
typeof id === "string") as string[]}
+ data={tags?.ids}
style={{
width: "100%"
}}
keyboardShouldPersistTaps
keyboardDismissMode="interactive"
- keyExtractor={(item) => item as string}
+ keyExtractor={(item) => item + "_tag"}
renderItem={renderTag}
ListEmptyComponent={
;
onPress: (id: string) => void;
}) => {
const { colors } = useThemeColors();
const [tag] = useDBItem(id, "tag", tags);
- const selection = useTagItemSelection((state) => state.selection[id]);
+ const selection = useTagItemSelection((state) =>
+ tag?.id ? state.selection[tag?.id] : false
+ );
- return (
+ return !tag ? null : (
onPress(id)}
+ onPress={() => {
+ if (!tag) return;
+ onPress(tag.id);
+ }}
type="gray"
>
{!tag ? null : (
onPress(id)}
+ onPress={() => {
+ if (!tag) return;
+ onPress(tag.id);
+ }}
color={
selection === "selected" || selection === "intermediate"
? colors.selected.icon
@@ -406,7 +414,6 @@ const TagItem = ({
style={{
width: 200,
height: 30,
- // backgroundColor: colors.secondary.background,
borderRadius: 5
}}
/>
diff --git a/apps/mobile/app/components/sheets/move-notes/movenote.tsx b/apps/mobile/app/components/sheets/move-notes/movenote.tsx
index 9936328af..9ce5d8d51 100644
--- a/apps/mobile/app/components/sheets/move-notes/movenote.tsx
+++ b/apps/mobile/app/components/sheets/move-notes/movenote.tsx
@@ -36,6 +36,7 @@ import { IconButton } from "../../ui/icon-button";
import { PressableButton } from "../../ui/pressable";
import Seperator from "../../ui/seperator";
import Paragraph from "../../ui/typography/paragraph";
+import { useDBItem } from "../../../hooks/use-db-item";
export const MoveNotes = ({
notebook,
@@ -45,11 +46,13 @@ export const MoveNotes = ({
fwdRef: RefObject;
}) => {
const { colors } = useThemeColors();
- const [currentNotebook, setCurrentNotebook] = useState(notebook);
+ const currentNotebook = notebook;
+
const { height } = useWindowDimensions();
const [selectedNoteIds, setSelectedNoteIds] = useState([]);
const [notes, setNotes] = useState>();
const [existingNoteIds, setExistingNoteIds] = useState([]);
+
useEffect(() => {
db.notes?.all.sorted(db.settings.getGroupOptions("notes")).then((notes) => {
setNotes(notes);
@@ -85,17 +88,18 @@ export const MoveNotes = ({
);
const renderItem = React.useCallback(
- ({ item }: { item: string }) => {
+ ({ index }: { item: string | number; index: number }) => {
return (
-1}
+ selected={(id) => selectedNoteIds?.indexOf(id) > -1}
+ exists={(id) => existingNoteIds.indexOf(id) > -1}
/>
);
},
- [notes, select, selectedNoteIds]
+ [existingNoteIds, notes, select, selectedNoteIds]
);
return (
@@ -128,9 +132,7 @@ export const MoveNotes = ({
}
- data={(notes?.ids as string[])?.filter(
- (id) => existingNoteIds?.indexOf(id) === -1
- )}
+ data={notes?.ids}
renderItem={renderItem}
/>
{selectedNoteIds.length > 0 ? (
@@ -157,21 +159,19 @@ const SelectableNoteItem = ({
id,
items,
select,
- selected
+ selected,
+ exists
}: {
- id: string;
+ id: string | number;
items?: VirtualizedGrouping;
select: (id: string) => void;
- selected?: boolean;
+ selected?: (id: string) => boolean;
+ exists: (id: string) => boolean;
}) => {
const { colors } = useThemeColors();
- const [item, setItem] = useState();
+ const [item] = useDBItem(id, "note", items);
- useEffect(() => {
- items?.item(id).then((item) => setItem(item));
- }, [id, items]);
-
- return !item ? null : (
+ return !item || exists(item.id) ? null : (
{
@@ -197,10 +197,14 @@ const SelectableNoteItem = ({
select(item?.id);
}}
name={
- selected ? "check-circle-outline" : "checkbox-blank-circle-outline"
+ selected?.(item?.id)
+ ? "check-circle-outline"
+ : "checkbox-blank-circle-outline"
}
type="selected"
- color={selected ? colors.selected.icon : colors.primary.icon}
+ color={
+ selected?.(item?.id) ? colors.selected.icon : colors.primary.icon
+ }
/>
{
nestedNotebooks: notebooks,
nestedNotebookNotesCount: totalNotes,
groupOptions
- } = useNotebook(currentRoute === "Notebook" ? root : undefined);
+ } = useNotebook(
+ currentRoute === "Notebook" ? root : undefined,
+ undefined,
+ true
+ );
const PLACEHOLDER_DATA = {
heading: "Notebooks",
@@ -140,17 +144,16 @@ export const NotebookSheet = () => {
item,
index
}: {
- item: string | GroupHeader;
+ item: string | number;
index: number;
- }) =>
- (item as GroupHeader).type === "header" ? null : (
-
- );
+ }) => (
+
+ );
const selectionContext = {
selection: selection,
@@ -396,7 +399,6 @@ export const NotebookSheet = () => {
progressBackgroundColor={colors.primary.background}
/>
}
- keyExtractor={(item) => item as string}
renderItem={renderNotebook}
ListEmptyComponent={
number;
currentLevel?: number;
index: number;
@@ -437,7 +439,7 @@ const NotebookItem = ({
nestedNotebookNotesCount,
nestedNotebooks,
notebook: item
- } = useNotebook(id, items);
+ } = useNotebook(id, items, true);
const isFocused = useNavigationStore((state) => state.focusedRouteId === id);
const { colors } = useThemeColors("sheet");
const selection = useSelection();
@@ -445,7 +447,9 @@ const NotebookItem = ({
selection.selection.findIndex((selected) => selected.id === item?.id) > -1;
const { fontScale } = useWindowDimensions();
- const expanded = useNotebookExpandedStore((state) => state.expanded[id]);
+ const expanded = useNotebookExpandedStore((state) =>
+ item?.id ? state.expanded[item?.id] : undefined
+ );
return (
{
- useNotebookExpandedStore.getState().setExpanded(id);
+ if (!item?.id) return;
+ useNotebookExpandedStore.getState().setExpanded(item?.id);
}}
top={0}
left={0}
@@ -553,9 +558,9 @@ const NotebookItem = ({
alignItems: "center"
}}
>
- {totalNotes?.(id) ? (
+ {item?.id && totalNotes?.(item?.id) ? (
- {totalNotes(id)}
+ {totalNotes(item?.id)}
) : null}
(
+ : item &&
+ nestedNotebooks?.ids.map((id, index) => (
.
import { useEffect, useState } from "react";
import { useAttachmentStore } from "../stores/use-attachment-store";
+import { Attachment } from "@notesnook/core";
type AttachmentProgress = {
type: string;
@@ -27,7 +28,7 @@ type AttachmentProgress = {
};
export const useAttachmentProgress = (
- attachment: any,
+ attachment?: Attachment,
encryption?: boolean
): [
AttachmentProgress | undefined,
@@ -45,7 +46,9 @@ export const useAttachmentProgress = (
);
useEffect(() => {
- const attachmentProgress = progress?.[attachment?.metadata?.hash];
+ const attachmentProgress = !attachment
+ ? null
+ : progress?.[attachment?.hash];
if (attachmentProgress) {
const type = attachmentProgress.type;
const loaded =
diff --git a/apps/mobile/app/hooks/use-db-item.ts b/apps/mobile/app/hooks/use-db-item.ts
index 7f722cf4f..29369c581 100644
--- a/apps/mobile/app/hooks/use-db-item.ts
+++ b/apps/mobile/app/hooks/use-db-item.ts
@@ -26,7 +26,7 @@ import {
Tag,
VirtualizedGrouping
} from "@notesnook/core";
-import React, { useEffect, useState } from "react";
+import React, { useEffect, useRef, useState } from "react";
import { db } from "../common/database";
import {
eSendEvent,
@@ -45,22 +45,36 @@ type ItemTypeKey = {
shortcut: Shortcut;
};
+function isValidIdOrIndex(idOrIndex?: string | number) {
+ return typeof idOrIndex === "number" || typeof idOrIndex === "string";
+}
+
export const useDBItem = (
- id?: string,
+ idOrIndex?: string | number,
type?: T,
items?: VirtualizedGrouping
): [ItemTypeKey[T] | undefined, () => void] => {
const [item, setItem] = useState();
+ const itemIdRef = useRef();
+ const prevIdOrIndexRef = useRef();
+
+ if (prevIdOrIndexRef.current !== idOrIndex) {
+ itemIdRef.current = undefined;
+ prevIdOrIndexRef.current = idOrIndex;
+ }
useEffect(() => {
const onUpdateItem = (itemId?: string) => {
- if (typeof itemId === "string" && itemId !== id) return;
- if (!id) return;
- console.log("useDBItem.onUpdateItem", id, type);
+ if (typeof itemId === "string" && itemId !== itemIdRef.current) return;
- if (items) {
- items.item(id).then((item) => {
- setItem(item);
+ if (!isValidIdOrIndex(idOrIndex)) return;
+
+ console.log("useDBItem.onUpdateItem", idOrIndex, type);
+
+ if (items && typeof idOrIndex === "number") {
+ items.item(idOrIndex).then((item) => {
+ setItem(item.item);
+ itemIdRef.current = item.item.id;
});
} else {
if (!(db as any)[type + "s"][type]) {
@@ -69,9 +83,14 @@ export const useDBItem = (
`db.${type}s.${type}(id: string)`
);
} else {
+ console.log("get notebook");
+
(db as any)[type + "s"]
- ?.[type]?.(id)
- .then((item: ItemTypeKey[T]) => setItem(item));
+ ?.[type]?.(idOrIndex as string)
+ .then((item: ItemTypeKey[T]) => {
+ setItem(item);
+ itemIdRef.current = item.id;
+ });
}
}
};
@@ -80,46 +99,42 @@ export const useDBItem = (
return () => {
eUnSubscribeEvent(eDBItemUpdate, onUpdateItem);
};
- }, [id, type, items]);
+ }, [idOrIndex, type, items]);
return [
- id ? (item as ItemTypeKey[T]) : undefined,
+ isValidIdOrIndex(idOrIndex) ? (item as ItemTypeKey[T]) : undefined,
() => {
- if (id) {
- eSendEvent(eDBItemUpdate, id);
+ if (idOrIndex) {
+ eSendEvent(eDBItemUpdate, itemIdRef.current || idOrIndex);
}
}
];
};
-export const useTotalNotes = (
- ids: string[],
- type: "notebook" | "tag" | "color"
-) => {
+export const useTotalNotes = (type: "notebook" | "tag" | "color") => {
const [totalNotesById, setTotalNotesById] = useState<{
[id: string]: number;
}>({});
- const getTotalNotes = React.useCallback(() => {
- if (!ids || !ids.length || !type) return;
- db.relations
- .from({ type: "notebook", ids: ids as string[] }, ["notebook", "note"])
- .get()
- .then((relations) => {
- const totalNotesById: any = {};
- for (const id of ids) {
- totalNotesById[id] = relations.filter(
- (relation) => relation.fromId === id && relation.toType === "note"
- )?.length;
- }
- setTotalNotesById(totalNotesById);
- });
- console.log("useTotalNotes.getTotalNotes");
- }, [ids, type]);
-
- useEffect(() => {
- getTotalNotes();
- }, [ids, type, getTotalNotes]);
+ const getTotalNotes = React.useCallback(
+ (ids: string[]) => {
+ if (!ids || !ids.length || !type) return;
+ db.relations
+ .from({ type: type, ids: ids as string[] }, ["note"])
+ .get()
+ .then((relations) => {
+ const totalNotesById: any = {};
+ for (const id of ids) {
+ totalNotesById[id] = relations.filter(
+ (relation) => relation.fromId === id && relation.toType === "note"
+ )?.length;
+ }
+ setTotalNotesById(totalNotesById);
+ });
+ console.log("useTotalNotes.getTotalNotes");
+ },
+ [type]
+ );
return {
totalNotes: (id: string) => {
diff --git a/apps/mobile/app/hooks/use-notebook.ts b/apps/mobile/app/hooks/use-notebook.ts
index 4cb6fca8e..de5e7c211 100644
--- a/apps/mobile/app/hooks/use-notebook.ts
+++ b/apps/mobile/app/hooks/use-notebook.ts
@@ -24,40 +24,47 @@ import { eGroupOptionsUpdated, eOnNotebookUpdated } from "../utils/events";
import { useDBItem, useTotalNotes } from "./use-db-item";
export const useNotebook = (
- id?: string,
- items?: VirtualizedGrouping
+ id?: string | number,
+ items?: VirtualizedGrouping,
+ nestedNotebooks?: boolean
) => {
const [item, refresh] = useDBItem(id, "notebook", items);
const [groupOptions, setGroupOptions] = useState(
db.settings.getGroupOptions("notebooks")
);
const [notebooks, setNotebooks] = useState>();
- const { totalNotes: nestedNotebookNotesCount } = useTotalNotes(
- notebooks?.ids as string[],
- "notebook"
- );
+ const { totalNotes: nestedNotebookNotesCount, getTotalNotes } =
+ useTotalNotes("notebook");
const onRequestUpdate = React.useCallback(() => {
- if (!id) return;
- console.log("useNotebook.onRequestUpdate", id, Date.now());
- db.relations
- .from(
- {
- type: "notebook",
- id: id
- },
- "notebook"
- )
- .selector.sorted(db.settings.getGroupOptions("notebooks"))
+ if (!item?.id) return;
+ console.log("useNotebook.onRequestUpdate", item?.id, Date.now());
+
+ const selector = db.relations.from(
+ {
+ type: "notebook",
+ id: item.id
+ },
+ "notebook"
+ ).selector;
+
+ selector.ids().then((notebookIds) => {
+ getTotalNotes(notebookIds);
+ });
+
+ selector
+ .sorted(db.settings.getGroupOptions("notebooks"))
.then((notebooks) => {
setNotebooks(notebooks);
});
- }, [id]);
+ }, [getTotalNotes, item?.id]);
useEffect(() => {
- console.log("useNotebook.useEffect.onRequestUpdate");
- onRequestUpdate();
- }, [onRequestUpdate]);
+ if (nestedNotebooks) {
+ console.log("useNotebook.useEffect.onRequestUpdate");
+ onRequestUpdate();
+ }
+ }, [item?.id, onRequestUpdate, nestedNotebooks]);
const onUpdate = useCallback(
(type: string) => {
@@ -73,7 +80,9 @@ export const useNotebook = (
const onNotebookUpdate = (id?: string) => {
if (typeof id === "string" && id !== id) return;
setImmediate(() => {
- onRequestUpdate();
+ if (nestedNotebooks) {
+ onRequestUpdate();
+ }
refresh();
});
};
@@ -84,7 +93,7 @@ export const useNotebook = (
eUnSubscribeEvent(eGroupOptionsUpdated, onUpdate);
eUnSubscribeEvent(eOnNotebookUpdated, onNotebookUpdate);
};
- }, [onUpdate, onRequestUpdate, id, refresh]);
+ }, [onUpdate, onRequestUpdate, id, refresh, nestedNotebooks]);
return {
notebook: item,
diff --git a/apps/mobile/app/stores/use-notes-store.ts b/apps/mobile/app/stores/use-notes-store.ts
index ee22206f1..587278d83 100644
--- a/apps/mobile/app/stores/use-notes-store.ts
+++ b/apps/mobile/app/stores/use-notes-store.ts
@@ -33,11 +33,13 @@ export const useNoteStore = create((set) => ({
notes: undefined,
loading: true,
setLoading: (loading) => set({ loading: loading }),
- setNotes: () => {
- db.notes.all.grouped(db.settings.getGroupOptions("home")).then((notes) => {
- set({
- notes: notes
- });
+ setNotes: async () => {
+ const notes = await db.notes.all.grouped(
+ db.settings.getGroupOptions("home")
+ );
+ await notes.item(0);
+ set({
+ notes: notes
});
},
clearNotes: () => set({ notes: undefined })
diff --git a/apps/mobile/share/search.tsx b/apps/mobile/share/search.tsx
index 3c0c018fb..5eb15fb4b 100644
--- a/apps/mobile/share/search.tsx
+++ b/apps/mobile/share/search.tsx
@@ -17,20 +17,24 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { Item, Note, Notebook, VirtualizedGrouping } from "@notesnook/core";
+import {
+ Item,
+ Note,
+ Notebook,
+ Tag,
+ VirtualizedGrouping
+} from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useRef, useState } from "react";
import {
FlatList,
Platform,
- StatusBar,
Text,
TextInput,
TouchableOpacity,
View,
useWindowDimensions
} from "react-native";
-import { useSafeAreaInsets } from "react-native-safe-area-context";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import create from "zustand";
import { db } from "../app/common/database";
@@ -121,23 +125,29 @@ const NotebookItem = ({
items,
level = 0
}: {
- id: string;
+ id: string | number;
mode: SearchMode;
close?: () => void;
level?: number;
items?: VirtualizedGrouping;
}) => {
- const isExpanded = useNotebookExpandedStore((state) => state.expanded[id]);
const { nestedNotebooks, notebook } = useNotebook(id, items);
+ const isExpanded = useNotebookExpandedStore((state) =>
+ !notebook ? false : state.expanded[notebook.id]
+ );
const { colors } = useThemeColors();
- const isSelected = useShareStore(
- (state) =>
- state.selectedNotebooks.findIndex((selectedId) => id === selectedId) > -1
+ const isSelected = useShareStore((state) =>
+ !notebook
+ ? false
+ : state.selectedNotebooks.findIndex(
+ (selectedId) => notebook?.id === selectedId
+ ) > -1
);
const set = SearchSetters[mode];
const onSelectItem = async () => {
- set(id);
+ if (!notebook) return;
+ set(notebook.id);
};
return !notebook ? (
@@ -172,7 +182,8 @@ const NotebookItem = ({
alignItems: "center"
}}
onPress={() => {
- useNotebookExpandedStore.getState().setExpanded(id);
+ if (!notebook) return;
+ useNotebookExpandedStore.getState().setExpanded(notebook.id);
}}
activeOpacity={1}
>
@@ -230,10 +241,10 @@ const NotebookItem = ({
marginTop: 5
}}
>
- {(nestedNotebooks.ids as string[]).map((id) => (
+ {nestedNotebooks.ids.map((item, index) => (
void;
items?: VirtualizedGrouping
- ;
@@ -259,21 +270,23 @@ const ListItem = ({
const [item] = useDBItem(
id,
mode === "appendNote" ? "note" : "tag",
- items as any
+ items as VirtualizedGrouping
);
const { colors } = useThemeColors();
const isSelected = useShareStore((state) =>
- mode === "appendNote" ? false : state.selectedTags.indexOf(id as never) > -1
+ mode === "appendNote" || !item
+ ? false
+ : state.selectedTags.indexOf(item?.id as never) > -1
);
const set = SearchSetters[mode];
const onSelectItem = async () => {
- if ((item as Note)?.locked) {
+ if ((item as Note)?.locked || !item) {
return;
}
- set(id);
+ set(item.id);
};
return !item ? null : (
@@ -356,12 +369,6 @@ export const Search = ({
.then((exists) => setQueryExists(!!exists));
};
- const insets =
- Platform.OS === "android"
- ? { top: StatusBar.currentHeight }
- : // eslint-disable-next-line react-hooks/rules-of-hooks
- useSafeAreaInsets();
-
const get = SearchGetters[mode];
const lookup = SearchLookup[mode];
@@ -389,16 +396,16 @@ export const Search = ({
}, [get]);
const renderItem = React.useCallback(
- ({ item }: { item: string }) =>
+ ({ index }: { item: string | number; index: number }) =>
mode === "selectNotebooks" ? (
}
/>
) : (
-
+
),
[close, mode, items]
);
@@ -502,7 +509,7 @@ export const Search = ({
) : null}