diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 2fdc432f4..816bdbb7f 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -235,6 +235,18 @@ "message":"Enable experimental features (About<\/a>)", "description":"English: Enable experimental features" }, + "settingsStorageUsed":{ + "message":"Storage used: {{value}} bytes", + "description":"English: Storage used: {{}} bytes" + }, + "settingsLastRestorePrompt":{ + "message":"Last restore:", + "description":"English: Last restore:" + }, + "settingsLastBackupPrompt":{ + "message":"Last backup:", + "description":"English: Last backup:" + }, "3pListsOfBlockedHostsPrompt":{ "message":"{{netFilterCount}} network filters + {{cosmeticFilterCount}} cosmetic filters from:", "description":"English: {{netFilterCount}} network filters + {{cosmeticFilterCount}} cosmetic filters from:" diff --git a/src/js/background.js b/src/js/background.js index da7dad1d9..1419a43b7 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -92,6 +92,13 @@ return { selfieMagic: 'spqmeuaftfra' }, + restoreBackupSettings: { + lastRestoreFile: '', + lastRestoreTime: 0, + lastBackupFile: '', + lastBackupTime: 0 + }, + // EasyList, EasyPrivacy and many others have an 4-day update period, // as per list headers. updateAssetsEvery: 97 * oneHour, diff --git a/src/js/messaging.js b/src/js/messaging.js index e818b35c1..72b51f895 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -954,24 +954,58 @@ var µb = µBlock; /******************************************************************************/ -var getUserData = function(callback) { - var onUserFiltersReady = function(details) { +var getLocalData = function(callback) { + var onStorageInfoReady = function(bytesInUse) { + var o = µb.restoreBackupSettings; callback({ - 'timeStamp': Date.now(), - 'version': vAPI.app.version, - 'userSettings': µb.userSettings, - 'filterLists': µb.remoteBlacklists, - 'netWhitelist': µb.stringFromWhitelist(µb.netWhitelist), - 'userFilters': details.content + storageUsed: bytesInUse, + lastRestoreFile: o.lastRestoreFile, + lastRestoreTime: o.lastRestoreTime, + lastBackupFile: o.lastBackupFile, + lastBackupTime: o.lastBackupTime }); }; + + µb.getBytesInUse(onStorageInfoReady); +}; + +/******************************************************************************/ + +var backupUserData = function(callback) { + var onUserFiltersReady = function(details) { + var userData = { + timeStamp: Date.now(), + version: vAPI.app.version, + userSettings: µb.userSettings, + filterLists: µb.remoteBlacklists, + netWhitelist: µb.stringFromWhitelist(µb.netWhitelist), + userFilters: details.content + }; + var now = new Date(); + var filename = vAPI.i18n('aboutBackupFilename') + .replace('{{datetime}}', now.toLocaleString()) + .replace(/ +/g, '_'); + + vAPI.download({ + 'url': 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(userData, null, ' ')), + 'filename': filename + }); + + µb.restoreBackupSettings.lastBackupFile = filename; + µb.restoreBackupSettings.lastBackupTime = Date.now(); + µb.XAL.keyvalSetMany(µb.restoreBackupSettings); + + getLocalData(callback); + }; + µb.assets.get('assets/user/filters.txt', onUserFiltersReady); }; /******************************************************************************/ -var restoreUserData = function(userData) { - var countdown = 5; +var restoreUserData = function(request) { + var userData = request.userData; + var countdown = 6; var onCountdown = function() { countdown -= 1; if ( countdown === 0 ) { @@ -987,6 +1021,13 @@ var restoreUserData = function(userData) { µb.XAL.keyvalSetOne('remoteBlacklists', userData.filterLists, onCountdown); µb.XAL.keyvalSetOne('netWhitelist', userData.netWhitelist, onCountdown); µb.assets.put('assets/user/filters.txt', userData.userFilters, onCountdown); + + µb.XAL.keyvalSetMany({ + lastRestoreFile: request.file || '', + lastRestoreTime: Date.now(), + lastBackupFile: '', + lastBackupTime: 0 + }, onCountdown); }; // If we are going to restore all, might as well wipe out clean local @@ -999,7 +1040,7 @@ var restoreUserData = function(userData) { var resetUserData = function() { µb.XAL.keyvalRemoveAll(); // Keep global counts, people can become quite attached to numbers - µBlock.saveLocalSettings(); + µb.saveLocalSettings(); vAPI.app.restart(); }; @@ -1008,8 +1049,11 @@ var resetUserData = function() { var onMessage = function(request, sender, callback) { // Async switch ( request.what ) { - case 'getUserData': - return getUserData(callback); + case 'backupUserData': + return backupUserData(callback); + + case 'getLocalData': + return getLocalData(callback); default: break; @@ -1020,7 +1064,7 @@ var onMessage = function(request, sender, callback) { switch ( request.what ) { case 'restoreUserData': - restoreUserData(request.userData); + restoreUserData(request); break; case 'resetUserData': diff --git a/src/js/settings.js b/src/js/settings.js index cf9981b2f..ddd8bb05c 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -33,27 +33,16 @@ var messager = vAPI.messaging.channel('settings.js'); /******************************************************************************/ -var exportToFile = function() { - var onUserDataReady = function(userData) { - if (!userData) { - return; - } - var now = new Date(); - var filename = vAPI.i18n('aboutBackupFilename') - .replace('{{datetime}}', now.toLocaleString()) - .replace(/ +/g, '_'); - vAPI.download({ - 'url': 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(userData, null, ' ')), - 'filename': filename - }); - }; - - messager.send({ what: 'getUserData' }, onUserDataReady); -}; - -/******************************************************************************/ - var handleImportFilePicker = function() { + var file = this.files[0]; + if ( file === undefined || file.name === '' ) { + return; + } + if ( file.type.indexOf('text') !== 0 ) { + return; + } + var filename = file.name; + var fileReaderOnLoadHandler = function() { var userData; try { @@ -80,20 +69,17 @@ var handleImportFilePicker = function() { } var time = new Date(userData.timeStamp); var msg = vAPI.i18n('aboutRestoreDataConfirm') - .replace('{{time}}', time.toLocaleString()); + .replace('{{time}}', time.toLocaleString()); var proceed = window.confirm(msg); if ( proceed ) { - messager.send({ what: 'restoreUserData', userData: userData }); + messager.send({ + what: 'restoreUserData', + userData: userData, + file: filename + }); } }; - var file = this.files[0]; - if ( file === undefined || file.name === '' ) { - return; - } - if ( file.type.indexOf('text') !== 0 ) { - return; - } var fr = new FileReader(); fr.onload = fileReaderOnLoadHandler; fr.readAsText(file); @@ -112,6 +98,47 @@ var startImportFilePicker = function() { /******************************************************************************/ +var exportToFile = function() { + messager.send({ what: 'backupUserData' }, onLocalDataReceived); +}; + +/******************************************************************************/ + +var onLocalDataReceived = function(details) { + uDom('#localData > ul > li:nth-of-type(1)').text( + vAPI.i18n('settingsStorageUsed').replace('{{value}}', details.storageUsed.toLocaleString()) + ); + + var elem, dt; + var timeOptions = { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + timeZoneName: 'short' + }; + var lastBackupFile = details.lastBackupFile || ''; + if ( lastBackupFile !== '' ) { + dt = new Date(details.lastBackupTime); + uDom('#localData > ul > li:nth-of-type(2) > ul > li:nth-of-type(1)').text(dt.toLocaleString('fullwide', timeOptions)); + uDom('#localData > ul > li:nth-of-type(2) > ul > li:nth-of-type(2)').text(lastBackupFile); + uDom('#localData > ul > li:nth-of-type(2)').css('display', ''); + } + + var lastRestoreFile = details.lastRestoreFile || ''; + elem = uDom('#localData > p:nth-of-type(3)'); + if ( lastRestoreFile !== '' ) { + dt = new Date(details.lastRestoreTime); + uDom('#localData > ul > li:nth-of-type(3) > ul > li:nth-of-type(1)').text(dt.toLocaleString('fullwide', timeOptions)); + uDom('#localData > ul > li:nth-of-type(3) > ul > li:nth-of-type(2)').text(lastRestoreFile); + uDom('#localData > ul > li:nth-of-type(3)').css('display', ''); + } +}; + +/******************************************************************************/ + var resetUserData = function() { var msg = vAPI.i18n('aboutResetDataConfirm'); var proceed = window.confirm(msg); @@ -175,6 +202,7 @@ var onUserSettingsReceived = function(details) { uDom.onLoad(function() { messager.send({ what: 'userSettings' }, onUserSettingsReceived); + messager.send({ what: 'getLocalData' }, onLocalDataReceived); }); /******************************************************************************/ diff --git a/src/js/start.js b/src/js/start.js index bca966bb0..b1c82f84a 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -143,12 +143,6 @@ var onUserSettingsReady = function(fetched) { /******************************************************************************/ -var onLocalSettingsReady = function(fetched) { - fromFetch(µb.localSettings, fetched); -}; - -/******************************************************************************/ - // Housekeeping, as per system setting changes var onSystemSettingsReady = function(fetched) { @@ -170,11 +164,11 @@ var onSystemSettingsReady = function(fetched) { /******************************************************************************/ var onFirstFetchReady = function(fetched) { - // Order is important -- do not change: onSystemSettingsReady(fetched); - onLocalSettingsReady(fetched); + fromFetch(µb.localSettings, fetched); onUserSettingsReady(fetched); + fromFetch(µb.restoreBackupSettings, fetched); onNetWhitelistReady(fetched.netWhitelist); onVersionReady(fetched.version); @@ -191,6 +185,10 @@ var onFirstFetchReady = function(fetched) { var fetchableProps = { 'compiledMagic': '', + 'lastRestoreFile': '', + 'lastRestoreTime': 0, + 'lastBackupFile': '', + 'lastBackupTime': 0, 'netWhitelist': '', 'selfie': null, 'selfieMagic': '', @@ -222,6 +220,7 @@ var fromFetch = function(to, fetched) { toFetch(µb.localSettings, fetchableProps); toFetch(µb.userSettings, fetchableProps); +toFetch(µb.restoreBackupSettings, fetchableProps); vAPI.storage.get(fetchableProps, onFirstFetchReady); diff --git a/src/js/storage.js b/src/js/storage.js index 0c414113c..478f3a200 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -25,9 +25,13 @@ /******************************************************************************/ -µBlock.getBytesInUse = function() { +µBlock.getBytesInUse = function(callback) { + if ( typeof callback !== 'function' ) { + callback = this.noopFunc; + } var getBytesInUseHandler = function(bytesInUse) { µBlock.storageUsed = bytesInUse; + callback(bytesInUse); }; vAPI.storage.getBytesInUse(null, getBytesInUseHandler); }; diff --git a/src/settings.html b/src/settings.html index fe376538c..e52cbd790 100644 --- a/src/settings.html +++ b/src/settings.html @@ -7,9 +7,21 @@