2021-08-16 16:20:14 +02:00
import * as React from 'react' ;
import { useState , useRef , useCallback } from 'react' ;
import { _ } from '@joplin/lib/locale' ;
import DialogButtonRow from '../DialogButtonRow' ;
import Dialog from '../Dialog' ;
import styled from 'styled-components' ;
import DialogTitle from '../DialogTitle' ;
import SyncTargetRegistry , { SyncTargetInfo } from '@joplin/lib/SyncTargetRegistry' ;
import useElementSize from '@joplin/lib/hooks/useElementSize' ;
import Button , { ButtonLevel } from '../Button/Button' ;
import bridge from '../../services/bridge' ;
import StyledInput from '../style/StyledInput' ;
2021-08-19 14:01:56 +02:00
import Setting from '@joplin/lib/models/Setting' ;
import SyncTargetJoplinCloud from '@joplin/lib/SyncTargetJoplinCloud' ;
2021-08-16 16:20:14 +02:00
import StyledLink from '../style/StyledLink' ;
interface Props {
themeId : number ;
2023-06-30 11:30:29 +02:00
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
2021-08-16 16:20:14 +02:00
dispatch : Function ;
}
const StyledRoot = styled . div `
min - width : 500px ;
max - width : 1200px ;
` ;
2023-02-16 11:23:19 +02:00
const SyncTargetDescription = styled . div < { height : number } > `
2021-08-16 16:20:14 +02:00
$ { props = > props . height ? ` height: ${ props . height } px ` : '' } ;
margin - bottom : 1.3em ;
line - height : $ { props = > props . theme . lineHeight } ;
font - size : 16px ;
` ;
const CreateAccountLink = styled ( StyledLink ) `
font - size : 16px ;
` ;
const ContentRoot = styled . div `
background - color : $ { props = > props . theme . backgroundColor3 } ;
padding : 1em ;
padding - right : 0 ;
` ;
const SelfHostingMessage = styled . div `
color : $ { props = > props . theme . color } ;
padding - right : 1em ;
font - style : italic ;
margin - top : 1em ;
opacity : 0.6 ;
` ;
const SyncTargetBoxes = styled . div `
display : flex ;
flex - direction : row ;
justify - content : center ;
` ;
const SyncTargetTitle = styled . p `
display : flex ;
flex - direction : row ;
font - weight : bold ;
font - size : 1.7em ;
align - items : center ;
white - space : nowrap ;
` ;
const SyncTargetLogo = styled . img `
height : 1.3em ;
margin - right : 0.4em ;
` ;
2023-02-16 11:23:19 +02:00
const SyncTargetBox = styled . div < { faded : boolean } > `
2021-08-16 16:20:14 +02:00
display : flex ;
flex : 1 ;
flex - direction : column ;
font - family : $ { props = > props . theme . fontFamily } ;
color : $ { props = > props . theme . color } ;
background - color : $ { props = > props . theme . backgroundColor } ;
border : 1px solid $ { props = > props . theme . dividerColor } ;
border - radius : 8px ;
2023-12-24 13:06:28 +02:00
padding : 2em 2.2 em 2 em 2.2 em ;
2021-08-16 16:20:14 +02:00
margin - right : 1em ;
max - width : 400px ;
opacity : $ { props = > props . faded ? 0.5 : 1 } ;
` ;
const FeatureList = styled . div `
margin - bottom : 1em ;
` ;
const FeatureIcon = styled . i `
display : inline - flex ;
width : 16px ;
justify - content : center ;
color : $ { props = > props . theme . color4 } ;
position : absolute ;
` ;
2023-02-16 11:23:19 +02:00
const FeatureLine = styled . div < { enabled : boolean } > `
2021-08-16 16:20:14 +02:00
margin - bottom : .5em ;
opacity : $ { props = > props . enabled ? 1 : 0.5 } ;
position : relative ;
font - size : 16px ;
` ;
const FeatureLabel = styled . div `
margin - left : 24px ;
line - height : $ { props = > props . theme . lineHeight } ;
` ;
const SelectButton = styled ( Button ) `
padding : 10px 10 px ;
height : auto ;
min - height : auto ;
max - height : fit - content ;
font - size : 1em ;
` ;
const JoplinCloudLoginForm = styled . div `
display : flex ;
flex - direction : column ;
` ;
const FormLabel = styled . label `
font - weight : bold ;
margin : 1em 0 0.6 em 0 ;
` ;
2023-12-24 13:06:28 +02:00
const SlowSyncWarning = styled . div `
margin - top : 1em ;
opacity : 0.8 ;
font - family : $ { props = > props . theme . fontFamily } ;
color : $ { props = > props . theme . color } ;
font - size : 14px ;
` ;
2021-08-16 16:20:14 +02:00
const syncTargetNames : string [ ] = [
'joplinCloud' ,
'dropbox' ,
'onedrive' ,
'nextcloud' ,
'webdav' ,
'amazon_s3' ,
'joplinServer' ,
] ;
const logosImageNames : Record < string , string > = {
2021-09-20 17:29:05 +02:00
'dropbox' : 'SyncTarget_Dropbox.svg' ,
'joplinCloud' : 'SyncTarget_JoplinCloud.svg' ,
'onedrive' : 'SyncTarget_OneDrive.svg' ,
2021-08-16 16:20:14 +02:00
} ;
export default function ( props : Props ) {
const [ showJoplinCloudForm , setShowJoplinCloudForm ] = useState ( false ) ;
const joplinCloudDescriptionRef = useRef ( null ) ;
const [ joplinCloudEmail , setJoplinCloudEmail ] = useState ( '' ) ;
const [ joplinCloudPassword , setJoplinCloudPassword ] = useState ( '' ) ;
const [ joplinCloudLoginInProgress , setJoplinCloudLoginInProgress ] = useState ( false ) ;
2023-06-30 11:30:29 +02:00
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
2021-08-16 16:20:14 +02:00
function closeDialog ( dispatch : Function ) {
dispatch ( {
type : 'DIALOG_CLOSE' ,
name : 'syncWizard' ,
} ) ;
}
const onButtonRowClick = useCallback ( ( ) = > {
closeDialog ( props . dispatch ) ;
} , [ props . dispatch ] ) ;
const { height : descriptionHeight } = useElementSize ( joplinCloudDescriptionRef ) ;
function renderFeature ( enabled : boolean , label : string ) {
const className = enabled ? 'fas fa-check' : 'fas fa-times' ;
return (
< FeatureLine enabled = { enabled } key = { label } > < FeatureIcon className = { className } > < / FeatureIcon > < FeatureLabel > { label } < / FeatureLabel > < / FeatureLine >
) ;
}
function renderFeatures ( name : string ) {
return (
< FeatureList >
{ [
renderFeature ( true , _ ( 'Sync your notes' ) ) ,
renderFeature ( name === 'joplinCloud' , _ ( 'Publish notes to the internet' ) ) ,
renderFeature ( name === 'joplinCloud' , _ ( 'Collaborate on notebooks with others' ) ) ,
] }
< / FeatureList >
) ;
}
const onJoplinCloudEmailChange = useCallback ( ( event : any ) = > {
setJoplinCloudEmail ( event . target . value ) ;
} , [ ] ) ;
const onJoplinCloudPasswordChange = useCallback ( ( event : any ) = > {
setJoplinCloudPassword ( event . target . value ) ;
} , [ ] ) ;
const onJoplinCloudLoginClick = useCallback ( async ( ) = > {
setJoplinCloudLoginInProgress ( true ) ;
2022-02-11 13:04:10 +02:00
let result = null ;
2021-08-16 16:20:14 +02:00
try {
2022-02-11 13:04:10 +02:00
result = await SyncTargetJoplinCloud . checkConfig ( {
2021-08-16 16:20:14 +02:00
password : ( ) = > joplinCloudPassword ,
path : ( ) = > Setting . value ( 'sync.10.path' ) ,
userContentPath : ( ) = > Setting . value ( 'sync.10.userContentPath' ) ,
username : ( ) = > joplinCloudEmail ,
} ) ;
2022-02-11 13:04:10 +02:00
} finally {
setJoplinCloudLoginInProgress ( false ) ;
}
2021-08-16 16:20:14 +02:00
2022-02-11 13:04:10 +02:00
if ( result . ok ) {
Setting . setValue ( 'sync.target' , 10 ) ;
Setting . setValue ( 'sync.10.username' , joplinCloudEmail ) ;
Setting . setValue ( 'sync.10.password' , joplinCloudPassword ) ;
await Setting . saveAll ( ) ;
2021-08-16 16:20:14 +02:00
2022-02-11 13:04:10 +02:00
alert ( _ ( 'Thank you! Your Joplin Cloud account is now setup and ready to use.' ) ) ;
2021-08-16 16:20:14 +02:00
2022-02-11 13:04:10 +02:00
closeDialog ( props . dispatch ) ;
2021-08-16 16:20:14 +02:00
2022-02-11 13:04:10 +02:00
props . dispatch ( {
type : 'NAV_GO' ,
routeName : 'Main' ,
} ) ;
} else {
alert ( _ ( 'There was an error setting up your Joplin Cloud account. Please verify your email and password and try again. Error was:\n\n%s' , result . errorMessage ) ) ;
2021-08-16 16:20:14 +02:00
}
} , [ joplinCloudEmail , joplinCloudPassword , props . dispatch ] ) ;
const onJoplinCloudCreateAccountClick = useCallback ( ( ) = > {
2021-12-28 15:17:59 +02:00
void bridge ( ) . openExternal ( 'https://joplinapp.org/plans/' ) ;
2021-08-16 16:20:14 +02:00
} , [ ] ) ;
function renderJoplinCloudLoginForm() {
return (
< JoplinCloudLoginForm >
2022-02-11 13:04:10 +02:00
< div style = { { fontSize : '16px' } } > { _ ( 'Login below.' ) } < CreateAccountLink href = "#" onClick = { onJoplinCloudCreateAccountClick } > { _ ( 'Or create an account.' ) } < / CreateAccountLink > < / div >
< FormLabel > { _ ( 'Email' ) } < / FormLabel >
2021-08-16 16:20:14 +02:00
< StyledInput type = "email" onChange = { onJoplinCloudEmailChange } / >
2022-02-11 13:04:10 +02:00
< FormLabel > { _ ( 'Password' ) } < / FormLabel >
2021-08-16 16:20:14 +02:00
< StyledInput type = "password" onChange = { onJoplinCloudPasswordChange } / >
< SelectButton mt = "1.3em" disabled = { joplinCloudLoginInProgress } level = { ButtonLevel . Primary } title = { _ ( 'Login' ) } onClick = { onJoplinCloudLoginClick } / >
< / JoplinCloudLoginForm >
) ;
}
const onSelectButtonClick = useCallback ( async ( name : string ) = > {
if ( name === 'joplinCloud' ) {
setShowJoplinCloudForm ( true ) ;
} else {
Setting . setValue ( 'sync.target' , name === 'dropbox' ? 7 : 3 ) ;
await Setting . saveAll ( ) ;
closeDialog ( props . dispatch ) ;
props . dispatch ( {
type : 'NAV_GO' ,
routeName : name === 'dropbox' ? 'DropboxLogin' : 'OneDriveLogin' ,
} ) ;
}
} , [ props . dispatch ] ) ;
function renderSelectArea ( info : SyncTargetInfo ) {
if ( info . name === 'joplinCloud' && showJoplinCloudForm ) {
return renderJoplinCloudLoginForm ( ) ;
} else {
return (
< SelectButton
level = { ButtonLevel . Primary }
title = { _ ( 'Select' ) }
onClick = { ( ) = > onSelectButtonClick ( info . name ) }
disabled = { joplinCloudLoginInProgress }
/ >
) ;
}
}
function renderSyncTarget ( info : SyncTargetInfo ) {
const key = ` syncTarget_ ${ info . name } ` ;
const height = info . name !== 'joplinCloud' ? descriptionHeight : null ;
const logoImageName = logosImageNames [ info . name ] ;
2021-09-20 17:29:05 +02:00
const logoImageSrc = logoImageName ? ` ${ bridge ( ) . buildDir ( ) } /images/ ${ logoImageName } ` : '' ;
2021-08-16 16:20:14 +02:00
const logo = logoImageSrc ? < SyncTargetLogo src = { logoImageSrc } / > : null ;
const descriptionComp = < SyncTargetDescription height = { height } ref = { info . name === 'joplinCloud' ? joplinCloudDescriptionRef : null } > { info . description } < / SyncTargetDescription > ;
const featuresComp = showJoplinCloudForm && info . name === 'joplinCloud' ? null : renderFeatures ( info . name ) ;
2023-12-24 13:06:28 +02:00
const renderSlowSyncWarning = ( ) = > {
if ( info . name === 'joplinCloud' ) return null ;
return < SlowSyncWarning > { ` ⚠️ ${ _ ( '%s is not optimised for synchronising many small files so your initial synchronisation will be slow.' , info . label ) } ` } < / SlowSyncWarning > ;
} ;
2021-08-16 16:20:14 +02:00
return (
< SyncTargetBox id = { key } key = { key } faded = { showJoplinCloudForm && info . name !== 'joplinCloud' } >
< SyncTargetTitle > { logo } { info . label } < / SyncTargetTitle >
{ descriptionComp }
{ featuresComp }
{ renderSelectArea ( info ) }
2023-12-24 13:06:28 +02:00
{ renderSlowSyncWarning ( ) }
2021-08-16 16:20:14 +02:00
< / SyncTargetBox >
) ;
}
const onSelfHostingClick = useCallback ( ( ) = > {
closeDialog ( props . dispatch ) ;
props . dispatch ( {
type : 'NAV_GO' ,
routeName : 'Config' ,
props : {
defaultSection : 'sync' ,
} ,
} ) ;
} , [ props . dispatch ] ) ;
function renderContent() {
const boxes : any [ ] = [ ] ;
for ( const name of syncTargetNames ) {
const info = SyncTargetRegistry . infoByName ( name ) ;
if ( info . supportsSelfHosted ) continue ;
boxes . push ( renderSyncTarget ( info ) ) ;
}
const selfHostingMessage = showJoplinCloudForm ? null : < SelfHostingMessage > Self - hosting ? Joplin also supports various self - hosting options such as Nextcloud , WebDAV , AWS S3 and Joplin Server . < a href = "#" onClick = { onSelfHostingClick } > Click here to select one < / a > . < / SelfHostingMessage > ;
return (
< ContentRoot >
< SyncTargetBoxes >
{ boxes }
< / SyncTargetBoxes >
{ selfHostingMessage }
< / ContentRoot >
) ;
}
function renderDialogWrapper() {
return (
< StyledRoot >
< DialogTitle title = { _ ( 'Joplin can synchronise your notes using various providers. Select one from the list below.' ) } justifyContent = "center" / >
{ renderContent ( ) }
< DialogButtonRow
themeId = { props . themeId }
onClick = { onButtonRowClick }
okButtonShow = { false }
cancelButtonLabel = { _ ( 'Close' ) }
/ >
< / StyledRoot >
) ;
}
return (
< Dialog renderContent = { renderDialogWrapper } / >
) ;
}