Drop Moment.JS, use custom small lib for relative dates and formatting

This commit is contained in:
Lucas Verney 2018-08-28 20:36:36 +02:00
parent 2ccc4110d6
commit f0fc8f287d
15 changed files with 164 additions and 121 deletions

View File

@ -19,7 +19,6 @@
"gps-to-gpx": "^1.4.0", "gps-to-gpx": "^1.4.0",
"howler": "^2.0.15", "howler": "^2.0.15",
"material-icons": "^0.2.3", "material-icons": "^0.2.3",
"moment": "^2.22.2",
"nosleep.js": "^0.7.0", "nosleep.js": "^0.7.0",
"ol": "^5.1.3", "ol": "^5.1.3",
"roboto-fontface": "^0.9.0", "roboto-fontface": "^0.9.0",

View File

@ -49,10 +49,10 @@
</template> </template>
<script> <script>
import moment from 'moment';
import { Howl } from 'howler'; import { Howl } from 'howler';
import { REPORT_TYPES, REPORT_ALARM_VIBRATION_SEQUENCE } from '@/constants'; import { REPORT_TYPES, REPORT_ALARM_VIBRATION_SEQUENCE } from '@/constants';
import { distanceInWordsToNow } from '@/tools/date';
import beepSound from '@/assets/beep.mp3'; import beepSound from '@/assets/beep.mp3';
export default { export default {
@ -62,7 +62,7 @@ export default {
if (reportID != null) { if (reportID != null) {
const report = this.$store.state.reports.find(item => item.id === reportID); const report = this.$store.state.reports.find(item => item.id === reportID);
return { return {
fromNow: moment(report.attributes.datetime).fromNow(), fromNow: distanceInWordsToNow(Date.parse(report.attributes.datetime)),
icon: this.icons[report.attributes.type], icon: this.icons[report.attributes.type],
id: report.id, id: report.id,
label: this.$t(`reportLabels.${report.attributes.type}`), label: this.$t(`reportLabels.${report.attributes.type}`),

View File

@ -69,6 +69,15 @@
"preventSuspendDescription": "When the map is displayed, the device will be prevented from going to sleep.", "preventSuspendDescription": "When the map is displayed, the device will be prevented from going to sleep.",
"vibrate": "Vibrate" "vibrate": "Vibrate"
}, },
"relativeDate": {
"ago": "{duration} ago",
"second": "one second | {count} seconds",
"minute": "one minute | {count} minutes",
"hour": "one hour | {count} hours",
"day": "one day | {count} days",
"month": "one month | {count} months",
"year": "one year | {count} years"
},
"reportCard": { "reportCard": {
"Reported": "Reported {fromNow}." "Reported": "Reported {fromNow}."
}, },

View File

@ -69,6 +69,15 @@
"preventSuspendDescription": "Quand la carte est affich\u00e9e, l'appareil ne pourra pas passer en veille automatiquement.", "preventSuspendDescription": "Quand la carte est affich\u00e9e, l'appareil ne pourra pas passer en veille automatiquement.",
"vibrate": "Vibrer" "vibrate": "Vibrer"
}, },
"relativeDate": {
"ago": "il y a {duration}",
"day": "un jour | {count} jours",
"hour": "une heure | {count} heures",
"minute": "une minute | {count} minutes",
"month": "un mois | {count} mois",
"second": "une seconde | {count} secondes",
"year": "un an | {count} ans"
},
"reportCard": { "reportCard": {
"Reported": "Signal\u00e9 {fromNow}." "Reported": "Signal\u00e9 {fromNow}."
}, },

View File

@ -1,35 +1,27 @@
import Vue from 'vue'; import Vue from 'vue';
import VueI18n from 'vue-i18n'; import VueI18n from 'vue-i18n';
// Moment locales
import 'moment/locale/en-gb';
import 'moment/locale/fr';
// App locales // App locales
import en from './en.json'; import en from './en.json';
import fr from './fr.json'; import fr from './fr.json';
import oc from './oc.json'; import oc from './oc.json';
// Local moment locales export const AVAILABLE_LOCALES = {
import './moment/oc'; en: {
export const AVAILABLE_LOCALES = [
{
iso: 'en',
name: 'English',
messages: en, messages: en,
name: 'English',
}, },
{ fr: {
iso: 'fr', iso: 'fr',
name: 'Français',
messages: fr, messages: fr,
name: 'Français',
}, },
{ oc: {
iso: 'oc', iso: 'oc',
name: 'Occitan',
messages: oc, messages: oc,
name: 'Occitan',
}, },
]; };
export function getBrowserLocales() { export function getBrowserLocales() {
let langs = []; let langs = [];
@ -59,8 +51,8 @@ export function getBrowserLocales() {
Vue.use(VueI18n); Vue.use(VueI18n);
export const messages = {}; export const messages = {};
AVAILABLE_LOCALES.forEach((item) => { Object.keys(AVAILABLE_LOCALES).forEach((iso) => {
messages[item.iso] = item.messages; messages[iso] = AVAILABLE_LOCALES[iso].messages;
}); });
export default new VueI18n({ export default new VueI18n({

View File

@ -1,80 +0,0 @@
//! moment.js locale configuration
//! locale : Occitan, lengadocian dialecte [oc-lnc]
//! author : Quentí
/* eslint-disable */
import moment from 'moment';
export default moment.defineLocale('oc', {
months : {
standalone: 'genièr_febrièr_març_abril_mai_junh_julhet_agost_setembre_octòbre_novembre_decembre'.split('_'),
format: 'de genièr_de febrièr_de març_d\'abril_de mai_de junh_de julhet_d\'agost_de setembre_d\'octòbre_de novembre_de decembre'.split('_'),
isFormat: /D[oD]?(\s)+MMMM/
},
monthsShort : 'gen._febr._març._abr._mai._junh._jul._ag._set._oct._nov._dec.'.split('_'),
monthsParseExact : true,
weekdays : 'dimenge_diluns_dimars_dimècres_dijòus_divendres_dissabte'.split('_'),
weekdaysShort : 'dg._dl._dm._dc._dj._dv._ds.'.split('_'),
weekdaysMin : 'dg_dl_dm_dc_dj_dv_ds'.split('_'),
weekdaysParseExact : true,
longDateFormat : {
LT : 'H:mm',
LTS : 'H:mm:ss',
L : 'DD/MM/YYYY',
LL : 'D MMMM [de] YYYY',
ll : 'D MMM YYYY',
LLL : 'D MMMM [de] YYYY [a] H:mm',
lll : 'D MMM YYYY, H:mm',
LLLL : 'dddd D MMMM [de] YYYY [a] H:mm',
llll : 'ddd D MMM YYYY, H:mm'
},
calendar : {
sameDay : function () {
return '[uèi a] LT';
},
nextDay : function () {
return '[deman a] LT';
},
nextWeek : function () {
return 'dddd [a] LT';
},
lastDay : function () {
return '[ièr a] LT';
},
lastWeek : function () {
return 'dddd [passat a] LT';
},
sameElse : 'L'
},
relativeTime : {
future : 'd\'aquí %s',
past : 'fa %s',
s : 'unas segondas',
ss : '%d segondas',
m : 'una minuta',
mm : '%d minutas',
h : 'una ora',
hh : '%d oras',
d : 'un jorn',
dd : '%d jorns',
M : 'un mes',
MM : '%d meses',
y : 'un an',
yy : '%d ans'
},
dayOfMonthOrdinalParse: /\d{1,2}(r|n|t|è|a)/,
ordinal : function (number, period) {
var output = (number === 1) ? 'r' :
(number === 2) ? 'n' :
(number === 3) ? 'r' :
(number === 4) ? 't' : 'è';
if (period === 'w' || period === 'W') {
output = 'a';
}
return number + output;
},
week : {
dow : 1, // Monday is the first day of the week.
doy : 4 // The week that contains Jan 4th is the first week of the year.
}
});

View File

@ -50,6 +50,7 @@
"About": "Ajuda", "About": "Ajuda",
"Settings": "Prefer\u00e9ncias", "Settings": "Prefer\u00e9ncias",
"exportGPX": "Exportar en GPX", "exportGPX": "Exportar en GPX",
"reportIssue": "Senhalar un probl\u00e8ma",
"shareMapView": "Partejar la vista de la mapa" "shareMapView": "Partejar la vista de la mapa"
}, },
"misc": { "misc": {
@ -68,6 +69,15 @@
"preventSuspendDescription": "Quand la mapa es mostrada, lo periferic passar\u00e0 pas en velha.", "preventSuspendDescription": "Quand la mapa es mostrada, lo periferic passar\u00e0 pas en velha.",
"vibrate": "Vibracions" "vibrate": "Vibracions"
}, },
"relativeDate": {
"ago": "fa {duration}",
"day": "un jorn | {count} jorns",
"hour": "una ora | {count} oras",
"minute": "una minuta | {count} minutas",
"month": "un mes | {count} meses",
"second": "unas segondas | {count} segondas",
"year": "un an | {count} ans"
},
"reportCard": { "reportCard": {
"Reported": "Senhalat {fromNow}." "Reported": "Senhalat {fromNow}."
}, },
@ -75,6 +85,12 @@
"unableToSendDescription": "Una error ret empacha lo mandad\u00eds del darri\u00e8r senhalament.", "unableToSendDescription": "Una error ret empacha lo mandad\u00eds del darri\u00e8r senhalament.",
"unableToSendTitle": "Impossible d'enviar lo darri\u00e8r senhalament" "unableToSendTitle": "Impossible d'enviar lo darri\u00e8r senhalament"
}, },
"reportIssueModal": {
"description": "Descripcion",
"includeGPX": "Inclure la tra\u00e7a GPS actuala\u00a0?",
"reportIssue": "Senhalar un probl\u00e8ma",
"send": "Enviar"
},
"reportLabels": { "reportLabels": {
"accident": "Accident", "accident": "Accident",
"accidentDescription": "Un accident sus la rota (automaticament suprimit apr\u00e8p una ora).", "accidentDescription": "Un accident sus la rota (automaticament suprimit apr\u00e8p una ora).",
@ -90,9 +106,12 @@
"potholeDescription": "Un trauc per la rota." "potholeDescription": "Un trauc per la rota."
}, },
"settings": { "settings": {
"autorotate": "Vira automaticament (direccion actuala amont)",
"customTileServer": "Servidor de teulas personalizat", "customTileServer": "Servidor de teulas personalizat",
"customTileServerURL": "URL del servidor de teulas personalizat", "customTileServerURL": "URL del servidor de teulas personalizat",
"customTileServerURLHint": "Per exemple\u00a0: http://tile.thunderforest.com/cycle/{z}/{x}/{y}.png", "customTileServerURLHint": "Per exemple\u00a0: http://tile.thunderforest.com/cycle/{z}/{x}/{y}.png",
"defaultOrientationMode": "M\u00f2de orientacion per defaut",
"fixedNorth": "Fixada (N\u00f2rd amont)",
"locale": "Lenga", "locale": "Lenga",
"skipOnboarding": "Passar l'introduccion", "skipOnboarding": "Passar l'introduccion",
"tileServer": "Servidor de teulas per la mapa" "tileServer": "Servidor de teulas per la mapa"

View File

@ -1,5 +1,3 @@
import moment from 'moment';
import * as api from '@/api'; import * as api from '@/api';
import * as constants from '@/constants'; import * as constants from '@/constants';
import i18n from '@/i18n'; import i18n from '@/i18n';
@ -87,8 +85,6 @@ export function showReportDetails({ commit }, { id, userAsked }) {
export function setLocale({ commit }, { locale }) { export function setLocale({ commit }, { locale }) {
// Set global Vue-i18n locale // Set global Vue-i18n locale
i18n.locale = locale; i18n.locale = locale;
// Set moment locale
moment.locale(locale);
// Commit setting into the store // Commit setting into the store
return commit(SET_SETTING, { setting: 'locale', value: locale }); return commit(SET_SETTING, { setting: 'locale', value: locale });
} }

View File

@ -1,4 +1,3 @@
import moment from 'moment';
import Vue from 'vue'; import Vue from 'vue';
import { messages, getBrowserLocales } from '@/i18n'; import { messages, getBrowserLocales } from '@/i18n';
@ -80,8 +79,6 @@ if (storageAvailable('localStorage')) {
} }
} }
} }
// Set moment locale
moment.locale(locale);
} }
export const initialState = { export const initialState = {

102
src/tools/date.js Normal file
View File

@ -0,0 +1,102 @@
import i18n from '@/i18n';
/*
* Code below is dapted from
* https://github.com/date-fns/date-fns/blob/v1/src/format/index.js
* Licensed under MIT © Sasha Koss.
*/
export function addLeadingZeros(number, targetLength) {
let output = Math.abs(number).toString();
while (output.length < targetLength) {
output = `0${output}`;
}
return output;
}
const FORMATTERS = {
// Month: 01, 02, ..., 12
MM: date => addLeadingZeros(date.getMonth() + 1, 2),
// Day of month: 01, 02, ..., 31
DD: date => addLeadingZeros(date.getDate(), 2),
// Year: 1900, 1901, ..., 2099
YYYY: date => addLeadingZeros(date.getFullYear(), 4),
// Hour: 00, 01, ..., 23
HH: date => addLeadingZeros(date.getHours(), 2),
// Minute: 00, 01, ..., 59
mm: date => addLeadingZeros(date.getMinutes(), 2),
// Day of the week: Mon, Tue, Wed, Thu, Fri, Sat, Sun
ddd: date => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][date.getDay()],
};
export function formatDate(date, format) {
let output = format;
Object.keys(FORMATTERS).forEach((formatter) => {
output = output.replace(formatter, FORMATTERS[formatter](date));
});
return output;
}
/*
* Code below is adapted from
* https://github.com/yahoo/intl-relativeformat/blob/master/src/core.js
* Copyright (c) 2014, Yahoo! Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
*/
const THRESHOLDS = {
second: 45,
minute: 45,
hour: 22,
day: 26,
month: 11,
};
function daysToYears(days) {
// 400 years have 146097 days (taking into account leap year rules)
return days * 400 / 146097;
}
export function diff(from, to) {
// Convert to ms timestamps.
const millisecond = Math.round(+to - (+from));
const second = Math.round(millisecond / 1000);
const minute = Math.round(second / 60);
const hour = Math.round(minute / 60);
const day = Math.round(hour / 24);
const week = Math.round(day / 7);
const rawYears = daysToYears(day);
const month = Math.round(rawYears * 12);
const year = Math.round(rawYears);
return {
millisecond,
second,
minute,
hour,
day,
week,
month,
year,
};
}
export function selectUnits(diffReport) {
const fields = Object.keys(THRESHOLDS);
for (let i = 0; i < fields.length; i += 1) {
const units = fields[i];
if (Math.abs(diffReport[units]) < THRESHOLDS[units]) {
return units;
}
}
return 'year';
}
export function distanceInWordsToNow(from) {
const diffReport = diff(from, new Date());
const units = selectUnits(diffReport);
const diffInUnits = Math.abs(diffReport[units]);
return i18n.t('relativeDate.ago', {
duration: i18n.tc(`relativeDate.${units}`, diffInUnits, { count: diffInUnits }),
});
}

View File

@ -1,8 +1,8 @@
import FileSaver from 'file-saver'; import FileSaver from 'file-saver';
import createGPX from 'gps-to-gpx'; import createGPX from 'gps-to-gpx';
import moment from 'moment';
import { VERSION } from '@/constants'; import { VERSION } from '@/constants';
import { formatDate } from '@/tools/date';
export default function (activityName, locationGPX) { export default function (activityName, locationGPX) {
const courseKey = 'heading'; const courseKey = 'heading';
@ -36,6 +36,6 @@ export default function (activityName, locationGPX) {
}); });
FileSaver.saveAs( FileSaver.saveAs(
new Blob([gpx], { type: 'application/gpx+xml;charset=utf-8' }), new Blob([gpx], { type: 'application/gpx+xml;charset=utf-8' }),
`cyclassist_${moment().locale('en').format('YYYY-MM-DD_HH-mm_ddd')}.gpx`, `cyclassist_${formatDate(new Date(), 'YYYY-MM-DD_HH-mm_ddd')}.gpx`,
); );
} }

View File

@ -1,4 +1,3 @@
import moment from 'moment';
import { import {
EARTH_RADIUS, EARTH_RADIUS,
@ -61,8 +60,8 @@ export function mockLocationWithGPX(index, setPosition) {
setPosition(mockGPX[index]); setPosition(mockGPX[index]);
if (mockGPX[index + 1]) { if (mockGPX[index + 1]) {
const delay = ( const delay = (
moment(mockGPX[index + 1].time).valueOf() Date.parse(mockGPX[index + 1].time).getTime()
- moment(mockGPX[index].time).valueOf() - Date.parse(mockGPX[index].time).getTime()
); );
setTimeout( setTimeout(
() => mockLocationWithGPX(index + 1, setPosition), () => mockLocationWithGPX(index + 1, setPosition),

View File

@ -26,9 +26,8 @@
</template> </template>
<script> <script>
import moment from 'moment';
import { getStats } from '@/api'; import { getStats } from '@/api';
import { distanceInWordsToNow } from '@/tools/date';
import ReportsDescription from '@/components/ReportsDescription.vue'; import ReportsDescription from '@/components/ReportsDescription.vue';
@ -49,7 +48,7 @@ export default {
getStats().then((stats) => { getStats().then((stats) => {
this.stats = stats; this.stats = stats;
this.stats.last_added_report_datetime = ( this.stats.last_added_report_datetime = (
moment(this.stats.last_added_report_datetime).fromNow() distanceInWordsToNow(Date.parse(this.stats.last_added_report_datetime))
); );
}); });
}, },

View File

@ -124,8 +124,14 @@ export default {
}, },
data() { data() {
const $t = this.$t.bind(this); const $t = this.$t.bind(this);
const i18nItems = [];
Object.keys(AVAILABLE_LOCALES).forEach(iso => i18nItems.push({
iso,
name: AVAILABLE_LOCALES[iso].name,
}));
i18nItems.sort((a, b) => a.iso > b.iso);
return { return {
i18nItems: AVAILABLE_LOCALES, i18nItems,
orientationModes: [ orientationModes: [
{ {
text: $t('settings.fixedNorth'), text: $t('settings.fixedNorth'),

View File

@ -5980,10 +5980,6 @@ mkpath@^0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/mkpath/-/mkpath-0.1.0.tgz#7554a6f8d871834cc97b5462b122c4c124d6de91" resolved "https://registry.yarnpkg.com/mkpath/-/mkpath-0.1.0.tgz#7554a6f8d871834cc97b5462b122c4c124d6de91"
moment@^2.22.2:
version "2.22.2"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
mout@1.0.0: mout@1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/mout/-/mout-1.0.0.tgz#9bdf1d4af57d66d47cb353a6335a3281098e1501" resolved "https://registry.yarnpkg.com/mout/-/mout-1.0.0.tgz#9bdf1d4af57d66d47cb353a6335a3281098e1501"