From d8b19f7d08a7896d418d3e42f88329c4f7000189 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Thu, 23 Nov 2017 18:41:35 +0000 Subject: [PATCH] Mobile: Also do multi-selection from search page --- ReactNativeClient/lib/components/note-item.js | 22 ++++++++------- .../lib/components/screen-header.js | 28 ++++++++++++++++--- .../lib/components/screens/notes.js | 18 ++---------- .../lib/components/screens/search.js | 15 +++++++++- ReactNativeClient/lib/dialogs.js | 6 ++-- 5 files changed, 56 insertions(+), 33 deletions(-) diff --git a/ReactNativeClient/lib/components/note-item.js b/ReactNativeClient/lib/components/note-item.js index b48d62d44..f5035555d 100644 --- a/ReactNativeClient/lib/components/note-item.js +++ b/ReactNativeClient/lib/components/note-item.js @@ -100,7 +100,7 @@ class NoteItemComponent extends Component { if (!this.props.note) return; this.props.dispatch({ - type: 'NOTE_SELECTION_START', + type: this.props.noteSelectionEnabled ? 'NOTE_SELECTION_TOGGLE' : 'NOTE_SELECTION_START', id: this.props.note.id, }); } @@ -126,21 +126,23 @@ class NoteItemComponent extends Component { const listItemStyle = isTodo ? this.styles().listItemWithCheckbox : this.styles().listItem; const listItemTextStyle = isTodo ? this.styles().listItemTextWithCheckbox : this.styles().listItemText; - const rootStyle = isTodo && checkboxChecked ? {opacity: 0.4} : {}; + const opacityStyle = isTodo && checkboxChecked ? {opacity: 0.4} : {}; const isSelected = this.props.noteSelectionEnabled && this.props.selectedNoteIds.indexOf(note.id) >= 0; const selectionWrapperStyle = isSelected ? this.styles().selectionWrapperSelected : this.styles().selectionWrapper; return ( - this.onPress()} style={rootStyle} onLongPress={() => this.onLongPress()}> + this.onPress()} onLongPress={() => this.onLongPress()}> - - this.todoCheckbox_change(checked)} - /> - {note.title} + + + this.todoCheckbox_change(checked)} + /> + {note.title} + diff --git a/ReactNativeClient/lib/components/screen-header.js b/ReactNativeClient/lib/components/screen-header.js index c87b8d3a3..678054553 100644 --- a/ReactNativeClient/lib/components/screen-header.js +++ b/ReactNativeClient/lib/components/screen-header.js @@ -9,6 +9,7 @@ const { Menu, MenuOptions, MenuOption, MenuTrigger } = require('react-native-pop const { _ } = require('lib/locale.js'); const { Setting } = require('lib/models/setting.js'); const { Note } = require('lib/models/note.js'); +const { Folder } = require('lib/models/folder.js'); const { FileApi } = require('lib/file-api.js'); const { FileApiDriverOneDrive } = require('lib/file-api-driver-onedrive.js'); const { reg } = require('lib/registry.js'); @@ -162,7 +163,6 @@ class ScreenHeaderComponent extends Component { async deleteButton_press() { // Dialog needs to be displayed as a child of the parent component, otherwise // it won't be visible within the header component. - if (!this.props.parentComponent) throw new Error('parentComponent not set'); const ok = await dialogs.confirm(this.props.parentComponent, _('Delete these notes?')); if (!ok) return; @@ -368,9 +368,29 @@ class ScreenHeaderComponent extends Component { color: theme.color, fontSize: theme.fontSize, }} - onValueChange={(itemValue, itemIndex) => { - if (!folderPickerOptions.onValueChange) return; - folderPickerOptions.onValueChange(itemValue, itemIndex); + onValueChange={async (folderId, itemIndex) => { + // If onValueChange is specified, use this as a callback, otherwise do the default + // which is to take the selectedNoteIds from the state and move them to the + // chosen folder. + + if (folderPickerOptions.onValueChange) { + folderPickerOptions.onValueChange(folderId, itemIndex); + return; + } + + if (!folderId) return; + const noteIds = this.props.selectedNoteIds; + if (!noteIds.length) return; + + const folder = await Folder.load(folderId); + + const ok = noteIds.length > 1 ? await dialogs.confirm(this.props.parentComponent, _('Move %d notes to notebook "%s"?', noteIds.length, folder.title)) : true; + if (!ok) return; + + this.props.dispatch({ type: 'NOTE_SELECTION_END' }); + for (let i = 0; i < noteIds.length; i++) { + await Note.moveToFolder(noteIds[i], folderId); + } }} /> ); diff --git a/ReactNativeClient/lib/components/screens/notes.js b/ReactNativeClient/lib/components/screens/notes.js index ea6786979..66402ed23 100644 --- a/ReactNativeClient/lib/components/screens/notes.js +++ b/ReactNativeClient/lib/components/screens/notes.js @@ -143,6 +143,7 @@ class NotesScreenComponent extends BaseScreenComponent { let title = parent ? parent.title : null; const addFolderNoteButtons = this.props.selectedFolderId && this.props.selectedFolderId != Folder.conflictFolderId(); const thisComp = this; + const actionButtonComp = this.props.noteSelectionEnabled ? null : return ( @@ -153,25 +154,10 @@ class NotesScreenComponent extends BaseScreenComponent { folderPickerOptions={{ enabled: this.props.noteSelectionEnabled, mustSelect: true, - onValueChange: async (folderId, itemIndex) => { - if (!folderId) return; - const noteIds = this.props.selectedNoteIds; - if (!noteIds.length) return; - - const folder = await Folder.load(folderId); - - const ok = await dialogs.confirm(this, _('Move %d note(s) to notebook "%s"?', noteIds.length, folder.title)); - if (!ok) return; - - this.props.dispatch({ type: 'NOTE_SELECTION_END' }); - for (let i = 0; i < noteIds.length; i++) { - await Note.moveToFolder(noteIds[i], folderId); - } - }, }} /> - + { actionButtonComp } { this.dialogbox = dialogbox }}/> ); diff --git a/ReactNativeClient/lib/components/screens/search.js b/ReactNativeClient/lib/components/screens/search.js index 47550d945..3ef5b6096 100644 --- a/ReactNativeClient/lib/components/screens/search.js +++ b/ReactNativeClient/lib/components/screens/search.js @@ -8,6 +8,8 @@ const { Note } = require('lib/models/note.js'); const { NoteItem } = require('lib/components/note-item.js'); const { BaseScreenComponent } = require('lib/components/base-screen.js'); const { themeStyle } = require('lib/components/global-style.js'); +const { dialogs } = require('lib/dialogs.js'); +const DialogBox = require('react-native-dialogbox').default; class SearchScreenComponent extends BaseScreenComponent { @@ -139,9 +141,18 @@ class SearchScreenComponent extends BaseScreenComponent { rootStyle.flex = 0.001; // This is a bit of a hack but it seems to work fine - it makes the component invisible but without unmounting it } + const thisComponent = this; + return ( - + } /> + { this.dialogbox = dialogbox }}/> ); } @@ -174,6 +186,7 @@ const SearchScreen = connect( return { query: state.searchQuery, theme: state.settings.theme, + noteSelectionEnabled: state.noteSelectionEnabled, }; } )(SearchScreenComponent) diff --git a/ReactNativeClient/lib/dialogs.js b/ReactNativeClient/lib/dialogs.js index 072d07568..4a8402a6c 100644 --- a/ReactNativeClient/lib/dialogs.js +++ b/ReactNativeClient/lib/dialogs.js @@ -8,7 +8,8 @@ const { Keyboard } = require('react-native'); let dialogs = {}; dialogs.confirm = (parentComponent, message) => { - if (!'dialogbox' in parentComponent) throw new Error('A "dialogbox" component must be defined on the parent component!'); + if (!parentComponent) throw new Error('parentComponent is required'); + if (!('dialogbox' in parentComponent)) throw new Error('A "dialogbox" component must be defined on the parent component!'); return new Promise((resolve, reject) => { Keyboard.dismiss(); @@ -33,7 +34,8 @@ dialogs.confirm = (parentComponent, message) => { }; dialogs.pop = (parentComponent, message, buttons) => { - if (!'dialogbox' in parentComponent) throw new Error('A "dialogbox" component must be defined on the parent component!'); + if (!parentComponent) throw new Error('parentComponent is required'); + if (!('dialogbox' in parentComponent)) throw new Error('A "dialogbox" component must be defined on the parent component!'); return new Promise((resolve, reject) => { Keyboard.dismiss();