From 35ae19accef4641698b06b40465d5fd930c23ee8 Mon Sep 17 00:00:00 2001 From: Ammar Ahmed Date: Wed, 27 Dec 2023 09:40:15 +0500 Subject: [PATCH] mobile: sync with latest core changes --- .../common/filesystem/download-attachment.js | 56 ++++++++++--------- apps/mobile/app/common/filesystem/download.js | 2 +- apps/mobile/app/common/filesystem/io.js | 18 +++--- apps/mobile/app/common/filesystem/upload.js | 7 +-- .../attachments/download-attachments.tsx | 26 ++++----- .../app/components/attachments/index.tsx | 12 ++-- .../components/dialogs/pdf-preview/index.js | 49 ++++++++-------- apps/mobile/app/components/list/index.tsx | 2 +- .../app/components/selection-header/index.tsx | 17 ++---- .../app/components/sheets/add-to/index.tsx | 11 ++-- .../sheets/add-to/notebook-item.tsx | 4 +- .../components/sheets/manage-tags/index.tsx | 6 +- .../components/sheets/move-notes/movenote.tsx | 4 +- .../sheets/notebook-sheet/index.tsx | 14 ++--- .../sheets/relations-list/index.tsx | 7 +-- .../sheets/reminder-notify/index.tsx | 6 +- apps/mobile/app/screens/notebook/index.tsx | 2 +- apps/mobile/app/screens/notebooks/index.tsx | 8 ++- apps/mobile/app/screens/notes/index.tsx | 5 +- apps/mobile/app/screens/search/index.tsx | 6 +- apps/mobile/app/screens/trash/index.tsx | 2 +- apps/mobile/share/search.tsx | 10 ++-- apps/web/src/common/attachments.ts | 2 +- 23 files changed, 127 insertions(+), 149 deletions(-) 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" ? (