Reimplement relative time formatting as a filter, dropping dependency on deprecated moment.js

Signed-off-by: Alec Kojaev <alec@kojaev.name>
This commit is contained in:
Alec Kojaev 2021-07-16 15:40:04 +03:00 committed by Sean Molenaar
parent 691c42bdbc
commit 65c15dac38
10 changed files with 62 additions and 41 deletions

View File

@ -7,6 +7,8 @@ The format is almost based on [Keep a Changelog](https://keepachangelog.com/en/1
## [16.x.x]
### Changed
- Added vue and ng-vue packages
- Reimplemented relative time formatting as a filter
### Fixed
## [15.x.x]

View File

@ -25,7 +25,6 @@
"jquery": true,
"globals": {
"angular": true,
"moment": true,
"app": true,
"OC": true,
"csrfToken": true,

View File

@ -198,15 +198,6 @@ app.controller('ContentController', function (Publisher, FeedResource, ItemResou
});
};
this.getRelativeDate = function (timestamp) {
if (timestamp !== undefined && timestamp !== '') {
var languageCode = SettingsResource.get('language');
return moment.unix(timestamp).locale(languageCode).fromNow() + '';
} else {
return '';
}
};
this.refresh = function () {
$route.reload();
};

View File

@ -0,0 +1,58 @@
/**
* Nextcloud - News
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Alec Kojaev <alec@kojaev.name>
* @copyright Alec Kojaev 2021
*/
app.filter('relativeTimestamp', ['SettingsResource', function (SettingsResource) {
'use strict';
const languageCode = SettingsResource.get('language');
const relFormat = Intl.RelativeTimeFormat ?
new Intl.RelativeTimeFormat(languageCode, { numeric: 'auto' }) : null;
const maxRelDistance = 90*86400*1000;
const relLimits = [
[ 7*86400*1000, 'week' ],
[ 86400*1000, 'day' ],
[ 3600*1000, 'hour' ],
[ 60*1000, 'minute' ],
[ 1*1000, 'second' ]
];
const absLimits = [
[ 7*86400*1000, { hour: '2-digit', minute: '2-digit', dayPeriod: 'narrow',
year: 'numeric', month: 'short', day: 'numeric' } ],
[ 43200*1000, { hour: '2-digit', minute: '2-digit', dayPeriod: 'narrow',
weekday: 'long' } ],
[ 0, { hour: '2-digit', minute: '2-digit', dayPeriod: 'narrow' } ]
];
return function (timestamp) {
if (!Number.isFinite(timestamp)) {
return timestamp;
}
const ts = new Date(timestamp);
const dist = ts.getTime() - Date.now();
const absDist = Math.abs(dist);
if (relFormat && absDist < maxRelDistance) {
for (const [ scale, unit ] of relLimits) {
const value = Math.trunc(dist / scale);
if (value !== 0) {
return relFormat.format(value, unit);
}
}
// We arrive here only if distance from now is less than 1 second
return relFormat.format(0, 'second');
} else {
for (const [ limit, options ] of absLimits) {
if (absDist >= limit) {
return ts.toLocaleString(languageCode, options);
}
}
// We shouldn't be here
return ts.toLocaleString(languageCode, absLimits[absLimits.length - 1][1]);
}
};
}]);

View File

@ -30,7 +30,6 @@ const sources = [
'node_modules/angular-animate/angular-animate.min.js',
'node_modules/angular-route/angular-route.min.js',
'node_modules/angular-sanitize/angular-sanitize.min.js',
'node_modules/moment/min/moment-with-locales.min.js',
'node_modules/masonry-layout/dist/masonry.pkgd.min.js',
'node_modules/vue/dist/vue.js',
'node_modules/ngVue/build/index.js',

View File

@ -17,7 +17,6 @@ module.exports = function (config) {
// list of files / patterns to load in the browser
files: [
'node_modules/jquery/dist/jquery.js',
'node_modules/moment/min/moment-with-locales.js',
'node_modules/angular/angular.js',
'node_modules/angular-mocks/angular-mocks.js',
'node_modules/angular-route/angular-route.js',

5
js/package-lock.json generated
View File

@ -5601,11 +5601,6 @@
"minimist": "1.2.5"
}
},
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",

View File

@ -63,7 +63,6 @@
"angular-sanitize": "^1.8.2",
"debug": "^4.3.2",
"masonry-layout": "^4.2.2",
"moment": "^2.29.1",
"ngVue": "^1.7.8",
"vue": "^2.6.14"
}

View File

@ -513,27 +513,6 @@ describe('ContentController', function () {
expect(ctrl.autoPagingEnabled()).toBe(true);
}));
it('should return relative date', inject(function ($controller,
SettingsResource) {
SettingsResource.receive({language: 'en'});
var ctrl = $controller('ContentController', {
data: {},
});
expect(ctrl.getRelativeDate(12)).not.toBe('');
}));
it('should return relative date empty', inject(function ($controller) {
var ctrl = $controller('ContentController', {
data: {}
});
expect(ctrl.getRelativeDate('')).toBe('');
}));
it('should refresh the page', inject(function ($controller) {
var route = {
current: {

View File

@ -50,7 +50,7 @@
date:'yyyy-MM-dd HH:mm:ss' }}"
datetime="{{ item.pubDate*1000 |
date:'yyyy-MM-ddTHH:mm:ssZ' }}">
{{ Content.getRelativeDate(item.pubDate) }}
{{ item.pubDate*1000 | relativeTimestamp }}
</time>
</li>
<li ng-click="Content.toggleStar(item.id)"
@ -176,7 +176,7 @@
date:'yyyy-MM-dd HH:mm:ss' }}"
datetime="{{ item.pubDate*1000 |
date:'yyyy-MM-ddTHH:mm:ssZ' }}">
{{ Content.getRelativeDate(item.pubDate) }}
{{ item.pubDate*1000 | relativeTimestamp }}
</time>
<h1 ng-attr-dir="{{item.rtl && 'rtl'}}">
<a class="external"