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

Commit 27b0547

Browse files
authored
Merge pull request #9726 from brave/fix/9724
Revert "handle empty objects in sync DELETEs"
2 parents b364c56 + 720ecc4 commit 27b0547

File tree

10 files changed

+132
-109
lines changed

10 files changed

+132
-109
lines changed

app/browser/reducers/sitesReducer.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const filtering = require('../../filtering')
99
const siteCache = require('../../common/state/siteCache')
1010
const siteTags = require('../../../js/constants/siteTags')
1111
const siteUtil = require('../../../js/state/siteUtil')
12+
const syncActions = require('../../../js/actions/syncActions')
1213
const syncUtil = require('../../../js/state/syncUtil')
1314
const Immutable = require('immutable')
1415
const settings = require('../../../js/constants/settings')
@@ -83,7 +84,8 @@ const sitesReducer = (state, action, immutableAction) => {
8384
state = updateActiveTabBookmarked(state)
8485
break
8586
case appConstants.APP_REMOVE_SITE:
86-
state = siteUtil.removeSite(state, action.siteDetail, action.tag, true)
87+
const removeSiteSyncCallback = action.skipSync ? undefined : syncActions.removeSite
88+
state = siteUtil.removeSite(state, action.siteDetail, action.tag, true, removeSiteSyncCallback)
8789
if (syncEnabled()) {
8890
state = syncUtil.updateSiteCache(state, action.siteDetail)
8991
}
@@ -100,7 +102,6 @@ const sitesReducer = (state, action, immutableAction) => {
100102
}
101103
break
102104
case appConstants.APP_APPLY_SITE_RECORDS:
103-
// Applies site records fetched by sync
104105
let nextFolderId = siteUtil.getNextFolderId(state.get('sites'))
105106
// Ensure that all folders are assigned folderIds
106107
action.records.forEach((record, i) => {

app/sync.js

+73-81
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ const qr = require('qr-image')
1010
const ipcMain = electron.ipcMain
1111
const locale = require('./locale')
1212
const messages = require('../js/constants/messages')
13-
const siteTags = require('../js/constants/siteTags')
1413
const syncMessages = require('../js/constants/sync/messages')
1514
const categories = require('../js/constants/sync/proto').categories
1615
const writeActions = require('../js/constants/sync/proto').actions
@@ -28,10 +27,11 @@ const extensions = require('./extensions')
2827

2928
const CATEGORY_MAP = syncUtil.CATEGORY_MAP
3029
const CATEGORY_NAMES = Object.keys(categories)
30+
const SYNC_ACTIONS = Object.values(syncConstants)
3131

3232
// Fields that should trigger a sync SEND when changed
3333
const SYNC_FIELDS = {
34-
sites: ['location', 'customTitle', 'folderId', 'parentFolderId'],
34+
sites: ['location', 'customTitle', 'folderId', 'parentFolderId', 'tags'],
3535
siteSettings: Object.keys(syncUtil.siteSettingDefaults)
3636
}
3737

@@ -53,15 +53,12 @@ let pollIntervalId = null
5353
let deviceIdSent = false
5454
let bookmarksToolbarShown = false
5555

56-
// Syncs state diffs to the sync server if needed
56+
// Determines what to sync
5757
const appStoreChangeCallback = function (diffs) {
5858
if (!backgroundSender || !diffs) {
5959
return
6060
}
6161

62-
// Site locations created by the diffs
63-
let createdSiteLocations = []
64-
6562
diffs.forEach((diff) => {
6663
if (!diff || !diff.path) {
6764
return
@@ -73,92 +70,54 @@ const appStoreChangeCallback = function (diffs) {
7370
}
7471

7572
const type = path[1]
73+
const field = path[3]
7674
const isSite = type === 'sites'
77-
const siteLocation = isSite ? path[2].split('|')[0] : null
7875

7976
const fieldsToPick = SYNC_FIELDS[type]
8077
if (!fieldsToPick) {
8178
return
8279
}
8380

84-
let action = null
85-
let maybeSkipSync = false
86-
if (siteLocation && diff.op !== 'remove') {
87-
createdSiteLocations.push(siteLocation)
81+
const statePath = path.slice(1, 3).map((item) => item.replace(/~1/g, '/'))
82+
if (field === 'skipSync' && diff.value === true) {
83+
// Remove the flag so that this gets synced next time it is updated
84+
appActions.setSkipSync(statePath, false)
85+
return
8886
}
8987

90-
// XXX: adding/removing a tag (e.g. 'bookmark') corresponds to adding/deleting a site record in sync
91-
if (path.length === 3 || path[3] === 'tags') {
92-
if (diff.op === 'add') {
93-
maybeSkipSync = true
94-
if (isSite) {
95-
// Send UPDATE instead of CREATE for sites to work around sync#111
96-
action = writeActions.UPDATE
97-
} else {
98-
action = writeActions.CREATE
99-
}
100-
} else if (diff.op === 'remove') {
101-
if (path.length === 3 && createdSiteLocations.includes(siteLocation)) {
102-
// We are removing a site that is created in the same diff set.
103-
// This probably means we are moving a site, not actually deleting
104-
// it, so ignore this update.
105-
console.log('Skipping site deletion', siteLocation)
106-
return
107-
}
108-
action = writeActions.DELETE
109-
} else {
110-
action = writeActions.UPDATE
88+
const isInsert = diff.op === 'add' && path.length === 3
89+
const isUpdate = fieldsToPick.includes(field) // Ignore insignicant updates
90+
91+
// DELETES are handled in appState because the old object is no longer
92+
// available by the time emitChanges is received
93+
if (isInsert || isUpdate) {
94+
// Get the item's path and entry in appStore
95+
const entry = AppStore.getState().getIn(statePath)
96+
if (!entry || !entry.toJS) {
97+
return
98+
}
99+
if (entry.get('skipSync')) {
100+
// Remove the flag so that this gets synced next time it is updated
101+
appActions.setSkipSync(statePath, false)
102+
return
111103
}
112-
} else if (fieldsToPick.includes(path[3])) {
113-
action = writeActions.UPDATE
114-
}
115104

116-
if (action === null) {
117-
return
118-
}
105+
let action = null
119106

120-
const statePath = path.slice(1, 3).map((item) => item.replace(/~1/g, '/'))
121-
const state = AppStore.getState()
122-
const entry = state.getIn(statePath)
107+
if (isInsert) {
108+
// Send UPDATE instead of CREATE for sites to work around sync#111
109+
action = isSite ? writeActions.UPDATE : writeActions.CREATE
110+
} else if (isUpdate) {
111+
action = writeActions.UPDATE
112+
}
123113

124-
if (maybeSkipSync && entry && entry.get('skipSync')) {
125-
// Don't re-create objects that were fetched by sync
126-
return
127-
}
114+
if (action !== null) {
115+
// Set the object ID if there is not already one
116+
const entryJS = entry.toJS()
117+
entryJS.objectId = entryJS.objectId || syncUtil.newObjectId(statePath)
128118

129-
if (isSite && action === writeActions.DELETE && !entry) {
130-
// If we deleted the site, it is no longer availble in appState.
131-
// Find the corresponding objectId using the sync cache
132-
// and send this in the sync record
133-
const objectId = syncUtil.siteKeyToObjectId(state, statePath[1])
134-
if (objectId) {
135-
// Delete the site from both history and bookmarks
136-
sendSyncRecords(backgroundSender, action, [{
137-
name: 'bookmark',
138-
objectId,
139-
value: {}
140-
}])
141-
sendSyncRecords(backgroundSender, action, [{
142-
name: 'historySite',
143-
objectId,
144-
value: {}
145-
}])
146-
}
147-
} else if (entry && entry.toJS) {
148-
const entryJS = entry.toJS()
149-
if (action === writeActions.DELETE && isSite) {
150-
const tags = entryJS.tags || []
151-
sendSyncRecords(backgroundSender, action, [{
152-
objectId: entryJS.objectId,
153-
value: {},
154-
name: tags.includes(siteTags.BOOKMARK) || siteTags.includes(siteTags.BOOKMARK_FOLDER)
155-
? 'historySite' // if the site is still a bookmark, it must have been deleted from history
156-
: 'bookmark'
157-
}])
158-
} else {
159-
sendSyncRecords(backgroundSender, action, [
160-
isSite ? syncUtil.createSiteData(entryJS, state) : syncUtil.createSiteSettingsData(statePath[1], entryJS)
161-
])
119+
sendSyncRecords(backgroundSender, action,
120+
[type === 'sites' ? syncUtil.createSiteData(entryJS) : syncUtil.createSiteSettingsData(statePath[1], entryJS)])
162121
}
163122
}
164123
})
@@ -168,7 +127,7 @@ const appStoreChangeCallback = function (diffs) {
168127
* Sends sync records of the same category to the sync server.
169128
* @param {event.sender} sender
170129
* @param {number} action
171-
* @param {Array.<{objectId: Array, name: string, value: Object}>} data
130+
* @param {Array.<{name: string, value: Object}>} data
172131
*/
173132
const sendSyncRecords = (sender, action, data) => {
174133
if (!deviceId) {
@@ -183,7 +142,7 @@ const sendSyncRecords = (sender, action, data) => {
183142
return
184143
}
185144
sender.send(syncMessages.SEND_SYNC_RECORDS, category.categoryName, data.map((item) => {
186-
if (!item || !item.name || !item.value || !item.objectId) {
145+
if (!item || !item.name || !item.value) {
187146
return
188147
}
189148
return {
@@ -195,6 +154,34 @@ const sendSyncRecords = (sender, action, data) => {
195154
}))
196155
}
197156

157+
/**
158+
* @param {Object} action
159+
* @returns {boolean}
160+
*/
161+
const validateAction = (action) => {
162+
const SYNC_ACTIONS_WITHOUT_ITEMS = [
163+
syncConstants.SYNC_CLEAR_HISTORY,
164+
syncConstants.SYNC_CLEAR_SITE_SETTINGS
165+
]
166+
if (SYNC_ACTIONS.includes(action.actionType) !== true) {
167+
return false
168+
}
169+
170+
// If the action requires an item, validate the item.
171+
if (SYNC_ACTIONS_WITHOUT_ITEMS.includes(action.actionType) !== true) {
172+
if (!action.item || !action.item.toJS) {
173+
log('Missing item!')
174+
return false
175+
}
176+
// Only accept items who have an objectId set already
177+
if (!action.item.get('objectId')) {
178+
log(`Missing object ID! ${action.item.toJS()}`)
179+
return false
180+
}
181+
}
182+
return true
183+
}
184+
198185
const dispatcherCallback = (action) => {
199186
if (!backgroundSender) {
200187
return
@@ -206,10 +193,14 @@ const dispatcherCallback = (action) => {
206193
}
207194
}
208195
// If sync is not enabled, the following actions should be ignored.
209-
if (!syncEnabled() || backgroundSender.isDestroyed()) {
196+
if (!syncEnabled() || validateAction(action) !== true || backgroundSender.isDestroyed()) {
210197
return
211198
}
212199
switch (action.actionType) {
200+
case syncConstants.SYNC_REMOVE_SITE:
201+
sendSyncRecords(backgroundSender, writeActions.DELETE,
202+
[syncUtil.createSiteData(action.item.toJS())])
203+
break
213204
case syncConstants.SYNC_CLEAR_HISTORY:
214205
backgroundSender.send(syncMessages.DELETE_SYNC_CATEGORY, CATEGORY_MAP.historySite.categoryName)
215206
break
@@ -343,6 +334,7 @@ module.exports.init = function (appState) {
343334
}
344335
// sent by about:preferences when sync should be reloaded
345336
ipcMain.on(messages.RELOAD_SYNC_EXTENSION, () => {
337+
console.log('reloading sync')
346338
extensions.reloadExtension(syncExtensionId)
347339
})
348340
// sent by about:preferences when resetting sync

docs/appActions.md

+12
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,18 @@ Removes a site setting
393393

394394

395395

396+
### setSkipSync(path, skipSync)
397+
398+
Changes the skipSync flag on an appState path.
399+
400+
**Parameters**
401+
402+
**path**: `Array.&lt;string&gt;`, Changes the skipSync flag on an appState path.
403+
404+
**skipSync**: `boolean`, Changes the skipSync flag on an appState path.
405+
406+
407+
396408
### updateLedgerInfo(ledgerInfo)
397409

398410
Updates ledger information for the payments pane

js/actions/appActions.js

+13
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,19 @@ const appActions = {
485485
})
486486
},
487487

488+
/**
489+
* Changes the skipSync flag on an appState path.
490+
* @param {Array.<string>} path
491+
* @param {boolean} skipSync
492+
*/
493+
setSkipSync: function (path, skipSync) {
494+
dispatch({
495+
actionType: appConstants.APP_SET_SKIP_SYNC,
496+
path,
497+
skipSync
498+
})
499+
},
500+
488501
/**
489502
* Updates ledger information for the payments pane
490503
* @param {object} ledgerInfo - the current ledger state

js/actions/syncActions.js

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

99
const syncActions = {
10+
removeSite: function (item) {
11+
AppDispatcher.dispatch({
12+
actionType: syncConstants.SYNC_REMOVE_SITE,
13+
item
14+
})
15+
},
16+
1017
clearHistory: function () {
1118
AppDispatcher.dispatch({
1219
actionType: syncConstants.SYNC_CLEAR_HISTORY

js/constants/appConstants.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ const appConstants = {
135135
APP_UPDATE_LOG_OPENED: _,
136136
APP_URL_BAR_SELECTED_INDEX_CHANGED: _,
137137
APP_ON_TOGGLE_BROWSING_DATA: _,
138-
APP_ON_CANCEL_BROWSING_DATA: _
138+
APP_ON_CANCEL_BROWSING_DATA: _,
139+
APP_SET_SKIP_SYNC: _
139140
}
140141

141142
module.exports = mapValuesByKeys(appConstants)

js/constants/syncConstants.js

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

77
const _ = null
88
const syncConstants = {
9+
SYNC_REMOVE_SITE: _, /** @param {Immutable.Map} item */
910
SYNC_CLEAR_HISTORY: _,
1011
SYNC_CLEAR_SITE_SETTINGS: _
1112
}

js/state/siteUtil.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -369,14 +369,18 @@ module.exports.removeSiteByObjectId = function (state, objectId, objectData) {
369369
* @param {Immutable.Map} siteDetail The siteDetail to remove a tag from
370370
* @param {string} tag
371371
* @param {boolean} reorder whether to reorder sites (default with reorder)
372+
* @param {Function=} syncCallback
372373
* @return {Immutable.Map} The new state Immutable object
373374
*/
374-
module.exports.removeSite = function (state, siteDetail, tag, reorder = true) {
375+
module.exports.removeSite = function (state, siteDetail, tag, reorder = true, syncCallback) {
375376
let sites = state.get('sites')
376377
const key = module.exports.getSiteKey(siteDetail)
377378
if (!key) {
378379
return state
379380
}
381+
if (getSetting(settings.SYNC_ENABLED) === true && syncCallback) {
382+
syncCallback(sites.getIn([key]))
383+
}
380384

381385
const tags = sites.getIn([key, 'tags'])
382386
if (isBookmarkFolder(tags)) {
@@ -385,7 +389,7 @@ module.exports.removeSite = function (state, siteDetail, tag, reorder = true) {
385389
childSites.forEach((site) => {
386390
const tags = site.get('tags')
387391
tags.forEach((tag) => {
388-
state = module.exports.removeSite(state, site, tag, false)
392+
state = module.exports.removeSite(state, site, tag, false, syncCallback)
389393
})
390394
})
391395
}

0 commit comments

Comments
 (0)