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' ;
import { PureComponent , Component } from 'react' ;
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' ;
import { _ } from '@joplin/lib/locale' ;
import Setting from '@joplin/lib/models/Setting' ;
import Note from '@joplin/lib/models/Note' ;
import Folder , { FolderEntityWithChildren } from '@joplin/lib/models/Folder' ;
2020-11-05 18:58:23 +02:00
const { themeStyle } = require ( './global-style.js' ) ;
2022-08-27 14:36:59 +02:00
import Dropdown , { DropdownListItem , 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' ;
2017-05-16 21:57:09 +02:00
2020-02-09 16:51:12 +02:00
Icon . loadFont ( ) ;
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 ;
}
interface MenuOptionType {
onPress : OnPressCallback ;
isDivider? : boolean ;
title : string ;
}
type DispatchCommandType = ( event : { type : string } ) = > void ;
interface ScreenHeaderProps {
selectedNoteIds : string [ ] ;
noteSelectionEnabled : boolean ;
parentComponent : Component ;
showUndoButton : boolean ;
undoButtonDisabled? : boolean ;
showRedoButton : boolean ;
menuOptions : MenuOptionType [ ] ;
title? : string | null ;
folders : FolderEntity [ ] ;
folderPickerOptions ? : {
enabled : boolean ;
selectedFolderId : string ;
onValueChange : OnValueChangedListener ;
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 ;
shouldUpgradeSyncTarget? : boolean ;
showShouldUpgradeSyncTargetMessage? : boolean ;
}
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() {
2018-03-09 22:59:12 +02:00
const themeId = Setting . value ( 'theme' ) ;
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
2017-08-01 19:59:01 +02:00
styleObject . topIcon = Object . assign ( { } , theme . icon ) ;
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
2017-08-01 19:59:01 +02:00
styleObject . backButton = Object . assign ( { } , styleObject . iconButton ) ;
styleObject . backButton . marginRight = 1 ;
2017-07-16 01:30:54 +02:00
2017-08-01 19:59:01 +02:00
styleObject . backButtonDisabled = Object . assign ( { } , styleObject . backButton , { opacity : theme.disabledOpacity } ) ;
styleObject . saveButtonDisabled = Object . assign ( { } , styleObject . saveButton , { opacity : theme.disabledOpacity } ) ;
2020-05-09 17:01:39 +02:00
styleObject . iconButtonDisabled = Object . assign ( { } , 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 ;
2019-10-14 01:47:21 +02:00
// Duplicate all selected notes. ensureUniqueTitle is set to true to use the
// original note's name as a root for the new unique identifier.
2020-02-05 00:09:34 +02:00
await Note . duplicateMultipleNotes ( noteIds , { ensureUniqueTitle : true } ) ;
2019-10-12 00:37:16 +02:00
this . props . dispatch ( { type : 'NOTE_SELECTION_END' } ) ;
}
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' } ) ;
2017-11-23 20:47:51 +02:00
await Note . batchDelete ( noteIds ) ;
}
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() {
2022-08-21 23:03:41 +02:00
const themeId = Setting . value ( 'theme' ) ;
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 (
styles : any , onPress : OnPressCallback , disabled : boolean , show : boolean
) {
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 (
2019-09-19 23:51:18 +02:00
< MenuOption value = { o . onPress } key = { ` menuOption_ ${ key ++ } ` } style = { this . styles ( ) . contextMenuItem } >
2017-11-28 22:31:14 +02:00
< Text style = { this . styles ( ) . contextMenuItemText } > { o . title } < / Text >
2019-07-29 15:43:53 +02:00
< / MenuOption >
) ;
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 >
2019-07-29 15:43:53 +02:00
< / MenuOption >
) ;
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 >
< / MenuOption >
) ;
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 ) = > {
const themeId = Setting . value ( 'theme' ) ;
2017-11-19 01:59:07 +02:00
const theme = themeStyle ( themeId ) ;
2017-11-23 20:47:51 +02:00
const folderPickerOptions = this . props . folderPickerOptions ;
if ( folderPickerOptions && folderPickerOptions . enabled ) {
2022-08-27 14:36:59 +02:00
const addFolderChildren = (
folders : FolderEntityWithChildren [ ] , pickerItems : DropdownListItem [ ] , indent : number
) = > {
2018-12-16 18:18:24 +02:00
folders . sort ( ( a , b ) = > {
2019-06-20 00:16:37 +02:00
const aTitle = a && a . title ? a . title : '' ;
const bTitle = b && b . title ? b . title : '' ;
return aTitle . toLowerCase ( ) < bTitle . toLowerCase ( ) ? - 1 : + 1 ;
2018-12-16 18:18:24 +02:00
} ) ;
for ( let i = 0 ; i < folders . length ; i ++ ) {
const f = folders [ i ] ;
2021-11-15 19:19:51 +02:00
const icon = Folder . unserializeIcon ( f . icon ) ;
const iconString = icon ? ` ${ icon . emoji } ` : '' ;
pickerItems . push ( { label : ` ${ ' ' . repeat ( indent ) } ${ iconString + Folder . displayTitle ( f ) } ` , value : f.id } ) ;
2018-12-16 18:18:24 +02:00
pickerItems = addFolderChildren ( f . children , pickerItems , indent + 1 ) ;
}
return pickerItems ;
2019-07-29 15:43:53 +02:00
} ;
2018-12-16 18:18:24 +02:00
2022-08-27 14:36:59 +02:00
const titlePickerItems = ( mustSelect : boolean ) = > {
2020-05-21 10:14:33 +02:00
const folders = this . props . folders . filter ( f = > f . id !== Folder . conflictFolderId ( ) ) ;
2017-11-23 20:47:51 +02:00
let output = [ ] ;
2018-03-09 22:59:12 +02:00
if ( mustSelect ) output . push ( { label : _ ( 'Move to notebook...' ) , value : null } ) ;
2019-07-12 19:32:08 +02:00
const folderTree = Folder . buildTree ( folders ) ;
2018-12-16 18:18:24 +02:00
output = addFolderChildren ( folderTree , output , 0 ) ;
2017-11-23 20:47:51 +02:00
return output ;
2019-07-29 15:43:53 +02:00
} ;
2017-11-19 01:59:07 +02:00
2017-07-16 18:06:05 +02:00
return (
2017-11-19 01:59:07 +02:00
< Dropdown
2017-11-23 20:47:51 +02:00
items = { titlePickerItems ( ! ! folderPickerOptions . mustSelect ) }
2020-05-09 17:01:39 +02:00
disabled = { disabled }
2018-12-16 18:18:24 +02:00
labelTransform = "trim"
2019-07-29 15:43:53 +02:00
selectedValue = { 'selectedFolderId' in folderPickerOptions ? folderPickerOptions.selectedFolderId : null }
2017-11-19 01:59:07 +02:00
itemListStyle = { {
backgroundColor : theme.backgroundColor ,
} }
headerStyle = { {
2020-06-10 23:08:59 +02:00
color : theme.colorBright2 ,
2017-11-19 01:59:07 +02:00
fontSize : theme.fontSize ,
2020-05-09 17:01:39 +02:00
opacity : disabled ? theme.disabledOpacity : 1 ,
2017-11-19 01:59:07 +02:00
} }
itemStyle = { {
color : theme.color ,
fontSize : theme.fontSize ,
} }
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' } ) ;
2017-11-23 20:41:35 +02:00
for ( let i = 0 ; i < noteIds . length ; i ++ ) {
await Note . moveToFolder ( noteIds [ i ] , folderId ) ;
}
2017-11-23 20:47:51 +02:00
} }
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
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 ,
this . props . showSaveButton === true
) }
{ 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 ,
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 } ;