1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Mobile: Also do multi-selection from search page

This commit is contained in:
Laurent Cozic 2017-11-23 18:41:35 +00:00
parent acc4eb5d28
commit d8b19f7d08
5 changed files with 56 additions and 33 deletions

View File

@ -100,7 +100,7 @@ class NoteItemComponent extends Component {
if (!this.props.note) return; if (!this.props.note) return;
this.props.dispatch({ this.props.dispatch({
type: 'NOTE_SELECTION_START', type: this.props.noteSelectionEnabled ? 'NOTE_SELECTION_TOGGLE' : 'NOTE_SELECTION_START',
id: this.props.note.id, id: this.props.note.id,
}); });
} }
@ -126,21 +126,23 @@ class NoteItemComponent extends Component {
const listItemStyle = isTodo ? this.styles().listItemWithCheckbox : this.styles().listItem; const listItemStyle = isTodo ? this.styles().listItemWithCheckbox : this.styles().listItem;
const listItemTextStyle = isTodo ? this.styles().listItemTextWithCheckbox : this.styles().listItemText; 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 isSelected = this.props.noteSelectionEnabled && this.props.selectedNoteIds.indexOf(note.id) >= 0;
const selectionWrapperStyle = isSelected ? this.styles().selectionWrapperSelected : this.styles().selectionWrapper; const selectionWrapperStyle = isSelected ? this.styles().selectionWrapperSelected : this.styles().selectionWrapper;
return ( return (
<TouchableOpacity onPress={() => this.onPress()} style={rootStyle} onLongPress={() => this.onLongPress()}> <TouchableOpacity onPress={() => this.onPress()} onLongPress={() => this.onLongPress()}>
<View style={ selectionWrapperStyle }> <View style={ selectionWrapperStyle }>
<View style={ listItemStyle }> <View style={ opacityStyle }>
<Checkbox <View style={ listItemStyle }>
style={checkboxStyle} <Checkbox
checked={checkboxChecked} style={checkboxStyle}
onChange={(checked) => this.todoCheckbox_change(checked)} checked={checkboxChecked}
/> onChange={(checked) => this.todoCheckbox_change(checked)}
<Text style={listItemTextStyle}>{note.title}</Text> />
<Text style={listItemTextStyle}>{note.title}</Text>
</View>
</View> </View>
</View> </View>
</TouchableOpacity> </TouchableOpacity>

View File

@ -9,6 +9,7 @@ const { Menu, MenuOptions, MenuOption, MenuTrigger } = require('react-native-pop
const { _ } = require('lib/locale.js'); const { _ } = require('lib/locale.js');
const { Setting } = require('lib/models/setting.js'); const { Setting } = require('lib/models/setting.js');
const { Note } = require('lib/models/note.js'); const { Note } = require('lib/models/note.js');
const { Folder } = require('lib/models/folder.js');
const { FileApi } = require('lib/file-api.js'); const { FileApi } = require('lib/file-api.js');
const { FileApiDriverOneDrive } = require('lib/file-api-driver-onedrive.js'); const { FileApiDriverOneDrive } = require('lib/file-api-driver-onedrive.js');
const { reg } = require('lib/registry.js'); const { reg } = require('lib/registry.js');
@ -162,7 +163,6 @@ class ScreenHeaderComponent extends Component {
async deleteButton_press() { async deleteButton_press() {
// Dialog needs to be displayed as a child of the parent component, otherwise // Dialog needs to be displayed as a child of the parent component, otherwise
// it won't be visible within the header component. // 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?')); const ok = await dialogs.confirm(this.props.parentComponent, _('Delete these notes?'));
if (!ok) return; if (!ok) return;
@ -368,9 +368,29 @@ class ScreenHeaderComponent extends Component {
color: theme.color, color: theme.color,
fontSize: theme.fontSize, fontSize: theme.fontSize,
}} }}
onValueChange={(itemValue, itemIndex) => { onValueChange={async (folderId, itemIndex) => {
if (!folderPickerOptions.onValueChange) return; // If onValueChange is specified, use this as a callback, otherwise do the default
folderPickerOptions.onValueChange(itemValue, itemIndex); // 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);
}
}} }}
/> />
); );

View File

@ -143,6 +143,7 @@ class NotesScreenComponent extends BaseScreenComponent {
let title = parent ? parent.title : null; let title = parent ? parent.title : null;
const addFolderNoteButtons = this.props.selectedFolderId && this.props.selectedFolderId != Folder.conflictFolderId(); const addFolderNoteButtons = this.props.selectedFolderId && this.props.selectedFolderId != Folder.conflictFolderId();
const thisComp = this; const thisComp = this;
const actionButtonComp = this.props.noteSelectionEnabled ? null : <ActionButton addFolderNoteButtons={addFolderNoteButtons} parentFolderId={this.props.selectedFolderId}></ActionButton>
return ( return (
<View style={rootStyle}> <View style={rootStyle}>
@ -153,25 +154,10 @@ class NotesScreenComponent extends BaseScreenComponent {
folderPickerOptions={{ folderPickerOptions={{
enabled: this.props.noteSelectionEnabled, enabled: this.props.noteSelectionEnabled,
mustSelect: true, 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);
}
},
}} }}
/> />
<NoteList style={{flex: 1}}/> <NoteList style={{flex: 1}}/>
<ActionButton addFolderNoteButtons={addFolderNoteButtons} parentFolderId={this.props.selectedFolderId}></ActionButton> { actionButtonComp }
<DialogBox ref={dialogbox => { this.dialogbox = dialogbox }}/> <DialogBox ref={dialogbox => { this.dialogbox = dialogbox }}/>
</View> </View>
); );

View File

@ -8,6 +8,8 @@ const { Note } = require('lib/models/note.js');
const { NoteItem } = require('lib/components/note-item.js'); const { NoteItem } = require('lib/components/note-item.js');
const { BaseScreenComponent } = require('lib/components/base-screen.js'); const { BaseScreenComponent } = require('lib/components/base-screen.js');
const { themeStyle } = require('lib/components/global-style.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 { 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 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 ( return (
<View style={rootStyle}> <View style={rootStyle}>
<ScreenHeader title={_('Search')}/> <ScreenHeader
title={_('Search')}
parentComponent={thisComponent}
folderPickerOptions={{
enabled: this.props.noteSelectionEnabled,
mustSelect: true,
}}
/>
<View style={this.styles().body}> <View style={this.styles().body}>
<View style={this.styles().searchContainer}> <View style={this.styles().searchContainer}>
<TextInput <TextInput
@ -163,6 +174,7 @@ class SearchScreenComponent extends BaseScreenComponent {
renderItem={(event) => <NoteItem note={event.item}/>} renderItem={(event) => <NoteItem note={event.item}/>}
/> />
</View> </View>
<DialogBox ref={dialogbox => { this.dialogbox = dialogbox }}/>
</View> </View>
); );
} }
@ -174,6 +186,7 @@ const SearchScreen = connect(
return { return {
query: state.searchQuery, query: state.searchQuery,
theme: state.settings.theme, theme: state.settings.theme,
noteSelectionEnabled: state.noteSelectionEnabled,
}; };
} }
)(SearchScreenComponent) )(SearchScreenComponent)

View File

@ -8,7 +8,8 @@ const { Keyboard } = require('react-native');
let dialogs = {}; let dialogs = {};
dialogs.confirm = (parentComponent, message) => { 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) => { return new Promise((resolve, reject) => {
Keyboard.dismiss(); Keyboard.dismiss();
@ -33,7 +34,8 @@ dialogs.confirm = (parentComponent, message) => {
}; };
dialogs.pop = (parentComponent, message, buttons) => { 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) => { return new Promise((resolve, reject) => {
Keyboard.dismiss(); Keyboard.dismiss();