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

Commit 6ef7e4e

Browse files
committed
refactor sync to use appStore change listener
and only trigger sync when important site fields are modified. fix #8415 may also fix brave/sync#31 test plan: 0. automated sync tests should pass 1. enable sync and go to https://example.com 2. bookmark it. you should see a message in the console indicating a record was sent. 3. close the page and visit it again. you should not see a message in the console. 4. unbookmark it. you should see a message in the console indicating a record was sent.
1 parent 02e8e79 commit 6ef7e4e

File tree

10 files changed

+144
-167
lines changed

10 files changed

+144
-167
lines changed

app/browser/reducers/sitesReducer.js

+39-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const Immutable = require('immutable')
1313
const {makeImmutable} = require('../../common/state/immutableUtil')
1414
const settings = require('../../../js/constants/settings')
1515
const {getSetting} = require('../../../js/settings')
16+
const writeActions = require('../../../js/constants/sync/proto').actions
1617

1718
const syncEnabled = () => {
1819
return getSetting(settings.SYNC_ENABLED) === true
@@ -21,17 +22,16 @@ const syncEnabled = () => {
2122
const sitesReducer = (state, action, emitChanges) => {
2223
switch (action.actionType) {
2324
case appConstants.APP_ADD_SITE:
24-
const addSiteSyncCallback = action.skipSync ? undefined : syncActions.updateSite
2525
if (action.siteDetail.constructor === Immutable.List) {
2626
action.siteDetail.forEach((s) => {
27-
state = state.set('sites', siteUtil.addSite(state.get('sites'), s, action.tag, undefined, addSiteSyncCallback))
27+
state = state.set('sites', siteUtil.addSite(state.get('sites'), s, action.tag, undefined, action.skipSync))
2828
})
2929
} else {
3030
let sites = state.get('sites')
3131
if (!action.siteDetail.get('folderId') && siteUtil.isFolder(action.siteDetail)) {
3232
action.siteDetail = action.siteDetail.set('folderId', siteUtil.getNextFolderId(sites))
3333
}
34-
state = state.set('sites', siteUtil.addSite(sites, action.siteDetail, action.tag, action.originalSiteDetail, addSiteSyncCallback))
34+
state = state.set('sites', siteUtil.addSite(sites, action.siteDetail, action.tag, action.originalSiteDetail, action.skipSync))
3535
}
3636
if (action.destinationDetail) {
3737
state = state.set('sites', siteUtil.moveSite(state.get('sites'),
@@ -51,11 +51,46 @@ const sitesReducer = (state, action, emitChanges) => {
5151
case appConstants.APP_MOVE_SITE:
5252
state = state.set('sites', siteUtil.moveSite(state.get('sites'),
5353
action.sourceDetail, action.destinationDetail, action.prepend,
54-
action.destinationIsParent, false, syncActions.updateSite))
54+
action.destinationIsParent, false))
5555
if (syncEnabled()) {
5656
state = syncUtil.updateSiteCache(state, action.destinationDetail)
5757
}
5858
break
59+
case appConstants.APP_APPLY_SITE_RECORDS:
60+
let nextFolderId = siteUtil.getNextFolderId(state.get('sites'))
61+
// Ensure that all folders are assigned folderIds
62+
action.records.forEach((record, i) => {
63+
if (record.action !== writeActions.DELETE &&
64+
record.bookmark && record.bookmark.isFolder &&
65+
record.bookmark.site &&
66+
typeof record.bookmark.site.folderId !== 'number') {
67+
record.bookmark.site.folderId = nextFolderId
68+
action.records.set(i, record)
69+
nextFolderId = nextFolderId + 1
70+
}
71+
})
72+
action.records.forEach((record) => {
73+
const siteData = syncUtil.getSiteDataFromRecord(record, state, action.records)
74+
const tag = siteData.tag
75+
let siteDetail = siteData.siteDetail
76+
const sites = state.get('sites')
77+
switch (record.action) {
78+
case writeActions.CREATE:
79+
state = state.set('sites',
80+
siteUtil.addSite(sites, siteDetail, tag, undefined, true))
81+
break
82+
case writeActions.UPDATE:
83+
state = state.set('sites',
84+
siteUtil.addSite(sites, siteDetail, tag, siteData.existingObjectData, true))
85+
break
86+
case writeActions.DELETE:
87+
state = state.set('sites',
88+
siteUtil.removeSite(sites, siteDetail, tag))
89+
break
90+
}
91+
state = syncUtil.updateSiteCache(state, siteDetail)
92+
})
93+
break
5994
case appConstants.APP_TAB_PINNED:
6095
const tab = state.get('tabs').find((tab) => tab.get('tabId') === action.tabId)
6196
if (!tab) {

app/sync.js

+78-38
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ const CATEGORY_MAP = syncUtil.CATEGORY_MAP
2626
const CATEGORY_NAMES = Object.keys(categories)
2727
const SYNC_ACTIONS = Object.values(syncConstants)
2828

29-
let dispatcherCallback = null
29+
// The sync background script message sender
30+
let backgroundSender = null
3031

3132
const log = (message) => {
3233
if (!config.debug) { return }
@@ -43,6 +44,65 @@ let pollIntervalId = null
4344
let deviceIdSent = false
4445
let bookmarksToolbarShown = false
4546

47+
// Determines what to sync
48+
const appStoreChangeCallback = function (diffs) {
49+
if (!backgroundSender) {
50+
return
51+
}
52+
53+
// Fields that should trigger a sync SEND when changed
54+
const syncFields = {
55+
sites: ['location', 'tags', 'customTitle', 'folderId', 'parentFolderId'],
56+
siteSettings: Object.keys(syncUtil.siteSettingDefaults)
57+
}
58+
diffs.forEach((diff) => {
59+
if (!diff || !diff.path) {
60+
return
61+
}
62+
const path = diff.path.split('/')
63+
if (path.length < 3) {
64+
// We are looking for paths like ['', 'sites', 'https://brave.com/', 'title']
65+
return
66+
}
67+
const type = path[1]
68+
const fieldsToPick = syncFields[type]
69+
if (!fieldsToPick) {
70+
return
71+
}
72+
73+
const isInsert = diff.op === 'add' && path.length === 3
74+
const isUpdate = fieldsToPick.includes(path[3]) // Ignore insignicant updates
75+
76+
// DELETES are handled in appState because the old object is no longer
77+
// available by the time emitChanges is received
78+
// const isDelete = diff.op === 'remove' && path.length === 3
79+
if (isInsert || isUpdate) {
80+
// This item should be synced
81+
const statePath = path.slice(1, 3).map((item) => item.replace(/~1/g, '/'))
82+
const entry = AppStore.getState().getIn(statePath)
83+
if (!entry || !entry.toJS) {
84+
return
85+
}
86+
// Set the object ID if there is not already one
87+
const entryJS = entry.toJS()
88+
entryJS.objectId = entryJS.objectId || syncUtil.newObjectId(statePath)
89+
90+
let action = null
91+
92+
if (isInsert && !entry.get('skipSync')) {
93+
action = writeActions.CREATE
94+
} else if (isUpdate) {
95+
action = writeActions.UPDATE
96+
}
97+
98+
if (action !== null) {
99+
sendSyncRecords(backgroundSender, action,
100+
[type === 'sites' ? syncUtil.createSiteData(entryJS) : syncUtil.createSiteSettingsData(statePath[1], entryJS)])
101+
}
102+
}
103+
})
104+
}
105+
46106
/**
47107
* Sends sync records of the same category to the sync server.
48108
* @param {event.sender} sender
@@ -81,8 +141,7 @@ const sendSyncRecords = (sender, action, data) => {
81141
const validateAction = (action) => {
82142
const SYNC_ACTIONS_WITHOUT_ITEMS = [
83143
syncConstants.SYNC_CLEAR_HISTORY,
84-
syncConstants.SYNC_CLEAR_SITE_SETTINGS,
85-
syncConstants.SYNC_DELETE_USER
144+
syncConstants.SYNC_CLEAR_SITE_SETTINGS
86145
]
87146
if (SYNC_ACTIONS.includes(action.actionType) !== true) {
88147
return false
@@ -103,53 +162,30 @@ const validateAction = (action) => {
103162
return true
104163
}
105164

106-
const doAction = (sender, action) => {
165+
const dispatcherCallback = (action) => {
166+
if (!backgroundSender) {
167+
return
168+
}
169+
107170
if (action.key === settings.SYNC_ENABLED) {
108171
if (action.value === false) {
109172
module.exports.stop()
110173
}
111174
}
112175
// If sync is not enabled, the following actions should be ignored.
113-
if (!syncEnabled() || validateAction(action) !== true || sender.isDestroyed()) {
176+
if (!syncEnabled() || validateAction(action) !== true || backgroundSender.isDestroyed()) {
114177
return
115178
}
116179
switch (action.actionType) {
117-
case syncConstants.SYNC_ADD_SITE:
118-
sendSyncRecords(sender, writeActions.CREATE,
119-
[syncUtil.createSiteData(action.item.toJS())])
120-
break
121-
case syncConstants.SYNC_UPDATE_SITE:
122-
sendSyncRecords(sender, writeActions.UPDATE,
123-
[syncUtil.createSiteData(action.item.toJS())])
124-
break
125180
case syncConstants.SYNC_REMOVE_SITE:
126-
sendSyncRecords(sender, writeActions.DELETE,
181+
sendSyncRecords(backgroundSender, writeActions.DELETE,
127182
[syncUtil.createSiteData(action.item.toJS())])
128183
break
129184
case syncConstants.SYNC_CLEAR_HISTORY:
130-
sender.send(messages.DELETE_SYNC_CATEGORY, CATEGORY_MAP.historySite.categoryName)
131-
break
132-
case syncConstants.SYNC_ADD_SITE_SETTING:
133-
if (syncUtil.isSyncable('siteSetting', action.item)) {
134-
sendSyncRecords(sender, writeActions.CREATE,
135-
[syncUtil.createSiteSettingsData(action.hostPattern, action.item.toJS())])
136-
}
137-
break
138-
case syncConstants.SYNC_UPDATE_SITE_SETTING:
139-
if (syncUtil.isSyncable('siteSetting', action.item)) {
140-
sendSyncRecords(sender, writeActions.UPDATE,
141-
[syncUtil.createSiteSettingsData(action.hostPattern, action.item.toJS())])
142-
}
143-
break
144-
case syncConstants.SYNC_REMOVE_SITE_SETTING:
145-
sendSyncRecords(sender, writeActions.DELETE,
146-
[syncUtil.createSiteSettingsData(action.hostPattern, action.item.toJS())])
185+
backgroundSender.send(messages.DELETE_SYNC_CATEGORY, CATEGORY_MAP.historySite.categoryName)
147186
break
148187
case syncConstants.SYNC_CLEAR_SITE_SETTINGS:
149-
sender.send(messages.DELETE_SYNC_SITE_SETTINGS)
150-
break
151-
case syncConstants.SYNC_DELETE_USER:
152-
sender.send(messages.DELETE_SYNC_USER)
188+
backgroundSender.send(messages.DELETE_SYNC_SITE_SETTINGS)
153189
break
154190
default:
155191
}
@@ -165,6 +201,8 @@ module.exports.onSyncReady = (isFirstRun, e) => {
165201
if (!syncEnabled()) {
166202
return
167203
}
204+
AppStore.addChangeListener(appStoreChangeCallback)
205+
168206
if (!deviceIdSent && isFirstRun) {
169207
// Sync the device id for this device
170208
sendSyncRecords(e.sender, writeActions.CREATE, [{
@@ -283,9 +321,9 @@ module.exports.init = function (appState) {
283321
})
284322
// sent by about:preferences when resetting sync
285323
ipcMain.on(RESET_SYNC, (e) => {
286-
if (dispatcherCallback) {
324+
if (backgroundSender) {
287325
// send DELETE_SYNC_USER to sync client. it replies with DELETED_SYNC_USER
288-
dispatcherCallback({actionType: syncConstants.SYNC_DELETE_USER})
326+
backgroundSender.send(messages.DELETE_SYNC_USER)
289327
} else {
290328
reset()
291329
}
@@ -295,14 +333,15 @@ module.exports.init = function (appState) {
295333
})
296334
// GET_INIT_DATA is the first message sent by the sync-client when it starts
297335
ipcMain.on(messages.GET_INIT_DATA, (e) => {
336+
// Set the message sender
337+
backgroundSender = e.sender
298338
// Clear any old errors
299339
appActions.setSyncSetupError(null)
300340
// Unregister the previous dispatcher cb
301341
if (dispatcherCallback) {
302342
appDispatcher.unregister(dispatcherCallback)
303343
}
304344
// Register the dispatcher callback now that we have a valid sender
305-
dispatcherCallback = doAction.bind(null, e.sender)
306345
appDispatcher.register(dispatcherCallback)
307346
// Send the initial data
308347
if (syncEnabled()) {
@@ -413,4 +452,5 @@ module.exports.stop = function () {
413452
clearInterval(pollIntervalId)
414453
}
415454
appActions.setSyncSetupError(null)
455+
AppStore.removeChangeListener(appStoreChangeCallback)
416456
}

docs/state.md

+2
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ AppStore
233233
originalSeed: Array.<number>, // only set for bookmarks that have been synced before a sync profile reset
234234
parentFolderId: number, // set for bookmarks and bookmark folders only
235235
partitionNumber: number, // optionally specifies a specific session
236+
skipSync: boolean, // Set for objects FETCHed by sync
236237
tags: [string], // empty, 'bookmark', 'bookmark-folder', 'default', or 'reader'
237238
themeColor: string, // CSS compatible color string
238239
title: string
@@ -259,6 +260,7 @@ AppStore
259260
openExternalPermission: boolean,
260261
pointerLockPermission: boolean,
261262
protocolRegistrationPermission: boolean,
263+
skipSync: boolean, // Set for objects FETCHed by sync
262264
runInsecureContent: boolean, // allow active mixed content
263265
safeBrowsing: boolean,
264266
savePasswords: boolean, // only false or undefined/null

js/actions/syncActions.js

-38
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,6 @@ const AppDispatcher = require('../dispatcher/appDispatcher')
77
const syncConstants = require('../constants/syncConstants')
88

99
const syncActions = {
10-
addSite: function (item) {
11-
AppDispatcher.dispatch({
12-
actionType: syncConstants.SYNC_ADD_SITE,
13-
item
14-
})
15-
},
16-
17-
updateSite: function (item) {
18-
AppDispatcher.dispatch({
19-
actionType: syncConstants.SYNC_UPDATE_SITE,
20-
item
21-
})
22-
},
23-
2410
removeSite: function (item) {
2511
AppDispatcher.dispatch({
2612
actionType: syncConstants.SYNC_REMOVE_SITE,
@@ -38,30 +24,6 @@ const syncActions = {
3824
AppDispatcher.dispatch({
3925
actionType: syncConstants.SYNC_CLEAR_SITE_SETTINGS
4026
})
41-
},
42-
43-
addSiteSetting: function (hostPattern, item) {
44-
AppDispatcher.dispatch({
45-
actionType: syncConstants.SYNC_ADD_SITE_SETTING,
46-
item,
47-
hostPattern
48-
})
49-
},
50-
51-
updateSiteSetting: function (hostPattern, item) {
52-
AppDispatcher.dispatch({
53-
actionType: syncConstants.SYNC_UPDATE_SITE_SETTING,
54-
item,
55-
hostPattern
56-
})
57-
},
58-
59-
removeSiteSetting: function (hostPattern, item) {
60-
AppDispatcher.dispatch({
61-
actionType: syncConstants.SYNC_REMOVE_SITE_SETTING,
62-
item,
63-
hostPattern
64-
})
6527
}
6628
}
6729

js/constants/syncConstants.js

+1-7
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,9 @@ const mapValuesByKeys = require('../lib/functional').mapValuesByKeys
66

77
const _ = null
88
const syncConstants = {
9-
SYNC_ADD_SITE: _, /** @param {Immutable.Map} item */
10-
SYNC_UPDATE_SITE: _, /** @param {Immutable.Map} item */
119
SYNC_REMOVE_SITE: _, /** @param {Immutable.Map} item */
1210
SYNC_CLEAR_HISTORY: _,
13-
SYNC_CLEAR_SITE_SETTINGS: _,
14-
SYNC_DELETE_USER: _,
15-
SYNC_ADD_SITE_SETTING: _, /** @param {string} hostPattern, @param {Immutable.Map} item */
16-
SYNC_UPDATE_SITE_SETTING: _, /** @param {string} hostPattern, @param {Immutable.Map} item */
17-
SYNC_REMOVE_SITE_SETTING: _ /** @param {string} hostPattern, @param {Immutable.Map} item */
11+
SYNC_CLEAR_SITE_SETTINGS: _
1812
}
1913

2014
module.exports = mapValuesByKeys(syncConstants)

0 commit comments

Comments
 (0)