1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-01-02 12:47:41 +02:00

Mobile: Fixes #11264: Fix editor shows nothing when there are no selected note IDs (#11514)

This commit is contained in:
Henry Heino 2024-12-18 02:58:36 -08:00 committed by GitHub
parent 9e80e2e072
commit 5078341c15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 57 additions and 19 deletions

View File

@ -550,7 +550,14 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade
try {
for (let i = 0; i < noteIds.length; i++) {
await Note.moveToFolder(noteIds[i], folderId);
await Note.moveToFolder(
noteIds[i],
folderId,
// By default, the note selection is preserved on mobile when a note is moved to
// a different folder. However, when moving notes from the note list, this shouldn't be
// the case:
{ dispatchOptions: { preserveSelection: false } },
);
}
} catch (error) {
alert(_n('This note could not be moved: %s', 'These notes could not be moved: %s', noteIds.length, error.message));

View File

@ -45,7 +45,7 @@ import { isSupportedLanguage } from '../../../services/voiceTyping/vosk';
import { ChangeEvent as EditorChangeEvent, SelectionRangeChangeEvent, UndoRedoDepthChangeEvent } from '@joplin/editor/events';
import { join } from 'path';
import { Dispatch } from 'redux';
import { RefObject, useContext, useRef } from 'react';
import { RefObject, useContext } from 'react';
import { SelectionRange } from '../../NoteEditor/types';
import { getNoteCallbackUrl } from '@joplin/lib/callbackUrlUtils';
import { AppState } from '../../../utils/types';
@ -1615,19 +1615,9 @@ class NoteScreenComponent extends BaseScreenComponent<ComponentProps, State> imp
// which can cause some bugs where previously set state to another note would interfere
// how the new note should be rendered
const NoteScreenWrapper = (props: Props) => {
const lastNonNullNoteIdRef = useRef(props.noteId);
if (props.noteId) {
lastNonNullNoteIdRef.current = props.noteId;
}
// This keeps the current note open even if it's no longer present in selectedNoteIds.
// This might happen, for example, if the selected note is moved to an unselected
// folder.
const noteId = lastNonNullNoteIdRef.current;
const dialogs = useContext(DialogContext);
return (
<NoteScreenComponent key={noteId} dialogs={dialogs} {...props} />
<NoteScreenComponent key={props.noteId} dialogs={dialogs} {...props} />
);
};

View File

@ -35,6 +35,7 @@ interface Props {
showCompletedTodos: boolean;
noteSelectionEnabled: boolean;
selectedNoteIds: string[];
activeFolderId: string;
selectedFolderId: string;
selectedTagId: string;

View File

@ -18,5 +18,8 @@ const appDefaultState: AppState = {
showPanelsDialog: false,
newNoteAttachFileAction: null,
...defaultState,
// On mobile, it's possible to select notes that aren't in the selected folder/tag/etc.
allowSelectionInOtherFolders: true,
};
export default appDefaultState;

View File

@ -709,13 +709,19 @@ describe('reducer', () => {
// Regression test for #10589.
it.each([
true, false,
])('should preserve note selection if specified while moving a note (preserveSelection: %j)', async (preserveSelection) => {
[true, false],
[undefined, false],
[undefined, true],
[false, true],
])('should preserve note selection if specified with an option (preserveSelection: %j, allowSelectionInOtherFolders: %j)', async (
preserveSelectionOption, allowSelectionInOtherFolders,
) => {
const folders = await createNTestFolders(3);
const notes = await createNTestNotes(5, folders[0]);
// select the 1st folder and the 1st note
let state = initTestState(folders, 0, notes, [0]);
state = { ...state, allowSelectionInOtherFolders };
state = goToNote(notes, [0], state);
expect(state.selectedNoteIds).toHaveLength(1);
@ -729,11 +735,15 @@ describe('reducer', () => {
await Note.moveToFolder(
state.selectedNoteIds[0],
folders[1].id,
preserveSelection ? { dispatchOptions: { preserveSelection: true } } : undefined,
{ dispatchOptions: { preserveSelection: preserveSelectionOption } },
);
expect(BaseModel.dispatch).toHaveBeenCalled();
if (preserveSelection) {
// preserveSelectionOption takes precedence over allowSelectionInOtherFolders
const shouldPreserveSelection = preserveSelectionOption ?? allowSelectionInOtherFolders;
if (shouldPreserveSelection) {
expect(state.selectedNoteIds).toMatchObject([notes[0].id]);
} else {
expect(state.selectedNoteIds).toMatchObject([notes[1].id]);
@ -743,6 +753,27 @@ describe('reducer', () => {
expect(state.selectedFolderId).toBe(folders[0].id);
});
test('when selection is allowed in unselected folders, NOTE_UPDATE_ALL should not remove items from the selection', async () => {
const folders = await createNTestFolders(2);
const notes = await createNTestNotes(2, folders[0]);
// select the 1st folder and the 1st note
let state = initTestState(folders, 0, notes, [0]);
state = { ...state, allowSelectionInOtherFolders: true };
state = goToNote(notes, [0], state);
expect(state.selectedNoteIds).toEqual([notes[0].id]);
expect(state.notes).toHaveLength(2);
state = reducer(state, {
type: 'NOTE_UPDATE_ALL',
notes: [],
});
expect(state.notes).toHaveLength(0);
expect(state.selectedNoteIds).toEqual([notes[0].id]);
});
// window tests
test('switching windows should move state from background to the foreground', async () => {
const folders = await createNTestFolders(2);

View File

@ -169,6 +169,8 @@ export interface State extends WindowState {
mustUpgradeAppMessage: string;
mustAuthenticate: boolean;
allowSelectionInOtherFolders: boolean;
// Extra reducer keys go here:
pluginService: PluginServiceState;
shareService: ShareServiceState;
@ -236,6 +238,7 @@ export const defaultState: State = {
lastDeletionNotificationTime: 0,
mustUpgradeAppMessage: '',
mustAuthenticate: false,
allowSelectionInOtherFolders: false,
pluginService: pluginServiceDefaultState,
shareService: shareServiceDefaultState,
@ -1080,7 +1083,9 @@ const reducer = produce((draft: Draft<State> = defaultState, action: any) => {
draft.notes = action.notes;
draft.notesSource = action.notesSource;
draft.noteListLastSortTime = Date.now(); // Notes are already sorted when they are set this way.
updateSelectedNotesFromExistingNotes(draft);
if (!draft.allowSelectionInOtherFolders) {
updateSelectedNotesFromExistingNotes(draft);
}
break;
// Insert the note into the note list if it's new, or
@ -1148,7 +1153,8 @@ const reducer = produce((draft: Draft<State> = defaultState, action: any) => {
// For example, if the user drags the current note to a different folder,
// a new note should be selected.
// In some cases, however, the selection needs to be preserved (e.g. the mobile app).
if (noteFolderHasChanged && !action.preserveSelection) {
const preserveSelection = action.preserveSelection ?? draft.allowSelectionInOtherFolders;
if (noteFolderHasChanged && !preserveSelection) {
let newIndex = movedNotePreviousIndex;
if (newIndex >= newNotes.length) newIndex = newNotes.length - 1;
if (!newNotes.length) newIndex = -1;