diff --git a/src/Frontend/package-lock.json b/src/Frontend/package-lock.json index 38a02e0a9..6f94d10e7 100644 --- a/src/Frontend/package-lock.json +++ b/src/Frontend/package-lock.json @@ -21,6 +21,7 @@ "codemirror": "^6.0.1", "hex-to-css-filter": "^6.0.0", "lodash.debounce": "^4.0.8", + "lodash.throttle": "^4.0.8", "lossless-json": "^4.0.2", "memoize-one": "^6.0.0", "moment": "^2.30.1", @@ -5332,6 +5333,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" + }, "node_modules/lossless-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/lossless-json/-/lossless-json-4.0.2.tgz", diff --git a/src/Frontend/package.json b/src/Frontend/package.json index b2fea93c5..c30241de5 100644 --- a/src/Frontend/package.json +++ b/src/Frontend/package.json @@ -30,6 +30,7 @@ "codemirror": "^6.0.1", "hex-to-css-filter": "^6.0.0", "lodash.debounce": "^4.0.8", + "lodash.throttle": "^4.0.8", "lossless-json": "^4.0.2", "memoize-one": "^6.0.0", "moment": "^2.30.1", diff --git a/src/Frontend/src/components/RefreshConfig.vue b/src/Frontend/src/components/RefreshConfig.vue index 78a0f641b..11eab58d7 100644 --- a/src/Frontend/src/components/RefreshConfig.vue +++ b/src/Frontend/src/components/RefreshConfig.vue @@ -1,45 +1,72 @@ @@ -47,40 +74,34 @@ function validateTimeout() { .refresh-config { display: flex; align-items: center; - gap: 0.5em; -} - -.refresh-config .unit { - margin-left: -0.45em; + gap: 1em; + margin-bottom: 0.5em; } -.refresh-config label { - margin: 0; -} - -.refresh-config input { - width: 3.5em; +.filter { + display: flex; + align-items: center; } -.refresh-config button { - background: none; - border: none; - width: 2em; +.filter-label { + font-weight: bold; } -.refresh-config button .fa { - transition: all 0.15s ease-in-out; - transition: rotate 0.05s ease-in-out; - transform-origin: center; +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } } -.refresh-config button:hover .fa { - color: #00a3c4; - transform: scale(1.1); +.fa-refresh { + display: inline-block; } -.refresh-config button:active .fa { - transform: rotate(25deg); - text-shadow: #929e9e 0.25px 0.25px; +/* You can add this class dynamically when needed */ +.fa-refresh.spinning { + animation: spin 1s linear infinite; } diff --git a/src/Frontend/src/components/audit/AuditList.vue b/src/Frontend/src/components/audit/AuditList.vue index 9e956e23b..97342f0de 100644 --- a/src/Frontend/src/components/audit/AuditList.vue +++ b/src/Frontend/src/components/audit/AuditList.vue @@ -8,13 +8,33 @@ import ResultsCount from "@/components/ResultsCount.vue"; import { dotNetTimespanToMilliseconds, formatDotNetTimespan } from "@/composables/formatUtils.ts"; import "@vuepic/vue-datepicker/dist/main.css"; import FiltersPanel from "@/components/audit/FiltersPanel.vue"; -import { onBeforeMount, watch } from "vue"; +import { onBeforeMount, onUnmounted, ref, watch } from "vue"; import RefreshConfig from "../RefreshConfig.vue"; +import useAutoRefresh from "@/composables/autoRefresh.ts"; +import throttle from "lodash/throttle"; const store = useAuditStore(); const { messages, totalCount, sortBy, messageFilterString, selectedEndpointName, itemsPerPage, dateRange } = storeToRefs(store); const route = useRoute(); const router = useRouter(); +const autoRefreshValue = ref(null); +const isLoading = ref(false); + +const dataRetriever = useAutoRefresh( + throttle(async () => { + isLoading.value = true; + try { + await store.refresh(); + } finally { + isLoading.value = false; + } + }, 2000), + null +); + +onUnmounted(() => { + dataRetriever.updateTimeout(null); +}); function statusToName(messageStatus: MessageStatus) { switch (messageStatus) { @@ -78,7 +98,7 @@ onBeforeMount(() => { //without setTimeout, this happens before the store is properly initialised, and therefore the query route values aren't applied to the refresh //TODO: is there a better way to achieve this? - setTimeout(async () => await Promise.all([store.refresh(), store.loadEndpoints()]), 0); + setTimeout(async () => await Promise.all([dataRetriever.executeAndResetTimer(), store.loadEndpoints()]), 0); firstLoad = false; }); @@ -87,7 +107,7 @@ watch( () => router.currentRoute.value.query, async () => { setQuery(); - await store.refresh(); + await dataRetriever.executeAndResetTimer(); }, { deep: true } ); @@ -113,7 +133,7 @@ const watchHandle = watch([() => route.query, itemsPerPage, sortBy, messageFilte }, }); - await store.refresh(); + await dataRetriever.executeAndResetTimer(); }); function setQuery() { @@ -132,12 +152,14 @@ function setQuery() { watchHandle.resume(); } + +watch(autoRefreshValue, (newValue) => dataRetriever.updateTimeout(newValue));