diff --git a/apps/mobile/app/common/filesystem/download-attachment.js b/apps/mobile/app/common/filesystem/download-attachment.js
index 0f5d731ff..3fa79c97a 100644
--- a/apps/mobile/app/common/filesystem/download-attachment.js
+++ b/apps/mobile/app/common/filesystem/download-attachment.js
@@ -46,15 +46,15 @@ export const FileDownloadStatus = {
* Download all user's attachments
* @returns
*/
-export function downloadAllAttachments() {
- const attachments = db.attachments.all;
+export async function downloadAllAttachments() {
+ const attachments = await db.attachments.all.ids();
return downloadAttachments(attachments);
}
/**
* Downloads provided attachments to a .zip file
* on user's device.
- * @param attachments
+ * @param {string[]} attachments
* @param onProgress
* @returns
*/
@@ -89,8 +89,8 @@ export async function downloadAttachments(
await RNFetchBlob.fs.mkdir(zipSourceFolder);
for (let i = 0; i < attachments.length; i++) {
- let attachment = attachments[i];
- const hash = attachment.metadata.hash;
+ let attachment = await db.attachments.attachment(attachments[i]);
+ const hash = attachment.hash;
try {
if (canceled.current) {
RNFetchBlob.fs.unlink(zipSourceFolder).catch(console.log);
@@ -114,15 +114,16 @@ export async function downloadAttachments(
}
if (!uri) throw new Error("Failed to download file");
// Move file to the source folder we will zip eventually and rename the file to it's actual name.
- const filePath = `${zipSourceFolder}/${attachment.metadata.filename}`;
+ const filePath = `${zipSourceFolder}/${attachment.filename}`;
await RNFetchBlob.fs.mv(`${cacheDir}/${uri}`, filePath);
result.set(hash, {
- filename: attachment.metadata.filename,
- status: FileDownloadStatus.Success
+ filename: attachment.filename,
+ status: FileDownloadStatus.Success,
+ attachment: attachment
});
} catch (e) {
result.set(hash, {
- filename: attachment.metadata.filename,
+ filename: attachment.filename,
status: FileDownloadStatus.Fail,
reason: e
});
@@ -208,14 +209,12 @@ export default async function downloadAttachment(
try {
console.log(
"starting download attachment",
- attachment.metadata.hash,
+ attachment.hash,
options.groupId
);
- await db.fs.downloadFile(
- options.groupId || attachment.metadata.hash,
- attachment.metadata.hash
- );
-
+ await db
+ .fs()
+ .downloadFile(options.groupId || attachment.hash, attachment.hash);
if (!(await exists(attachment.metadata.hash))) {
return;
}
@@ -228,19 +227,19 @@ export default async function downloadAttachment(
}
let filename = getFileNameWithExtension(
- attachment.metadata.filename,
- attachment.metadata.type
+ attachment.filename,
+ attachment.mimeType
);
let key = await db.attachments.decryptKey(attachment.key);
let info = {
iv: attachment.iv,
salt: attachment.salt,
- length: attachment.length,
+ length: attachment.size,
alg: attachment.alg,
- hash: attachment.metadata.hash,
- hashType: attachment.metadata.hashType,
- mime: attachment.metadata.type,
+ hash: attachment.hash,
+ hashType: attachment.hashType,
+ mime: attachment.mimeType,
fileName: options.cache ? undefined : filename,
uri: options.cache ? undefined : folder.uri,
chunkSize: attachment.chunkSize,
@@ -263,11 +262,11 @@ export default async function downloadAttachment(
if (
attachment.dateUploaded &&
- !isImage(attachment.metadata?.type) &&
- !isDocument(attachment.metadata?.type)
+ !isImage(attachment.mimeType) &&
+ !isDocument(attachment.mimeType)
) {
RNFetchBlob.fs
- .unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.metadata.hash}`)
+ .unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.hash}`)
.catch(console.log);
}
if (Platform.OS === "ios" && !options.cache) {
@@ -282,7 +281,7 @@ export default async function downloadAttachment(
: "File Manager/Notesnook/downloads"
}`,
icon: "download",
- context: global ? null : attachment.metadata.hash,
+ context: global ? null : attachment.hash,
component:
});
}
@@ -292,10 +291,13 @@ export default async function downloadAttachment(
console.log("download attachment error: ", e);
if (attachment.dateUploaded) {
RNFetchBlob.fs
- .unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.metadata.hash}`)
+ .unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.hash}`)
+ .catch(console.log);
+ RNFetchBlob.fs
+ .unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.hash}_dcache`)
.catch(console.log);
}
- useAttachmentStore.getState().remove(attachment.metadata.hash);
+ useAttachmentStore.getState().remove(attachment.hash);
if (options.throwError) {
throw e;
}
diff --git a/apps/mobile/app/common/filesystem/download.js b/apps/mobile/app/common/filesystem/download.js
index 4f3ea9dc9..6876c1377 100644
--- a/apps/mobile/app/common/filesystem/download.js
+++ b/apps/mobile/app/common/filesystem/download.js
@@ -119,7 +119,7 @@ export async function checkAttachment(hash) {
const isInternetReachable =
internetState.isConnected && internetState.isInternetReachable;
if (!isInternetReachable) return { success: true };
- const attachment = db.attachments.attachment(hash);
+ const attachment = await db.attachments.attachment(hash);
if (!attachment) return { failed: "Attachment not found." };
try {
diff --git a/apps/mobile/app/common/filesystem/io.js b/apps/mobile/app/common/filesystem/io.js
index ec9943eb3..49c5935a8 100644
--- a/apps/mobile/app/common/filesystem/io.js
+++ b/apps/mobile/app/common/filesystem/io.js
@@ -35,14 +35,10 @@ export async function readEncrypted(filename, key, cipherData) {
return false;
}
- const attachment = db.attachments.attachment(filename);
- const isPng = !attachment.metadata.type
- ? false
- : /(png)/g.test(attachment?.metadata.type);
- const isJpeg = !attachment.metadata.type
- ? false
- : /(jpeg|jpg)/g.test(attachment?.metadata.type);
-
+ const attachment = await db.attachments.attachment(filename);
+ const isPng = /(png)/g.test(attachment?.mimeType || "");
+ const isJpeg = /(jpeg|jpg)/g.test(attachment?.mimeType || "");
+ console.log("decrypting....");
let output = await Sodium.decryptFile(
key,
{
@@ -194,10 +190,10 @@ export async function exists(filename) {
}
if (exists || existsInAppGroup) {
- const attachment = db.attachments.attachment(filename);
- const totalChunks = Math.ceil(attachment.length / attachment.chunkSize);
+ const attachment = await db.attachments.attachment(filename);
+ const totalChunks = Math.ceil(attachment.size / attachment.chunkSize);
const totalAbytes = totalChunks * ABYTES;
- const expectedFileSize = attachment.length + totalAbytes;
+ const expectedFileSize = attachment.size + totalAbytes;
const stat = await RNFetchBlob.fs.stat(
existsInAppGroup ? appGroupPath : path
diff --git a/apps/mobile/app/common/filesystem/upload.js b/apps/mobile/app/common/filesystem/upload.js
index 16c3ec75a..5060cd1dc 100644
--- a/apps/mobile/app/common/filesystem/upload.js
+++ b/apps/mobile/app/common/filesystem/upload.js
@@ -87,12 +87,9 @@ export async function uploadFile(filename, data, cancelToken) {
DatabaseLogger.info(
`File upload status: ${filename}, ${status}, ${text}`
);
- let attachment = db.attachments.attachment(filename);
+ let attachment = await db.attachments.attachment(filename);
if (!attachment) return result;
- if (
- !isImage(attachment.metadata.type) &&
- !isDocument(attachment.metadata?.type)
- ) {
+ if (!isImage(attachment.mimeType) && !isDocument(attachment.mimeType)) {
RNFetchBlob.fs.unlink(`${cacheDir}/${filename}`).catch(console.log);
}
} else {
diff --git a/apps/mobile/app/components/attachments/download-attachments.tsx b/apps/mobile/app/components/attachments/download-attachments.tsx
index 2c2a5aa5d..58d7b99f8 100644
--- a/apps/mobile/app/components/attachments/download-attachments.tsx
+++ b/apps/mobile/app/components/attachments/download-attachments.tsx
@@ -63,7 +63,7 @@ const DownloadAttachments = ({
canceled.current = false;
groupId.current = Date.now().toString();
const result = await downloadAttachments(
- attachments,
+ await attachments.ids(),
(progress: number, statusText: string) =>
setProgress({ value: progress, statusText }),
canceled,
@@ -89,29 +89,21 @@ const DownloadAttachments = ({
groupId.current = undefined;
};
- const successResults = () => {
- const results = [];
- for (const [key, value] of result.entries()) {
- if (value.status === 1) results.push(db.attachments.attachment(key));
- }
- return results;
- };
-
const failedResults = () => {
const results = [];
- for (const [key, value] of result.entries()) {
- if (value.status === 0) results.push(db.attachments.attachment(key));
+ for (const value of result.values()) {
+ if (value.status === 0) results.push(value.attachment);
}
return results;
};
function getResultText() {
const downloadedAttachmentsCount =
- attachments?.ids?.length - failedResults().length;
+ attachments?.placeholders?.length - failedResults().length;
if (downloadedAttachmentsCount === 0)
return "Failed to download all attachments";
return `Successfully downloaded ${downloadedAttachmentsCount}/${
- attachments?.ids.length
+ attachments?.placeholders.length
} attachments as a zip file at ${
Platform.OS === "android" ? "the selected folder" : "Notesnook/downloads"
}`;
@@ -174,7 +166,9 @@ const DownloadAttachments = ({
animated={true}
useNativeDriver
progress={
- progress.value ? progress.value / attachments.ids?.length : 0
+ progress.value
+ ? progress.value / attachments.placeholders?.length
+ : 0
}
unfilledColor={colors.secondary.background}
color={colors.primary.accent}
@@ -192,7 +186,7 @@ const DownloadAttachments = ({
borderRadius: 5,
marginVertical: 12
}}
- data={downloading ? attachments.ids : undefined}
+ data={downloading ? attachments.placeholders : undefined}
ListEmptyComponent={
}
- keyExtractor={(item, index) => "attachment" + index}
+ keyExtractor={(index) => "attachment" + index}
renderItem={({ index }) => {
return (
{
}, 300);
};
- const renderItem = ({ item }: { item: string | number }) => (
+ const renderItem = ({ index }: { item: boolean; index: number }) => (
{
setAttachments(filterAttachments(currentFilter));
}}
attachments={attachments}
- id={item}
+ id={index}
context="attachments-list"
/>
);
@@ -106,10 +106,10 @@ export const AttachmentDialog = ({ note }: { note?: Note }) => {
const onCheck = async () => {
if (!attachments) return;
setLoading(true);
- for (const id of attachments.ids) {
- const attachment = (await attachments.item(id))?.item;
- if (!attachment) continue;
+ for (let i = 0; i < attachments.placeholders.length; i++) {
+ const attachment = (await attachments.item(i))?.item;
+ if (!attachment) continue;
const result = await filesystem.checkAttachment(attachment.hash);
if (result.failed) {
await db.attachments.markAsFailed(attachment.hash, result.failed);
@@ -310,7 +310,7 @@ export const AttachmentDialog = ({ note }: { note?: Note }) => {
/>
}
estimatedItemSize={50}
- data={attachments?.ids}
+ data={attachments?.placeholders}
renderItem={renderItem}
/>
diff --git a/apps/mobile/app/components/dialogs/pdf-preview/index.js b/apps/mobile/app/components/dialogs/pdf-preview/index.js
index 4b5e6aa5a..91c837e4e 100644
--- a/apps/mobile/app/components/dialogs/pdf-preview/index.js
+++ b/apps/mobile/app/components/dialogs/pdf-preview/index.js
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import React, { useEffect, useRef, useState } from "react";
+import React, { useCallback, useEffect, useRef, useState } from "react";
import { Dimensions, TextInput, View } from "react-native";
import {
addOrientationListener,
@@ -85,7 +85,7 @@ const PDFPreview = () => {
return () => {
eUnSubscribeEvent("PDFPreview", open);
};
- }, []);
+ }, [open]);
const onOrientationChange = (o) => {
if (o.includes("LANDSCAPE")) {
@@ -102,23 +102,26 @@ const PDFPreview = () => {
};
}, []);
- const open = async (attachment) => {
- setVisible(true);
- setLoading(true);
- setTimeout(async () => {
- setAttachment(attachment);
- let hash = attachment.metadata.hash;
- if (!hash) return;
- const uri = await downloadAttachment(hash, false, {
- silent: true,
- cache: true
- });
- const path = `${cacheDir}/${uri}`;
- snapshotValue.current = snapshot.current;
- setPDFSource("file://" + path);
- setLoading(false);
- }, 100);
- };
+ const open = useCallback(
+ async (attachment) => {
+ setVisible(true);
+ setLoading(true);
+ setTimeout(async () => {
+ setAttachment(attachment);
+ let hash = attachment.hash;
+ if (!hash) return;
+ const uri = await downloadAttachment(hash, false, {
+ silent: true,
+ cache: true
+ });
+ const path = `${cacheDir}/${uri}`;
+ snapshotValue.current = snapshot.current;
+ setPDFSource("file://" + path);
+ setLoading(false);
+ }, 100);
+ },
+ [snapshot]
+ );
const close = () => {
setPDFSource(null);
@@ -130,7 +133,7 @@ const PDFPreview = () => {
if (error?.message === "Password required or incorrect password.") {
await sleep(300);
presentDialog({
- context: attachment?.metadata?.hash,
+ context: attachment?.hash,
input: true,
inputPlaceholder: "Enter password",
positiveText: "Unlock",
@@ -151,8 +154,8 @@ const PDFPreview = () => {
return (
visible && (
-
-
+
+
{
color={colors.static.white}
name="download"
onPress={() => {
- downloadAttachment(attachment.metadata.hash, false);
+ downloadAttachment(attachment.hash, false);
}}
/>
diff --git a/apps/mobile/app/components/list/index.tsx b/apps/mobile/app/components/list/index.tsx
index 02cb0bf7d..37e89fe36 100644
--- a/apps/mobile/app/components/list/index.tsx
+++ b/apps/mobile/app/components/list/index.tsx
@@ -155,7 +155,7 @@ export default function List(props: ListProps) {
style={styles}
ref={scrollRef}
testID={notesnook.list.id}
- data={props.data?.ids || []}
+ data={props.data?.placeholders || []}
renderItem={renderItem}
onScroll={onListScroll}
nestedScrollEnabled={true}
diff --git a/apps/mobile/app/components/selection-header/index.tsx b/apps/mobile/app/components/selection-header/index.tsx
index a1c217f8a..ffdf8311e 100644
--- a/apps/mobile/app/components/selection-header/index.tsx
+++ b/apps/mobile/app/components/selection-header/index.tsx
@@ -19,13 +19,14 @@ along with this program. If not, see .
import { Item, ItemType, VirtualizedGrouping } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme";
-import React, { useCallback, useEffect } from "react";
+import React, { useEffect } from "react";
import {
BackHandler,
NativeEventSubscription,
Platform,
View
} from "react-native";
+import Animated, { FadeInUp } from "react-native-reanimated";
import { db } from "../../common/database";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { ToastManager } from "../../services/event-manager";
@@ -43,7 +44,6 @@ import ExportNotesSheet from "../sheets/export-notes";
import ManageTagsSheet from "../sheets/manage-tags";
import { IconButton } from "../ui/icon-button";
import Heading from "../ui/typography/heading";
-import Animated, { FadeInUp } from "react-native-reanimated";
export const SelectionHeader = React.memo(
({
@@ -65,8 +65,7 @@ export const SelectionHeader = React.memo(
const clearSelection = useSelectionStore((state) => state.clearSelection);
const insets = useGlobalSafeAreaInsets();
const allSelected =
- items?.ids?.filter((id) => typeof id === "string").length ===
- selectedItemsList.length;
+ items?.placeholders?.length === selectedItemsList.length;
const focusedRouteId = useNavigationStore((state) => state.focusedRouteId);
useEffect(() => {
@@ -200,15 +199,7 @@ export const SelectionHeader = React.memo(
onPress={async () => {
useSelectionStore
.getState()
- .setAll(
- allSelected
- ? []
- : [
- ...((items?.ids.filter(
- (id) => typeof id !== "object"
- ) as string[]) || [])
- ]
- );
+ .setAll(allSelected ? [] : [...((await items?.ids()) || [])]);
}}
tooltipText="Select all"
tooltipPosition={4}
diff --git a/apps/mobile/app/components/sheets/add-to/index.tsx b/apps/mobile/app/components/sheets/add-to/index.tsx
index 88e508ef9..59a1c8325 100644
--- a/apps/mobile/app/components/sheets/add-to/index.tsx
+++ b/apps/mobile/app/components/sheets/add-to/index.tsx
@@ -31,14 +31,12 @@ import { db } from "../../../common/database";
import { presentSheet } from "../../../services/event-manager";
import Navigation from "../../../services/navigation";
import { ItemSelection } from "../../../stores/item-selection-store";
-import {
- useNotebookStore,
- useNotebooks
-} from "../../../stores/use-notebook-store";
+import { useNotebooks } from "../../../stores/use-notebook-store";
import { useRelationStore } from "../../../stores/use-relation-store";
import { useSelectionStore } from "../../../stores/use-selection-store";
import { useSettingStore } from "../../../stores/use-setting-store";
import { updateNotebook } from "../../../utils/notebooks";
+import { SIZE } from "../../../utils/size";
import { Dialog } from "../../dialog";
import DialogHeader from "../../dialog/dialog-header";
import SheetProvider from "../../sheet-provider";
@@ -47,7 +45,6 @@ import { IconButton } from "../../ui/icon-button";
import Paragraph from "../../ui/typography/paragraph";
import { NotebookItem } from "./notebook-item";
import { useNotebookItemSelectionStore } from "./store";
-import { SIZE } from "../../../utils/size";
async function updateInitialSelectionState(items: string[]) {
const relations = await db.relations
@@ -144,7 +141,7 @@ const MoveNoteSheet = ({
};
const renderNotebook = useCallback(
- ({ item, index }: { item: string | number; index: number }) => (
+ ({ index }: { item: boolean; index: number }) => (
),
[notebooks]
@@ -228,7 +225,7 @@ const MoveNoteSheet = ({
}}
>
- {nestedNotebooks?.ids.length ? (
+ {nestedNotebooks?.placeholders.length ? (
(
+ : nestedNotebooks?.placeholders.map((id, index) => (
{
- console.log("items loaded tags", items.ids);
+ console.log("items loaded tags", items.placeholders.length);
setTags(items);
});
}
@@ -237,7 +237,7 @@ const ManageTagsSheet = (props: {
);
const renderTag = useCallback(
- ({ item, index }: { item: string | number; index: number }) => (
+ ({ index }: { item: boolean; index: number }) => (
}
id={index}
@@ -302,7 +302,7 @@ const ManageTagsSheet = (props: {
) : null}
{
+ ({ index }: { item: boolean; index: number }) => {
return (
}
- data={notes?.ids}
+ data={notes?.placeholders}
renderItem={renderItem}
/>
{selectedNoteIds.length > 0 ? (
diff --git a/apps/mobile/app/components/sheets/notebook-sheet/index.tsx b/apps/mobile/app/components/sheets/notebook-sheet/index.tsx
index 38fa4f698..ab9fcf0d6 100644
--- a/apps/mobile/app/components/sheets/notebook-sheet/index.tsx
+++ b/apps/mobile/app/components/sheets/notebook-sheet/index.tsx
@@ -140,13 +140,7 @@ export const NotebookSheet = () => {
loading: "Loading notebook topics"
};
- const renderNotebook = ({
- item,
- index
- }: {
- item: string | number;
- index: number;
- }) => (
+ const renderNotebook = ({ index }: { item: boolean; index: number }) => (
{
- {nestedNotebooks?.ids.length ? (
+ {nestedNotebooks?.placeholders.length ? (
(
+ nestedNotebooks?.placeholders.map((id, index) => (
>();
- const hasNoRelations = !items || items?.ids?.length === 0;
+ const hasNoRelations = !items || items?.placeholders?.length === 0;
useEffect(() => {
db.relations?.[relationType]?.(
@@ -85,13 +85,12 @@ export const RelationsList = ({
)
.selector.sorted({
sortBy: "dateEdited",
- sortDirection: "desc",
- groupBy: "default"
+ sortDirection: "desc"
})
.then((grouped) => {
setItems(grouped);
});
- }, [relationType, referenceType]);
+ }, [relationType, referenceType, item?.id, item?.type]);
return (
- {references?.ids && references?.ids?.length > 0 ? (
+ {references?.placeholders && references?.placeholders?.length > 0 ? (
) => {
AddNotebookSheet.present(params.current.item);
}}
notebook={params.current.item}
- totalNotes={notes?.ids.length || 0}
+ totalNotes={notes?.placeholders.length || 0}
/>
}
placeholder={{
diff --git a/apps/mobile/app/screens/notebooks/index.tsx b/apps/mobile/app/screens/notebooks/index.tsx
index e130c8d1d..b7059a1d1 100644
--- a/apps/mobile/app/screens/notebooks/index.tsx
+++ b/apps/mobile/app/screens/notebooks/index.tsx
@@ -56,8 +56,8 @@ export const Notebooks = ({
});
useEffect(() => {
- if (notebooks?.ids) {
- if (notebooks?.ids?.length === 0 && !Config.isTesting) {
+ if (notebooks?.placeholders) {
+ if (notebooks?.placeholders?.length === 0 && !Config.isTesting) {
Walkthrough.present("notebooks");
} else {
Walkthrough.update("notebooks");
@@ -99,7 +99,9 @@ export const Notebooks = ({
headerTitle="Notebooks"
/>
- {!notebooks || notebooks.ids.length === 0 || !isFocused ? null : (
+ {!notebooks ||
+ notebooks.placeholders.length === 0 ||
+ !isFocused ? null : (
;
- if (notes.ids.length === 0) setLoadingNotes(false);
+ if (notes.placeholders.length === 0) setLoadingNotes(false);
setNotes(notes);
await notes.item(0, resolveItems);
setLoadingNotes(false);
@@ -204,7 +204,8 @@ const NotesPage = ({
/>
{!isMonograph &&
- ((notes?.ids && (notes?.ids?.length || 0) > 0) || isFocused) ? (
+ ((notes?.placeholders && (notes?.placeholders?.length || 0) > 0) ||
+ isFocused) ? (
) => {
query,
route.params.items as FilteredSelector
).sorted();
- console.log(`Found ${results.ids?.length} results for ${query}`);
+ console.log(
+ `Found ${results.placeholders?.length} results for ${query}`
+ );
setResults(results);
- if (results.ids?.length === 0) {
+ if (results.placeholders?.length === 0) {
setSearchStatus(`No results found for ${query}`);
} else {
setSearchStatus(undefined);
diff --git a/apps/mobile/app/screens/trash/index.tsx b/apps/mobile/app/screens/trash/index.tsx
index 1557502ed..b7e3bc6d3 100644
--- a/apps/mobile/app/screens/trash/index.tsx
+++ b/apps/mobile/app/screens/trash/index.tsx
@@ -107,7 +107,7 @@ export const Trash = ({ navigation, route }: NavigationProps<"Trash">) => {
headerTitle="Trash"
/>
- {trash && trash?.ids?.length !== 0 ? (
+ {trash && trash?.placeholders?.length !== 0 ? (
- {nestedNotebooks?.ids.length ? (
+ {nestedNotebooks?.placeholders.length ? (
- {nestedNotebooks?.ids?.length && isExpanded ? (
+ {nestedNotebooks?.placeholders?.length && isExpanded ? (
0 && level + 1 < 5 ? 15 : 0,
marginTop: 5
}}
>
- {nestedNotebooks.ids.map((item, index) => (
+ {nestedNotebooks.placeholders.map((item, index) => (
+ ({ index }: { item: boolean; index: number }) =>
mode === "selectNotebooks" ? (