Feature: Added pull to refresh and websocket indicator

This commit is contained in:
Thomas Basler 2024-01-06 20:03:52 +01:00
parent 7b5d31efca
commit 024ee26705
7 changed files with 147 additions and 18 deletions

View File

@ -28,6 +28,7 @@
"@tsconfig/node18": "^18.2.2", "@tsconfig/node18": "^18.2.2",
"@types/bootstrap": "^5.2.10", "@types/bootstrap": "^5.2.10",
"@types/node": "^20.10.6", "@types/node": "^20.10.6",
"@types/pulltorefreshjs": "^0.1.7",
"@types/sortablejs": "^1.15.7", "@types/sortablejs": "^1.15.7",
"@types/spark-md5": "^3.0.4", "@types/spark-md5": "^3.0.4",
"@vitejs/plugin-vue": "^5.0.2", "@vitejs/plugin-vue": "^5.0.2",
@ -36,6 +37,7 @@
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-plugin-vue": "^9.19.2", "eslint-plugin-vue": "^9.19.2",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"pulltorefreshjs": "^0.1.22",
"sass": "^1.69.7", "sass": "^1.69.7",
"terser": "^5.26.0", "terser": "^5.26.0",
"typescript": "^5.3.3", "typescript": "^5.3.3",

View File

@ -4,7 +4,12 @@
<div class="page-header"> <div class="page-header">
<div class="row"> <div class="row">
<div class="col-sm-11"> <div class="col-sm-11">
<h1>{{ title }}</h1> <h1>{{ title }}
<span v-if="showWebSocket" :class="{
'onlineMarker': isWebsocketConnected,
'offlineMarker': !isWebsocketConnected,
}"></span>
</h1>
</div> </div>
<div class="col-sm-1" v-if="showReload"> <div class="col-sm-1" v-if="showReload">
<button type="button" class="float-end btn btn-outline-primary" <button type="button" class="float-end btn btn-outline-primary"
@ -28,6 +33,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { BIconArrowClockwise } from 'bootstrap-icons-vue'; import { BIconArrowClockwise } from 'bootstrap-icons-vue';
import PullToRefresh from 'pulltorefreshjs';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -37,7 +43,81 @@ export default defineComponent({
title: { type: String, required: true }, title: { type: String, required: true },
isLoading: { type: Boolean, required: false, default: false }, isLoading: { type: Boolean, required: false, default: false },
isWideScreen: { type: Boolean, required: false, default: false }, isWideScreen: { type: Boolean, required: false, default: false },
isWebsocketConnected: { type: Boolean, required: false, default: false },
showWebSocket: { type: Boolean, required: false, default: false },
showReload: { type: Boolean, required: false, default: false }, showReload: { type: Boolean, required: false, default: false },
}, },
mounted() {
var self = this;
console.log("init");
PullToRefresh.init({
mainElement: 'main', // above which element?
instructionsPullToRefresh: this.$t('base.Pull'),
instructionsReleaseToRefresh: this.$t('base.Release'),
instructionsRefreshing: this.$t('base.Refreshing'),
onRefresh: function() {
self.$emit('reload');
}
});
},
unmounted() {
console.log("destroy");
PullToRefresh.destroyAll();
},
}); });
</script> </script>
<style>
.ptr--text {
color: var(--bs-primary-text-emphasis) !important;
}
.ptr--icon {
color: var(--bs-primary-text-emphasis) !important;
}
.offlineMarker:before {
content: '';
position: absolute;
width: 8px;
height: 8px;
background: #ff0000;
border-color: #ff0000;
border-radius: 50%;
}
.onlineMarker:before {
content: '';
position: absolute;
width: 8px;
height: 8px;
background: #00bb00;
border-color: #00bb00;
border-radius: 50%;
}
.onlineMarker:after {
content: '';
position: absolute;
width: 32px;
height: 32px;
margin: -12px 0 0 -12px;
border: 1px solid #00bb00;
border-radius: 50%;
box-shadow: 0 0 4px #00bb00, inset 0 0 4px rgb(56, 111, 169);
transform: scale(0);
animation: online 2.5s ease-in-out infinite;
}
@keyframes online {
0% {
transform: scale(.1);
opacity: 1;
}
70% {
transform: scale(2.5);
opacity: 0;
}
100% {
opacity: 0;
}
}
</style>

View File

@ -26,7 +26,10 @@
"Loading": "Lade...", "Loading": "Lade...",
"Reload": "Aktualisieren", "Reload": "Aktualisieren",
"Cancel": "Abbrechen", "Cancel": "Abbrechen",
"Save": "Speichern" "Save": "Speichern",
"Refreshing": "Aktualisieren",
"Pull": "Zum Aktualisieren nach unten ziehen",
"Release": "Loslassen zum Aktualisieren"
}, },
"localeswitcher": { "localeswitcher": {
"Dark": "Dunkel", "Dark": "Dunkel",

View File

@ -26,7 +26,10 @@
"Loading": "Loading...", "Loading": "Loading...",
"Reload": "Reload", "Reload": "Reload",
"Cancel": "Cancel", "Cancel": "Cancel",
"Save": "Save" "Save": "Save",
"Refreshing": "Refreshing",
"Pull": "Pull down to refresh",
"Release": "Release to refresh"
}, },
"localeswitcher": { "localeswitcher": {
"Dark": "Dark", "Dark": "Dark",

View File

@ -26,7 +26,10 @@
"Loading": "Chargement...", "Loading": "Chargement...",
"Reload": "Reload", "Reload": "Reload",
"Cancel": "Annuler", "Cancel": "Annuler",
"Save": "Sauvegarder" "Save": "Sauvegarder",
"Refreshing": "Refreshing",
"Pull": "Pull down to refresh",
"Release": "Release to refresh"
}, },
"localeswitcher": { "localeswitcher": {
"Dark": "Sombre", "Dark": "Sombre",

View File

@ -1,5 +1,5 @@
<template> <template>
<BasePage :title="$t('home.LiveData')" :isLoading="dataLoading" :isWideScreen="true"> <BasePage :title="$t('home.LiveData')" :isLoading="dataLoading" :isWideScreen="true" :showWebSocket="true" :isWebsocketConnected="isWebsocketConnected" @reload="reloadData">
<HintView :hints="liveData.hints" /> <HintView :hints="liveData.hints" />
<InverterTotalInfo :totalData="liveData.total" /><br /> <InverterTotalInfo :totalData="liveData.total" /><br />
<div class="row gy-3"> <div class="row gy-3">
@ -448,6 +448,8 @@ export default defineComponent({
alertTypePower: "info", alertTypePower: "info",
showAlertPower: false, showAlertPower: false,
successCommandPower: "", successCommandPower: "",
isWebsocketConnected: false,
}; };
}, },
created() { created() {
@ -475,17 +477,23 @@ export default defineComponent({
this.closeSocket(); this.closeSocket();
}, },
updated() { updated() {
console.log("Updated");
// Select first tab // Select first tab
if (this.isFirstFetchAfterConnect) { if (this.isFirstFetchAfterConnect) {
this.isFirstFetchAfterConnect = false; console.log("isFirstFetchAfterConnect");
const firstTabEl = document.querySelector( this.$nextTick(() => {
"#v-pills-tab:first-child button" console.log("nextTick");
); const firstTabEl = document.querySelector(
if (firstTabEl != null) { "#v-pills-tab:first-child button"
const firstTab = new bootstrap.Tab(firstTabEl); );
firstTab.show(); if (firstTabEl != null) {
} this.isFirstFetchAfterConnect = false;
console.log("Show");
const firstTab = new bootstrap.Tab(firstTabEl);
firstTab.show();
}
});
} }
}, },
computed: { computed: {
@ -508,15 +516,27 @@ export default defineComponent({
}, },
methods: { methods: {
isLoggedIn, isLoggedIn,
getInitialData() { getInitialData(triggerLoading : boolean = true) {
this.dataLoading = true; if (triggerLoading) {
this.dataLoading = true;
}
fetch("/api/livedata/status", { headers: authHeader() }) fetch("/api/livedata/status", { headers: authHeader() })
.then((response) => handleResponse(response, this.$emitter, this.$router)) .then((response) => handleResponse(response, this.$emitter, this.$router))
.then((data) => { .then((data) => {
this.liveData = data; this.liveData = data;
this.dataLoading = false; if (triggerLoading) {
this.dataLoading = false;
}
}); });
}, },
reloadData() {
this.closeSocket();
setTimeout(() => {
this.getInitialData(false);
this.initSocket();
}, 1000);
},
initSocket() { initSocket() {
console.log("Starting connection to WebSocket Server"); console.log("Starting connection to WebSocket Server");
@ -540,11 +560,19 @@ export default defineComponent({
} }
}; };
var self = this;
this.socket.onopen = function (event) { this.socket.onopen = function (event) {
console.log(event); console.log(event);
console.log("Successfully connected to the echo websocket server..."); console.log("Successfully connected to the echo websocket server...");
self.isWebsocketConnected = true;
}; };
this.socket.onclose = function() {
console.log("Connection to websocket closed...")
self.isWebsocketConnected = false;
}
// Listen to window events , When the window closes , Take the initiative to disconnect websocket Connect // Listen to window events , When the window closes , Take the initiative to disconnect websocket Connect
window.onbeforeunload = () => { window.onbeforeunload = () => {
this.closeSocket(); this.closeSocket();

View File

@ -442,6 +442,11 @@
dependencies: dependencies:
undici-types "~5.26.4" undici-types "~5.26.4"
"@types/pulltorefreshjs@^0.1.7":
version "0.1.7"
resolved "https://registry.yarnpkg.com/@types/pulltorefreshjs/-/pulltorefreshjs-0.1.7.tgz#1948638b0c7071282e47bd236d2ccb88bdf66753"
integrity sha512-Y0g/yfuycIvpvUmP97n5NE2+HDAOwfREGVERjhMWw2Y0ODh5wvbflcQ5gXPZ+ihgoq+BQZjA1DL8apw2wAsJXA==
"@types/semver@^7.5.0": "@types/semver@^7.5.0":
version "7.5.1" version "7.5.1"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.1.tgz#0480eeb7221eb9bc398ad7432c9d7e14b1a5a367" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.1.tgz#0480eeb7221eb9bc398ad7432c9d7e14b1a5a367"
@ -2142,6 +2147,11 @@ prelude-ls@~1.1.2:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==
pulltorefreshjs@^0.1.22:
version "0.1.22"
resolved "https://registry.yarnpkg.com/pulltorefreshjs/-/pulltorefreshjs-0.1.22.tgz#ddb5e3feee0b2a49fd46e1b18e84fffef2c47ac0"
integrity sha512-haxNVEHnS4NCQA7NeG7TSV69z4uqy/N7nfPRuc4dPWe8H6ygUrMjdNeohE+6v0lVVX/ukSjbLYwPUGUYtFKfvQ==
punycode@^2.1.0: punycode@^2.1.0:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"