From da393f6c34f8989b6078a3ec116672ebcdb2444a Mon Sep 17 00:00:00 2001 From: Siddhant Paritosh Rao <97161173+Sidd-R@users.noreply.github.com> Date: Mon, 11 Mar 2024 20:52:26 +0530 Subject: [PATCH] Mobile: Fixes #10065: New note button crashes app when there are no notebooks (#10087) --- packages/app-mobile/components/NoteList.tsx | 4 +++- packages/app-mobile/components/screens/Notes.tsx | 2 +- packages/lib/models/Folder.test.ts | 16 ++++++++++++++++ packages/lib/models/Folder.ts | 6 ++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/app-mobile/components/NoteList.tsx b/packages/app-mobile/components/NoteList.tsx index 223ad18ce..4911a700a 100644 --- a/packages/app-mobile/components/NoteList.tsx +++ b/packages/app-mobile/components/NoteList.tsx @@ -6,6 +6,7 @@ import { connect } from 'react-redux'; import { FlatList, Text, StyleSheet, Button, View } from 'react-native'; import { FolderEntity, NoteEntity } from '@joplin/lib/services/database/types'; import { AppState } from '../utils/types'; +import Folder from '@joplin/lib/models/Folder'; const { _ } = require('@joplin/lib/locale'); const { NoteItem } = require('./note-item.js'); @@ -90,7 +91,7 @@ class NoteListComponent extends Component { keyExtractor={item => item.id} />; } else { - if (!this.props.folders.length) { + if (!Folder.atLeastOneRealFolderExists(this.props.folders)) { const noItemMessage = _('You currently have no notebooks.'); return ( @@ -106,6 +107,7 @@ class NoteListComponent extends Component { } } + const NoteList = connect((state: AppState) => { return { items: state.notes, diff --git a/packages/app-mobile/components/screens/Notes.tsx b/packages/app-mobile/components/screens/Notes.tsx index e9970b3b0..142b450f3 100644 --- a/packages/app-mobile/components/screens/Notes.tsx +++ b/packages/app-mobile/components/screens/Notes.tsx @@ -238,7 +238,7 @@ class NotesScreenComponent extends BaseScreenComponent { const thisComp = this; const makeActionButtonComp = () => { - if (this.props.notesParentType === 'Folder' && itemIsInTrash(parent)) return null; + if ((this.props.notesParentType === 'Folder' && itemIsInTrash(parent)) || !Folder.atLeastOneRealFolderExists(this.props.folders)) return null; const getTargetFolderId = async () => { if (!buttonFolderId && isAllNotes) { diff --git a/packages/lib/models/Folder.test.ts b/packages/lib/models/Folder.test.ts index ed33d2a02..1eaec5d3b 100644 --- a/packages/lib/models/Folder.test.ts +++ b/packages/lib/models/Folder.test.ts @@ -353,4 +353,20 @@ describe('models/Folder', () => { await expectThrow(async () => Folder.delete(folder2.id, { toTrash: true, toTrashParentId: folder2.id })); }); + it('should tell if at least one folder other than trash and deleted exists', async () => { + let folders: FolderEntity[] = []; + expect(Folder.atLeastOneRealFolderExists(folders)).toBe(false); + + folders = await Folder.all({ includeTrash: true }); + expect(Folder.atLeastOneRealFolderExists(folders)).toBe(false); + + const f1 = await Folder.save({ title: 'folder1' }); + folders = await Folder.all({ includeTrash: true }); + expect(Folder.atLeastOneRealFolderExists(folders)).toBe(true); + + await Folder.delete(f1.id, { toTrash: true }); + folders = await Folder.all({ includeTrash: true }); + expect(Folder.atLeastOneRealFolderExists(folders)).toBe(false); + }); + }); diff --git a/packages/lib/models/Folder.ts b/packages/lib/models/Folder.ts index 9cf706d98..f7016eca4 100644 --- a/packages/lib/models/Folder.ts +++ b/packages/lib/models/Folder.ts @@ -934,4 +934,10 @@ export default class Folder extends BaseItem { return !!folders.find(f => !!f.icon); } + public static atLeastOneRealFolderExists(folders: FolderEntity[]) { + // returns true if at least one folder exists other than trash folder and deleted folders + const trashFolderId = getTrashFolderId(); + return folders.filter((folder) => folder.id !== trashFolderId && folder.deleted_time === 0).length > 0; + } + }