2019-07-29 15:43:53 +02:00
const React = require ( 'react' ) ;
2019-07-29 15:58:33 +02:00
2022-08-27 14:36:59 +02:00
import { connect } from 'react-redux' ;
2022-12-30 16:12:07 +02:00
import { PureComponent } from 'react' ;
2022-08-27 14:36:59 +02:00
import { View , Text , StyleSheet , TouchableOpacity , Image , ScrollView , Dimensions , ViewStyle } from 'react-native' ;
2018-03-09 22:59:12 +02:00
const Icon = require ( 'react-native-vector-icons/Ionicons' ) . default ;
2020-11-05 18:58:23 +02:00
const { BackButtonService } = require ( '../services/back-button.js' ) ;
2022-08-27 14:36:59 +02:00
import NavService from '@joplin/lib/services/NavService' ;
import { Menu , MenuOptions , MenuOption , MenuTrigger } from 'react-native-popup-menu' ;
2023-07-16 18:42:42 +02:00
import { _ , _n } from '@joplin/lib/locale' ;
2022-08-27 14:36:59 +02:00
import Setting from '@joplin/lib/models/Setting' ;
import Note from '@joplin/lib/models/Note' ;
2023-05-29 12:31:21 +02:00
import Folder from '@joplin/lib/models/Folder' ;
2020-11-05 18:58:23 +02:00
const { themeStyle } = require ( './global-style.js' ) ;
2023-05-29 12:31:21 +02:00
import { OnValueChangedListener } from './Dropdown' ;
2020-11-05 18:58:23 +02:00
const { dialogs } = require ( '../utils/dialogs.js' ) ;
2020-06-13 17:20:59 +02:00
const DialogBox = require ( 'react-native-dialogbox' ) . default ;
2022-08-27 14:36:59 +02:00
import { localSyncInfoFromState } from '@joplin/lib/services/synchronizer/syncInfoUtils' ;
import { showMissingMasterKeyMessage } from '@joplin/lib/services/e2ee/utils' ;
import { FolderEntity } from '@joplin/lib/services/database/types' ;
import { State } from '@joplin/lib/reducer' ;
2022-08-21 23:03:41 +02:00
import CustomButton from './CustomButton' ;
2023-05-29 12:31:21 +02:00
import FolderPicker from './FolderPicker' ;
2017-05-16 21:57:09 +02:00
2023-01-04 14:54:13 +02:00
// We need this to suppress the useless warning
// https://github.com/oblador/react-native-vector-icons/issues/1465
2023-02-16 12:55:24 +02:00
// eslint-disable-next-line no-console
2023-01-04 22:25:09 +02:00
Icon . loadFont ( ) . catch ( ( error : any ) = > { console . info ( error ) ; } ) ;
2020-02-09 16:51:12 +02:00
2017-07-31 20:19:58 +02:00
// Rather than applying a padding to the whole bar, it is applied to each
// individual component (button, picker, etc.) so that the touchable areas
// are widder and to give more room to the picker component which has a larger
// default height.
const PADDING_V = 10 ;
2022-08-27 14:36:59 +02:00
type OnSelectCallbackType = ( ) = > void ;
type OnPressCallback = ( ) = > void ;
interface NavButtonPressEvent {
// Name of the screen to navigate to
screen : string ;
}
2023-07-16 18:42:42 +02:00
export interface MenuOptionType {
2022-08-27 14:36:59 +02:00
onPress : OnPressCallback ;
isDivider? : boolean ;
title : string ;
2023-07-16 18:42:42 +02:00
disabled? : boolean ;
2022-08-27 14:36:59 +02:00
}
type DispatchCommandType = ( event : { type : string } ) = > void ;
interface ScreenHeaderProps {
selectedNoteIds : string [ ] ;
noteSelectionEnabled : boolean ;
2022-12-30 16:12:07 +02:00
parentComponent : any ;
2022-08-27 14:36:59 +02:00
showUndoButton : boolean ;
undoButtonDisabled? : boolean ;
showRedoButton : boolean ;
menuOptions : MenuOptionType [ ] ;
title? : string | null ;
folders : FolderEntity [ ] ;
folderPickerOptions ? : {
enabled : boolean ;
2022-12-30 16:12:07 +02:00
selectedFolderId? : string ;
onValueChange? : OnValueChangedListener ;
2022-08-27 14:36:59 +02:00
mustSelect? : boolean ;
} ;
dispatch : DispatchCommandType ;
onUndoButtonPress : OnPressCallback ;
onRedoButtonPress : OnPressCallback ;
onSaveButtonPress : OnPressCallback ;
sortButton_press? : OnPressCallback ;
showSideMenuButton? : boolean ;
showSearchButton? : boolean ;
showContextMenuButton? : boolean ;
showBackButton? : boolean ;
saveButtonDisabled? : boolean ;
showSaveButton? : boolean ;
historyCanGoBack? : boolean ;
showMissingMasterKeyMessage? : boolean ;
hasDisabledSyncItems? : boolean ;
2023-07-17 13:12:42 +02:00
hasDisabledEncryptionItems? : boolean ;
2022-08-27 14:36:59 +02:00
shouldUpgradeSyncTarget? : boolean ;
showShouldUpgradeSyncTargetMessage? : boolean ;
2023-07-18 15:46:11 +02:00
themeId : number ;
2022-08-27 14:36:59 +02:00
}
interface ScreenHeaderState {
}
class ScreenHeaderComponent extends PureComponent < ScreenHeaderProps , ScreenHeaderState > {
private cachedStyles : any ;
public dialogbox? : typeof DialogBox ;
public constructor ( props : ScreenHeaderProps ) {
super ( props ) ;
this . cachedStyles = { } ;
2017-07-21 23:40:02 +02:00
}
2017-07-16 01:30:54 +02:00
2022-08-27 14:36:59 +02:00
private styles() {
2023-07-18 15:46:11 +02:00
const themeId = this . props . themeId ;
2022-08-27 14:36:59 +02:00
if ( this . cachedStyles [ themeId ] ) return this . cachedStyles [ themeId ] ;
this . cachedStyles = { } ;
2017-08-01 19:59:01 +02:00
const theme = themeStyle ( themeId ) ;
2022-08-27 14:36:59 +02:00
const styleObject : any = {
2017-08-01 19:59:01 +02:00
container : {
2018-03-09 22:59:12 +02:00
flexDirection : 'column' ,
2020-06-10 23:08:59 +02:00
backgroundColor : theme.backgroundColor2 ,
2018-03-09 22:59:12 +02:00
alignItems : 'center' ,
shadowColor : '#000000' ,
2017-08-01 19:59:01 +02:00
elevation : 5 ,
} ,
divider : {
borderBottomWidth : 1 ,
borderColor : theme.dividerColor ,
2019-07-29 15:43:53 +02:00
backgroundColor : '#0000ff' ,
2017-08-01 19:59:01 +02:00
} ,
sideMenuButton : {
flex : 1 ,
2018-03-09 22:59:12 +02:00
alignItems : 'center' ,
2020-06-10 23:08:59 +02:00
backgroundColor : theme.backgroundColor2 ,
2017-08-01 19:59:01 +02:00
paddingLeft : theme.marginLeft ,
paddingRight : 5 ,
marginRight : 2 ,
paddingTop : PADDING_V ,
paddingBottom : PADDING_V ,
} ,
iconButton : {
flex : 1 ,
2020-06-10 23:08:59 +02:00
backgroundColor : theme.backgroundColor2 ,
2020-06-13 17:20:18 +02:00
paddingLeft : 10 ,
paddingRight : 10 ,
2017-08-01 19:59:01 +02:00
paddingTop : PADDING_V ,
paddingBottom : PADDING_V ,
} ,
saveButton : {
flex : 0 ,
2018-03-09 22:59:12 +02:00
flexDirection : 'row' ,
alignItems : 'center' ,
2017-08-01 19:59:01 +02:00
padding : 10 ,
borderWidth : 1 ,
2020-06-10 23:08:59 +02:00
borderColor : theme.colorBright2 ,
2017-08-01 19:59:01 +02:00
borderRadius : 4 ,
marginRight : 8 ,
} ,
saveButtonText : {
2018-03-09 22:59:12 +02:00
textAlignVertical : 'center' ,
2020-06-10 23:08:59 +02:00
color : theme.colorBright2 ,
2018-03-09 22:59:12 +02:00
fontWeight : 'bold' ,
2017-08-01 19:59:01 +02:00
} ,
savedButtonIcon : {
fontSize : 20 ,
2020-06-10 23:08:59 +02:00
color : theme.colorBright2 ,
2017-08-01 19:59:01 +02:00
width : 18 ,
height : 18 ,
} ,
saveButtonIcon : {
width : 18 ,
height : 18 ,
} ,
contextMenuTrigger : {
2018-05-01 20:30:41 +02:00
fontSize : 30 ,
paddingLeft : 10 ,
2017-08-01 19:59:01 +02:00
paddingRight : theme.marginRight ,
2020-06-10 23:08:59 +02:00
color : theme.color2 ,
2018-03-09 22:59:12 +02:00
fontWeight : 'bold' ,
2017-08-01 19:59:01 +02:00
} ,
contextMenu : {
2020-06-10 23:08:59 +02:00
backgroundColor : theme.backgroundColor2 ,
2017-08-01 19:59:01 +02:00
} ,
contextMenuItem : {
backgroundColor : theme.backgroundColor ,
} ,
contextMenuItemText : {
flex : 1 ,
2018-03-09 22:59:12 +02:00
textAlignVertical : 'center' ,
2017-08-01 19:59:01 +02:00
paddingLeft : theme.marginLeft ,
paddingRight : theme.marginRight ,
paddingTop : theme.itemMarginTop ,
paddingBottom : theme.itemMarginBottom ,
color : theme.color ,
backgroundColor : theme.backgroundColor ,
fontSize : theme.fontSize ,
} ,
titleText : {
flex : 1 ,
2018-03-09 22:59:12 +02:00
textAlignVertical : 'center' ,
2019-06-28 01:51:02 +02:00
marginLeft : 10 ,
2020-06-10 23:08:59 +02:00
color : theme.colorBright2 ,
2018-03-09 22:59:12 +02:00
fontWeight : 'bold' ,
2017-08-01 19:59:01 +02:00
fontSize : theme.fontSize ,
2018-03-16 22:17:52 +02:00
paddingTop : 15 ,
paddingBottom : 15 ,
2017-12-30 21:57:34 +02:00
} ,
warningBox : {
2019-07-29 15:43:53 +02:00
backgroundColor : '#ff9900' ,
2018-03-09 22:59:12 +02:00
flexDirection : 'row' ,
2017-12-30 21:57:34 +02:00
padding : theme.marginLeft ,
} ,
2017-08-01 19:59:01 +02:00
} ;
2017-07-22 18:36:55 +02:00
2023-07-16 18:42:42 +02:00
styleObject . contextMenuItemTextDisabled = {
. . . styleObject . contextMenuItemText ,
opacity : 0.5 ,
} ;
2023-06-01 13:02:36 +02:00
styleObject . topIcon = { . . . theme . icon } ;
2017-08-01 19:59:01 +02:00
styleObject . topIcon . flex = 1 ;
2018-03-09 22:59:12 +02:00
styleObject . topIcon . textAlignVertical = 'center' ;
2020-06-10 23:08:59 +02:00
styleObject . topIcon . color = theme . colorBright2 ;
2017-07-23 00:52:24 +02:00
2023-06-01 13:02:36 +02:00
styleObject . backButton = { . . . styleObject . iconButton } ;
2017-08-01 19:59:01 +02:00
styleObject . backButton . marginRight = 1 ;
2017-07-16 01:30:54 +02:00
2023-06-01 13:02:36 +02:00
styleObject . backButtonDisabled = { . . . styleObject . backButton , opacity : theme.disabledOpacity } ;
styleObject . saveButtonDisabled = { . . . styleObject . saveButton , opacity : theme.disabledOpacity } ;
styleObject . iconButtonDisabled = { . . . styleObject . iconButton , opacity : theme.disabledOpacity } ;
2017-05-16 22:25:19 +02:00
2022-08-27 14:36:59 +02:00
this . cachedStyles [ themeId ] = StyleSheet . create ( styleObject ) ;
return this . cachedStyles [ themeId ] ;
2017-08-01 19:59:01 +02:00
}
2017-05-16 21:57:09 +02:00
2022-08-27 14:36:59 +02:00
private sideMenuButton_press() {
2018-03-09 22:59:12 +02:00
this . props . dispatch ( { type : 'SIDE_MENU_TOGGLE' } ) ;
2017-05-24 21:27:13 +02:00
}
2022-08-27 14:36:59 +02:00
private async backButton_press() {
2019-07-12 20:36:12 +02:00
if ( this . props . noteSelectionEnabled ) {
this . props . dispatch ( { type : 'NOTE_SELECTION_END' } ) ;
2019-07-29 15:43:53 +02:00
} else {
2019-07-12 20:36:12 +02:00
await BackButtonService . back ( ) ;
2019-07-29 15:43:53 +02:00
}
2017-05-16 21:57:09 +02:00
}
2022-08-27 14:36:59 +02:00
private selectAllButton_press() {
2020-03-14 00:41:56 +02:00
this . props . dispatch ( { type : 'NOTE_SELECT_ALL_TOGGLE' } ) ;
}
2022-08-27 14:36:59 +02:00
private searchButton_press() {
void NavService . go ( 'Search' ) ;
2017-07-23 00:52:24 +02:00
}
2022-08-27 14:36:59 +02:00
private async duplicateButton_press() {
2019-10-12 00:37:16 +02:00
const noteIds = this . props . selectedNoteIds ;
this . props . dispatch ( { type : 'NOTE_SELECTION_END' } ) ;
2023-07-16 18:42:42 +02:00
try {
// Duplicate all selected notes. ensureUniqueTitle is set to true to use the
// original note's name as a root for the new unique identifier.
await Note . duplicateMultipleNotes ( noteIds , { ensureUniqueTitle : true } ) ;
} catch ( error ) {
alert ( _n ( 'This note could not be duplicated: %s' , 'These notes could not be duplicated: %s' , noteIds . length , error . message ) ) ;
}
2019-10-12 00:37:16 +02:00
}
2022-08-27 14:36:59 +02:00
private async deleteButton_press() {
2017-11-23 20:47:51 +02:00
// Dialog needs to be displayed as a child of the parent component, otherwise
// it won't be visible within the header component.
2021-10-03 19:41:32 +02:00
const noteIds = this . props . selectedNoteIds ;
const msg = await Note . deleteMessage ( noteIds ) ;
if ( ! msg ) return ;
const ok = await dialogs . confirm ( this . props . parentComponent , msg ) ;
2017-11-23 20:47:51 +02:00
if ( ! ok ) return ;
2018-03-09 22:59:12 +02:00
this . props . dispatch ( { type : 'NOTE_SELECTION_END' } ) ;
2023-07-16 18:42:42 +02:00
try {
await Note . batchDelete ( noteIds ) ;
} catch ( error ) {
alert ( _n ( 'This note could not be deleted: %s' , 'These notes could not be deleted: %s' , noteIds . length , error . message ) ) ;
}
2017-11-23 20:47:51 +02:00
}
2022-08-27 14:36:59 +02:00
private menu_select ( value : OnSelectCallbackType ) {
2022-07-23 09:31:32 +02:00
if ( typeof value === 'function' ) {
2017-05-16 22:25:19 +02:00
value ( ) ;
}
}
2022-08-27 14:36:59 +02:00
private warningBox_press ( event : NavButtonPressEvent ) {
void NavService . go ( event . screen ) ;
2019-12-28 21:50:06 +02:00
}
2022-08-27 14:36:59 +02:00
private renderWarningBox ( screen : string , message : string ) {
2019-12-28 21:50:06 +02:00
return (
< TouchableOpacity key = { screen } style = { this . styles ( ) . warningBox } onPress = { ( ) = > this . warningBox_press ( { screen : screen } ) } activeOpacity = { 0.8 } >
< Text style = { { flex : 1 } } > { message } < / Text >
< / TouchableOpacity >
) ;
2017-12-30 21:57:34 +02:00
}
2022-08-27 14:36:59 +02:00
public render() {
2023-07-18 15:46:11 +02:00
const themeId = this . props . themeId ;
2022-08-27 14:36:59 +02:00
function sideMenuButton ( styles : any , onPress : OnPressCallback ) {
2017-07-16 01:30:54 +02:00
return (
2022-06-26 19:23:41 +02:00
< TouchableOpacity
onPress = { onPress }
accessibilityLabel = { _ ( 'Sidebar' ) }
accessibilityHint = { _ ( 'Show/hide the sidebar' ) }
accessibilityRole = "button" >
2017-07-16 01:30:54 +02:00
< View style = { styles . sideMenuButton } >
2019-07-29 15:43:53 +02:00
< Icon name = "md-menu" style = { styles . topIcon } / >
2017-07-16 01:30:54 +02:00
< / View >
< / TouchableOpacity >
) ;
}
2022-08-27 14:36:59 +02:00
function backButton ( styles : any , onPress : OnPressCallback , disabled : boolean ) {
2017-07-16 01:30:54 +02:00
return (
2022-06-26 19:23:41 +02:00
< TouchableOpacity
onPress = { onPress }
disabled = { disabled }
accessibilityLabel = { _ ( 'Back' ) }
accessibilityRole = "button" >
2017-07-16 01:30:54 +02:00
< View style = { disabled ? styles.backButtonDisabled : styles.backButton } >
2022-06-26 19:23:41 +02:00
< Icon
name = "md-arrow-back"
style = { styles . topIcon }
/ >
2017-07-16 01:30:54 +02:00
< / View >
< / TouchableOpacity >
) ;
}
2022-08-27 14:36:59 +02:00
function saveButton (
2023-08-22 12:58:53 +02:00
styles : any , onPress : OnPressCallback , disabled : boolean , show : boolean ,
2022-08-27 14:36:59 +02:00
) {
2017-07-16 12:17:40 +02:00
if ( ! show ) return null ;
2019-07-29 15:43:53 +02:00
const icon = disabled ? < Icon name = "md-checkmark" style = { styles . savedButtonIcon } / > : < Image style = { styles . saveButtonIcon } source = { require ( './SaveIcon.png' ) } / > ;
2017-07-30 23:33:54 +02:00
2017-07-16 12:17:40 +02:00
return (
2022-06-26 19:23:41 +02:00
< TouchableOpacity
onPress = { onPress }
disabled = { disabled }
style = { { padding : 0 } }
accessibilityLabel = { _ ( 'Save changes' ) }
accessibilityRole = "button" >
2019-07-29 15:43:53 +02:00
< View style = { disabled ? styles.saveButtonDisabled : styles.saveButton } > { icon } < / View >
2017-07-16 12:17:40 +02:00
< / TouchableOpacity >
) ;
}
2022-08-27 14:36:59 +02:00
interface TopButtonOptions {
visible : boolean ;
iconName : string ;
disabled? : boolean ;
description : string ;
onPress : OnPressCallback ;
}
const renderTopButton = ( options : TopButtonOptions ) = > {
2022-06-21 12:50:10 +02:00
if ( ! options . visible ) return null ;
2020-06-13 17:20:18 +02:00
const icon = < Icon name = { options . iconName } style = { this . styles ( ) . topIcon } / > ;
const viewStyle = options . disabled ? this . styles ( ) . iconButtonDisabled : this.styles ( ) . iconButton ;
return (
2022-08-21 23:03:41 +02:00
< CustomButton
2022-06-26 19:23:41 +02:00
onPress = { options . onPress }
style = { { padding : 0 } }
2022-08-21 23:03:41 +02:00
themeId = { themeId }
2022-06-26 19:23:41 +02:00
disabled = { ! ! options . disabled }
2022-08-21 23:03:41 +02:00
description = { options . description }
contentStyle = { viewStyle }
>
{ icon }
< / CustomButton >
2020-06-13 17:20:18 +02:00
) ;
} ;
const renderUndoButton = ( ) = > {
return renderTopButton ( {
2020-10-16 17:26:19 +02:00
iconName : 'arrow-undo-circle-sharp' ,
2022-08-21 23:03:41 +02:00
description : _ ( 'Undo' ) ,
2020-06-13 17:20:18 +02:00
onPress : this.props.onUndoButtonPress ,
visible : this.props.showUndoButton ,
disabled : this.props.undoButtonDisabled ,
} ) ;
} ;
const renderRedoButton = ( ) = > {
return renderTopButton ( {
2020-10-16 17:26:19 +02:00
iconName : 'arrow-redo-circle-sharp' ,
2022-08-21 23:03:41 +02:00
description : _ ( 'Redo' ) ,
2020-06-13 17:20:18 +02:00
onPress : this.props.onRedoButtonPress ,
visible : this.props.showRedoButton ,
} ) ;
} ;
2022-08-27 14:36:59 +02:00
function selectAllButton ( styles : any , onPress : OnPressCallback ) {
2020-03-14 00:41:56 +02:00
return (
2022-08-21 23:03:41 +02:00
< CustomButton
2022-06-26 19:23:41 +02:00
onPress = { onPress }
2022-08-21 23:03:41 +02:00
themeId = { themeId }
description = { _ ( 'Select all' ) }
contentStyle = { styles . iconButton }
>
< Icon name = "md-checkmark-circle-outline" style = { styles . topIcon } / >
< / CustomButton >
2020-03-14 00:41:56 +02:00
) ;
}
2022-08-27 14:36:59 +02:00
function searchButton ( styles : any , onPress : OnPressCallback ) {
2017-07-23 00:52:24 +02:00
return (
2022-08-21 23:03:41 +02:00
< CustomButton
2022-06-26 19:23:41 +02:00
onPress = { onPress }
2022-08-21 23:03:41 +02:00
description = { _ ( 'Search' ) }
themeId = { themeId }
contentStyle = { styles . iconButton }
>
< Icon name = "md-search" style = { styles . topIcon } / >
< / CustomButton >
2017-07-23 00:52:24 +02:00
) ;
}
2022-08-27 14:36:59 +02:00
function deleteButton ( styles : any , onPress : OnPressCallback , disabled : boolean ) {
2017-11-23 20:47:51 +02:00
return (
2022-08-21 23:03:41 +02:00
< CustomButton
2022-06-26 19:23:41 +02:00
onPress = { onPress }
disabled = { disabled }
2022-08-21 23:03:41 +02:00
themeId = { themeId }
description = { _ ( 'Delete' ) }
2022-06-26 19:23:41 +02:00
accessibilityHint = {
disabled ? null : _ ( 'Delete selected notes' )
}
2022-08-21 23:03:41 +02:00
contentStyle = { disabled ? styles.iconButtonDisabled : styles.iconButton }
>
< Icon name = "md-trash" style = { styles . topIcon } / >
< / CustomButton >
2017-11-23 20:47:51 +02:00
) ;
}
2022-08-27 14:36:59 +02:00
function duplicateButton ( styles : any , onPress : OnPressCallback , disabled : boolean ) {
2019-10-12 00:37:16 +02:00
return (
2022-08-21 23:03:41 +02:00
< CustomButton
2022-06-26 19:23:41 +02:00
onPress = { onPress }
disabled = { disabled }
2022-08-21 23:03:41 +02:00
themeId = { themeId }
description = { _ ( 'Duplicate' ) }
2022-06-26 19:23:41 +02:00
accessibilityHint = {
disabled ? null : _ ( 'Duplicate selected notes' )
}
2022-08-21 23:03:41 +02:00
contentStyle = { disabled ? styles.iconButtonDisabled : styles.iconButton }
>
< Icon name = "md-copy" style = { styles . topIcon } / >
< / CustomButton >
2019-10-12 00:37:16 +02:00
) ;
}
2022-08-27 14:36:59 +02:00
function sortButton ( styles : any , onPress : OnPressCallback ) {
2018-02-22 20:58:15 +02:00
return (
2022-06-26 19:23:41 +02:00
< TouchableOpacity
onPress = { onPress }
accessibilityLabel = { _ ( 'Sort notes by' ) }
accessibilityRole = "button" >
2018-02-22 20:58:15 +02:00
< View style = { styles . iconButton } >
2020-10-16 17:26:19 +02:00
< Icon name = "filter-outline" style = { styles . topIcon } / >
2018-02-22 20:58:15 +02:00
< / View >
< / TouchableOpacity >
) ;
}
2017-05-16 23:46:21 +02:00
let key = 0 ;
2020-03-14 01:46:14 +02:00
const menuOptionComponents = [ ] ;
2017-05-16 23:46:21 +02:00
2017-11-23 20:47:51 +02:00
if ( ! this . props . noteSelectionEnabled ) {
for ( let i = 0 ; i < this . props . menuOptions . length ; i ++ ) {
2020-03-14 01:46:14 +02:00
const o = this . props . menuOptions [ i ] ;
2017-11-28 22:31:14 +02:00
if ( o . isDivider ) {
2019-09-19 23:51:18 +02:00
menuOptionComponents . push ( < View key = { ` menuOption_ ${ key ++ } ` } style = { this . styles ( ) . divider } / > ) ;
2017-11-28 22:31:14 +02:00
} else {
menuOptionComponents . push (
2023-07-16 18:42:42 +02:00
< MenuOption value = { o . onPress } key = { ` menuOption_ ${ key ++ } ` } style = { this . styles ( ) . contextMenuItem } disabled = { ! ! o . disabled } >
< Text style = { o . disabled ? this . styles ( ) . contextMenuItemTextDisabled : this.styles ( ) . contextMenuItemText } > { o . title } < / Text >
2023-08-22 12:58:53 +02:00
< / MenuOption > ,
2019-07-29 15:43:53 +02:00
) ;
2017-11-28 22:31:14 +02:00
}
2017-11-23 20:47:51 +02:00
}
2017-09-24 16:48:23 +02:00
if ( menuOptionComponents . length ) {
2019-09-19 23:51:18 +02:00
menuOptionComponents . push ( < View key = { ` menuOption_ ${ key ++ } ` } style = { this . styles ( ) . divider } / > ) ;
2017-09-24 16:48:23 +02:00
}
2017-11-23 20:47:51 +02:00
} else {
2017-09-24 16:48:23 +02:00
menuOptionComponents . push (
2018-03-09 22:59:12 +02:00
< MenuOption value = { ( ) = > this . deleteButton_press ( ) } key = { 'menuOption_delete' } style = { this . styles ( ) . contextMenuItem } >
< Text style = { this . styles ( ) . contextMenuItemText } > { _ ( 'Delete' ) } < / Text >
2023-08-22 12:58:53 +02:00
< / MenuOption > ,
2019-07-29 15:43:53 +02:00
) ;
2019-10-12 00:37:16 +02:00
menuOptionComponents . push (
< MenuOption value = { ( ) = > this . duplicateButton_press ( ) } key = { 'menuOption_duplicate' } style = { this . styles ( ) . contextMenuItem } >
< Text style = { this . styles ( ) . contextMenuItemText } > { _ ( 'Duplicate' ) } < / Text >
2023-08-22 12:58:53 +02:00
< / MenuOption > ,
2019-10-12 00:37:16 +02:00
) ;
2017-09-24 16:48:23 +02:00
}
2017-07-10 21:16:59 +02:00
2022-08-27 14:36:59 +02:00
const createTitleComponent = ( disabled : boolean ) = > {
2017-11-23 20:47:51 +02:00
const folderPickerOptions = this . props . folderPickerOptions ;
if ( folderPickerOptions && folderPickerOptions . enabled ) {
2017-07-16 18:06:05 +02:00
return (
2023-05-29 12:31:21 +02:00
< FolderPicker
themeId = { themeId }
2020-05-09 17:01:39 +02:00
disabled = { disabled }
2023-05-29 12:31:21 +02:00
selectedFolderId = { 'selectedFolderId' in folderPickerOptions ? folderPickerOptions.selectedFolderId : null }
2022-08-27 14:36:59 +02:00
onValueChange = { async ( folderId ) = > {
2017-11-23 20:41:35 +02:00
// 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 ) {
2022-08-27 14:36:59 +02:00
folderPickerOptions . onValueChange ( folderId ) ;
2017-11-23 20:41:35 +02:00
return ;
}
if ( ! folderId ) return ;
const noteIds = this . props . selectedNoteIds ;
if ( ! noteIds . length ) return ;
const folder = await Folder . load ( folderId ) ;
2020-06-13 17:20:59 +02:00
const ok = noteIds . length > 1 ? await dialogs . confirm ( this . props . parentComponent , _ ( 'Move %d notes to notebook "%s"?' , noteIds . length , folder . title ) ) : true ;
2017-11-23 20:41:35 +02:00
if ( ! ok ) return ;
2018-03-09 22:59:12 +02:00
this . props . dispatch ( { type : 'NOTE_SELECTION_END' } ) ;
2023-07-16 18:42:42 +02:00
try {
for ( let i = 0 ; i < noteIds . length ; i ++ ) {
await Note . moveToFolder ( noteIds [ i ] , folderId ) ;
}
} catch ( error ) {
alert ( _n ( 'This note could not be moved: %s' , 'These notes could not be moved: %s' , noteIds . length , error . message ) ) ;
2017-11-23 20:41:35 +02:00
}
2017-11-23 20:47:51 +02:00
} }
2023-05-29 12:31:21 +02:00
mustSelect = { ! ! folderPickerOptions . mustSelect }
folders = { this . props . folders }
2017-11-19 01:59:07 +02:00
/ >
2017-07-16 18:06:05 +02:00
) ;
} else {
2020-03-14 01:46:14 +02:00
const title = 'title' in this . props && this . props . title !== null ? this . props . title : '' ;
2020-05-09 16:33:06 +02:00
return < Text ellipsizeMode = { 'tail' } numberOfLines = { 1 } style = { this . styles ( ) . titleText } > { title } < / Text > ;
2017-07-16 18:06:05 +02:00
}
2019-07-29 15:43:53 +02:00
} ;
2017-07-16 18:06:05 +02:00
2019-12-28 21:50:06 +02:00
const warningComps = [ ] ;
if ( this . props . showMissingMasterKeyMessage ) warningComps . push ( this . renderWarningBox ( 'EncryptionConfig' , _ ( 'Press to set the decryption password.' ) ) ) ;
if ( this . props . hasDisabledSyncItems ) warningComps . push ( this . renderWarningBox ( 'Status' , _ ( 'Some items cannot be synchronised. Press for more info.' ) ) ) ;
2020-08-02 13:28:50 +02:00
if ( this . props . shouldUpgradeSyncTarget && this . props . showShouldUpgradeSyncTargetMessage !== false ) warningComps . push ( this . renderWarningBox ( 'UpgradeSyncTarget' , _ ( 'The sync target needs to be upgraded. Press this banner to proceed.' ) ) ) ;
2017-12-30 21:57:34 +02:00
2023-07-17 13:12:42 +02:00
if ( this . props . hasDisabledEncryptionItems ) {
warningComps . push ( this . renderWarningBox ( 'Status' , _ ( 'Some items cannot be decrypted.' ) ) ) ;
}
2019-07-12 20:36:12 +02:00
const showSideMenuButton = ! ! this . props . showSideMenuButton && ! this . props . noteSelectionEnabled ;
2020-03-14 00:41:56 +02:00
const showSelectAllButton = this . props . noteSelectionEnabled ;
2019-07-12 20:36:12 +02:00
const showSearchButton = ! ! this . props . showSearchButton && ! this . props . noteSelectionEnabled ;
2018-03-16 22:17:52 +02:00
const showContextMenuButton = this . props . showContextMenuButton !== false ;
2019-07-12 20:36:12 +02:00
const showBackButton = ! ! this . props . noteSelectionEnabled || this . props . showBackButton !== false ;
let backButtonDisabled = ! this . props . historyCanGoBack ;
2019-07-29 15:43:53 +02:00
if ( this . props . noteSelectionEnabled ) backButtonDisabled = false ;
2022-08-27 14:36:59 +02:00
const headerItemDisabled = ! ( this . props . selectedNoteIds . length > 0 ) ;
2018-03-16 22:17:52 +02:00
2020-05-09 17:01:39 +02:00
const titleComp = createTitleComponent ( headerItemDisabled ) ;
2018-03-16 22:17:52 +02:00
const sideMenuComp = ! showSideMenuButton ? null : sideMenuButton ( this . styles ( ) , ( ) = > this . sideMenuButton_press ( ) ) ;
2019-07-12 20:36:12 +02:00
const backButtonComp = ! showBackButton ? null : backButton ( this . styles ( ) , ( ) = > this . backButton_press ( ) , backButtonDisabled ) ;
2020-03-14 00:41:56 +02:00
const selectAllButtonComp = ! showSelectAllButton ? null : selectAllButton ( this . styles ( ) , ( ) = > this . selectAllButton_press ( ) ) ;
2018-03-16 22:17:52 +02:00
const searchButtonComp = ! showSearchButton ? null : searchButton ( this . styles ( ) , ( ) = > this . searchButton_press ( ) ) ;
2020-05-09 17:01:39 +02:00
const deleteButtonComp = this . props . noteSelectionEnabled ? deleteButton ( this . styles ( ) , ( ) = > this . deleteButton_press ( ) , headerItemDisabled ) : null ;
const duplicateButtonComp = this . props . noteSelectionEnabled ? duplicateButton ( this . styles ( ) , ( ) = > this . duplicateButton_press ( ) , headerItemDisabled ) : null ;
2019-07-12 20:36:12 +02:00
const sortButtonComp = ! this . props . noteSelectionEnabled && this . props . sortButton_press ? sortButton ( this . styles ( ) , ( ) = > this . props . sortButton_press ( ) ) : null ;
2018-03-09 22:59:12 +02:00
const windowHeight = Dimensions . get ( 'window' ) . height - 50 ;
2017-11-23 20:47:51 +02:00
2022-08-27 14:36:59 +02:00
const contextMenuStyle : ViewStyle = {
paddingTop : PADDING_V ,
paddingBottom : PADDING_V ,
} ;
2019-07-29 15:43:53 +02:00
2019-07-12 20:36:12 +02:00
// 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)
2019-07-29 15:43:53 +02:00
if ( this . props . noteSelectionEnabled ) contextMenuStyle . width = 1 ;
const menuComp =
! menuOptionComponents . length || ! showContextMenuButton ? null : (
2020-05-21 10:14:33 +02:00
< Menu onSelect = { value = > this . menu_select ( value ) } style = { this . styles ( ) . contextMenu } >
2019-07-29 15:43:53 +02:00
< MenuTrigger style = { contextMenuStyle } >
2020-10-16 17:26:19 +02:00
< Icon name = "md-ellipsis-vertical" style = { this . styles ( ) . contextMenuTrigger } / >
2019-07-29 15:43:53 +02:00
< / MenuTrigger >
< MenuOptions >
< ScrollView style = { { maxHeight : windowHeight } } > { menuOptionComponents } < / ScrollView >
< / MenuOptions >
< / Menu >
) ;
2017-05-16 22:25:19 +02:00
2017-05-16 21:57:09 +02:00
return (
2019-07-29 15:43:53 +02:00
< View style = { this . styles ( ) . container } >
< View style = { { flexDirection : 'row' , alignItems : 'center' } } >
{ sideMenuComp }
{ backButtonComp }
2022-08-27 14:36:59 +02:00
{ renderUndoButton ( ) }
{ renderRedoButton ( ) }
2019-07-29 15:43:53 +02:00
{ saveButton (
this . styles ( ) ,
( ) = > {
if ( this . props . onSaveButtonPress ) this . props . onSaveButtonPress ( ) ;
} ,
this . props . saveButtonDisabled === true ,
2023-08-22 12:58:53 +02:00
this . props . showSaveButton === true ,
2019-07-29 15:43:53 +02:00
) }
{ titleComp }
2020-03-14 00:41:56 +02:00
{ selectAllButtonComp }
2019-07-29 15:43:53 +02:00
{ searchButtonComp }
{ deleteButtonComp }
2019-10-12 00:37:16 +02:00
{ duplicateButtonComp }
2019-07-29 15:43:53 +02:00
{ sortButtonComp }
{ menuComp }
2017-12-30 21:57:34 +02:00
< / View >
2019-12-28 21:50:06 +02:00
{ warningComps }
2020-06-13 17:20:59 +02:00
< DialogBox
2022-08-27 14:36:59 +02:00
ref = { ( dialogbox : typeof DialogBox ) = > {
2020-06-13 17:20:59 +02:00
this . dialogbox = dialogbox ;
} }
/ >
2017-05-16 21:57:09 +02:00
< / View >
) ;
}
2022-08-29 15:19:04 +02:00
public static defaultProps : Partial < ScreenHeaderProps > = {
2022-08-27 14:36:59 +02:00
menuOptions : [ ] ,
} ;
}
2017-06-11 23:11:14 +02:00
2022-08-27 14:36:59 +02:00
const ScreenHeader = connect ( ( state : State ) = > {
2021-08-12 17:54:10 +02:00
const syncInfo = localSyncInfoFromState ( state ) ;
2019-07-29 15:43:53 +02:00
return {
historyCanGoBack : state.historyCanGoBack ,
locale : state.settings.locale ,
folders : state.folders ,
2020-09-15 15:01:07 +02:00
themeId : state.settings.theme ,
2023-07-17 13:12:42 +02:00
hasDisabledEncryptionItems : state.hasDisabledEncryptionItems ,
2019-07-29 15:43:53 +02:00
noteSelectionEnabled : state.noteSelectionEnabled ,
selectedNoteIds : state.selectedNoteIds ,
2021-08-17 13:03:19 +02:00
showMissingMasterKeyMessage : showMissingMasterKeyMessage ( syncInfo , state . notLoadedMasterKeys ) ,
2019-12-28 21:50:06 +02:00
hasDisabledSyncItems : state.hasDisabledSyncItems ,
2020-08-02 13:28:50 +02:00
shouldUpgradeSyncTarget : state.settings [ 'sync.upgradeState' ] === Setting . SYNC_UPGRADE_STATE_SHOULD_DO ,
2019-07-29 15:43:53 +02:00
} ;
} ) ( ScreenHeaderComponent ) ;
2022-08-27 14:36:59 +02:00
export default ScreenHeader ;
export { ScreenHeader } ;