From 4cb4248d00f65389b6c3b5ceedd931c7c27b7238 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Mon, 20 Jan 2020 10:03:26 +0000 Subject: [PATCH] Replace localStorage with indexedDB. --- templates/web/base/common_scripts.html | 1 + web/cobrands/fixmystreet/offline.js | 269 +++++++++++++------------ 2 files changed, 145 insertions(+), 125 deletions(-) diff --git a/templates/web/base/common_scripts.html b/templates/web/base/common_scripts.html index 7df8feaa0f5..f94b3d464ba 100644 --- a/templates/web/base/common_scripts.html +++ b/templates/web/base/common_scripts.html @@ -46,6 +46,7 @@ END; IF c.user.has_body_permission_to('planned_reports'); scripts.push( + 'https://cdn.jsdelivr.net/npm/idb-keyval@3/dist/idb-keyval-iife.min.js', version('/cobrands/fixmystreet/offline.js'), ); END; diff --git a/web/cobrands/fixmystreet/offline.js b/web/cobrands/fixmystreet/offline.js index 76a6afc1f70..6c984072bc2 100644 --- a/web/cobrands/fixmystreet/offline.js +++ b/web/cobrands/fixmystreet/offline.js @@ -8,8 +8,7 @@ fixmystreet.offlineBanner = (function() { } // Note this non-global way of handling plurals may need looking at in future - function formText() { - var num = fixmystreet.offlineData.getForms().length; + function formText(num) { if ( num === 1 ) { return num + ' ' + translation_strings.offline.update_single; } else { @@ -17,12 +16,12 @@ fixmystreet.offlineBanner = (function() { } } - function onlineText() { - return sprintf(translation_strings.offline.saved_to_submit, formText()); + function onlineText(num) { + return sprintf(translation_strings.offline.saved_to_submit, formText(num)); } - function offlineText() { - return translation_strings.offline.you_are_offline + ' \u2013 ' + sprintf(translation_strings.offline.N_saved, formText()); + function offlineText(num) { + return translation_strings.offline.you_are_offline + ' \u2013 ' + sprintf(translation_strings.offline.N_saved, formText(num)); } function remove() { @@ -31,24 +30,29 @@ fixmystreet.offlineBanner = (function() { return { make: function(offline) { - var num = fixmystreet.offlineData.getForms().length; - var banner = ['

']; - if (offline || num > 0) { - banner.push(offline ? offlineText() : onlineText()); - } - banner.push('

'); - banner = $(banner.join('')); - banner.prependTo('.content'); - if (num === 0) { - banner.hide(); - } + fixmystreet.offlineData.getFormsLength().then(function(num) { + var banner = ['

']; + if (offline || num > 0) { + banner.push(offline ? offlineText(num) : onlineText(num)); + } + banner.push('

'); + banner = $(banner.join('')); + banner.prependTo('.content'); + if (num === 0) { + banner.hide(); + } + }); window.addEventListener("offline", function(e) { - $('#offline_forms').html(offlineText()); + fixmystreet.offlineData.getFormsLength().then(function(num) { + $('#offline_forms').html(offlineText(num)); + }); }); window.addEventListener("online", function(e) { - $('#offline_forms').html(onlineText()); + fixmystreet.offlineData.getFormsLength().then(function(num) { + $('#offline_forms').html(onlineText(num)); + }); }); function nextForm(DataOrJqXHR, textStatus, jqXHROrErrorThrown) { @@ -62,37 +66,41 @@ fixmystreet.offlineBanner = (function() { $(document).on('click', '#oFN', function(e) { e.preventDefault(); - fixmystreet.offlineData.getForms().forEach(function(form) { - $(document).queue('postForm', function() { - postForm(form[0], form[1]).fail(function(jqXHR) { - if (jqXHR.status !== 400) { - return nextForm(); - } - // In case the request failed due to out-of-date CSRF token, - // try once more with a new token given in the error response. - var m = jqXHR.responseText.match(/content="([^"]*)" name="csrf-token"/); - if (!m) { - return nextForm(); - } - var token = m[1]; - if (!token) { - return nextForm(); - } - var param = form[1].replace(/&token=[^&]*/, '&token=' + token); - return postForm(form[0], param).fail(nextForm); + fixmystreet.offlineData.getForms().then(function(forms) { + console.log(forms); + forms.forEach(function(form) { + $(document).queue('postForm', function() { + postForm(form[0], form[1]).fail(function(jqXHR) { + if (jqXHR.status !== 400) { + return nextForm(); + } + // In case the request failed due to out-of-date CSRF token, + // try once more with a new token given in the error response. + var m = jqXHR.responseText.match(/content="([^"]*)" name="csrf-token"/); + if (!m) { + return nextForm(); + } + var token = m[1]; + if (!token) { + return nextForm(); + } + var param = form[1].replace(/&token=[^&]*/, '&token=' + token); + return postForm(form[0], param).fail(nextForm); + }); }); }); + $(document).dequeue('postForm'); }); - $(document).dequeue('postForm'); }); }, update: function() { $('.top_banner--offline').slideDown(); - $('#offline_forms span').text(formText()); - var num = fixmystreet.offlineData.getForms().length; - if (num === 0) { - window.setTimeout(remove, 3000); - } + fixmystreet.offlineData.getFormsLength().then(function(num) { + $('#offline_forms span').text(formText(num)); + if (num === 0) { + window.setTimeout(remove, 3000); + } + }); }, startProgress: function(l) { $('.top_banner--offline').slideDown(); @@ -115,61 +123,64 @@ fixmystreet.offlineData = (function() { var data; function getData() { - if (data === undefined) { - data = JSON.parse(localStorage.getItem('offlineData')); - if (!data) { - data = { cachedReports: {}, forms: [] }; - } + if (data !== undefined) { + return Promise.resolve(data); } - return data; + return idbKeyval.get('offlineData').then(function(d) { + data = d || { cachedReports: {}, forms: [] }; + return data; + }); } - function saveData() { - localStorage.setItem('offlineData', JSON.stringify(getData())); + function updateData(cb) { + getData().then(function(data) { + cb(data); + idbKeyval.set('offlineData', data); + }); } return { + getFormsLength: function() { + return getData().then(function(data) { return data.forms.length; }); + }, getForms: function() { - return getData().forms; + return getData().then(function(data) { return data.forms; }); }, addForm: function(action, formData) { - var forms = getData().forms; - if (!forms.length || formData != forms[forms.length - 1][1]) { - forms.push([action, formData]); - saveData(); - } - fixmystreet.offlineBanner.update(); + updateData(function(data) { + var forms = data.forms; + if (!forms.length || formData != forms[forms.length - 1][1]) { + forms.push([action, formData]); + } + fixmystreet.offlineBanner.update(); + }); }, shiftForm: function(idx) { - getData().forms.shift(); - saveData(); - fixmystreet.offlineBanner.update(); + updateData(function(data) { + data.forms.shift(); + fixmystreet.offlineBanner.update(); + }); }, clearForms: function(idx) { - getData().forms = []; - saveData(); - fixmystreet.offlineBanner.update(); - }, - getCachedUrls: function() { - return Object.keys(getData().cachedReports); + updateData(function(data) { + data.forms = []; + fixmystreet.offlineBanner.update(); + }); }, - isIndexed: function(url, lastupdate) { - if (lastupdate) { - return getData().cachedReports[url] === lastupdate; - } - return !!getData().cachedReports[url]; + getCachedReports: function() { + return getData().then(function(data) { return data.cachedReports; }); }, add: function(url, lastupdate) { - var data = getData(); - data.cachedReports[url] = lastupdate || "-"; - saveData(); + updateData(function(data) { + data.cachedReports[url] = lastupdate || "-"; + }); }, remove: function(urls) { - var data = getData(); - urls.forEach(function(url) { - delete data.cachedReports[url]; + updateData(function(data) { + urls.forEach(function(url) { + delete data.cachedReports[url]; + }); }); - saveData(); } }; })(); @@ -200,7 +211,7 @@ fixmystreet.cachet = (function(){ item.url + '/map' // Static map image ]; $reportPage.find('img').each(function(i, img) { - if (img.src.indexOf('/photo/') === -1 || fixmystreet.offlineData.isIndexed(img.src) || urlsInProgress[img.src]) { + if (img.src.indexOf('/photo/') === -1 || urlsInProgress[img.src]) { return; } imagesToGet.push(img.src); @@ -247,27 +258,30 @@ fixmystreet.offline = (function() { var toRemove = []; var shouldBeCached = {}; - localStorage.setItem('/my/planned', $('.item-list').html()); + idbKeyval.set('/my/planned', $('.item-list').html()); - getReportsFromList().forEach(function(item, i) { - if (!fixmystreet.offlineData.isIndexed(item.url, item.lastupdate)) { - toCache.push(item); - } - shouldBeCached[item.url] = 1; - }); + fixmystreet.offlineData.getCachedReports().then(function(reports) { + getReportsFromList().forEach(function(item, i) { + var t = reports[item.url]; + if (t !== item.lastupdate) { + toCache.push(item); + } + shouldBeCached[item.url] = 1; + }); + + Object.keys(reports).forEach(function(url) { + if ( !shouldBeCached[url] ) { + toRemove.push(url); + } + }); - fixmystreet.offlineData.getCachedUrls().forEach(function(url) { - if ( !shouldBeCached[url] ) { - toRemove.push(url); + if (toRemove[0]) { + removeReports(toRemove); + } + if (toCache[0]) { + fixmystreet.cachet.cacheReports(toCache); } }); - - if (toRemove[0]) { - removeReports(toRemove); - } - if (toCache[0]) { - fixmystreet.cachet.cacheReports(toCache); - } } // Remove a list of reports from the offline cache @@ -299,20 +313,22 @@ fixmystreet.offline = (function() { $('.js-back-to-report-list').attr('href', '/my/planned'); // Refill form with saved data if there is any - var savedForm; - fixmystreet.offlineData.getForms().forEach(function(form) { - if (form[0].endsWith(url)) { - savedForm = form[1]; - } - }); - if (savedForm) { - savedForm.replace(/\+/g, '%20').split('&').forEach(function(kv) { - kv = kv.split('=', 2); - if (kv[0] != 'include_update' && kv[0] != 'public_update' && kv[0] != 'save') { - $('[name=' + kv[0] + ']').val(decodeURIComponent(kv[1])); + fixmystreet.offlineData.getForms().then(function(forms) { + var savedForm; + forms.forEach(function(form) { + if (form[0].endsWith(url)) { + savedForm = form[1]; } }); - } + if (savedForm) { + savedForm.replace(/\+/g, '%20').split('&').forEach(function(kv) { + kv = kv.split('=', 2); + if (kv[0] != 'include_update' && kv[0] != 'public_update' && kv[0] != 'save') { + $('[name=' + kv[0] + ']').val(decodeURIComponent(kv[1])); + } + }); + } + }); // If we catch the form submit, e.g. Chrome still seems to // try and submit and we get the Chrome offline error page @@ -343,33 +359,36 @@ if (!navigator.onLine && location.pathname.indexOf('/report') === 0) { if ($('#offline_list').length) { // We are OFFLINE - var html = localStorage.getItem('/my/planned'); - if (html) { + idbKeyval.get('/my/planned').then(function(html) { + if (!html) { return; } $('#offline_list').before('

'+translation_strings.offline.your_reports+'

'); $('#offline_list').html(html); if (location.search.indexOf('saved=1') > 0) { $('#offline_list').before('

'+translation_strings.offline.update_saved+'

'); } - var offlineForms = fixmystreet.offlineData.getForms(); - var savedForms = {}; - offlineForms.forEach(function(form) { - savedForms[form[0]] = 1; - }); - $('#offline_list a').each(function(i, a) { - if (savedForms[a.href]) { - $(this).find('h3').prepend(''+translation_strings.offline.update_data_saved+' '); - } + fixmystreet.offlineData.getForms().then(function(offlineForms) { + var savedForms = {}; + offlineForms.forEach(function(form) { + savedForms[form[0]] = 1; + }); + $('#offline_list a').each(function(i, a) { + if (savedForms[a.href]) { + $(this).find('h3').prepend(''+translation_strings.offline.update_data_saved+' '); + } + }); }); - $('#offline_clear').css('margin-top', '5em').html(''); - $('#js-clear-localStorage').click(function() { + $('#offline_clear').css('margin-top', '5em').html(''); + $('#js-clear-storage').click(function() { if (window.confirm(translation_strings.offline.are_you_sure)) { - fixmystreet.offline.removeReports(fixmystreet.offlineData.getCachedUrls()); + fixmystreet.offlineData.getCachedReports().then(function(reports) { + fixmystreet.offline.removeReports(Object.keys(reports)); + }); fixmystreet.offlineData.clearForms(); - localStorage.removeItem('/my/planned'); + idbKeyval.del('/my/planned'); alert(translation_strings.offline.data_cleared); } }); - } + }); fixmystreet.offlineBanner.make(true); } else { fixmystreet.offlineBanner.make(false);