extract sync and cache from styleMan
This commit is contained in:
parent
e54178a43c
commit
c95f74e089
|
@ -8,7 +8,7 @@
|
||||||
/* exported
|
/* exported
|
||||||
addAPI
|
addAPI
|
||||||
bgReady
|
bgReady
|
||||||
compareRevision
|
createCache
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const bgReady = {};
|
const bgReady = {};
|
||||||
|
@ -26,6 +26,63 @@ function addAPI(methods) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function compareRevision(rev1, rev2) {
|
/** Creates a FIFO limit-size map. */
|
||||||
return rev1 - rev2;
|
function createCache({size = 1000, onDeleted} = {}) {
|
||||||
|
const map = new Map();
|
||||||
|
const buffer = Array(size);
|
||||||
|
let index = 0;
|
||||||
|
let lastIndex = 0;
|
||||||
|
return {
|
||||||
|
get(id) {
|
||||||
|
const item = map.get(id);
|
||||||
|
return item && item.data;
|
||||||
|
},
|
||||||
|
set(id, data) {
|
||||||
|
if (map.size === size) {
|
||||||
|
// full
|
||||||
|
map.delete(buffer[lastIndex].id);
|
||||||
|
if (onDeleted) {
|
||||||
|
onDeleted(buffer[lastIndex].id, buffer[lastIndex].data);
|
||||||
|
}
|
||||||
|
lastIndex = (lastIndex + 1) % size;
|
||||||
|
}
|
||||||
|
const item = {id, data, index};
|
||||||
|
map.set(id, item);
|
||||||
|
buffer[index] = item;
|
||||||
|
index = (index + 1) % size;
|
||||||
|
},
|
||||||
|
delete(id) {
|
||||||
|
const item = map.get(id);
|
||||||
|
if (!item) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
map.delete(item.id);
|
||||||
|
const lastItem = buffer[lastIndex];
|
||||||
|
lastItem.index = item.index;
|
||||||
|
buffer[item.index] = lastItem;
|
||||||
|
lastIndex = (lastIndex + 1) % size;
|
||||||
|
if (onDeleted) {
|
||||||
|
onDeleted(item.id, item.data);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
map.clear();
|
||||||
|
index = lastIndex = 0;
|
||||||
|
},
|
||||||
|
has: id => map.has(id),
|
||||||
|
*entries() {
|
||||||
|
for (const [id, item] of map) {
|
||||||
|
yield [id, item.data];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
*values() {
|
||||||
|
for (const item of map.values()) {
|
||||||
|
yield item.data;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get size() {
|
||||||
|
return map.size;
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* global API msg */// msg.js
|
/* global API msg */// msg.js
|
||||||
/* global CHROME URLS isEmptyObj stringAsRegExp tryRegExp tryURL */// toolbox.js
|
/* global CHROME URLS isEmptyObj stringAsRegExp tryRegExp tryURL */// toolbox.js
|
||||||
/* global bgReady compareRevision */// common.js
|
/* global bgReady createCache */// common.js
|
||||||
/* global calcStyleDigest styleCodeEmpty styleSectionGlobal */// sections-util.js
|
/* global calcStyleDigest styleCodeEmpty styleSectionGlobal */// sections-util.js
|
||||||
/* global db */
|
/* global db */
|
||||||
/* global prefs */
|
/* global prefs */
|
||||||
|
@ -18,8 +18,15 @@ The live preview feature relies on `runtime.connect` and `port.onDisconnect`
|
||||||
to cleanup the temporary code. See livePreview in /edit.
|
to cleanup the temporary code. See livePreview in /edit.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const styleUtil = {};
|
||||||
|
|
||||||
const styleMan = (() => {
|
const styleMan = (() => {
|
||||||
|
|
||||||
|
Object.assign(styleUtil, {
|
||||||
|
id2style,
|
||||||
|
handleSave,
|
||||||
|
});
|
||||||
|
|
||||||
//#region Declarations
|
//#region Declarations
|
||||||
|
|
||||||
/** @typedef {{
|
/** @typedef {{
|
||||||
|
@ -29,7 +36,6 @@ const styleMan = (() => {
|
||||||
}} StyleMapData */
|
}} StyleMapData */
|
||||||
/** @type {Map<number,StyleMapData>} */
|
/** @type {Map<number,StyleMapData>} */
|
||||||
const dataMap = new Map();
|
const dataMap = new Map();
|
||||||
const uuidIndex = new Map();
|
|
||||||
/** @typedef {Object<styleId,{id: number, code: string[]}>} StyleSectionsToApply */
|
/** @typedef {Object<styleId,{id: number, code: string[]}>} StyleSectionsToApply */
|
||||||
/** @type {Map<string,{maybeMatch: Set<styleId>, sections: StyleSectionsToApply}>} */
|
/** @type {Map<string,{maybeMatch: Set<styleId>, sections: StyleSectionsToApply}>} */
|
||||||
const cachedStyleForUrl = createCache({
|
const cachedStyleForUrl = createCache({
|
||||||
|
@ -77,16 +83,17 @@ const styleMan = (() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
prefs.subscribe(['injectionOrder'], (key, value) => {
|
// TODO: will fix in subsequent commit
|
||||||
order = {};
|
// prefs.subscribe(['injectionOrder'], (key, value) => {
|
||||||
value.forEach((uid, i) => {
|
// order = {};
|
||||||
const id = uuidIndex.get(uid);
|
// value.forEach((uid, i) => {
|
||||||
if (id) {
|
// const id = uuidIndex.get(uid);
|
||||||
order[id] = i;
|
// if (id) {
|
||||||
}
|
// order[id] = i;
|
||||||
});
|
// }
|
||||||
msg.broadcast({method: 'styleSort', order});
|
// });
|
||||||
});
|
// msg.broadcast({method: 'styleSort', order});
|
||||||
|
// });
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
//#region Exports
|
//#region Exports
|
||||||
|
@ -107,7 +114,6 @@ const styleMan = (() => {
|
||||||
if (cache) delete cache.sections[id];
|
if (cache) delete cache.sections[id];
|
||||||
}
|
}
|
||||||
dataMap.delete(id);
|
dataMap.delete(id);
|
||||||
uuidIndex.delete(style._id);
|
|
||||||
if (style._usw && style._usw.token) {
|
if (style._usw && style._usw.token) {
|
||||||
// Must be called after the style is deleted from dataMap
|
// Must be called after the style is deleted from dataMap
|
||||||
API.usw.revoke(id);
|
API.usw.revoke(id);
|
||||||
|
@ -120,17 +126,6 @@ const styleMan = (() => {
|
||||||
return id;
|
return id;
|
||||||
},
|
},
|
||||||
|
|
||||||
/** @returns {Promise<number>} style id */
|
|
||||||
async deleteByUUID(_id, rev) {
|
|
||||||
if (ready.then) await ready;
|
|
||||||
const id = uuidIndex.get(_id);
|
|
||||||
const oldDoc = id && id2style(id);
|
|
||||||
if (oldDoc && compareRevision(oldDoc._rev, rev) <= 0) {
|
|
||||||
// FIXME: does it make sense to set reason to 'sync' in deleteByUUID?
|
|
||||||
return styleMan.delete(id, 'sync');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/** @returns {Promise<StyleObj>} */
|
/** @returns {Promise<StyleObj>} */
|
||||||
async editSave(style) {
|
async editSave(style) {
|
||||||
if (ready.then) await ready;
|
if (ready.then) await ready;
|
||||||
|
@ -157,12 +152,6 @@ const styleMan = (() => {
|
||||||
return Array.from(dataMap.values(), data2style);
|
return Array.from(dataMap.values(), data2style);
|
||||||
},
|
},
|
||||||
|
|
||||||
/** @returns {Promise<StyleObj>} */
|
|
||||||
async getByUUID(uuid) {
|
|
||||||
if (ready.then) await ready;
|
|
||||||
return id2style(uuidIndex.get(uuid));
|
|
||||||
},
|
|
||||||
|
|
||||||
/** @returns {Promise<StyleSectionsToApply>} */
|
/** @returns {Promise<StyleSectionsToApply>} */
|
||||||
async getSectionsByUrl(url, id, isInitialApply) {
|
async getSectionsByUrl(url, id, isInitialApply) {
|
||||||
if (ready.then) await ready;
|
if (ready.then) await ready;
|
||||||
|
@ -263,10 +252,9 @@ const styleMan = (() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const events = await db.exec('putMany', items);
|
const events = await db.exec('putMany', items);
|
||||||
return Promise.all(items.map((item, i) => {
|
return Promise.all(items.map((item, i) =>
|
||||||
afterSave(item, events[i]);
|
handleSave(item, {reason: 'import'}, events[i])
|
||||||
return handleSave(item, {reason: 'import'});
|
));
|
||||||
}));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/** @returns {Promise<StyleObj>} */
|
/** @returns {Promise<StyleObj>} */
|
||||||
|
@ -279,31 +267,6 @@ const styleMan = (() => {
|
||||||
return saveStyle(style, {reason});
|
return saveStyle(style, {reason});
|
||||||
},
|
},
|
||||||
|
|
||||||
/** @returns {Promise<?StyleObj>} */
|
|
||||||
async putByUUID(doc) {
|
|
||||||
if (ready.then) await ready;
|
|
||||||
const id = uuidIndex.get(doc._id);
|
|
||||||
if (id) {
|
|
||||||
doc.id = id;
|
|
||||||
} else {
|
|
||||||
delete doc.id;
|
|
||||||
}
|
|
||||||
const oldDoc = id && id2style(id);
|
|
||||||
let diff = -1;
|
|
||||||
if (oldDoc) {
|
|
||||||
diff = compareRevision(oldDoc._rev, doc._rev);
|
|
||||||
if (diff > 0) {
|
|
||||||
API.sync.put(oldDoc._id, oldDoc._rev);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (diff < 0) {
|
|
||||||
doc.id = await db.exec('put', doc);
|
|
||||||
uuidIndex.set(doc._id, doc.id);
|
|
||||||
return handleSave(doc, {reason: 'sync'});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
save: saveStyle,
|
save: saveStyle,
|
||||||
|
|
||||||
/** @returns {Promise<number>} style id */
|
/** @returns {Promise<number>} style id */
|
||||||
|
@ -472,29 +435,24 @@ const styleMan = (() => {
|
||||||
fixKnownProblems(style);
|
fixKnownProblems(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
function afterSave(style, newId) {
|
|
||||||
if (style.id == null) {
|
|
||||||
style.id = newId;
|
|
||||||
}
|
|
||||||
uuidIndex.set(style._id, style.id);
|
|
||||||
API.sync.put(style._id, style._rev);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveStyle(style, handlingOptions) {
|
async function saveStyle(style, handlingOptions) {
|
||||||
beforeSave(style);
|
beforeSave(style);
|
||||||
const newId = await db.exec('put', style);
|
const newId = await db.exec('put', style);
|
||||||
afterSave(style, newId);
|
return handleSave(style, handlingOptions, newId);
|
||||||
return handleSave(style, handlingOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSave(style, {reason, broadcast = true}) {
|
function handleSave(style, {reason, broadcast = true}, id = style.id) {
|
||||||
const data = id2data(style.id);
|
if (style.id == null) style.id = id;
|
||||||
|
const data = id2data(id);
|
||||||
const method = data ? 'styleUpdated' : 'styleAdded';
|
const method = data ? 'styleUpdated' : 'styleAdded';
|
||||||
if (!data) {
|
if (!data) {
|
||||||
storeInMap(style);
|
storeInMap(style);
|
||||||
} else {
|
} else {
|
||||||
data.style = style;
|
data.style = style;
|
||||||
}
|
}
|
||||||
|
if (reason !== 'sync') {
|
||||||
|
API.sync.putStyle(style);
|
||||||
|
}
|
||||||
if (broadcast) broadcastStyleUpdated(style, reason, method);
|
if (broadcast) broadcastStyleUpdated(style, reason, method);
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
|
@ -524,10 +482,7 @@ const styleMan = (() => {
|
||||||
if (updated.length) {
|
if (updated.length) {
|
||||||
await db.exec('putMany', updated);
|
await db.exec('putMany', updated);
|
||||||
}
|
}
|
||||||
for (const style of styles) {
|
styles.forEach(storeInMap);
|
||||||
storeInMap(style);
|
|
||||||
uuidIndex.set(style._id, style.id);
|
|
||||||
}
|
|
||||||
ready = true;
|
ready = true;
|
||||||
bgReady._resolveStyles();
|
bgReady._resolveStyles();
|
||||||
}
|
}
|
||||||
|
@ -732,64 +687,3 @@ const styleMan = (() => {
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/** Creates a FIFO limit-size map. */
|
|
||||||
function createCache({size = 1000, onDeleted} = {}) {
|
|
||||||
const map = new Map();
|
|
||||||
const buffer = Array(size);
|
|
||||||
let index = 0;
|
|
||||||
let lastIndex = 0;
|
|
||||||
return {
|
|
||||||
get(id) {
|
|
||||||
const item = map.get(id);
|
|
||||||
return item && item.data;
|
|
||||||
},
|
|
||||||
set(id, data) {
|
|
||||||
if (map.size === size) {
|
|
||||||
// full
|
|
||||||
map.delete(buffer[lastIndex].id);
|
|
||||||
if (onDeleted) {
|
|
||||||
onDeleted(buffer[lastIndex].id, buffer[lastIndex].data);
|
|
||||||
}
|
|
||||||
lastIndex = (lastIndex + 1) % size;
|
|
||||||
}
|
|
||||||
const item = {id, data, index};
|
|
||||||
map.set(id, item);
|
|
||||||
buffer[index] = item;
|
|
||||||
index = (index + 1) % size;
|
|
||||||
},
|
|
||||||
delete(id) {
|
|
||||||
const item = map.get(id);
|
|
||||||
if (!item) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
map.delete(item.id);
|
|
||||||
const lastItem = buffer[lastIndex];
|
|
||||||
lastItem.index = item.index;
|
|
||||||
buffer[item.index] = lastItem;
|
|
||||||
lastIndex = (lastIndex + 1) % size;
|
|
||||||
if (onDeleted) {
|
|
||||||
onDeleted(item.id, item.data);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
clear() {
|
|
||||||
map.clear();
|
|
||||||
index = lastIndex = 0;
|
|
||||||
},
|
|
||||||
has: id => map.has(id),
|
|
||||||
*entries() {
|
|
||||||
for (const [id, item] of map) {
|
|
||||||
yield [id, item.data];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
*values() {
|
|
||||||
for (const item of map.values()) {
|
|
||||||
yield item.data;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
get size() {
|
|
||||||
return map.size;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
/* global API msg */// msg.js
|
/* global API msg */// msg.js
|
||||||
/* global chromeLocal chromeSync */// storage-util.js
|
/* global chromeLocal chromeSync */// storage-util.js
|
||||||
/* global compareRevision */// common.js
|
/* global db */
|
||||||
/* global iconMan */
|
/* global iconMan */
|
||||||
/* global prefs */
|
/* global prefs */
|
||||||
|
/* global styleUtil */
|
||||||
/* global tokenMan */
|
/* global tokenMan */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -28,11 +29,17 @@ const syncMan = (() => {
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
login: false,
|
login: false,
|
||||||
};
|
};
|
||||||
|
const uuidIndex = new Map();
|
||||||
|
const uuid2style = uuid => styleUtil.id2style(uuidIndex.get(uuid));
|
||||||
|
const compareRevision = (rev1, rev2) => rev1 - rev2;
|
||||||
let lastError = null;
|
let lastError = null;
|
||||||
let ctrl;
|
let ctrl;
|
||||||
let currentDrive;
|
let currentDrive;
|
||||||
/** @type {Promise|boolean} will be `true` to avoid wasting a microtask tick on each `await` */
|
/** @type {Promise|boolean} will be `true` to avoid wasting a microtask tick on each `await` */
|
||||||
let ready = prefs.ready.then(() => {
|
let ready = prefs.ready.then(async () => {
|
||||||
|
for (const {id, _id} of await API.styles.getAll()) {
|
||||||
|
uuidIndex.set(_id, id);
|
||||||
|
}
|
||||||
ready = true;
|
ready = true;
|
||||||
prefs.subscribe('sync.enabled',
|
prefs.subscribe('sync.enabled',
|
||||||
(_, val) => val === 'none'
|
(_, val) => val === 'none'
|
||||||
|
@ -52,11 +59,12 @@ const syncMan = (() => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
||||||
async delete(...args) {
|
async delete(_id, rev) {
|
||||||
if (ready.then) await ready;
|
if (ready.then) await ready;
|
||||||
if (!currentDrive) return;
|
if (!currentDrive) return;
|
||||||
schedule();
|
schedule();
|
||||||
return ctrl.delete(...args);
|
uuidIndex.delete(_id);
|
||||||
|
return ctrl.delete(_id, rev);
|
||||||
},
|
},
|
||||||
|
|
||||||
/** @returns {Promise<SyncManager.Status>} */
|
/** @returns {Promise<SyncManager.Status>} */
|
||||||
|
@ -86,6 +94,11 @@ const syncMan = (() => {
|
||||||
return ctrl.put(...args);
|
return ctrl.put(...args);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
putStyle({id, _id, _rev}) {
|
||||||
|
uuidIndex.set(_id, id);
|
||||||
|
return syncMan.put(_id, _rev);
|
||||||
|
},
|
||||||
|
|
||||||
async setDriveOptions(driveName, options) {
|
async setDriveOptions(driveName, options) {
|
||||||
const key = `secure/sync/driveOptions/${driveName}`;
|
const key = `secure/sync/driveOptions/${driveName}`;
|
||||||
await chromeSync.setValue(key, options);
|
await chromeSync.setValue(key, options);
|
||||||
|
@ -178,14 +191,37 @@ const syncMan = (() => {
|
||||||
async function initController() {
|
async function initController() {
|
||||||
await require(['/vendor/db-to-cloud/db-to-cloud.min']); /* global dbToCloud */
|
await require(['/vendor/db-to-cloud/db-to-cloud.min']); /* global dbToCloud */
|
||||||
ctrl = dbToCloud.dbToCloud({
|
ctrl = dbToCloud.dbToCloud({
|
||||||
onGet(id) {
|
onGet: uuid2style,
|
||||||
return API.styles.getByUUID(id);
|
async onPut(doc) {
|
||||||
|
const id = uuidIndex.get(doc._id);
|
||||||
|
const oldDoc = uuid2style(doc._id);
|
||||||
|
if (id) {
|
||||||
|
doc.id = id;
|
||||||
|
} else {
|
||||||
|
delete doc.id;
|
||||||
|
}
|
||||||
|
let diff = -1;
|
||||||
|
if (oldDoc) {
|
||||||
|
diff = compareRevision(oldDoc._rev, doc._rev);
|
||||||
|
if (diff > 0) {
|
||||||
|
syncMan.put(oldDoc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (diff < 0) {
|
||||||
|
doc.id = await db.exec('put', doc);
|
||||||
|
uuidIndex.set(doc._id, doc.id);
|
||||||
|
return styleUtil.handleSave(doc, {reason: 'sync'});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onPut(doc) {
|
onDelete(_id, rev) {
|
||||||
return API.styles.putByUUID(doc);
|
const id = uuidIndex.get(_id);
|
||||||
},
|
const oldDoc = uuid2style(_id);
|
||||||
onDelete(id, rev) {
|
if (oldDoc && compareRevision(oldDoc._rev, rev) <= 0) {
|
||||||
return API.styles.deleteByUUID(id, rev);
|
// FIXME: does it make sense to set reason to 'sync' in deleteByUUID?
|
||||||
|
uuidIndex.delete(id);
|
||||||
|
return API.styles.delete(id, 'sync');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async onFirstSync() {
|
async onFirstSync() {
|
||||||
for (const i of await API.styles.getAll()) {
|
for (const i of await API.styles.getAll()) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user