2021-08-16 16:20:14 +02:00
import * as React from 'react' ;
2024-03-09 12:35:54 +02:00
import { useRef , useCallback } from 'react' ;
2021-08-16 16:20:14 +02:00
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' ;
2021-08-19 14:01:56 +02:00
import Setting from '@joplin/lib/models/Setting' ;
2021-08-16 16:20:14 +02:00
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 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 ;
` ;
2024-03-09 12:35:54 +02:00
const SyncTargetBox = styled . div `
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 ;
2024-03-09 12:35:54 +02:00
opacity : 1 ;
2021-08-16 16:20:14 +02:00
` ;
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 ;
` ;
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
} ;
2024-03-09 12:35:54 +02:00
type SyncTargetInfoName = 'dropbox' | 'onedrive' | 'joplinCloud' ;
2021-08-16 16:20:14 +02:00
export default function ( props : Props ) {
const joplinCloudDescriptionRef = useRef ( null ) ;
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 >
) ;
}
2024-03-09 12:35:54 +02:00
const onSelectButtonClick = useCallback ( async ( name : SyncTargetInfoName ) = > {
const routes = {
'dropbox' : { name : 'DropboxLogin' , target : 7 } ,
'onedrive' : { name : 'OneDriveLogin' , target : 3 } ,
'joplinCloud' : { name : 'JoplinCloudLogin' , target : 10 } ,
} ;
const route = routes [ name ] ;
if ( ! route ) return ; // throw error??
2021-08-16 16:20:14 +02:00
2024-03-09 12:35:54 +02:00
Setting . setValue ( 'sync.target' , route . target ) ;
await Setting . saveAll ( ) ;
closeDialog ( props . dispatch ) ;
props . dispatch ( {
type : 'NAV_GO' ,
routeName : route.name ,
} ) ;
2021-08-16 16:20:14 +02:00
} , [ props . dispatch ] ) ;
function renderSelectArea ( info : SyncTargetInfo ) {
2024-03-09 12:35:54 +02:00
return (
< SelectButton
level = { ButtonLevel . Primary }
title = { _ ( 'Select' ) }
onClick = { ( ) = > onSelectButtonClick ( info . name as SyncTargetInfoName ) }
disabled = { false }
/ >
) ;
2021-08-16 16:20:14 +02:00
}
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 > ;
2024-03-09 12:35:54 +02:00
const featuresComp = renderFeatures ( info . name ) ;
2021-08-16 16:20:14 +02:00
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 (
2024-03-09 12:35:54 +02:00
< SyncTargetBox id = { key } key = { key } >
2021-08-16 16:20:14 +02:00
< 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() {
2024-04-05 13:16:49 +02:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
2021-08-16 16:20:14 +02:00
const boxes : any [ ] = [ ] ;
for ( const name of syncTargetNames ) {
const info = SyncTargetRegistry . infoByName ( name ) ;
if ( info . supportsSelfHosted ) continue ;
boxes . push ( renderSyncTarget ( info ) ) ;
}
2024-03-09 12:35:54 +02:00
const selfHostingMessage = < 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 > ;
2021-08-16 16:20:14 +02:00
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 } / >
) ;
}