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