mirror of
https://github.com/the-djmaze/snappymail.git
synced 2024-09-20 15:45:55 +08:00
Cleanup locate time handling
This commit is contained in:
parent
fca7e44aa0
commit
b0ed74575f
|
@ -12,7 +12,6 @@ const
|
||||||
init = () => {
|
init = () => {
|
||||||
if (rl.I18N) {
|
if (rl.I18N) {
|
||||||
I18N_DATA = rl.I18N;
|
I18N_DATA = rl.I18N;
|
||||||
Date.defineRelativeTimeFormat(rl.relativeTime || {});
|
|
||||||
rl.I18N = null;
|
rl.I18N = null;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +22,31 @@ const
|
||||||
getNotificationMessage = code => {
|
getNotificationMessage = code => {
|
||||||
let key = getKeyByValue(Notification, code);
|
let key = getKeyByValue(Notification, code);
|
||||||
return key ? I18N_DATA.NOTIFICATIONS[i18nKey(key).replace('_NOTIFICATION', '_ERROR')] : '';
|
return key ? I18N_DATA.NOTIFICATIONS[i18nKey(key).replace('_NOTIFICATION', '_ERROR')] : '';
|
||||||
|
},
|
||||||
|
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat
|
||||||
|
// see /snappymail/v/0.0.0/app/localization/relativetimeformat/
|
||||||
|
fromNow = date => {
|
||||||
|
let unit = 'second',
|
||||||
|
value = Math.round((date.getTime() - Date.now()) / 1000),
|
||||||
|
t = [[60,'minute'],[3600,'hour'],[86400,'day'],[2628000,'month'],[31536000,'year']],
|
||||||
|
i = 5,
|
||||||
|
abs = Math.abs(value);
|
||||||
|
while (i--) {
|
||||||
|
if (t[i][0] <= abs) {
|
||||||
|
value = Math.round(value / t[i][0]);
|
||||||
|
unit = t[i][1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Intl.RelativeTimeFormat) {
|
||||||
|
let rtf = new Intl.RelativeTimeFormat(doc.documentElement.lang);
|
||||||
|
return rtf.format(value, unit);
|
||||||
|
}
|
||||||
|
abs = Math.abs(value);
|
||||||
|
let rtf = rl.relativeTime.long[unit][0 > value ? 'past' : 'future'],
|
||||||
|
plural = rl.relativeTime.plural(abs);
|
||||||
|
return (rtf[plural] || rtf).replace('{0}', abs);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const
|
export const
|
||||||
|
@ -81,19 +105,21 @@ export const
|
||||||
const m = new Date(time), h = LanguageStore.hourCycle();
|
const m = new Date(time), h = LanguageStore.hourCycle();
|
||||||
switch (formatStr) {
|
switch (formatStr) {
|
||||||
case 'FROMNOW':
|
case 'FROMNOW':
|
||||||
return m.fromNow();
|
return fromNow(m);
|
||||||
case 'SHORT': {
|
case 'SHORT': {
|
||||||
if (4 >= (now - time) / 3600000)
|
// 4 hours
|
||||||
return m.fromNow();
|
if (14400000 >= now - time)
|
||||||
const mt = m.getTime(), date = new Date,
|
return fromNow(m);
|
||||||
|
const date = new Date,
|
||||||
dt = date.setHours(0,0,0,0);
|
dt = date.setHours(0,0,0,0);
|
||||||
if (mt > dt)
|
return (time > dt - 86400000)
|
||||||
return i18n('MESSAGE_LIST/TODAY_AT', {TIME: m.format('LT',0,h)});
|
? i18n(
|
||||||
if (mt > dt - 86400000)
|
time > dt ? 'MESSAGE_LIST/TODAY_AT' : 'MESSAGE_LIST/YESTERDAY_AT',
|
||||||
return i18n('MESSAGE_LIST/YESTERDAY_AT', {TIME: m.format('LT',0,h)});
|
{TIME: m.format('LT',0,h)}
|
||||||
if (date.getFullYear() === m.getFullYear())
|
)
|
||||||
return m.format('d M');
|
: m.format(
|
||||||
return m.format('LL',0,h);
|
date.getFullYear() === m.getFullYear() ? {day: '2-digit', month: 'short'} : {dateStyle: 'medium'}
|
||||||
|
, 0, h);
|
||||||
}
|
}
|
||||||
case 'FULL':
|
case 'FULL':
|
||||||
return m.format('LLL',0,h);
|
return m.format('LLL',0,h);
|
||||||
|
@ -116,10 +142,9 @@ export const
|
||||||
let key = element.dataset.momentFormat;
|
let key = element.dataset.momentFormat;
|
||||||
if (key) {
|
if (key) {
|
||||||
element.textContent = timestampToString(time, key);
|
element.textContent = timestampToString(time, key);
|
||||||
}
|
if ('FULL' !== key && 'FROMNOW' !== key) {
|
||||||
|
element.title = timestampToString(time, 'FULL');
|
||||||
if ((key = element.dataset.momentFormatTitle)) {
|
}
|
||||||
element.title = timestampToString(time, key);
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// prevent knockout crashes
|
// prevent knockout crashes
|
||||||
|
@ -127,9 +152,7 @@ export const
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
reloadTime = () => setTimeout(() =>
|
reloadTime = () => doc.querySelectorAll('time').forEach(element => timeToNode(element)),
|
||||||
doc.querySelectorAll('time').forEach(element => timeToNode(element))
|
|
||||||
, 1),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Function} startCallback
|
* @param {Function} startCallback
|
||||||
|
@ -171,15 +194,15 @@ export const
|
||||||
* @param {boolean} admin
|
* @param {boolean} admin
|
||||||
* @param {string} language
|
* @param {string} language
|
||||||
*/
|
*/
|
||||||
translatorReload = (admin, language) =>
|
translatorReload = (language, admin) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
const script = createElement('script');
|
const script = createElement('script');
|
||||||
script.onload = () => {
|
script.onload = () => {
|
||||||
// reload the data
|
// reload the data
|
||||||
if (init()) {
|
if (init()) {
|
||||||
i18nToNodes(doc);
|
i18nToNodes(doc);
|
||||||
admin || reloadTime();
|
|
||||||
translateTrigger(!translateTrigger());
|
translateTrigger(!translateTrigger());
|
||||||
|
// admin || reloadTime();
|
||||||
}
|
}
|
||||||
script.remove();
|
script.remove();
|
||||||
resolve();
|
resolve();
|
||||||
|
|
|
@ -91,7 +91,7 @@ export class AdminSettingsGeneral extends AbstractViewSettings {
|
||||||
addSubscribablesTo(this, {
|
addSubscribablesTo(this, {
|
||||||
languageAdmin: value => {
|
languageAdmin: value => {
|
||||||
this.languageAdminTrigger(SaveSettingStatus.Saving);
|
this.languageAdminTrigger(SaveSettingStatus.Saving);
|
||||||
translatorReload(true, value)
|
translatorReload(value, 1)
|
||||||
.then(fReloadLanguageHelper(SaveSettingStatus.Success), fReloadLanguageHelper(SaveSettingStatus.Failed))
|
.then(fReloadLanguageHelper(SaveSettingStatus.Success), fReloadLanguageHelper(SaveSettingStatus.Failed))
|
||||||
.then(() => Remote.saveSetting('LanguageAdmin', value));
|
.then(() => Remote.saveSetting('LanguageAdmin', value));
|
||||||
},
|
},
|
||||||
|
|
|
@ -111,7 +111,7 @@ export class UserSettingsGeneral extends AbstractViewSettings {
|
||||||
addSubscribablesTo(this, {
|
addSubscribablesTo(this, {
|
||||||
language: value => {
|
language: value => {
|
||||||
this.languageTrigger(SaveSettingStatus.Saving);
|
this.languageTrigger(SaveSettingStatus.Saving);
|
||||||
translatorReload(false, value)
|
translatorReload(value)
|
||||||
.then(fReloadLanguageHelper(SaveSettingStatus.Success), fReloadLanguageHelper(SaveSettingStatus.Failed))
|
.then(fReloadLanguageHelper(SaveSettingStatus.Success), fReloadLanguageHelper(SaveSettingStatus.Failed))
|
||||||
.then(() => Remote.saveSetting('Language', value));
|
.then(() => Remote.saveSetting('Language', value));
|
||||||
},
|
},
|
||||||
|
|
|
@ -696,7 +696,7 @@ export class ComposePopupView extends AbstractViewPopup {
|
||||||
.replace(/\r/g, '')
|
.replace(/\r/g, '')
|
||||||
.replace(/\s{1,2}?{{FROM}}/g, '')
|
.replace(/\s{1,2}?{{FROM}}/g, '')
|
||||||
.replace(/\s{1,2}?{{FROM-FULL}}/g, '')
|
.replace(/\s{1,2}?{{FROM-FULL}}/g, '')
|
||||||
.replace(/{{DATE}}/g, new Date().format('LLLL'))
|
.replace(/{{DATE}}/g, new Date().format({dateStyle: 'full', timeStyle: 'short'}))
|
||||||
.replace(/{{TIME}}/g, new Date().format('LT'))
|
.replace(/{{TIME}}/g, new Date().format('LT'))
|
||||||
.replace(/{{MOMENT:[^}]+}}/g, '');
|
.replace(/{{MOMENT:[^}]+}}/g, '');
|
||||||
signature.length && editor.setSignature(signature, isHtml, !!identity.signatureInsertBefore());
|
signature.length && editor.setSignature(signature, isHtml, !!identity.signatureInsertBefore());
|
||||||
|
|
|
@ -69,7 +69,7 @@ export class LoginUserView extends AbstractViewLogin {
|
||||||
|
|
||||||
language: value => {
|
language: value => {
|
||||||
this.langRequest(true);
|
this.langRequest(true);
|
||||||
translatorReload(false, value).then(
|
translatorReload(value).then(
|
||||||
() => {
|
() => {
|
||||||
this.langRequest(false);
|
this.langRequest(false);
|
||||||
this.bSendLanguage = true;
|
this.bSendLanguage = true;
|
||||||
|
|
|
@ -149,17 +149,18 @@ export class MailMessageList extends AbstractViewRight {
|
||||||
|
|
||||||
listByDay: () => {
|
listByDay: () => {
|
||||||
let list = [], current, today = Ymd(new Date()),
|
let list = [], current, today = Ymd(new Date()),
|
||||||
rtf = new Intl.RelativeTimeFormat(doc.documentElement.lang, { numeric: "auto" });
|
rtf = Intl.RelativeTimeFormat
|
||||||
|
? new Intl.RelativeTimeFormat(doc.documentElement.lang, { numeric: "auto" }) : 0;
|
||||||
MessagelistUserStore.forEach(msg => {
|
MessagelistUserStore.forEach(msg => {
|
||||||
let date = (new Date(msg.dateTimeStampInUTC() * 1000)),
|
let date = (new Date(msg.dateTimeStampInUTC() * 1000)),
|
||||||
ymd = Ymd(date);
|
ymd = Ymd(date);
|
||||||
if (!current || ymd != current.ymd) {
|
if (!current || ymd != current.ymd) {
|
||||||
if (today == ymd) {
|
if (rtf && today == ymd) {
|
||||||
date = rtf.format(0, 'day');
|
date = rtf.format(0, 'day');
|
||||||
} else if (today - 1 == ymd) {
|
} else if (rtf && today - 1 == ymd) {
|
||||||
date = rtf.format(-1, 'day');
|
date = rtf.format(-1, 'day');
|
||||||
// } else if (today - 7 < ymd) {
|
// } else if (today - 7 < ymd) {
|
||||||
// date = date.format('l');
|
// date = date.format({weekday: 'long'});
|
||||||
// date = date.format({dateStyle: 'full'},0,LanguageStore.hourCycle());
|
// date = date.format({dateStyle: 'full'},0,LanguageStore.hourCycle());
|
||||||
} else {
|
} else {
|
||||||
// date = date.format({dateStyle: 'medium'},0,LanguageStore.hourCycle());
|
// date = date.format({dateStyle: 'medium'},0,LanguageStore.hourCycle());
|
||||||
|
|
39
dev/prototype.js
vendored
39
dev/prototype.js
vendored
|
@ -5,23 +5,13 @@
|
||||||
return this.filter((v, i, a) => (fn ? fn(v) : v) && a.indexOf(v) === i);
|
return this.filter((v, i, a) => (fn ? fn(v) : v) && a.indexOf(v) === i);
|
||||||
};
|
};
|
||||||
|
|
||||||
Date.defineRelativeTimeFormat = config => relativeTime = config;
|
|
||||||
|
|
||||||
// full = Monday, December 12, 2022 at 12:16:21 PM Central European Standard Time
|
// full = Monday, December 12, 2022 at 12:16:21 PM Central European Standard Time
|
||||||
// long = December 12, 2022 at 12:16:21 PM GMT+1
|
// long = December 12, 2022 at 12:16:21 PM GMT+1
|
||||||
// medium = Dec 12, 2022, 12:16:21 PM
|
// medium = Dec 12, 2022, 12:16:21 PM
|
||||||
// short = 12/12/22, 12:16 PM
|
// short = 12/12/22, 12:16 PM
|
||||||
let formats = {
|
let formats = {
|
||||||
LT : {timeStyle: 'short'},
|
LT : {timeStyle: 'short'},
|
||||||
// LT : {hour: 'numeric', minute: 'numeric'},
|
LLL : {dateStyle: 'long', timeStyle: 'short'}
|
||||||
L : {},
|
|
||||||
LL : {dateStyle: 'medium'},
|
|
||||||
// LL : {year: 'numeric', month: 'short', day: 'numeric'},
|
|
||||||
LLL : {dateStyle: 'long', timeStyle: 'short'},
|
|
||||||
// LLL : {year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric'},
|
|
||||||
LLLL : {dateStyle: 'full', timeStyle: 'short'},
|
|
||||||
// LLLL : {weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric'},
|
|
||||||
'd M': {day: '2-digit', month: 'short'}
|
|
||||||
},
|
},
|
||||||
phpFormats = {
|
phpFormats = {
|
||||||
// Day
|
// Day
|
||||||
|
@ -50,9 +40,6 @@
|
||||||
v: {fractionalSecondDigits: 3},
|
v: {fractionalSecondDigits: 3},
|
||||||
Z: {timeZone: 'UTC'}
|
Z: {timeZone: 'UTC'}
|
||||||
},
|
},
|
||||||
relativeTime = {
|
|
||||||
// see /snappymail/v/0.0.0/app/localization/relativetimeformat/
|
|
||||||
},
|
|
||||||
pad2 = v => 10 > v ? '0' + v : v;
|
pad2 = v => 10 > v ? '0' + v : v;
|
||||||
|
|
||||||
// Format momentjs/PHP date formats to Intl.DateTimeFormat
|
// Format momentjs/PHP date formats to Intl.DateTimeFormat
|
||||||
|
@ -82,30 +69,6 @@
|
||||||
return new Intl.DateTimeFormat(doc.documentElement.lang, options).format(this);
|
return new Intl.DateTimeFormat(doc.documentElement.lang, options).format(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat
|
|
||||||
Date.prototype.fromNow = function() {
|
|
||||||
let unit = 'second',
|
|
||||||
value = Math.round((this.getTime() - Date.now()) / 1000),
|
|
||||||
t = [[60,'minute'],[3600,'hour'],[86400,'day'],[2628000,'month'],[31536000,'year']],
|
|
||||||
i = 5,
|
|
||||||
abs = Math.abs(value);
|
|
||||||
while (i--) {
|
|
||||||
if (t[i][0] <= abs) {
|
|
||||||
value = Math.round(value / t[i][0]);
|
|
||||||
unit = t[i][1];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Intl.RelativeTimeFormat) {
|
|
||||||
let rtf = new Intl.RelativeTimeFormat(doc.documentElement.lang);
|
|
||||||
return rtf.format(value, unit);
|
|
||||||
}
|
|
||||||
abs = Math.abs(value);
|
|
||||||
let rtf = relativeTime.long[unit][0 > value ? 'past' : 'future'],
|
|
||||||
plural = relativeTime.plural(abs);
|
|
||||||
return (rtf[plural] || rtf).replace('{0}', abs);
|
|
||||||
};
|
|
||||||
|
|
||||||
Element.prototype.closestWithin = function(selector, parent) {
|
Element.prototype.closestWithin = function(selector, parent) {
|
||||||
const el = this.closest(selector);
|
const el = this.closest(selector);
|
||||||
return (el && el !== parent && parent.contains(el)) ? el : null;
|
return (el && el !== parent && parent.contains(el)) ? el : null;
|
||||||
|
|
|
@ -145,7 +145,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="sizeParent actionHandle" data-bind="text: friendlySize()"></div>
|
<div class="sizeParent actionHandle" data-bind="text: friendlySize()"></div>
|
||||||
<div class="threads-len" data-bind="visible: 1 < threadsLen(), text: threadsLen"></div>
|
<div class="threads-len" data-bind="visible: 1 < threadsLen(), text: threadsLen"></div>
|
||||||
<time class="actionHandle" data-moment-format="LT" data-moment-format-title="FULL" data-bind="moment: dateTimeStampInUTC"></time>
|
<time class="actionHandle" data-moment-format="LT" data-bind="moment: dateTimeStampInUTC"></time>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- /ko -->
|
<!-- /ko -->
|
||||||
|
@ -165,7 +165,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="sizeParent actionHandle" data-bind="text: friendlySize()"></div>
|
<div class="sizeParent actionHandle" data-bind="text: friendlySize()"></div>
|
||||||
<div class="threads-len" data-bind="visible: 1 < threadsLen(), text: threadsLen"></div>
|
<div class="threads-len" data-bind="visible: 1 < threadsLen(), text: threadsLen"></div>
|
||||||
<time class="actionHandle" data-moment-format="SHORT" data-moment-format-title="FULL" data-bind="moment: dateTimeStampInUTC"></time>
|
<time class="actionHandle" data-moment-format="SHORT" data-bind="moment: dateTimeStampInUTC"></time>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -162,9 +162,9 @@
|
||||||
<tr data-bind="visible: dateTimeStampInUTC">
|
<tr data-bind="visible: dateTimeStampInUTC">
|
||||||
<td data-i18n="MESSAGE/LABEL_DATE"></td>
|
<td data-i18n="MESSAGE/LABEL_DATE"></td>
|
||||||
<td>
|
<td>
|
||||||
<time data-moment-format="FULL" data-bind="moment: dateTimeStampInUTC()"></time>
|
<time data-moment-format="FULL" data-bind="moment: dateTimeStampInUTC"></time>
|
||||||
|
|
||||||
(<time data-moment-format="FROMNOW" data-bind="moment: dateTimeStampInUTC()"></time>)
|
(<time data-moment-format="FROMNOW" data-bind="moment: dateTimeStampInUTC"></time>)
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr data-bind="visible: spamResult">
|
<tr data-bind="visible: spamResult">
|
||||||
|
|
Loading…
Reference in a new issue