diff --git a/build.gradle b/build.gradle index 349e0e665..61e52befe 100644 --- a/build.gradle +++ b/build.gradle @@ -18,6 +18,10 @@ buildscript { } } +plugins { + id "com.gorylenko.gradle-git-properties" version "2.4.1" +} + version "$biocollectVersion" group "au.org.ala" diff --git a/grails-app/assets/javascripts/offline-list.js b/grails-app/assets/javascripts/offline-list.js index 97d3f7ca6..ecc7c5bc6 100644 --- a/grails-app/assets/javascripts/offline-list.js +++ b/grails-app/assets/javascripts/offline-list.js @@ -108,6 +108,10 @@ function ActivitiesViewModel (config) { } } + /** + * Soft delete an activity from list + * @param activity + */ self.remove = function(activity) { self.activities.remove(activity); } @@ -204,16 +208,34 @@ function ActivityViewModel (activity, parent) { return deferred.promise(); } + /** + * Hard delete an activity from the database + */ self.deleteActivity = function() { bootbox.confirm("This operation cannot be reversed. Are you sure you want to delete this activity?", function (result) { if (result) { - entities.deleteActivity().then(function () { - parent.refreshPage(); + self.uploading(true); + images = images || []; + var documentIds = images.map(image => image.documentId); + self.deleteImages({data:documentIds}).then(self.deleteSite).then(self.deleteActivityById).then(function (){ + parent.refreshPage(0); + }).then(function () { + self.uploading(false); + }, function () { + self.uploading(false); }); } }) } + self.deleteSite = function () { + return entities.deleteSites([activity.siteId]); + } + + self.deleteActivityById = function () { + return self.deleteActivityFromDB({data: {oldActivityId: activity.activityId}}); + } + self.removeMeFromList = function() { parent.remove(self); } diff --git a/grails-app/assets/javascripts/pwa-offline-list-manifest.js b/grails-app/assets/javascripts/pwa-offline-list-manifest.js index 7a91379ba..856900c51 100644 --- a/grails-app/assets/javascripts/pwa-offline-list-manifest.js +++ b/grails-app/assets/javascripts/pwa-offline-list-manifest.js @@ -4,6 +4,8 @@ //= require emitter/emitter.js //= require moment/moment.min.js //= require moment/moment-timezone-with-data.min.js +//= require bootstrap4/js/bootstrap.bundle.min.js +//= require bootbox/bootbox.min.js //= require knockout-dates.js //= require fieldcapture-application.js //= require enterBioActivityData.js diff --git a/grails-app/assets/javascripts/pwa-settings-manifest.js b/grails-app/assets/javascripts/pwa-settings-manifest.js new file mode 100644 index 000000000..a3c358b5d --- /dev/null +++ b/grails-app/assets/javascripts/pwa-settings-manifest.js @@ -0,0 +1,8 @@ +//= require jquery/3.4.1/jquery-3.4.1.min.js +//= require knockout/3.4.0/knockout-3.4.0.js +//= require utils.js +//= require dexiejs/dexie.js +//= require bootstrap4/js/bootstrap.bundle.min.js +//= require bootbox/bootbox.min.js +//= require entities.js +//= require pwa-settings.js \ No newline at end of file diff --git a/grails-app/assets/javascripts/pwa-settings.js b/grails-app/assets/javascripts/pwa-settings.js new file mode 100644 index 000000000..2c445ca27 --- /dev/null +++ b/grails-app/assets/javascripts/pwa-settings.js @@ -0,0 +1,83 @@ +function StorageViewModel() { + var self = this, + deleteSteps = ['cache', 'db']; + self.maximum = ko.observable(); + self.used = ko.observable(); + self.free = ko.observable(); + self.percentage = ko.computed(function () { + return Math.round(self.used() / self.maximum() * 100); + }); + self.isOffline = ko.observable(false); + self.deleteProgress = ko.observable(0); + self.deleteSteps = ko.observable(deleteSteps.length); + self.deletePercentage = ko.computed(function () { + return Math.round(self.deleteProgress() / self.deleteSteps() * 100); + }); + self.supported = ko.observable(true); + self.refresh = function () { + if (navigator.storage && navigator.storage.estimate) { + navigator.storage.estimate().then( + ({ usage, quota }) => { + var gbUnit = 1024 * 1024 * 1024; + self.maximum(quota / gbUnit); + self.used(usage / gbUnit); + self.free(self.maximum() - self.used()); + }, + error => console.warn(`error estimating quota: ${error.name}: ${error.message}`) + ); + } + else { + self.supported(false); + } + } + + self.clearAll = function () { + self.deleteProgress(0); + bootbox.confirm("This operation cannot be reversed. Are you sure you want to delete?", function (result) { + if (result) { + self.deleteCache().then(self.deleteDBEntries).then(function () { + self.deleteProgress(self.deleteSteps()); + }); + } + }); + } + + self.deleteCache = function () { + return caches.keys().then(function (cacheNames) { + return Promise.all( + cacheNames.map(function (cacheName) { + return caches.delete(cacheName); + }) + ); + }).then(function () { + self.refresh(); + self.deleteProgress(self.deleteProgress() + 1); + }); + } + + self.deleteDBEntries = function () { + return entities.deleteTable('offlineMap').then(function () { + return entities.deleteTable('taxon').then(function () { + self.deleteProgress(self.deleteProgress() + 1); + }); + }); + } + + document.addEventListener('offline', function () { + self.isOffline(true); + }); + + document.addEventListener('online', function () { + self.isOffline(false); + }); + + self.refresh(); +} + +function initialise() { + var storageViewModel = new StorageViewModel(); + ko.applyBindings(storageViewModel, document.getElementById('storage-settings')); + checkOfflineForIntervalAndTriggerEvents(5000); +} + +$(document).ready(initialise); \ No newline at end of file diff --git a/grails-app/assets/stylesheets/pwa-settings-manifest.css b/grails-app/assets/stylesheets/pwa-settings-manifest.css new file mode 100644 index 000000000..2d3c4f7b0 --- /dev/null +++ b/grails-app/assets/stylesheets/pwa-settings-manifest.css @@ -0,0 +1,3 @@ +/** + *= require base-bs4.css + */ \ No newline at end of file diff --git a/grails-app/conf/application.yml b/grails-app/conf/application.yml index 97e488901..a2a025500 100644 --- a/grails-app/conf/application.yml +++ b/grails-app/conf/application.yml @@ -311,7 +311,7 @@ grails: --- #pwa pwa: - appUrl: "http://localhost:8087" + appUrl: "http://localhost:5173" cache: ignore: ["/image/upload", "/ws/attachment/upload"] baseMapUrl: "https://api.maptiler.com/maps/hybrid/{z}/{x}/{y}.jpg?key=WlLML7VfJfVwucTTgdcR" diff --git a/grails-app/controllers/au/org/ala/biocollect/BioActivityController.groovy b/grails-app/controllers/au/org/ala/biocollect/BioActivityController.groovy index ef74c8ea3..4b988fc10 100644 --- a/grails-app/controllers/au/org/ala/biocollect/BioActivityController.groovy +++ b/grails-app/controllers/au/org/ala/biocollect/BioActivityController.groovy @@ -413,6 +413,9 @@ class BioActivityController { def pwaConfig () { } + def pwaSettings () { + } + /** * Preview activity survey form template * @param formName Survey form name diff --git a/grails-app/controllers/au/org/ala/biocollect/UrlMappings.groovy b/grails-app/controllers/au/org/ala/biocollect/UrlMappings.groovy index 6281e23a0..a6c5a84c5 100644 --- a/grails-app/controllers/au/org/ala/biocollect/UrlMappings.groovy +++ b/grails-app/controllers/au/org/ala/biocollect/UrlMappings.groovy @@ -181,6 +181,7 @@ class UrlMappings { "/pwa/indexFragment/$projectActivityId" (controller: 'bioActivity', action: 'pwaIndexFragment') "/pwa/offlineList" ( controller: 'bioActivity', action: 'pwaOfflineList' ) + "/pwa/settings" (controller: 'bioActivity', action: 'pwaSettings') "500"(controller:'error', action:'response500') diff --git a/grails-app/i18n/messages.properties b/grails-app/i18n/messages.properties index 4bb2bee40..c0a47a32f 100644 --- a/grails-app/i18n/messages.properties +++ b/grails-app/i18n/messages.properties @@ -1037,4 +1037,18 @@ pwa.offlinelist.species.heading=Species pwa.offlinelist.record.image.alt=Image associated with record pwa.offlinelist.record.noimage.alt=No image associated with record label.upload=Upload -bioactivity.save=Save \ No newline at end of file +bioactivity.save=Save +pwa.settings.heading=Settings +pwa.settings.storage.heading=Storage quota +pwa.settings.storage.total=Maximum storage (GB) +pwa.settings.storage.totalPercentage=Percentage used +pwa.settings.storage.used=Disk used (GB) +pwa.settings.storage.free=Free space (GB) +pwa.settings.storage.alert.heading=Unsupported +pwa.settings.storage.alert.message=Your browser does not support storage estimation. Some browsers that support this feature are - Safari (17), Chrome (61), Edge(79), Firefox(57) etc. +pwa.settings.storage.btn.refresh=Refresh +pwa.settings.manage.title=Manage storage +pwa.settings.manage.alert.heading=Delete items +pwa.settings.manage.alert.message=Delete items to make disk space. The following items will be deleted. Unpublished records will not be deleted. You will have to re-download surveys for them to wrok offline. +pwa.settings.manage.btn.clearAll=Delete +pwa.settings.manage.delete.progress=Deletion progress \ No newline at end of file diff --git a/grails-app/views/bioActivity/pwaOfflineList.gsp b/grails-app/views/bioActivity/pwaOfflineList.gsp index 5148e4bf2..483033b13 100644 --- a/grails-app/views/bioActivity/pwaOfflineList.gsp +++ b/grails-app/views/bioActivity/pwaOfflineList.gsp @@ -62,21 +62,23 @@
  • - + + - - + - + + + @@ -86,7 +88,7 @@