diff --git a/browser/ui/webui/brave_webui_source.cc b/browser/ui/webui/brave_webui_source.cc index c8786e827d5d..3d0019485908 100644 --- a/browser/ui/webui/brave_webui_source.cc +++ b/browser/ui/webui/brave_webui_source.cc @@ -424,53 +424,78 @@ void CustomizeWebUIHTMLSource(const std::string &name, content::WebUIDataSource* } }, { std::string("sync"), { - { "sync", IDS_BRAVE_SYNC_TITLE }, - { "beta", IDS_BRAVE_SYNC_TITLE_BETA }, - { "syncInfo1", IDS_BRAVE_SYNC_INFO_1 }, - { "syncInfo2", IDS_BRAVE_SYNC_INFO_2 }, - { "done", IDS_BRAVE_SYNC_DONE }, - { "cancel", IDS_BRAVE_SYNC_CANCEL }, - { "areYouSure", IDS_BRAVE_SYNC_ARE_YOU_SURE }, - - { "iAmNewToSync", IDS_BRAVE_SYNC_I_AM_NEW_TO_SYNC }, - { "iHaveAnExistingSyncCode", IDS_BRAVE_SYNC_I_HAVE_AN_EXISTING_SYNC_CODE }, - - { "devices", IDS_BRAVE_SYNC_DEVICES }, - { "deviceName", IDS_BRAVE_SYNC_DEVICE_NAME }, - { "id", IDS_BRAVE_SYNC_ID }, - { "lastActive", IDS_BRAVE_SYNC_LAST_ACTIVE }, - { "syncANewDevice", IDS_BRAVE_SYNC_NEW_DEVICE }, - { "syncData", IDS_BRAVE_SYNC_DATA }, - { "syncDataInfo", IDS_BRAVE_SYNC_DATA_INFO }, - { "bookmarks", IDS_BRAVE_SYNC_BOOKMARKS }, - { "savedSiteSettings", IDS_BRAVE_SYNC_SAVED_SITE_SETTINGS }, - { "browsingHistory", IDS_BRAVE_SYNC_BROWSING_HISTORY }, - { "clearData", IDS_BRAVE_SYNC_CLEAR_DATA }, - { "resetSync", IDS_BRAVE_SYNC_RESET_SYNC }, - { "removeDevice", IDS_BRAVE_SYNC_REMOVE_DEVICE }, - - { "setUpSync", IDS_BRAVE_SYNC_SET_UP_SYNC }, - - { "enterYourSyncCodeWords", IDS_BRAVE_SYNC_ENTER_SYNC_CODE_WORDS }, - { "enterAnOptionalName", IDS_BRAVE_SYNC_ENTER_OPTIONAL_NAME_FOR_THIS_DEVICE }, - - { "syncANewDeviceFirstBulletText", IDS_BRAVE_SYNC_SYNC_NEW_DEVICE_FIRST_BULLET }, - { "syncANewDeviceSecondBulletText", IDS_BRAVE_SYNC_SYNC_NEW_DEVICE_SECOND_BULLET }, - { "syncANewDeviceThirdBulletText", IDS_BRAVE_SYNC_SYNC_NEW_DEVICE_THIRD_BULLET }, - { "showSecretQRCode", IDS_BRAVE_SYNC_SHOW_QR_CODE }, - { "showSecretCodeWords", IDS_BRAVE_SYNC_SHOW_SECRET_CODE_WORDS }, - - { "resetSyncFirstBullet", IDS_BRAVE_SYNC_RESET_SYNC_FIRST_BULLET }, - { "resetSyncSecondBullet", IDS_BRAVE_SYNC_RESET_SYNC_SECOND_BULLET }, - { "resetSyncThirdBullet", IDS_BRAVE_SYNC_RESET_SYNC_THIRD_BULLET }, - + { "done", IDS_BRAVE_SYNC_SHARED_DONE_BUTTON }, + { "remove", IDS_BRAVE_SYNC_SHARED_REMOVE_PARTIAL }, + { "copied", IDS_BRAVE_SYNC_SHARED_COPIED_TEXT }, + { "wordCount", IDS_BRAVE_SYNC_SHARED_WORD_COUNT_TEXT }, + { "ok", IDS_BRAVE_SYNC_SHARED_OK_BUTTON }, + { "cancel", IDS_BRAVE_SYNC_SHARED_CANCEL_BUTTON }, + { "thisSyncChain", IDS_BRAVE_SYNC_SHARED_FROM_THIS_CHAIN_PARTIAL }, + // Enabled Content + { "braveSync", IDS_BRAVE_SYNC_ENABLED_BRAVE_TITLE }, + { "syncChainDevices", IDS_BRAVE_SYNC_ENABLED_DEVICES_CHAIN_TITLE }, + { "deviceName", IDS_BRAVE_SYNC_ENABLED_TABLE_DEVICE_NAME_TITLE }, + { "mainDevice", IDS_BRAVE_SYNC_ENABLED_TABLE_MAIN_DEVICE_TEXT }, + { "addedOn", IDS_BRAVE_SYNC_ENABLED_TABLE_ADDED_ON_TITLE }, + { "addDevice", IDS_BRAVE_SYNC_ENABLED_ADD_DEVICE_BUTTON }, + { "viewSyncCode", IDS_BRAVE_SYNC_ENABLED_VIEW_CODE_BUTTON }, + { "dataToSync", IDS_BRAVE_SYNC_ENABLED_DATA_TITLE }, + { "bookmarks", IDS_BRAVE_SYNC_ENABLED_BOOKMARKS_LABEL }, + { "savedSiteSettings", IDS_BRAVE_SYNC_ENABLED_SITE_SETTINGS_LABEL }, + { "browsingHistory", IDS_BRAVE_SYNC_ENABLED_HISTORY_LABEL }, + { "leaveSyncChain", IDS_BRAVE_SYNC_ENABLED_LEAVE_CHAIN_BUTTON }, + // Disabled Content + { "syncTitle", IDS_BRAVE_SYNC_DISABLED_DESCRIPTION }, + { "syncDescription", IDS_BRAVE_SYNC_DISABLED_NEW_CHAIN_DESCRIPTION }, + { "startSyncChain", IDS_BRAVE_SYNC_DISABLED_START_NEW_CHAIN_BUTTON }, + { "enterSyncChainCode", IDS_BRAVE_SYNC_DISABLED_ENTER_CODE_BUTTON }, + { "confirmSyncCode", IDS_BRAVE_SYNC_DISABLED_CONFIRM_CODE_BUTTON }, + // [modal] Enter Sync Code + { "enterSyncCode", IDS_BRAVE_SYNC_ENTER_CODE_TITLE }, + { "enterSyncCodeDescription", IDS_BRAVE_SYNC_ENTER_CODE_DESCRIPTION }, + { "confirmCode", IDS_BRAVE_SYNC_ENTER_CODE_CONFIRM_CODE_BUTTON }, + // [modal] Remove Main Device + { "thisDeviceRemovalDescription", IDS_BRAVE_SYNC_REMOVE_MAIN_DEVICE_DESCRIPTION }, + { "joinSyncChain", IDS_BRAVE_SYNC_REMOVE_MAIN_DEVICE_JOIN_CHAIN_INSTRUCTIONS }, + // [modal] Remove Other Device + { "otherDeviceRemovalDescription", IDS_BRAVE_SYNC_REMOVE_OTHER_DEVICE_DESCRIPTION }, + // [modal] Reset Sync + { "warning", IDS_BRAVE_SYNC_RESET_WARNING_TITLE }, + { "removing", IDS_BRAVE_SYNC_RESET_REMOVING_PARTIAL }, + { "deleteSyncChain", IDS_BRAVE_SYNC_RESET_DELETE_CHAIN_PARTIAL }, + { "deleteSyncDescription", IDS_BRAVE_SYNC_RESET_REMOVAL_INSTRUCTIONS }, + { "startSyncChainHowTo", IDS_BRAVE_SYNC_RESET_START_NEW_INSTRUCTIONS }, + { "areYouSure", IDS_BRAVE_SYNC_RESET_ARE_YOU_SURE_TITLE }, + // [modal] Add New Chain + { "scanThisCode", IDS_BRAVE_SYNC_NEW_CHAIN_MAIN_DESCRIPTION }, + { "scanThisCodeHowTo", IDS_BRAVE_SYNC_NEW_CHAIN_MAIN_INSTRUCTIONS }, + { "enterCodeWordsInstead", IDS_BRAVE_SYNC_NEW_CHAIN_MAIN_CODE_WORDS_BUTTON }, + { "previous", IDS_BRAVE_SYNC_NEW_CHAIN_MAIN_PREVIOUS_BUTTON }, + { "lookingForDevice", IDS_BRAVE_SYNC_NEW_CHAIN_MAIN_LOOKING_FOR_DEVICE_BUTTON }, + // [modal] Add New Chain (Camera Option) + // [modal] Add New Chain (no Camera) + { "enterThisCode", IDS_BRAVE_SYNC_NEW_CHAIN_DESKTOP_TITLE }, + { "mobileEnterThisCode", IDS_BRAVE_SYNC_NEW_CHAIN_MOBILE_TITLE }, + { "syncChainCodeHowTo", IDS_BRAVE_SYNC_NEW_CHAIN_DESCRIPTION }, + { "useCameraInstead", IDS_BRAVE_SYNC_NEW_CHAIN_USE_CAMERA_BUTTON }, + // [modal] Choose Device Type + { "letsSync", IDS_BRAVE_SYNC_CHOOSE_DEVICE_NEW_DEVICE_TITLE }, + { "chooseDeviceType", IDS_BRAVE_SYNC_CHOOSE_DEVICE_CHOOSE_DEVICE_TITLE }, + { "phoneTablet", IDS_BRAVE_SYNC_CHOOSE_DEVICE_MOBILE_TITLE }, + { "computer", IDS_BRAVE_SYNC_CHOOSE_DEVICE_COMPUTER_TITLE }, + // [modal] View Sync Code + { "qrCode", IDS_BRAVE_SYNC_VIEW_CODE_QR_CODE_TITLE }, + { "wordCode", IDS_BRAVE_SYNC_VIEW_CODE_WORD_CODE_TITLE }, + { "privateKey", IDS_BRAVE_SYNC_VIEW_CODE_PRIVATE_KEY_PHRASE }, + // errors { "errorWrongCodeTitle", IDS_BRAVE_SYNC_ERROR_WRONG_CODE_TITLE }, { "errorWrongCodeDescription", IDS_BRAVE_SYNC_ERROR_WRONG_CODE_DESCRIPTION }, { "errorNoInternetTitle", IDS_BRAVE_SYNC_ERROR_NO_INTERNET_TITLE }, { "errorNoInternetDescription", IDS_BRAVE_SYNC_ERROR_NO_INTERNET_DESCRIPTION }, { "errorMissingDeviceNameTitle", IDS_BRAVE_SYNC_ERROR_MISSING_DEVICE_NAME_TITLE }, { "errorMissingCodeTitle", IDS_BRAVE_SYNC_ERROR_MISSING_SYNC_CODE_TITLE }, - { "ok", IDS_BRAVE_SYNC_OK_BUTTON }, + { "errorSyncInitFailedTitle", IDS_BRAVE_SYNC_ERROR_INIT_FAILED_TITLE }, + { "errorSyncInitFailedDescription", IDS_BRAVE_SYNC_ERROR_INIT_FAILED_DESCRIPTION }, } }, { std::string("adblock"), { diff --git a/components/brave_sync/extension/background.js b/components/brave_sync/extension/background.js index d16432dfb4db..9f75aceb0041 100644 --- a/components/brave_sync/extension/background.js +++ b/components/brave_sync/extension/background.js @@ -231,8 +231,7 @@ class InjectedObject { break; case "sync-setup-error": console.log(`"sync-setup-error" error=${JSON.stringify(arg1)}`); - // TODO(cezaraugusto): ERR_SYNC_INIT_FAILED without arg in #971 - chrome.braveSync.syncSetupError(arg1/*error*/); + chrome.braveSync.syncSetupError('ERR_SYNC_INIT_FAILED'); break; case "sync-debug": console.log(`"sync-debug" message=${JSON.stringify(arg1)}`); diff --git a/components/brave_sync/ui/BUILD.gn b/components/brave_sync/ui/BUILD.gn index a6e6136b1c06..c1f015272312 100644 --- a/components/brave_sync/ui/BUILD.gn +++ b/components/brave_sync/ui/BUILD.gn @@ -3,10 +3,14 @@ import("//brave/components/common/typescript.gni") transpile_web_ui("ui") { inputs = [ "actions/sync_actions.ts", - "components/modals/existingSyncCode.tsx", - "components/modals/newToSync.tsx", + "components/modals/addNewChainCameraOption.tsx", + "components/modals/addNewChainNoCamera.tsx", + "components/modals/deviceType.tsx", + "components/modals/enterSyncCode.tsx", + "components/modals/removeDevice.tsx", "components/modals/resetSync.tsx", - "components/modals/syncNewDevice.tsx", + "components/modals/scanCode.tsx", + "components/modals/viewSyncCode.tsx", "components/app.tsx", "components/disabledContent.tsx", "components/enabledContent.tsx", diff --git a/components/brave_sync/ui/components/app.tsx b/components/brave_sync/ui/components/app.tsx index e9b19cc80214..479ec0687cb9 100644 --- a/components/brave_sync/ui/components/app.tsx +++ b/components/brave_sync/ui/components/app.tsx @@ -6,22 +6,11 @@ import * as React from 'react' import { bindActionCreators, Dispatch } from 'redux' import { connect } from 'react-redux' -// Feature-specific components -import { - Main, - Title, - EmphasisText, - SecondaryText, - Link, - SectionBlock -} from 'brave-ui/features/sync' - // Component groups import DisabledContent from './disabledContent' import EnabledContent from './enabledContent' // Utils -import { getLocale } from '../../../common/locale' import * as syncActions from '../actions/sync_actions' // Assets @@ -34,26 +23,7 @@ interface Props { actions: any } -interface State { - deviceName: string - showQRCode: boolean - showSyncWords: boolean - syncWords: string -} - -const syncLink = 'https://github.com/brave/sync/wiki/Design' - -export class SyncPage extends React.PureComponent { - constructor (props: Props) { - super(props) - this.state = { - deviceName: '', - showQRCode: false, - showSyncWords: false, - syncWords: '' - } - } - +export class SyncPage extends React.PureComponent { componentDidMount () { // Inform the back-end that Sync can be loaded syncActions.onPageLoaded() @@ -61,26 +31,18 @@ export class SyncPage extends React.PureComponent { render () { const { syncData, actions } = this.props + if (!syncData) { return null } + return (
-
- {getLocale('sync')} - - {getLocale('syncInfo1')} - ? - - {getLocale('syncInfo2')} - - { - syncData.isSyncConfigured - ? - : - } - -
+ { + syncData.isSyncConfigured && syncData.devices.length > 1 + ? + : + }
) } diff --git a/components/brave_sync/ui/components/disabledContent.tsx b/components/brave_sync/ui/components/disabledContent.tsx index 3d23f0588354..9570857c827d 100644 --- a/components/brave_sync/ui/components/disabledContent.tsx +++ b/components/brave_sync/ui/components/disabledContent.tsx @@ -8,27 +8,37 @@ import * as React from 'react' import { Button } from 'brave-ui' // Component-specific components -import { Grid } from 'brave-ui/features/sync' +import { + Main, + Title, + SectionBlock, + DisabledContentButtonGrid, + TableGrid, + Paragraph +} from 'brave-ui/features/sync' + +import { SyncStartIcon } from 'brave-ui/features/sync/images' // Modals -import NewToSyncModal from './modals/newToSync' -import ExistingSyncCodeModal from './modals/existingSyncCode' +import DeviceTypeModal from './modals/deviceType' +import EnterSyncCodeModal from './modals/enterSyncCode' // Utils import { getLocale } from '../../../common/locale' +import { getDefaultDeviceName } from '../helpers' -interface SyncDisabledContentProps { +interface Props { syncData: Sync.State actions: any } -interface SyncDisabledContentState { +interface State { newToSync: boolean existingSyncCode: boolean } -class SyncDisabledContent extends React.PureComponent { - constructor (props: SyncDisabledContentProps) { +export default class SyncDisabledContent extends React.PureComponent { + constructor (props: Props) { super(props) this.state = { newToSync: false, @@ -36,61 +46,82 @@ class SyncDisabledContent extends React.PureComponent { + componentWillMount () { + // once the screen is rendered, create a sync chain. + // this allow us to request the qr code and sync words immediately + const { thisDeviceName } = this.props.syncData + if (thisDeviceName === '') { + this.props.actions.onSetupNewToSync(getDefaultDeviceName()) + } + } + + componentDidUpdate () { + // once this screen is rendered and component is updated, + // request sync qr code and words so it can be seen immediately + // upon user request via "start a new sync chain" button. + const { seedQRImageSource, syncWords } = this.props.syncData + if (!seedQRImageSource && !syncWords) { + this.props.actions.onRequestQRCode() + this.props.actions.onRequestSyncWords() + } + } + + onClickNewSyncChainButton = () => { this.setState({ newToSync: !this.state.newToSync }) } - existingSyncCodeModal = () => { + onClickEnterSyncChainCodeButton = () => { this.setState({ existingSyncCode: !this.state.existingSyncCode }) } render () { const { actions, syncData } = this.props + const { newToSync, existingSyncCode } = this.state + + if (!syncData) { + return null + } + return ( - +
{ - this.state.newToSync - ? ( - - ) + newToSync + ? : null } { - this.state.existingSyncCode - ? ( - - ) + existingSyncCode + ? : null } -
-
-
-
- + + +
+ {getLocale('syncTitle')} + {getLocale('syncDescription')} + + +
+
+
+
+
+
+
+
+
) } } - -export default SyncDisabledContent diff --git a/components/brave_sync/ui/components/enabledContent.tsx b/components/brave_sync/ui/components/enabledContent.tsx index a6d66300bf88..358b511cd090 100644 --- a/components/brave_sync/ui/components/enabledContent.tsx +++ b/components/brave_sync/ui/components/enabledContent.tsx @@ -6,41 +6,68 @@ import * as React from 'react' // Components import { Button } from 'brave-ui' -import SwitchButton from 'brave-ui/old/switchButton' import Table, { Cell, Row } from 'brave-ui/components/dataTables/table' +import { Toggle } from 'brave-ui/features/shields' // Feature-specific components import { - Grid, + Main, + Title, + SettingsToggleGrid, SwitchLabel, - Paragraph, SectionBlock, - SubTitle + SubTitle, + TableRowDevice, + TableRowRemove, + TableRowRemoveButton, + TableGrid, + TableButtonGrid } from 'brave-ui/features/sync' // Modals -import SyncANewDeviceModal from './modals/syncNewDevice' +import RemoveDeviceModal from './modals/removeDevice' +import ViewSyncCodeModal from './modals/viewSyncCode' +import DeviceTypeModal from './modals/deviceType' import ResetSyncModal from './modals/resetSync' // Utils import { getLocale } from '../../../common/locale' -interface SyncEnabledContentProps { +interface Props { syncData: Sync.State actions: any } -interface SyncEnabledContentState { - syncANewDevice: boolean +interface State { + removeDevice: boolean + viewSyncCode: boolean + addDevice: boolean resetSync: boolean + deviceToRemoveName: string | undefined + deviceToRemoveId: string | undefined } -class SyncEnabledContent extends React.PureComponent { - constructor (props: SyncEnabledContentProps) { +export default class SyncEnabledContent extends React.PureComponent { + constructor (props: Props) { super(props) this.state = { - syncANewDevice: false, - resetSync: false + removeDevice: false, + viewSyncCode: false, + addDevice: false, + resetSync: false, + deviceToRemoveName: '', + deviceToRemoveId: '' + } + } + + componentDidUpdate () { + // immediately request qr code and sync words + // in case they aren't already. this could happen if user + // had the sync word where the requests are stopped due to sync reset + const { seedQRImageSource, syncWords } = this.props.syncData + if (!seedQRImageSource && !syncWords) { + this.props.actions.onRequestQRCode() + this.props.actions.onRequestSyncWords() } } @@ -52,21 +79,23 @@ class SyncEnabledContent extends React.PureComponent { const cell: Row = { content: [ - { content: device.id }, - { content: device.name }, + { content: + + {device.name} {Number(device.id) === 0 ? getLocale('mainDevice') : null } + + }, { content: device.lastActive }, { content: ( - - × - - ), - customStyle: { 'text-align': 'center' } + × + + ) } ] } @@ -76,83 +105,111 @@ class SyncEnabledContent extends React.PureComponent{getLocale('deviceName')} }, + { content: getLocale('addedOn') }, + { content: {getLocale('remove')} } ] } - onRemoveDevice = (event: React.MouseEvent) => { - const target = event.target as HTMLSpanElement - this.props.actions.onRemoveDevice(Number(target.dataset.id), target.dataset.name) + onClickRemoveDeviceButton = (event: React.MouseEvent) => { + if (!event || !event.currentTarget || !event.currentTarget.dataset) { + return + } + const target = event.currentTarget as HTMLButtonElement + this.setState({ + deviceToRemoveName: target.dataset.name, + deviceToRemoveId: target.dataset.id, + removeDevice: !this.state.removeDevice + }) } - syncANewDeviceModal = () => { - this.setState({ syncANewDevice: !this.state.syncANewDevice }) + onClickViewSyncCodeButton = () => { + this.setState({ viewSyncCode: !this.state.viewSyncCode }) } - resetSyncModal = () => { - this.setState({ resetSync: !this.state.resetSync }) + onClickAddDeviceButton = () => { + this.setState({ addDevice: !this.state.addDevice }) } - onSyncReset = () => { - if (window.confirm(getLocale('areYouSure'))) { - this.props.actions.onSyncReset() - } + onClickResetSyncButton = () => { + this.setState({ resetSync: !this.state.resetSync }) } onSyncBookmarks = (event: React.ChangeEvent) => { this.props.actions.onSyncBookmarks(event.target.checked) } - onSyncSavedSiteSettings = (event: React.ChangeEvent) => { - this.props.actions.onSyncSavedSiteSettings(event.target.checked) - } - - onSyncBrowsingHistory = (event: React.ChangeEvent) => { - this.props.actions.onSyncBrowsingHistory(event.target.checked) - } - render () { const { actions, syncData } = this.props + const { + removeDevice, + viewSyncCode, + addDevice, + resetSync, + deviceToRemoveName, + deviceToRemoveId + } = this.state + + if (!syncData) { + return null + } + return ( - <> +
{ - this.state.syncANewDevice - ? ( - - ) : null } { - this.state.resetSync - ? + viewSyncCode + ? : null } + { + addDevice + ? + : null + } + { + resetSync + ? + : null + } + {getLocale('braveSync')} - {getLocale('devices')} - - Device list is empty -
-
) } } - -export default SyncEnabledContent diff --git a/components/brave_sync/ui/components/modals/addNewChainCameraOption.tsx b/components/brave_sync/ui/components/modals/addNewChainCameraOption.tsx new file mode 100644 index 000000000000..b88e71168233 --- /dev/null +++ b/components/brave_sync/ui/components/modals/addNewChainCameraOption.tsx @@ -0,0 +1,124 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License. v. 2.0. If a copy of the MPL was not distributed with this file. + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import * as React from 'react' + +// Components +import { Button, Modal, TextAreaClipboard } from 'brave-ui' + +// Feature-specific components +import { + ModalHeader, + ModalTitle, + ModalSubTitle, + ModalContent, + ThreeColumnButtonGrid, + ThreeColumnButtonGridCol2, + ThreeColumnButtonGridCol1 +} from 'brave-ui/features/sync' + +// Modals +import ScanCodeModal from './scanCode' + +// Utils +import { getLocale } from '../../../../common/locale' + +// Images +import { SyncAddIcon } from 'brave-ui/features/sync/images' + +interface Props { + fromMobileScreen?: boolean + syncData: Sync.State + actions: any + onClose: () => void +} + +interface State { + useCameraInstead: boolean +} + +export default class AddNewChainCameraOptionModal extends React.PureComponent { + constructor (props: Props) { + super(props) + this.state = { + useCameraInstead: false + } + } + + onClickUseCameraInsteadButton = () => { + this.setState({ useCameraInstead: !this.state.useCameraInstead }) + } + + render () { + const { fromMobileScreen, onClose, syncData, actions } = this.props + const { useCameraInstead } = this.state + + if (!syncData) { + return null + } + + return ( + + { + useCameraInstead + ? + : null + } + + +
+ + { + fromMobileScreen + ? getLocale('mobileEnterThisCode') + : getLocale('enterThisCode') + } + + {getLocale('syncChainCodeHowTo')} +
+
+ + + + + +