@@ -10,7 +10,6 @@ const qr = require('qr-image')
10
10
const ipcMain = electron . ipcMain
11
11
const locale = require ( './locale' )
12
12
const messages = require ( '../js/constants/messages' )
13
- const siteTags = require ( '../js/constants/siteTags' )
14
13
const syncMessages = require ( '../js/constants/sync/messages' )
15
14
const categories = require ( '../js/constants/sync/proto' ) . categories
16
15
const writeActions = require ( '../js/constants/sync/proto' ) . actions
@@ -28,10 +27,11 @@ const extensions = require('./extensions')
28
27
29
28
const CATEGORY_MAP = syncUtil . CATEGORY_MAP
30
29
const CATEGORY_NAMES = Object . keys ( categories )
30
+ const SYNC_ACTIONS = Object . values ( syncConstants )
31
31
32
32
// Fields that should trigger a sync SEND when changed
33
33
const SYNC_FIELDS = {
34
- sites : [ 'location' , 'customTitle' , 'folderId' , 'parentFolderId' ] ,
34
+ sites : [ 'location' , 'customTitle' , 'folderId' , 'parentFolderId' , 'tags' ] ,
35
35
siteSettings : Object . keys ( syncUtil . siteSettingDefaults )
36
36
}
37
37
@@ -53,15 +53,12 @@ let pollIntervalId = null
53
53
let deviceIdSent = false
54
54
let bookmarksToolbarShown = false
55
55
56
- // Syncs state diffs to the sync server if needed
56
+ // Determines what to sync
57
57
const appStoreChangeCallback = function ( diffs ) {
58
58
if ( ! backgroundSender || ! diffs ) {
59
59
return
60
60
}
61
61
62
- // Site locations created by the diffs
63
- let createdSiteLocations = [ ]
64
-
65
62
diffs . forEach ( ( diff ) => {
66
63
if ( ! diff || ! diff . path ) {
67
64
return
@@ -73,92 +70,54 @@ const appStoreChangeCallback = function (diffs) {
73
70
}
74
71
75
72
const type = path [ 1 ]
73
+ const field = path [ 3 ]
76
74
const isSite = type === 'sites'
77
- const siteLocation = isSite ? path [ 2 ] . split ( '|' ) [ 0 ] : null
78
75
79
76
const fieldsToPick = SYNC_FIELDS [ type ]
80
77
if ( ! fieldsToPick ) {
81
78
return
82
79
}
83
80
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
88
86
}
89
87
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
111
103
}
112
- } else if ( fieldsToPick . includes ( path [ 3 ] ) ) {
113
- action = writeActions . UPDATE
114
- }
115
104
116
- if ( action === null ) {
117
- return
118
- }
105
+ let action = null
119
106
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
+ }
123
113
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 )
128
118
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 ) ] )
162
121
}
163
122
}
164
123
} )
@@ -168,7 +127,7 @@ const appStoreChangeCallback = function (diffs) {
168
127
* Sends sync records of the same category to the sync server.
169
128
* @param {event.sender } sender
170
129
* @param {number } action
171
- * @param {Array.<{objectId: Array, name: string, value: Object}> } data
130
+ * @param {Array.<{name: string, value: Object}> } data
172
131
*/
173
132
const sendSyncRecords = ( sender , action , data ) => {
174
133
if ( ! deviceId ) {
@@ -183,7 +142,7 @@ const sendSyncRecords = (sender, action, data) => {
183
142
return
184
143
}
185
144
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 ) {
187
146
return
188
147
}
189
148
return {
@@ -195,6 +154,34 @@ const sendSyncRecords = (sender, action, data) => {
195
154
} ) )
196
155
}
197
156
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
+
198
185
const dispatcherCallback = ( action ) => {
199
186
if ( ! backgroundSender ) {
200
187
return
@@ -206,10 +193,14 @@ const dispatcherCallback = (action) => {
206
193
}
207
194
}
208
195
// If sync is not enabled, the following actions should be ignored.
209
- if ( ! syncEnabled ( ) || backgroundSender . isDestroyed ( ) ) {
196
+ if ( ! syncEnabled ( ) || validateAction ( action ) !== true || backgroundSender . isDestroyed ( ) ) {
210
197
return
211
198
}
212
199
switch ( action . actionType ) {
200
+ case syncConstants . SYNC_REMOVE_SITE :
201
+ sendSyncRecords ( backgroundSender , writeActions . DELETE ,
202
+ [ syncUtil . createSiteData ( action . item . toJS ( ) ) ] )
203
+ break
213
204
case syncConstants . SYNC_CLEAR_HISTORY :
214
205
backgroundSender . send ( syncMessages . DELETE_SYNC_CATEGORY , CATEGORY_MAP . historySite . categoryName )
215
206
break
@@ -343,6 +334,7 @@ module.exports.init = function (appState) {
343
334
}
344
335
// sent by about:preferences when sync should be reloaded
345
336
ipcMain . on ( messages . RELOAD_SYNC_EXTENSION , ( ) => {
337
+ console . log ( 'reloading sync' )
346
338
extensions . reloadExtension ( syncExtensionId )
347
339
} )
348
340
// sent by about:preferences when resetting sync
0 commit comments