diff --git a/ReactNativeClient/lib/components/screen-header.js b/ReactNativeClient/lib/components/screen-header.js index c629a6133..065ddf85a 100644 --- a/ReactNativeClient/lib/components/screen-header.js +++ b/ReactNativeClient/lib/components/screen-header.js @@ -158,7 +158,11 @@ class ScreenHeaderComponent extends React.PureComponent { } async backButton_press() { - await BackButtonService.back(); + if (this.props.noteSelectionEnabled) { + this.props.dispatch({ type: 'NOTE_SELECTION_END' }); + } else { + await BackButtonService.back(); + } } searchButton_press() { @@ -374,22 +378,30 @@ class ScreenHeaderComponent extends React.PureComponent { ) : null; - const showSideMenuButton = this.props.showSideMenuButton !== false && !this.props.noteSelectionEnabled; - const showSearchButton = this.props.showSearchButton !== false && !this.props.noteSelectionEnabled; + const showSideMenuButton = !!this.props.showSideMenuButton && !this.props.noteSelectionEnabled; + const showSearchButton = !!this.props.showSearchButton && !this.props.noteSelectionEnabled; const showContextMenuButton = this.props.showContextMenuButton !== false; - const showBackButton = this.props.showBackButton !== false; + const showBackButton = !!this.props.noteSelectionEnabled || this.props.showBackButton !== false; + + let backButtonDisabled = !this.props.historyCanGoBack; + if (!!this.props.noteSelectionEnabled) backButtonDisabled = false; const titleComp = createTitleComponent(); const sideMenuComp = !showSideMenuButton ? null : sideMenuButton(this.styles(), () => this.sideMenuButton_press()); - const backButtonComp = !showBackButton ? null : backButton(this.styles(), () => this.backButton_press(), !this.props.historyCanGoBack); + const backButtonComp = !showBackButton ? null : backButton(this.styles(), () => this.backButton_press(), backButtonDisabled); const searchButtonComp = !showSearchButton ? null : searchButton(this.styles(), () => this.searchButton_press()); const deleteButtonComp = this.props.noteSelectionEnabled ? deleteButton(this.styles(), () => this.deleteButton_press()) : null; - const sortButtonComp = this.props.sortButton_press ? sortButton(this.styles(), () => this.props.sortButton_press()) : null; + const sortButtonComp = !this.props.noteSelectionEnabled && this.props.sortButton_press ? sortButton(this.styles(), () => this.props.sortButton_press()) : null; const windowHeight = Dimensions.get('window').height - 50; + const contextMenuStyle = { paddingTop: PADDING_V, paddingBottom: PADDING_V }; + + // HACK: if this button is removed during selection mode, the header layout is broken, so for now just make it 1 pixel large (normally it should be hidden) + if (!!this.props.noteSelectionEnabled) contextMenuStyle.width = 1; + const menuComp = !menuOptionComponents.length || !showContextMenuButton ? null : ( this.menu_select(value)} style={this.styles().contextMenu}> - + diff --git a/ReactNativeClient/lib/components/screens/note.js b/ReactNativeClient/lib/components/screens/note.js index 249d7dbcc..701cd33c1 100644 --- a/ReactNativeClient/lib/components/screens/note.js +++ b/ReactNativeClient/lib/components/screens/note.js @@ -190,13 +190,20 @@ class NoteScreenComponent extends BaseScreenComponent { this.sideMenuOptions = this.sideMenuOptions.bind(this); this.folderPickerOptions_valueChanged = this.folderPickerOptions_valueChanged.bind(this); this.saveNoteButton_press = this.saveNoteButton_press.bind(this); + this.onAlarmDialogAccept = this.onAlarmDialogAccept.bind(this); + this.onAlarmDialogReject = this.onAlarmDialogReject.bind(this); + this.todoCheckbox_change = this.todoCheckbox_change.bind(this); + this.titleTextInput_contentSizeChange = this.titleTextInput_contentSizeChange.bind(this); + this.title_changeText = this.title_changeText.bind(this); } styles() { const themeId = this.props.theme; const theme = themeStyle(themeId); - if (this.styles_[themeId]) return this.styles_[themeId]; + const cacheKey = [themeId, this.state.titleTextInputHeight, this.state.HACK_webviewLoadingState].join('_'); + + if (this.styles_[cacheKey]) return this.styles_[cacheKey]; this.styles_ = {}; let styles = { @@ -216,6 +223,13 @@ class NoteScreenComponent extends BaseScreenComponent { paddingTop: theme.marginTop, paddingBottom: theme.marginBottom, }, + checkbox: { + color: theme.color, + paddingRight: 10, + paddingLeft: theme.marginLeft, + paddingTop: 10, // Added for iOS (Not needed for Android??) + paddingBottom: 10, // Added for iOS (Not needed for Android??) + }, }; styles.titleContainer = { @@ -230,8 +244,23 @@ class NoteScreenComponent extends BaseScreenComponent { styles.titleContainerTodo = Object.assign({}, styles.titleContainer); styles.titleContainerTodo.paddingLeft = 0; - this.styles_[themeId] = StyleSheet.create(styles); - return this.styles_[themeId]; + styles.titleTextInput = { + flex: 1, + marginTop: 0, + paddingLeft: 0, + color: theme.color, + backgroundColor: theme.backgroundColor, + fontWeight: 'bold', + fontSize: theme.fontSize, + paddingTop: 10, // Added for iOS (Not needed for Android??) + paddingBottom: 10, // Added for iOS (Not needed for Android??) + }; + + if (this.enableMultilineTitle_) styles.titleTextInput.height = this.state.titleTextInputHeight; + if (this.state.HACK_webviewLoadingState === 1) styles.titleTextInput.marginTop = 1; + + this.styles_[cacheKey] = StyleSheet.create(styles); + return this.styles_[cacheKey]; } isModified() { @@ -783,7 +812,7 @@ class NoteScreenComponent extends BaseScreenComponent { }, }); - if (this.state.mode == 'edit') return null;//; + if (this.state.mode == 'edit') return null; return } @@ -797,46 +826,20 @@ class NoteScreenComponent extends BaseScreenComponent { const titleContainerStyle = isTodo ? this.styles().titleContainerTodo : this.styles().titleContainer; - let titleTextInputStyle = { - flex: 1, - marginTop: 0, - paddingLeft: 0, - color: theme.color, - backgroundColor: theme.backgroundColor, - fontWeight: 'bold', - fontSize: theme.fontSize, - paddingTop: 10, // Added for iOS (Not needed for Android??) - paddingBottom: 10, // Added for iOS (Not needed for Android??) - }; - - if (this.enableMultilineTitle_) titleTextInputStyle.height = this.state.titleTextInputHeight; - - let checkboxStyle = { - color: theme.color, - paddingRight: 10, - paddingLeft: theme.marginLeft, - paddingTop: 10, // Added for iOS (Not needed for Android??) - paddingBottom: 10, // Added for iOS (Not needed for Android??) - } - - if (this.state.HACK_webviewLoadingState === 1) { - titleTextInputStyle.marginTop = 1; - } - - const dueDate = isTodo && note.todo_due ? new Date(note.todo_due) : null; + const dueDate = Note.dueDateObject(note); const titleComp = ( - { isTodo && { this.todoCheckbox_change(checked) }} /> } + { isTodo && } this.titleTextInput_contentSizeChange(event)} + onContentSizeChange={this.titleTextInput_contentSizeChange} multiline={this.enableMultilineTitle_} ref="titleTextField" underlineColorAndroid="#ffffff00" autoCapitalize="sentences" - style={titleTextInputStyle} + style={this.styles().titleTextInput} value={note.title} - onChangeText={(text) => this.title_changeText(text)} + onChangeText={this.title_changeText} selectionColor={theme.textSelectionColor} placeholder={_('Add title')} /> @@ -863,8 +866,8 @@ class NoteScreenComponent extends BaseScreenComponent { this.onAlarmDialogAccept(date) } - onReject={() => this.onAlarmDialogReject() } + onAccept={this.onAlarmDialogAccept} + onReject={this.onAlarmDialogReject} /> { this.dialogbox = dialogbox }}/> diff --git a/ReactNativeClient/lib/components/screens/notes.js b/ReactNativeClient/lib/components/screens/notes.js index 58dc65748..a481809c0 100644 --- a/ReactNativeClient/lib/components/screens/notes.js +++ b/ReactNativeClient/lib/components/screens/notes.js @@ -1,5 +1,5 @@ const React = require('react'); const Component = React.Component; -const { AppState, View, Button, Text } = require('react-native'); +const { AppState, View, Button, Text, StyleSheet } = require('react-native'); const { stateUtils } = require('lib/reducer.js'); const { connect } = require('react-redux'); const { reg } = require('lib/registry.js'); @@ -72,6 +72,25 @@ class NotesScreenComponent extends BaseScreenComponent { } } + styles() { + if (!this.styles_) this.styles_ = {}; + const themeId = this.props.theme; + const theme = themeStyle(themeId); + const cacheKey = themeId; + + if (this.styles_[cacheKey]) return this.styles_[cacheKey]; + this.styles_ = {}; + + let styles = { + noteList: { + flex: 1, + }, + }; + + this.styles_[cacheKey] = StyleSheet.create(styles); + return this.styles_[cacheKey]; + } + async componentDidMount() { await this.refreshNotes(); AppState.addEventListener('change', this.onAppStateChange_); @@ -151,23 +170,6 @@ class NotesScreenComponent extends BaseScreenComponent { }); } - menuOptions() { - if (this.props.notesParentType == 'Folder') { - if (this.props.selectedFolderId == Folder.conflictFolderId()) return []; - - const folder = this.parentItem(); - if (!folder) return []; - - let output = []; - // if (!folder.encryption_applied) output.push({ title: _('Edit notebook'), onPress: () => { this.editFolder_onPress(this.props.selectedFolderId); } }); - // output.push({ title: _('Delete notebook'), onPress: () => { this.deleteFolder_onPress(this.props.selectedFolderId); } }); - - return output; - } else { - return []; // For tags - TODO - } - } - parentItem(props = null) { if (!props) props = this.props; @@ -185,6 +187,18 @@ class NotesScreenComponent extends BaseScreenComponent { return output; } + folderPickerOptions() { + const options = { + enabled: this.props.noteSelectionEnabled, + mustSelect: true, + }; + + if (this.folderPickerOptions_ && options.enabled === this.folderPickerOptions_.enabled) return this.folderPickerOptions_; + + this.folderPickerOptions_ = options; + return this.folderPickerOptions_; + } + render() { const parent = this.parentItem(); const theme = themeStyle(this.props.theme); @@ -201,7 +215,7 @@ class NotesScreenComponent extends BaseScreenComponent { if (!parent) { return ( - + ) } @@ -216,15 +230,13 @@ class NotesScreenComponent extends BaseScreenComponent { - + { actionButtonComp } { this.dialogbox = dialogbox }}/> diff --git a/ReactNativeClient/lib/components/select-date-time-dialog.js b/ReactNativeClient/lib/components/select-date-time-dialog.js index 5a80713ba..ffc60a220 100644 --- a/ReactNativeClient/lib/components/select-date-time-dialog.js +++ b/ReactNativeClient/lib/components/select-date-time-dialog.js @@ -5,13 +5,15 @@ import DatePicker from 'react-native-datepicker' import moment from 'moment'; import { _ } from 'lib/locale.js'; -class SelectDateTimeDialog extends Component { +class SelectDateTimeDialog extends React.PureComponent { constructor() { super(); this.dialog_ = null; this.shown_ = false; this.state = { date: null }; + + this.onReject = this.onReject.bind(this); } UNSAFE_componentWillReceiveProps(newProps) { @@ -72,6 +74,7 @@ class SelectDateTimeDialog extends Component { ref={(dialog) => { this.dialog_ = dialog; }} dialogTitle={} actions={popupActions} + dismissOnTouchOutside={false} width={0.9} height={350} > diff --git a/ReactNativeClient/lib/models/Note.js b/ReactNativeClient/lib/models/Note.js index 5f9ab6563..42b5cb79e 100644 --- a/ReactNativeClient/lib/models/Note.js +++ b/ReactNativeClient/lib/models/Note.js @@ -603,6 +603,17 @@ class Note extends BaseItem { return note.is_todo && !note.todo_completed && note.todo_due >= time.unixMs() && !note.is_conflict; } + static dueDateObject(note) { + if (!!note.is_todo && note.todo_due) { + if (!this.dueDateObjects_) this.dueDateObjects_ = {}; + if (this.dueDateObjects_[note.todo_due]) return this.dueDateObjects_[note.todo_due]; + this.dueDateObjects_[note.todo_due] = new Date(note.todo_due); + return this.dueDateObjects_[note.todo_due]; + } + + return null; + } + // Tells whether the conflict between the local and remote note can be ignored. static mustHandleConflict(localNote, remoteNote) { // That shouldn't happen so throw an exception