Add offline indicator (#3516)
This commit is contained in:
parent
78e6ed0388
commit
22ecf15331
12 changed files with 120 additions and 41 deletions
|
|
@ -13,10 +13,12 @@ const api = axios.create({
|
|||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
const url = error.config.baseURL + error.config.url;
|
||||
const message = `${error.message}: API request failed ${url}`;
|
||||
window.app.error({ message });
|
||||
if (error.config.url !== "health") {
|
||||
const url = error.config.baseURL + error.config.url;
|
||||
const message = `${error.message}: API request failed ${url}`;
|
||||
window.app.error({ message });
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default api;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,12 @@ smoothscroll.polyfill();
|
|||
|
||||
const app = createApp({
|
||||
data() {
|
||||
return { notifications: [] };
|
||||
return { notifications: [], offline: false };
|
||||
},
|
||||
watch: {
|
||||
offline: function (value) {
|
||||
console.log(`we are ${value ? "offline" : "online"}`);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
raise: function (msg) {
|
||||
|
|
@ -42,13 +47,19 @@ const app = createApp({
|
|||
msg.type = "error";
|
||||
this.raise(msg);
|
||||
},
|
||||
setOnline: function () {
|
||||
this.offline = false;
|
||||
},
|
||||
setOffline: function () {
|
||||
this.offline = true;
|
||||
},
|
||||
warn: function (msg) {
|
||||
msg.type = "warn";
|
||||
this.raise(msg);
|
||||
},
|
||||
},
|
||||
render: function () {
|
||||
return h(App, { notifications: this.notifications });
|
||||
return h(App, { notifications: this.notifications, offline: this.offline });
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -60,7 +71,5 @@ app.use(featureflags);
|
|||
window.app = app.mount("#app");
|
||||
|
||||
window.setInterval(function () {
|
||||
api.get("health").catch(function () {
|
||||
window.app.error({ message: "Server unavailable" });
|
||||
});
|
||||
api.get("health").then(window.app.setOnline).catch(window.app.setOffline);
|
||||
}, 5000);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
'text-danger': msg.type === 'error',
|
||||
'text-warning': msg.type === 'warn',
|
||||
}"
|
||||
class="flex-grow-0 d-block"
|
||||
class="flex-grow-0 flex-shrink-0 d-block"
|
||||
></shopicon-regular-exclamationtriangle>
|
||||
<span class="flex-grow-1 px-2 py-1 text-break">
|
||||
{{ msg.message }}
|
||||
|
|
|
|||
25
assets/js/components/OfflineIndicator.vue
Normal file
25
assets/js/components/OfflineIndicator.vue
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<template>
|
||||
<div
|
||||
class="fixed-bottom alert alert-secondary d-flex justify-content-center align-items-center mb-0 rounded-0"
|
||||
role="alert"
|
||||
>
|
||||
<shopicon-regular-cloud class="me-2"></shopicon-regular-cloud>
|
||||
{{ $t("offline.message") }}
|
||||
<button class="btn btn-sm btn-link text-dark" @click="reload">
|
||||
{{ $t("offline.reload") }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import "@h2d2/shopicons/es/regular/cloud";
|
||||
|
||||
export default {
|
||||
name: "OfflineIndicator",
|
||||
methods: {
|
||||
reload() {
|
||||
window.location.reload();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
<template>
|
||||
<div class="d-flex flex-column site">
|
||||
<OfflineIndicator v-if="offline" />
|
||||
|
||||
<div class="container px-4 top-area">
|
||||
<div class="d-flex justify-content-between align-items-center my-3">
|
||||
<h1 class="d-block my-0">
|
||||
|
|
@ -23,6 +25,7 @@
|
|||
<script>
|
||||
import "@h2d2/shopicons/es/regular/arrowup";
|
||||
import TopNavigation from "./TopNavigation.vue";
|
||||
import OfflineIndicator from "./OfflineIndicator.vue";
|
||||
import Notifications from "./Notifications.vue";
|
||||
import Energyflow from "./Energyflow/Energyflow.vue";
|
||||
import Loadpoints from "./Loadpoints.vue";
|
||||
|
|
@ -33,12 +36,21 @@ import collector from "../mixins/collector";
|
|||
|
||||
export default {
|
||||
name: "Site",
|
||||
components: { Loadpoints, Energyflow, Footer, Notifications, TopNavigation, Vehicles },
|
||||
components: {
|
||||
Loadpoints,
|
||||
Energyflow,
|
||||
Footer,
|
||||
OfflineIndicator,
|
||||
Notifications,
|
||||
TopNavigation,
|
||||
Vehicles,
|
||||
},
|
||||
mixins: [formatter, collector],
|
||||
props: {
|
||||
loadpoints: Array,
|
||||
|
||||
notifications: Array,
|
||||
offline: Boolean,
|
||||
|
||||
// details
|
||||
gridConfigured: Boolean,
|
||||
|
|
|
|||
|
|
@ -137,4 +137,8 @@ export default {
|
|||
`,
|
||||
},
|
||||
},
|
||||
offline: {
|
||||
message: "Keine Verbindung zum Server.",
|
||||
reload: "Reload?",
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -137,4 +137,8 @@ export default {
|
|||
`,
|
||||
},
|
||||
},
|
||||
offline: {
|
||||
message: "No connection to server.",
|
||||
reload: "Reload?",
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<metainfo>
|
||||
<template #title="{ content }">{{ content ? `${content} | evcc` : `evcc` }}</template>
|
||||
</metainfo>
|
||||
<router-view :notifications="notifications"></router-view>
|
||||
<router-view :notifications="notifications" :offline="offline"></router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -14,6 +14,10 @@ export default {
|
|||
name: "App",
|
||||
props: {
|
||||
notifications: Array,
|
||||
offline: Boolean,
|
||||
},
|
||||
data: () => {
|
||||
return { reconnectTimeout: null };
|
||||
},
|
||||
created: function () {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
|
@ -21,7 +25,12 @@ export default {
|
|||
setTimeout(this.connect, 0);
|
||||
},
|
||||
methods: {
|
||||
reconnect: function () {
|
||||
window.clearTimeout(this.reconnectTimeout);
|
||||
this.reconnectTimeout = window.setTimeout(this.connect, 1000);
|
||||
},
|
||||
connect: function () {
|
||||
console.log("connecting websocket");
|
||||
const supportsWebSockets = "WebSocket" in window;
|
||||
if (!supportsWebSockets) {
|
||||
window.app.error({
|
||||
|
|
@ -39,12 +48,20 @@ export default {
|
|||
(loc.port ? ":" + loc.port : "") +
|
||||
loc.pathname +
|
||||
"ws";
|
||||
|
||||
const ws = new WebSocket(uri);
|
||||
ws.onerror = () => {
|
||||
console.error({ message: "Websocket error. Trying to reconnect." });
|
||||
ws.close();
|
||||
};
|
||||
ws.onopen = () => {
|
||||
console.log("websocket connected");
|
||||
window.app.setOnline();
|
||||
};
|
||||
ws.onclose = () => {
|
||||
window.setTimeout(this.connect, 1000);
|
||||
console.log("websocket disconnected");
|
||||
window.app.setOffline();
|
||||
this.reconnect();
|
||||
};
|
||||
ws.onmessage = (evt) => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
<template>
|
||||
<div>
|
||||
<Site v-if="configured" :notifications="notifications" v-bind="state"></Site>
|
||||
<Site
|
||||
v-if="configured"
|
||||
:notifications="notifications"
|
||||
:offline="offline"
|
||||
v-bind="state"
|
||||
></Site>
|
||||
<div v-else class="container">
|
||||
<div class="row py-5">
|
||||
<div class="col12">
|
||||
|
|
@ -88,6 +93,7 @@ export default {
|
|||
components: { Site },
|
||||
props: {
|
||||
notifications: Array,
|
||||
offline: Boolean,
|
||||
},
|
||||
data: function () {
|
||||
return store;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
dist/index.html
vendored
4
dist/index.html
vendored
|
|
@ -20,8 +20,8 @@
|
|||
<meta name="theme-color" content="#28293e" />
|
||||
|
||||
<title>evcc</title>
|
||||
<script type="module" crossorigin src="./assets/index.61d9e798.js"></script>
|
||||
<link rel="stylesheet" href="./assets/index.a4ef9a54.css">
|
||||
<script type="module" crossorigin src="./assets/index.e9c8c81b.js"></script>
|
||||
<link rel="stylesheet" href="./assets/index.dca955c6.css">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue