diff --git a/beat/web/reports/static/reports/app/directives/lastEdited.js b/beat/web/reports/static/reports/app/directives/lastEdited.js new file mode 100644 index 0000000000000000000000000000000000000000..5f2c83b864450ca0c59465364c19bbb97dbfdc14 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/lastEdited.js @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * lastEdited + * Desc: + * shows a live & humanized last-edited timestamp + */ +angular.module('reportApp') +.directive("lastEdited", ['ReportService', '$interval', '$timeout', function(ReportService, $interval, $timeout){ + return { + scope: { + initStr: '@' + }, + link: function(scope){ + scope.ReportService = ReportService; + + // humanize some amount of time + const nowFunc = () => 'Just now'; + const secondFunc = (s) => `${s} second${s === 1 ? '' : 's'} ago`; + const minuteFunc = (m) => `${m} minute${m === 1 ? '' : 's'} ago`; + const hourFunc = (h) => `${h} hour${h === 1 ? '' : 's'} ago`; + + // convert from/to ms to/from a different time seg + const intoSeconds = (t) => Math.floor(t / 1000); + const intoMinutes = (t) => Math.floor(intoSeconds(t) / 60); + const intoHours = (t) => Math.floor(intoMinutes(t) / 60); + const fromSeconds = (s) => s * 1000; + const fromMinutes = (m) => fromSeconds(m * 60); + const fromHours = (h) => fromMinutes(h * 60); + + // cutoffs & corresponding update freq + const before1m = fromSeconds(5); + const before10m = fromMinutes(1); + const before1h = fromMinutes(10); + const after1h = fromHours(1); + + // different update funcs for diff time intervals + const beginning = () => nowFunc(); + const every5s = (t) => secondFunc(intoSeconds(t)); + const every1m = (t) => minuteFunc(intoMinutes(t)); + const every10m = (t) => minuteFunc(intoMinutes(t)); + const every1h = (t) => hourFunc(intoHours(t)); + + // current string to be shown + scope.currStr = { + val: scope.initStr + }; + + const updateCurrStr = (t, strFunc) => { + const str = strFunc(Date.now() - t); + scope.currStr.val = str; + return str; + }; + + // curr interval ids + let currIntervalIds = []; + + // interval funcs + const secondsInterval = (t) => { + const id = $interval(updateCurrStr, before1m, 0, true, t, every5s); + $timeout(() => $interval.cancel(id), fromSeconds(59)); + + return id; + }; + const singleMinutesInterval = (t) => { + const id = $interval(updateCurrStr, before10m, 0, true, t, every1m); + $timeout(() => $interval.cancel(id), fromMinutes(9.9)); + + return id; + }; + const tenMinutesInterval = (t) => { + const id = $interval(updateCurrStr, before1h, 0, true, t, every10m); + $timeout(() => $interval.cancel(id), fromMinutes(59)); + + return id; + }; + const hourInterval = (t) => { + const id = $interval(updateCurrStr, after1h, 0, true, t, every1h); + $timeout(() => $interval.cancel(id), fromMinutes(61)); + + return id; + }; + + const setupIntervals = (t) => { + const intervalIds = [ + secondsInterval(t), + singleMinutesInterval(t), + tenMinutesInterval(t), + hourInterval(t) + ]; + + return intervalIds; + }; + + const cancelIntervals = () => currIntervalIds.forEach(id => $interval.cancel(id)); + + const reset = () => { + cancelIntervals(); + updateCurrStr(0, beginning); + currIntervalIds = setupIntervals(ReportService.lastEditedTime); + }; + + // watch lastEditedTime for a change and start/restart the timers + scope.$watch('ReportService.lastEditedTime', (newTime, oldTime) => { + if(newTime > oldTime && newTime !== 0 && Date.now() > newTime){ + reset(); + } + }); + + // need to clear timers/intervals when this el is destroyed + scope.$on('$destroy', cancelIntervals); + + if(ReportService.lastEditedTime > 0){ + reset(); + } + }, + template: ` +Last Edited: <strong>{{ currStr.val }}</strong> +` + }; +}]); diff --git a/beat/web/reports/static/reports/app/directives/save.js b/beat/web/reports/static/reports/app/directives/save.js index 1ee131a3ed17ac8b7be8f96f25ccdd2464175c55..186e720b9dfce7c58e3d9e290e1d1a643a01fe89 100644 --- a/beat/web/reports/static/reports/app/directives/save.js +++ b/beat/web/reports/static/reports/app/directives/save.js @@ -44,8 +44,7 @@ angular.module('reportApp') return reportFactory.removeExperiments(ExperimentsService.cachedDeletedExperiments) .then(() => reportFactory.updateReport(ReportService.author, ReportService.name, saveData, '')) .then(() => { - const lastUpdatedEl = document.querySelector('#last-edited-text'); - lastUpdatedEl.innerHTML = 'Just now'; + ReportService.lastEditedTime = Date.now(); }) .catch(e => { ErrorService.logError(e, `Could not save the report.`); diff --git a/beat/web/reports/static/reports/app/services/reportService.js b/beat/web/reports/static/reports/app/services/reportService.js index 8490735909bde59b908d5518df70298d30727419..1dd40c91964468a18278f0554f180daf3c2629f5 100644 --- a/beat/web/reports/static/reports/app/services/reportService.js +++ b/beat/web/reports/static/reports/app/services/reportService.js @@ -35,6 +35,7 @@ angular.module('reportApp').factory('ReportService', ['GroupsService', 'plotterF rs.number = undefined; rs.author = undefined; rs.name = undefined; + rs.lastEditedTime = 0; rs.plotters = []; rs.defaultPlotters = []; @@ -118,7 +119,6 @@ angular.module('reportApp').factory('ReportService', ['GroupsService', 'plotterF openSourceAlgs.length && openSourceAlgs.length > 0 ? data : undefined ) .catch(error => { - console.error(`Failed to publish report: ${error}`); throw error; }); ; @@ -132,7 +132,6 @@ angular.module('reportApp').factory('ReportService', ['GroupsService', 'plotterF '' ) .catch(error => { - console.error(`Failed to lock report: ${error}`); throw error; }); ; diff --git a/beat/web/reports/templates/reports/report.html b/beat/web/reports/templates/reports/report.html index 5a45d4284cbbe01c00c7474e8147b0fe7c46f4e2..8fad84ee4fe76f691400d5904dc2a9a6adb1a884 100644 --- a/beat/web/reports/templates/reports/report.html +++ b/beat/web/reports/templates/reports/report.html @@ -103,6 +103,7 @@ <script src="{% fingerprint "reports/app/directives/save.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% fingerprint "reports/app/directives/error.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% fingerprint "reports/app/directives/experimentsTable.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="{% fingerprint "reports/app/directives/lastEdited.js" %}" type="text/javascript" charset="utf-8"></script> <!-- edit view --> {% if not report_number and report.get_status_display == 'Editable' and owner %} @@ -175,7 +176,7 @@ <i class="fa fa-calendar-o"></i> Created: <strong>{{ report.creation_date|naturaltime }}</strong><br/> {% endif %} {% if report.last_edited_date != None and not report.publication_date %} - <i class="fa fa-calendar-o"></i> Last Edited: <strong id='last-edited-text'>{{ report.last_edited_date|naturaltime }}</strong><br/> + <i class="fa fa-calendar-o"></i> <last-edited init-str='{{ report.last_edited_date|naturaltime }}'></last-edited><br/> {% endif %} {% if status == 'Editable' %}