Notifications: all messages in chrono order; added timestamps and grouping (#1102)

This commit is contained in:
Michael Geers 2021-05-30 20:31:31 +02:00 • committed by GitHub
parent e34877ff75
commit eede2e8de9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 104 additions and 24 deletions

View file

@ -1,4 +1,4 @@
<link href="/css/bootstrap.min.css" rel="stylesheet" />
<script src="/js/bootstrap.min.js"></script>
<script src="/js/bootstrap.min.js" async></script>
<link href="/css/app.css" rel="stylesheet" />

View file

@ -24,8 +24,21 @@ window.app = new Vue({
methods: {
raise: function (msg) {
console[msg.type](msg);
const withoutThisMsg = this.notifications.filter((m) => m.message !== msg.message);
this.notifications = [msg, ...withoutThisMsg];
const now = new Date();
const latestMsg = this.notifications[0];
if (latestMsg && latestMsg.message === msg.message) {
latestMsg.count++;
latestMsg.time = now;
} else {
this.notifications = [
{
...msg,
count: 1,
time: now,
},
...this.notifications,
];
}
},
clear: function () {
this.notifications = [];

View file

@ -14,21 +14,53 @@ const Template = (args, { argTypes }) => ({
template: '<Notifications v-bind="$props"></Notifications>',
});
function timeAgo(hours = 0, minutes = 0, seconds = 0) {
const date = new Date();
date.setHours(date.getHours() - hours);
date.setMinutes(date.getMinutes() - minutes);
date.setSeconds(date.getSeconds() - seconds);
return date;
}
export const Base = Template.bind({});
Base.args = {
notifications: [
{
message: "Server unavailable",
type: "error",
time: timeAgo(),
count: 1,
},
{
message: "charger out of sync: expected disabled, got enabled",
type: "warn",
count: 4,
time: timeAgo(0, 0, 42),
},
{
message: "Sponsortoken: x509: certificate has expired",
type: "error",
count: 1,
time: timeAgo(1, 12, 44),
},
{
message: "charger out of sync: expected disabled, got enabled",
type: "warn",
count: 4,
time: timeAgo(1, 22, 0),
},
{
message: "vehicle remote charge start: invalid character '<' looking for beginning of value",
type: "warn",
count: 3,
time: timeAgo(4, 2, 0),
},
{
message:
"Amet irure quis incididunt voluptate esse. Commodo ea sunt est ipsum tempor nisi laboris voluptate labore elit laborum. Ex irure commodo reprehenderit consequat consequat do ad tempor aliquip deserunt eu. Laboris minim nostrud quis nisi. Dolor occaecat reprehenderit velit dolore exercitation cupidatat et voluptate. Nulla pariatur deserunt esse minim nisi nisi nulla. Sit eiusmod do incididunt sint minim pariatur aute.",
type: "warn",
count: 1,
time: timeAgo(5, 2, 44),
},
],
};

View file

@ -29,21 +29,30 @@
></button>
</div>
<div class="modal-body">
<p
v-for="(notification, index) in notifications"
:key="index"
class="d-flex align-items-baseline"
>
<fa-icon
:class="{
'text-danger': notification.type === 'error',
'text-warning': notification.type === 'warn',
}"
class="flex-grow-0 d-block"
icon="exclamation-triangle"
></fa-icon>
<span class="flex-grow-1 px-2 py-1">{{ notification.message }}</span>
</p>
<div v-for="(msg, index) in notifications" :key="index">
<small
class="d-flex justify-content-end mt-3"
:title="fmtAbsoluteDate(msg.time)"
>
{{ fmtTimeAgo(msg.time) }}
</small>
<p class="d-flex align-items-baseline">
<fa-icon
:class="{
'text-danger': msg.type === 'error',
'text-warning': msg.type === 'warn',
}"
class="flex-grow-0 d-block"
icon="exclamation-triangle"
></fa-icon>
<span class="flex-grow-1 px-2 py-1">
{{ msg.message }}
</span>
<span class="badge rounded-pill bg-secondary" v-if="msg.count > 1">
{{ msg.count }}
</span>
</p>
</div>
</div>
<div class="modal-footer">
<button
@ -83,9 +92,17 @@ export default {
},
methods: {
clear: function () {
window.app.clear();
window.app && window.app.clear();
},
},
created: function () {
this.interval = setInterval(() => {
this.$forceUpdate();
}, 10 * 1000);
},
destroyed: function () {
clearTimeout(this.interval);
},
mixins: [formatter],
};
</script>

View file

@ -76,5 +76,23 @@ export default {
minute: "numeric",
}).format(date);
},
fmtTimeAgo: function (date) {
const units = {
year: 24 * 60 * 60 * 1000 * 365,
month: (24 * 60 * 60 * 1000 * 365) / 12,
day: 24 * 60 * 60 * 1000,
hour: 60 * 60 * 1000,
minute: 60 * 1000,
second: 1000,
};
const rtf = new Intl.RelativeTimeFormat("default", { numeric: "auto" });
const elapsed = date - new Date();
// "Math.abs" accounts for both "past" & "future" scenarios
for (var u in units)
if (Math.abs(elapsed) > units[u] || u == "second")
return rtf.format(Math.round(elapsed / units[u]), u);
},
},
};

4
dist/index.html vendored
View file

@ -1,4 +1,4 @@
<!DOCTYPE html><html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="description" content="EV Charge Controller"><meta name="author" content="andig"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><link rel="apple-touch-icon" sizes="180x180" href="ico/apple-touch-icon.png?[[.Version]]"><link rel="icon" type="image/png" sizes="32x32" href="ico/favicon-32x32.png?[[.Version]]"><link rel="icon" type="image/png" sizes="16x16" href="ico/favicon-16x16.png?[[.Version]]"><link rel="manifest" href="ico/site.webmanifest"><link rel="mask-icon" href="ico/safari-pinned-tab.svg?[[.Version]]" color="#18191a"><link rel="shortcut icon" href="ico/favicon.ico?[[.Version]]"><meta name="apple-mobile-web-app-title" content="evcc"><meta name="application-name" content="evcc"><meta name="msapplication-TileColor" content="#18191a"><meta name="msapplication-config" content="ico/browserconfig.xml"><meta name="theme-color" content="#18191a"><title>evcc</title><link href="css/chunk-vendors.882a611f.css" rel="preload" as="style"><link href="css/index.f1264e7c.css" rel="preload" as="style"><link href="js/chunk-vendors.8d9f525d.js" rel="preload" as="script"><link href="js/index.11167e27.js" rel="preload" as="script"><link href="css/chunk-vendors.882a611f.css" rel="stylesheet"><link href="css/index.f1264e7c.css" rel="stylesheet"></head><body><script>window.evcc = {
<!DOCTYPE html><html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="description" content="EV Charge Controller"><meta name="author" content="andig"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><link rel="apple-touch-icon" sizes="180x180" href="ico/apple-touch-icon.png?[[.Version]]"><link rel="icon" type="image/png" sizes="32x32" href="ico/favicon-32x32.png?[[.Version]]"><link rel="icon" type="image/png" sizes="16x16" href="ico/favicon-16x16.png?[[.Version]]"><link rel="manifest" href="ico/site.webmanifest"><link rel="mask-icon" href="ico/safari-pinned-tab.svg?[[.Version]]" color="#18191a"><link rel="shortcut icon" href="ico/favicon.ico?[[.Version]]"><meta name="apple-mobile-web-app-title" content="evcc"><meta name="application-name" content="evcc"><meta name="msapplication-TileColor" content="#18191a"><meta name="msapplication-config" content="ico/browserconfig.xml"><meta name="theme-color" content="#18191a"><title>evcc</title><link href="css/chunk-vendors.882a611f.css" rel="preload" as="style"><link href="css/index.f1264e7c.css" rel="preload" as="style"><link href="js/chunk-vendors.8d9f525d.js" rel="preload" as="script"><link href="js/index.ce8c46e1.js" rel="preload" as="script"><link href="css/chunk-vendors.882a611f.css" rel="stylesheet"><link href="css/index.f1264e7c.css" rel="stylesheet"></head><body><script>window.evcc = {
version: "[[.Version]]",
configured: "[[.Configured]]",
};</script><div id="app"></div><script src="js/chunk-vendors.8d9f525d.js"></script><script src="js/index.11167e27.js"></script></body></html>
};</script><div id="app"></div><script src="js/chunk-vendors.8d9f525d.js"></script><script src="js/index.ce8c46e1.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/js/index.ce8c46e1.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/js/index.ce8c46e1.js.map vendored Normal file

File diff suppressed because one or more lines are too long