import * as React from 'react'; import { 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 Setting from '@joplin/lib/models/Setting'; interface Props { themeId: number; // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied dispatch: Function; } const StyledRoot = styled.div` min-width: 500px; max-width: 1200px; `; const SyncTargetDescription = styled.div<{ height: number }>` ${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; `; const SyncTargetBox = styled.div` 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; padding: 2em 2.2em 2em 2.2em; margin-right: 1em; max-width: 400px; opacity: 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; `; const FeatureLine = styled.div<{ enabled: boolean }>` 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 10px; height: auto; min-height: auto; max-height: fit-content; font-size: 1em; `; const SlowSyncWarning = styled.div` margin-top: 1em; opacity: 0.8; font-family: ${props => props.theme.fontFamily}; color: ${props => props.theme.color}; font-size: 14px; `; const syncTargetNames: string[] = [ 'joplinCloud', 'dropbox', 'onedrive', 'nextcloud', 'webdav', 'amazon_s3', 'joplinServer', ]; const logosImageNames: Record = { 'dropbox': 'SyncTarget_Dropbox.svg', 'joplinCloud': 'SyncTarget_JoplinCloud.svg', 'onedrive': 'SyncTarget_OneDrive.svg', }; type SyncTargetInfoName = 'dropbox' | 'onedrive' | 'joplinCloud'; export default function(props: Props) { const joplinCloudDescriptionRef = useRef(null); // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied 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 ( {label} ); } function renderFeatures(name: string) { return ( {[ renderFeature(true, _('Sync your notes')), renderFeature(name === 'joplinCloud', _('Publish notes to the internet')), renderFeature(name === 'joplinCloud', _('Collaborate on notebooks with others')), ]} ); } 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?? Setting.setValue('sync.target', route.target); await Setting.saveAll(); closeDialog(props.dispatch); props.dispatch({ type: 'NAV_GO', routeName: route.name, }); }, [props.dispatch]); function renderSelectArea(info: SyncTargetInfo) { return ( onSelectButtonClick(info.name as SyncTargetInfoName)} disabled={false} /> ); } function renderSyncTarget(info: SyncTargetInfo) { const key = `syncTarget_${info.name}`; const height = info.name !== 'joplinCloud' ? descriptionHeight : null; const logoImageName = logosImageNames[info.name]; const logoImageSrc = logoImageName ? `${bridge().buildDir()}/images/${logoImageName}` : ''; const logo = logoImageSrc ? : null; const descriptionComp = {info.description}; const featuresComp = renderFeatures(info.name); const renderSlowSyncWarning = () => { if (info.name === 'joplinCloud') return null; return {`⚠️ ${_('%s is not optimised for synchronising many small files so your initial synchronisation will be slow.', info.label)}`}; }; return ( {logo}{info.label} {descriptionComp} {featuresComp} {renderSelectArea(info)} {renderSlowSyncWarning()} ); } const onSelfHostingClick = useCallback(() => { closeDialog(props.dispatch); props.dispatch({ type: 'NAV_GO', routeName: 'Config', props: { defaultSection: 'sync', }, }); }, [props.dispatch]); function renderContent() { // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied const boxes: any[] = []; for (const name of syncTargetNames) { const info = SyncTargetRegistry.infoByName(name); if (info.supportsSelfHosted) continue; boxes.push(renderSyncTarget(info)); } const selfHostingMessage = Self-hosting? Joplin also supports various self-hosting options such as Nextcloud, WebDAV, AWS S3 and Joplin Server. Click here to select one.; return ( {boxes} {selfHostingMessage} ); } function renderDialogWrapper() { return ( {renderContent()} ); } return ( ); }