import * as React from "react" ; import { useSelector } from "react-redux" ; import { TextInput as NativeTextInput } from "react-native" ; import { HelperText , Paragraph } from "react-native-paper" ; import { useNavigation , RouteProp , useNavigationState , CommonActions } from "@react-navigation/native" ; import { StackNavigationProp } from "@react-navigation/stack" ; import { useSyncGate } from "../SyncGate" ; import { useCredentials } from "../credentials" ; import { StoreState , useAsyncDispatch } from "../store" ; import { collectionUpload , pushMessage } from "../store/actions" ; import TextInput from "../widgets/TextInput" ; import ScrollView from "../widgets/ScrollView" ; import Container from "../widgets/Container" ; import ConfirmationDialog from "../widgets/ConfirmationDialog" ; import ErrorOrLoadingDialog from "../widgets/ErrorOrLoadingDialog" ; import NotFound from "../widgets/NotFound" ; import FormButton from "../widgets/FormButton" ; import AppbarAction from "../widgets/AppbarAction" ; import { useLoading , defaultColor } from "../helpers" ; import { DefaultNavigationProp , RootStackParamList } from "../RootStackParamList" ; import ColorPicker from "../widgets/ColorPicker" ; import * as C from "../constants" ; interface FormErrors { name ? : string ; color ? : string ; } type NavigationProp = StackNavigationProp < RootStackParamList , "CollectionEdit" > | StackNavigationProp < RootStackParamList , "CollectionCreate" > ; interface PropsType { route : RouteProp < RootStackParamList , "CollectionEdit" > | RouteProp < RootStackParamList , "CollectionCreate" > ; } export default function CollectionEditScreen ( props ) { const [ errors , setErrors ] = React . useState ( { } as FormErrors ) ; const [ name , setName ] = React . useState < string > ( "" ) ; const [ description , setDescription ] = React . useState < string > ( "" ) ; const [ color , setColor ] = React . useState < string > ( "" ) ; const dispatch = useAsyncDispatch ( ) ; const cacheCollections = useSelector ( ( state ) => state . cache . collections ) ; const syncGate = useSyncGate ( ) ; const navigation = useNavigation < NavigationProp > ( ) ; const navigationState = useNavigationState ( ( state ) => ( state . index > 0 ) ? state . routes [ state . index - 1 ] : null ) ; const etebase = useCredentials ( ) ! ; const [ loading , error , setPromise ] = useLoading ( ) ; const colType = C . colType ; const colUid = props . route . params ?. colUid ; React . useEffect ( ( ) => { if ( syncGate ) { return ; } if ( colUid ) { const passedCollection = cacheCollections . get ( colUid ) ; if ( passedCollection ) { const { meta } = passedCollection ; setName ( meta . name ! ) ; setDescription ( meta . description ?? "" ) ; if ( meta . color !== undefined ) { setColor ( meta . color ) ; } } } } , [ syncGate , colUid ] ) ; React . useEffect ( ( ) => { navigation . setOptions ( { title : ( colUid ) ? "Edit Notebook" : "New Notebook" , headerRight : ( ) => ( < RightAction colUid = { colUid } / > ) } ) ; } , [ colUid ] ) ; if ( syncGate ) { return syncGate ; } if ( colUid && ! cacheCollections . has ( colUid ) ) { return < NotFound />; } function onSave ( ) { setPromise ( async ( ) => { const saveErrors = { } ; const fieldRequired = "This field is required!" ; if ( ! name ) { saveErrors . name = fieldRequired ; } if ( color && ! /^#[0-9a-f]{6}([0-9a-f]{2})?$/i . test ( color ) ) { saveErrors . color = "Must be of the form #RRGGBB or #RRGGBBAA or empty" ; } if ( Object . keys ( saveErrors ) . length > 0 ) { setErrors ( saveErrors ) ; return ; } const colMgr = etebase . getCollectionManager ( ) ; const mtime = ( new Date ( ) ) . getTime ( ) ; const meta = { name , description , color , mtime } ; let collection ; if ( colUid ) { collection = colMgr . cacheLoad ( cacheCollections . get ( colUid ) ! . cache ) ; const colMeta = collection . getMeta ( ) ; collection . setMeta ( { ... colMeta , ... meta } ) ; } else { collection = await colMgr . create ( colType , meta , "" ) ; } await dispatch ( collectionUpload ( colMgr , collection ) ) ; if ( colUid ) { dispatch ( pushMessage ( { message : "Notebook saved" , severity : "success" } ) ) ; navigation . goBack ( ) ; } else { dispatch ( pushMessage ( { message : "Notebook created" , severity : "success" } ) ) ; const previousScreen = navigationState ?. name ; if ( navigationState && previousScreen === "NoteCreate" ) { navigation . dispatch ( { ... CommonActions . setParams ( { colUid : collection . uid } ) , source : navigationState . key } ) ; } else { navigation . replace ( "Collection" , { colUid : collection . uid } ) ; } } } ) ; } const descriptionRef = React . createRef < NativeTextInput > ( ) ; const collectionColorBox = ( < > < ColorPicker error = { errors . color } defaultColor = { defaultColor } color = { color } onChange = { setColor } / > < />); return ( < ScrollView keyboardAware > < Container > < ErrorOrLoadingDialog loading = { loading } error = { error } onDismiss = { ( ) => setPromise ( undefined ) } /> < TextInput autoFocus returnKeyType = "next" onSubmitEditing = { ( ) => descriptionRef . current ! . focus ( ) } error = { ! ! errors . name } onChangeText = { setName } label = "Display name (title)" accessibilityLabel = "Display name (title)" value = { name } / > < HelperText type = "error" visible = { ! ! errors . name } > { errors . name } < /HelperText> < TextInput ref = { descriptionRef } onChangeText = { setDescription } label = "Description (optional)" accessibilityLabel = "Description (optional)" value = { description } / > < HelperText type = "error" visible = { false } > < React . Fragment /> < /HelperText> { collectionColorBox } < FormButton disabled = { loading } onPress = { onSave } > { loading ? "Loading…" : "Save" } < /FormButton> < /Container> < /ScrollView>); } function RightAction ( props ) { const [ confirmationVisible , setConfirmationVisible ] = React . useState ( false ) ; const navigation = useNavigation < DefaultNavigationProp > ( ) ; const etebase = useCredentials ( ) ! ; const dispatch = useAsyncDispatch ( ) ; const cacheCollections = useSelector ( ( state ) => state . cache . collections ) ; const colUid = props . colUid ; if ( ! colUid ) { return < React . Fragment />; } return ( < React . Fragment > < AppbarAction icon = "delete" accessibilityLabel = "Delete collection" onPress = { ( ) => { setConfirmationVisible ( true ) ; } } /> < ConfirmationDialog title = "Are you sure?" key = { confirmationVisible . toString ( ) } visible = { confirmationVisible } onOk = { async ( ) => { const colMgr = etebase . getCollectionManager ( ) ; const collection = colMgr . cacheLoad ( cacheCollections . get ( colUid ) ! . cache ) ; const mtime = ( new Date ( ) ) . getTime ( ) ; const meta = collection . getMeta ( ) ; collection . setMeta ( { ... meta , mtime } ) ; collection . delete ( ) ; await dispatch ( collectionUpload ( colMgr , collection ) ) ; dispatch ( pushMessage ( { message : "Collection deleted" , severity : "success" } ) ) ; navigation . navigate ( "Home" ) ; } } onCancel = { ( ) => { setConfirmationVisible ( false ) ; } } > < Paragraph > This colection and all of its data will be removed from the server . < /Paragraph> < /ConfirmationDialog> < /React.Fragment>); }