From f03077edb70e939bfb447fea4cf46340f736b657 Mon Sep 17 00:00:00 2001 From: Alejandro Sirgo Rica Date: Mon, 16 Sep 2024 17:29:14 +0200 Subject: js: add ogStorage to prevent localStorage key collission Define ogStorage class to manage the localStorage operations. The new keys are constructed with the following structure: "group-context-id" Where group is either "show" for the collapsed items in the sidebar, or "check" for the selected checkboxes of the sidebar. Add sotrage versioning to delete obsolete localStorage when a new design for the storage is included in ogCP. --- ogcp/static/js/ogcp.js | 113 +++++++++++++++++++++++++++++---------------- ogcp/templates/base.html | 2 +- ogcp/templates/images.html | 2 +- ogcp/templates/macros.html | 2 +- ogcp/templates/repos.html | 2 +- 5 files changed, 77 insertions(+), 44 deletions(-) diff --git a/ogcp/static/js/ogcp.js b/ogcp/static/js/ogcp.js index d445094..943fccf 100644 --- a/ogcp/static/js/ogcp.js +++ b/ogcp/static/js/ogcp.js @@ -3,6 +3,61 @@ const macs = new Map(); const Interval = 1000; let updateTimeoutId = null; +const StorageGroup = Object.freeze({ + SHOW: 'show', + CHECK: 'check', +}); + +class ogStorage { + static STORAGE_VERSION = Object.freeze(1); + + static store(group, context, elemId, value) { + const key = `${group}-${context}-${elemId}`; + localStorage.setItem(key, value); + } + + static remove(group, context, elemId) { + const key = `${group}-${context}-${elemId}`; + localStorage.removeItem(key); + } + + static hasKey(group, context, elemId) { + const key = `${group}-${context}-${elemId}`; + return localStorage.getItem(key) !== null; + } + + static deleteInvalidStorage(items, group, context) { + if (localStorage.getItem('storageVersion') < ogStorage.STORAGE_VERSION) { + localStorage.clear(); + localStorage.setItem('storageVersion', ogStorage.STORAGE_VERSION); + return + } + + const prefix = `${group}-${context}`; + const existingKeys = items.map(function() { + return `${group}-${context}-${this.id}`; + }).get(); + + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + + if (!key.startsWith(prefix)) { + continue; + } + if (!existingKeys.includes(key)) { + localStorage.removeItem(key); + } + } + } + + static storeCheckboxStatus(checkbox, context) { + if (checkbox.checked) + ogStorage.store(StorageGroup.CHECK, context, checkbox.id, ""); + else + ogStorage.remove(StorageGroup.CHECK, context, checkbox.id); + } +} + async function show_client_mac(pill_id) { const pill = $('#' +pill_id); @@ -54,13 +109,6 @@ function showSelectedClientsOnEvents() { }); } -function storeCheckboxStatus(checkbox, context) { - if (checkbox.checked) - localStorage.setItem(context + checkbox.id, "check"); - else - localStorage.removeItem(context + checkbox.id); -} - function findParentCheckboxes(element) { const $element = $(element); const parents = $element.parentsUntil('#scopes').not('ul'); @@ -89,27 +137,10 @@ function setParentStatus(checkboxes) { }); } -function deleteInvalidStorageItems(items, context) { - const existingIds = items.map(function() { - return context + this.id; - }).get(); - - for (let i = 0; i < localStorage.length; i++) { - const key = localStorage.key(i); - - if (!key.startsWith(context)) { - continue; - } - if (!existingIds.includes(key)) { - localStorage.removeItem(key); - } - } -} - function configureCommandCheckboxes(context) { const checkboxes = $('input:checkbox[form="scopesForm"]'); - deleteInvalidStorageItems(checkboxes, context); + ogStorage.deleteInvalidStorage(checkboxes, StorageGroup.CHECK, context); // Ensure the form fields are sent $('#scopesForm').on('submit', function() { @@ -151,7 +182,7 @@ function configureCommandCheckboxes(context) { checkboxes.each(function() { showSelectedClient(this); - storeCheckboxStatus(this, context); + ogStorage.storeCheckboxStatus(this, context); }); }); } @@ -160,33 +191,35 @@ function keepSelectedClients(context) { const checkboxes = $('#sidebar input:checkbox') checkboxes.on('change', function (event) { - storeCheckboxStatus(this, context); + ogStorage.storeCheckboxStatus(this, context); }); - deleteInvalidStorageItems(checkboxes, context); + ogStorage.deleteInvalidStorage(checkboxes, StorageGroup.CHECK, context); checkboxes.each(function () { - if (localStorage.getItem(context + this.id) == 'check') { + if (ogStorage.hasKey(StorageGroup.CHECK, context, this.id)) { this.checked = true; $(this).trigger('show-client'); } }); } -function keepTreeState(selector) { - const tree = $(selector + ' .collapse'); +function keepTreeState(selector, context) { + const tree_items = $(selector + ' .collapse'); + + ogStorage.deleteInvalidStorage(tree_items, StorageGroup.SHOW, context); - tree.on('hidden.bs.collapse', function (event) { + tree_items.on('hidden.bs.collapse', function (event) { event.stopPropagation(); - localStorage.removeItem(this.id); + ogStorage.remove(StorageGroup.SHOW, context, this.id) }); - tree.on('shown.bs.collapse', function (event) { + tree_items.on('shown.bs.collapse', function (event) { event.stopPropagation(); - localStorage.setItem(this.id, 'show'); + ogStorage.store(StorageGroup.SHOW, context, this.id, "") }); - tree.each(function () { - if (localStorage.getItem(this.id) == 'show') { + tree_items.each(function () { + if (ogStorage.hasKey(StorageGroup.SHOW, context, this.id)) { $(this).collapse('show'); } else { $(this).siblings('a').addClass('collapsed'); @@ -324,14 +357,14 @@ function checkFolderParent(context) { folder.on('change', function() { const folder_parent = $('#' + $.escapeSelector(this.dataset.parentInput)); folder_parent.prop('checked', this.checked); - storeCheckboxStatus(folder_parent.get(0), context); + ogStorage.storeCheckboxStatus(folder_parent.get(0), context); }); } function limitCheckboxes(context) { const checkboxes = $('#sidebar input:checkbox'); - deleteInvalidStorageItems(checkboxes, context); + ogStorage.deleteInvalidStorage(checkboxes, StorageGroup.CHECK, context); checkboxes.on('change', function () { const currentCheckbox = $(this); @@ -353,7 +386,7 @@ function limitCheckboxes(context) { checkCheckbox('scope-server'); checkboxes.each(function() { - storeCheckboxStatus(this, context); + ogStorage.storeCheckboxStatus(this, context); showSelectedClient(this); }); }); diff --git a/ogcp/templates/base.html b/ogcp/templates/base.html index 80a5f7a..5aa84c6 100644 --- a/ogcp/templates/base.html +++ b/ogcp/templates/base.html @@ -111,7 +111,7 @@ - +