scheduler implementation part/2

This commit is contained in:
Jeremy Schomery 2017-05-03 19:19:40 +04:30
parent 9f9b8be0f4
commit c2f993db27
5 changed files with 72 additions and 50 deletions

View File

@ -279,8 +279,7 @@ function onRuntimeMessage(request, sender, sendResponse) {
.catch(() => sendResponse(null)); .catch(() => sendResponse(null));
return KEEP_CHANNEL_OPEN; return KEEP_CHANNEL_OPEN;
case 'schedule': case 'schedule':
schedule.entry(request) schedule.entry(request);
.then(sendResponse) return KEEP_CHANNEL_OPEN;
.catch(e => sendResponse(e));
} }
} }

View File

@ -226,8 +226,8 @@
<script src="backup/fileSaveLoad.js"></script> <script src="backup/fileSaveLoad.js"></script>
<script src="msgbox/msgbox.js"></script> <script src="msgbox/msgbox.js"></script>
<script src="/schedule/ui.js"></script>
<script src="/schedule/core.js"></script> <script src="/schedule/core.js"></script>
<script src="/schedule/ui.js"></script>
</body> </body>
</html> </html>

View File

@ -131,6 +131,7 @@ function showStyles(styles = []) {
if (newUI.enabled && newUI.favicons) { if (newUI.enabled && newUI.favicons) {
debounce(handleEvent.loadFavicons, 16); debounce(handleEvent.loadFavicons, 16);
} }
document.dispatchEvent(new Event('styles-ready'));
} }
} }
@ -436,6 +437,10 @@ function handleUpdate(style, {reason, method} = {}) {
$('.update-note', entry).textContent = t('updateCompleted'); $('.update-note', entry).textContent = t('updateCompleted');
renderUpdatesOnlyFilter(); renderUpdatesOnlyFilter();
} }
document.dispatchEvent(new CustomEvent('style-edited', {
detail: style
}));
} }

View File

@ -6,13 +6,19 @@ var SCHEDULE_PREFIX = 'schedule';
var schedule = {}; var schedule = {};
schedule.prefs = { schedule.prefs = {
name (id) {
return SCHEDULE_PREFIX + '.' + id;
},
validate (name) {
return name.startsWith(SCHEDULE_PREFIX + '.');
},
get (name, callback) { get (name, callback) {
chrome.storage.local.get(name, callback); chrome.storage.local.get(name, callback);
}, },
getAll (callback) { getAll (callback) {
schedule.prefs.get(null, prefs => { schedule.prefs.get(null, prefs => {
callback( callback(
Object.keys(prefs).filter(n => n.startsWith(SCHEDULE_PREFIX + '.')) Object.keys(prefs).filter(schedule.prefs.validate)
.map(n => [n, prefs[n]]) .map(n => [n, prefs[n]])
); );
}); });
@ -33,21 +39,20 @@ schedule.prefs = {
}); });
} }
}; };
/* call this function when schedule timing is modified; if timing is not modified, nothing happens */
schedule.entry = request => { schedule.entry = request => {
console.error('schedule.entry', request); console.log('schedule.entry', 'schedule timing might have been changed', request);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
chrome.permissions.request({ chrome.permissions.request({
permissions: ['idle', 'alarms'] permissions: ['idle', 'alarms']
}, (granted) => { }, (granted) => {
if (granted) { if (granted) {
schedule.prefs.set(SCHEDULE_PREFIX + '.' + request.id, { schedule.prefs.set(schedule.prefs.name(request.id), {
id: request.id, id: request.id,
start: request.start, start: request.start,
end: request.end, end: request.end,
enabled: request.enabled enabled: request.enabled
}); }, resolve);
resolve();
} }
else { else {
reject(new Error('Required permissions are not granted')); reject(new Error('Required permissions are not granted'));
@ -55,25 +60,24 @@ schedule.entry = request => {
}); });
}); });
}; };
/* call this to update current alarm. If request.enabled = false, then alarm is cleared and this job will be removed from the storage */
schedule.execute = (name, request) => { schedule.execute = (name, request) => {
console.error('schedule.execute', name, request); console.log('schedule.execute', 'evaluating response', name, request);
chrome.alarms.clear(name, () => { chrome.alarms.clear(name, () => {
if (request.enabled) { if (request.enabled) {
const now = new Date(); const now = new Date();
let start = new Date(now.toDateString() + ' ' + request.start).getTime() - now; let start = new Date(now.toDateString() + ' ' + request.start).getTime() - now;
let end = new Date(now.toDateString() + ' ' + request.end).getTime() - now; let end = new Date(now.toDateString() + ' ' + request.end).getTime() - now;
console.error('next alarm is set for', request.id); const when = now.getTime() + Math.min(
chrome.alarms.create(name, { start < 0 ? start + 24 * 60 * 60 * 1000 : start,
when: now.getTime() + Math.min( end < 0 ? end + 24 * 60 * 60 * 1000 : end
start < 0 ? start + 24 * 60 * 60 * 1000 : start, );
end < 0 ? end + 24 * 60 * 60 * 1000 : end console.log(`next alarm is set for id = ${request.id}`, new Date(when), start, end);
) chrome.alarms.create(name, {when});
});
getStylesSafe({id: request.id}).then(([style]) => { getStylesSafe({id: request.id}).then(([style]) => {
if (style) { if (style) {
const enabled = start <= 0 && end > 0; const enabled = start <= 0 && end > 0;
console.error('Changing state', enabled, style.id); console.log(`style with id = ${style.id}; enabled = `, enabled);
saveStyleSafe({ saveStyleSafe({
id: request.id, id: request.id,
@ -82,7 +86,7 @@ schedule.execute = (name, request) => {
} }
else { else {
// clear schedule if style is not found // clear schedule if style is not found
console.error('removing since stlye is not found', request); console.log('removing from storage since style is not found', request);
schedule.execute(name, Object.assign( schedule.execute(name, Object.assign(
request, {enabled: false} request, {enabled: false}
)); ));
@ -90,7 +94,7 @@ schedule.execute = (name, request) => {
}); });
} }
else { else {
console.error('removing pref', name); console.log('removing pref since request.enabled is false', name);
schedule.prefs.remove(name); schedule.prefs.remove(name);
} }
}); });
@ -98,24 +102,18 @@ schedule.execute = (name, request) => {
// background only // background only
if (BG === window) { if (BG === window) {
schedule.prefs.subscribe((name, pref) => name.startsWith(SCHEDULE_PREFIX + '.') && schedule.execute(name, pref)); // listen for pref changes to update chrome.alarms
schedule.prefs.subscribe((name, pref) => schedule.prefs.validate(name) && schedule.execute(name, pref));
chrome.alarms.onAlarm.addListener(({name}) => { chrome.alarms.onAlarm.addListener(({name}) => {
schedule.prefs.get(name, prefs => { schedule.prefs.get(name, prefs => prefs[name] && schedule.execute(name, prefs[name]));
if (prefs[name]) {
schedule.execute(name, prefs[name]);
}
});
}); });
(function (callback) { (function (callback) {
chrome.idle.onStateChanged.addListener(state => { chrome.idle.onStateChanged.addListener(state => state === 'active' && callback());
if (state === 'active') {
callback();
}
});
window.setTimeout(callback); window.setTimeout(callback);
})(function () { })(function () {
console.log('updating all schedules');
schedule.prefs.getAll(prefs => prefs.forEach(a => schedule.execute(...a))); schedule.prefs.getAll(prefs => prefs.forEach(a => schedule.execute(...a)));
}); });
} }

View File

@ -1,13 +1,31 @@
/* global t, schedule */ /* global t, schedule, $, $$ */
'use strict'; 'use strict';
schedule.ui = {};
/* get start and end inputs */
schedule.ui.inputs = (parent) => $$('input[type=time]', parent);
/* updating schedule section of a single style */
schedule.ui.update = (request) => {
const parent = $(`[id="style-${request.id}"] .schedule`);
if (parent) {
parent.dataset.active = true;
$('input[type=button]', parent).value = t('scheduleButtonActive');
const [start, end] = schedule.ui.inputs(parent);
start.value = request.start;
end.value = request.end;
}
};
/* display schedule panel and hide it when user selects outside area */
document.addEventListener('click', e => { document.addEventListener('click', e => {
const target = e.target; const target = e.target;
let parent; let parent;
// hide schedule panel // hide schedule panel
function observe (e) { function observe (e) {
if (!parent.contains(e.target)) { if (!parent.contains(e.target)) {
const [start, end] = parent.querySelectorAll('input[type=time]'); const [start, end] = schedule.ui.inputs(parent);
const id = target.closest('.entry').id.replace('style-', ''); const id = target.closest('.entry').id.replace('style-', '');
switch ([start.value, end.value].filter(v => v).length) { switch ([start.value, end.value].filter(v => v).length) {
case 0: case 0:
@ -42,20 +60,22 @@ document.addEventListener('click', e => {
document.addEventListener('click', observe); document.addEventListener('click', observe);
} }
}); });
/* update schedule section on styles ready */
function test () { document.addEventListener('styles-ready', () => {
console.log('"styles-ready" is called');
schedule.prefs.getAll(prefs => { schedule.prefs.getAll(prefs => {
prefs.forEach(([name, pref]) => { prefs.forEach(([name, pref]) => schedule.ui.update(pref));
const parent = document.querySelector(`[id="style-${pref.id}"] .schedule`);
if (parent) {
parent.dataset.active = true;
parent.querySelector('input[type=button]').value = t('scheduleButtonActive');
const [start, end] = parent.querySelectorAll('input[type=time');
start.value = pref.start;
end.value = pref.end;
}
});
}); });
} });
/* update schedule section on style change */
window.setTimeout(test, 1000); document.addEventListener('style-edited', e => {
console.log('"style-edited" is called');
const id = e.detail.id;
const name = schedule.prefs.name(id);
schedule.prefs.get(name, prefs => {
const pref = prefs[name];
if (pref) {
schedule.ui.update(pref);
}
});
});