Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

Commit

Permalink
Merge pull request #8112 from brave/feature/sync-devices
Browse files Browse the repository at this point in the history
Sync profile devices list
  • Loading branch information
ayumi committed Apr 11, 2017
2 parents 4f3c6ad + 468e8be commit 01575aa
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 5 deletions.
2 changes: 2 additions & 0 deletions app/extensions/brave/locales/en-US/preferences.properties
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ syncBetaMessage=Note: Sync for iOS and Android is still being completed and will
syncEnable=Sync this device
syncData=Sync Data
syncDataMessage=Sync the following data from this device:
syncDevices=Devices
syncDeviceName=Device name
syncDeviceLastActive=Last active
syncBookmarks=Bookmarks
syncHistory=Browsing history
syncSiteSettings=Saved site settings
Expand Down
36 changes: 35 additions & 1 deletion app/renderer/components/preferences/syncTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const niceware = require('niceware')
const ModalOverlay = require('../../../../js/components/modalOverlay')
const Button = require('../../../../js/components/button')
const {SettingsList, SettingItem, SettingCheckbox} = require('../settings')
const SortableTable = require('../../../../js/components/sortableTable')

const aboutActions = require('../../../../js/about/aboutActions')
const getSetting = require('../../../../js/settings').getSetting
Expand Down Expand Up @@ -82,12 +83,45 @@ class SyncTab extends ImmutableComponent {
</div>
</div>
</div>
{this.enabled ? this.devicesContent : null}
<SettingItem>
<Button l10nId='syncNewDevice' className='whiteButton' onClick={this.props.showOverlay.bind(this, 'syncNewDevice')} />
<Button l10nId='syncNewDevice' className='primaryButton' onClick={this.props.showOverlay.bind(this, 'syncNewDevice')} />
</SettingItem>
</SettingsList>
}

get devicesTableRows () {
const devices = this.props.syncData.get('devices')
if (!devices) { return [] }
return devices.map((device, id) => [
{
html: id,
value: parseInt(id)
},
{
html: device.get('name'),
value: device.get('name')
},
{
html: new Date(device.get('lastRecordTimestamp')).toLocaleString(),
value: device.get('lastRecordTimestamp')
}
])
}

get devicesContent () {
return <div className='devices'>
<div className='sectionTitle' data-l10n-id='syncDevices' data-test-id='syncDevices' />
<SortableTable
headings={['id', 'syncDeviceName', 'syncDeviceLastActive']}
defaultHeading='syncDeviceLastActive'
defaultHeadingSortOrder='desc'
rows={this.devicesTableRows}
tableClassNames='devicesList'
/>
</div>
}

get qrcodeContent () {
if (!this.isSetup) {
return null
Expand Down
1 change: 1 addition & 0 deletions app/sessionStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,7 @@ module.exports.defaultAppState = () => {
return {
firstRunTimestamp: new Date().getTime(),
sync: {
devices: {},
lastFetchTimestamp: 0,
objectsById: {}
},
Expand Down
8 changes: 7 additions & 1 deletion app/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ module.exports.onSyncReady = (isFirstRun, e) => {
}

appActions.createSyncCache()
e.sender.send(messages.FETCH_SYNC_DEVICES)

// Periodically poll for new records
let startAt = appState.getIn(['sync', 'lastFetchTimestamp']) || 0
Expand Down Expand Up @@ -355,20 +356,25 @@ module.exports.init = function (appState) {
}
appActions.setSyncSetupError(error || locale.translation('unknownError'))
})
ipcMain.on(messages.GET_EXISTING_OBJECTS, (event, categoryName, records) => {
ipcMain.on(messages.GET_EXISTING_OBJECTS, (event, categoryName, records, lastRecordTimestamp) => {
if (!syncEnabled()) {
return
}
let devices = {}
log(`getting existing objects for ${records.length} ${categoryName}`)
if (!CATEGORY_NAMES.includes(categoryName) || !records || !records.length) {
return
}
const recordsAndExistingObjects = records.map((record) => {
const safeRecord = syncUtil.ipcSafeObject(record)
const deviceId = syncUtil.deviceIdString(safeRecord.deviceId)
devices[deviceId] = {lastRecordTimestamp: record.syncTimestamp}
const existingObject = syncUtil.getExistingObject(categoryName, record)
return [safeRecord, existingObject]
})
event.sender.send(messages.RESOLVE_SYNC_RECORDS, categoryName, recordsAndExistingObjects)
// For each device we saw, update its last record timestamp.
appActions.saveSyncDevices(devices)
})
ipcMain.on(messages.RESOLVED_SYNC_RECORDS, (event, categoryName, records) => {
if (!records || !records.length) {
Expand Down
12 changes: 12 additions & 0 deletions docs/appActions.md
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,18 @@ Dispatches a message to set objectId for a syncable object.



### saveSyncDevices(devices)

Dispatch to update sync devices cache.
NOTE: deviceId is a string! Normally it's Array.<number> but that can't
be an object key. Use syncUtil.deviceIdToString()

**Parameters**

**devices**: `Object`, {[deviceId]: {lastRecordTimestamp=, name=}}



### saveSyncInitData(seed, deviceId, lastFetchTimestamp, seedQr)

Dispatches a message when sync init data needs to be saved
Expand Down
6 changes: 6 additions & 0 deletions docs/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,12 @@ AppStore
sync: {
lastFetchTimestamp: integer // the last time new sync records were fetched in seconds
deviceId: Array.<number>,
devices: {
[deviceId]: {
name: string,
lastRecordTimestamp: number // last seen Sync record from this device
}
},
objectId: Array.<number>, // objectId for this sync device
objectsById: {
[string of objectId joined by pipes |]: Array.<string> // array key path within appState, so we can do appState.getIn({key path})
Expand Down
13 changes: 13 additions & 0 deletions js/actions/appActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,19 @@ const appActions = {
})
},

/**
* Dispatch to update sync devices cache.
* NOTE: deviceId is a string! Normally it's Array.<number> but that can't
* be an object key. Use syncUtil.deviceIdToString()
* @param {Object} devices {[deviceId]: {lastRecordTimestamp=, name=}}
*/
saveSyncDevices: function (devices) {
AppDispatcher.dispatch({
actionType: appConstants.APP_SAVE_SYNC_DEVICES,
devices
})
},

/**
* Dispatches a message when sync init data needs to be saved
* @param {Array.<number>|null} seed
Expand Down
1 change: 1 addition & 0 deletions js/constants/appConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ const appConstants = {
APP_TAB_CLONED: _,
APP_SET_OBJECT_ID: _,
APP_CREATE_SYNC_CACHE: _,
APP_SAVE_SYNC_DEVICES: _,
APP_SAVE_SYNC_INIT_DATA: _,
APP_RESET_SYNC_DATA: _,
APP_SET_SYNC_SETUP_ERROR: _,
Expand Down
7 changes: 6 additions & 1 deletion js/constants/sync/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ const messages = {
* with new records, do
* GET_EXISTING_OBJECTS -> RESOLVE_SYNC_RECORDS -> RESOLVED_SYNC_RECORDS
*/
FETCH_SYNC_RECORDS: _, /* @param Array.<string> categoryNames, @param {number} startAt (in seconds) */
FETCH_SYNC_RECORDS: _, /* @param Array.<string> categoryNames, @param {number} startAt (in seconds or milliseconds), @param {boolean=} limitResponse true to limit response to 1000 records */
/**
* browser -> webview
* sent to fetch all sync devices. webview responds with RESOLVED_SYNC_RECORDS.
*/
FETCH_SYNC_DEVICES: _,
/**
* webview -> browser
* after sync gets records, it requests the browser's existing objects so sync
Expand Down
15 changes: 14 additions & 1 deletion js/state/syncUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ const pickFields = (object, fields) => {
}, {})
}

/**
* Convert deviceId to a type better suited for object keys.
* @param {Array.<number>} deviceId
* @returns {string}
*/
const deviceIdString = (deviceId) => {
return deviceId.join('|')
}
module.exports.deviceIdString = deviceIdString

// Cache of bookmark folder object IDs mapped to folder IDs
let folderIdMap = new Immutable.Map()

Expand Down Expand Up @@ -191,7 +201,10 @@ const applySyncRecord = (record) => {
applySiteSettingRecord(record)
break
case 'device':
// TODO
const device = Object.assign({}, record.device, {lastRecordTimestamp: record.syncTimestamp})
require('../actions/appActions').saveSyncDevices({
[deviceIdString(record.deviceId)]: device
})
break
default:
throw new Error(`Invalid record objectData: ${record.objectData}`)
Expand Down
12 changes: 12 additions & 0 deletions js/stores/appStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,17 @@ const handleAppAction = (action) => {
action.objectId)
}
break
case appConstants.APP_SAVE_SYNC_DEVICES:
for (let deviceId of Object.keys(action.devices)) {
const device = action.devices[deviceId]
if (device.lastRecordTimestamp) {
appState = appState.setIn(['sync', 'devices', deviceId, 'lastRecordTimestamp'], device.lastRecordTimestamp)
}
if (device.name) {
appState = appState.setIn(['sync', 'devices', deviceId, 'name'], device.name)
}
}
break
case appConstants.APP_SAVE_SYNC_INIT_DATA:
if (action.deviceId) {
appState = appState.setIn(['sync', 'deviceId'], action.deviceId)
Expand Down Expand Up @@ -889,6 +900,7 @@ const handleAppAction = (action) => {
appState = appState.setIn(['sites', key, 'originalSeed'], originalSeed)
}
})
appState.setIn(['sync', 'devices'], {})
appState.setIn(['sync', 'objectsById'], {})
break
case appConstants.APP_SHOW_DOWNLOAD_DELETE_CONFIRMATION:
Expand Down
15 changes: 14 additions & 1 deletion less/about/preferences.less
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ table.sortableTable {
line-height: 1.8em;
margin: 1em 0;
padding: 0.5em 1em;
width: 500px;
width: 32em;
& > div, .settingItem {
display: table-cell;
vertical-align: middle;
Expand All @@ -665,6 +665,19 @@ table.sortableTable {
margin-bottom: 0.5em;
}
}
.devices {
margin: 1em 0;
}
.devicesList {
width: 34em;
margin-bottom: 1em;
td {
padding: 4px 8px;
}
}
.settingsListContainer {
margin-bottom: 1.5em;
}
}

#syncPassphrase {
Expand Down

0 comments on commit 01575aa

Please sign in to comment.