Skip updates while the tab/window is not visible

Pause updates while the window is minimized or the tab is in the background, but immediately continue updating once the wall is visible again. This should avoid a ton of unnecessary requests to servers.
This commit is contained in:
Marcel Hellkamp 2023-07-20 10:44:40 +02:00
parent 7ffa2a2f6d
commit aae24de78b

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, inject, onBeforeUnmount, onMounted, onUpdated, ref, watch } from 'vue'; import { computed, inject, onBeforeUnmount, onMounted, onUpdated, ref, watch } from 'vue';
import Card, { type Post } from './components/Card.vue'; import Card, { type Post } from './components/Card.vue';
import { useWindowSize, watchDebounced } from '@vueuse/core' import { useDocumentVisibility, useWindowSize, watchDebounced } from '@vueuse/core'
import ConfigModal from './components/ConfigModal.vue'; import ConfigModal from './components/ConfigModal.vue';
import { loadConfig, type Config } from './config'; import { loadConfig, type Config } from './config';
import InfoBar from './components/InfoBar.vue'; import InfoBar from './components/InfoBar.vue';
@ -14,17 +14,18 @@ const hidden = ref<Array<string>>([])
const banned = ref<Array<string>>([]) const banned = ref<Array<string>>([])
const updateInProgress = ref(false) const updateInProgress = ref(false)
var updateIntervalHandle: number;
const accountToLocalId: Record<string, string | null> = {} const accountToLocalId: Record<string, string | null> = {}
var updateIntervalHandle: number;
var lastUpdate = 0;
onMounted(async () => { onMounted(async () => {
config.value = await loadConfig() config.value = await loadConfig()
// Trigger update once. Timed updates are started by a watcher below. if (visibilityState.value !== "hidden")
await updateWall() restartUpdates()
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
clearInterval(updateIntervalHandle) stopUpdates()
}) })
// Re-layout Masonry on dom updates or window size changes // Re-layout Masonry on dom updates or window size changes
@ -39,19 +40,15 @@ watch(() => config.value?.theme, () => {
}) })
// Watch for a update interval changes // Watch for a update interval changes
watch(() => config.value?.interval, () => { watch(() => config.value?.interval, () => restartUpdates())
clearInterval(updateIntervalHandle)
if (!config.value) return
// Update interval is large enough to not hit the mastodon default rate limit // Pause updates while tab/window is hidden
const rqPerUpdate = config.value.accounts.length * 2 + config.value.tags.length const visibilityState = useDocumentVisibility()
const maxPerSecond = 300 / (5 * 60) / 2 // Be nice, use only half rate limit watch(visibilityState, () => {
const minInternal = Math.ceil(rqPerUpdate / maxPerSecond) if (visibilityState.value === "hidden")
const interval = Math.max(minInternal, config.value.interval) stopUpdates()
else
updateIntervalHandle = setInterval(() => { restartUpdates()
updateWall()
}, interval * 1000)
}) })
// Souces grouped by server // Souces grouped by server
@ -259,6 +256,35 @@ async function fetchAllPosts() {
return posts return posts
} }
/**
* Starts or restarts the update interval timer.
*/
const restartUpdates = () => {
stopUpdates()
if (!config.value) return
// Mastodon default rate limit is 1/s (300/5m)
// Be nice, only send one request every 2 seconds on average.
const rqPerUpdate = config.value.accounts.length + config.value.tags.length
const minInternal = Math.ceil(rqPerUpdate * 2)
const interval = Math.max(minInternal, config.value.interval)
updateIntervalHandle = setInterval(() => {
updateWall()
}, interval * 1000)
// Trigger update immediately if new interval allows it
if (lastUpdate + interval < new Date().getTime())
updateWall()
}
/**
* Stops the update interval
*/
const stopUpdates = () => {
clearInterval(updateIntervalHandle)
}
/** /**
* Trigger a wall update. * Trigger a wall update.
* *
@ -273,14 +299,16 @@ async function updateWall() {
return return
} }
console.debug("Startung wall update...") console.debug("Updating wall...")
updateInProgress.value = true updateInProgress.value = true
try { try {
allPosts.value = await fetchAllPosts() allPosts.value = await fetchAllPosts()
console.debug("Update completed") console.debug("Update completed")
} catch (e) { } catch (e) {
console.warn("Update failed", e) console.warn("Update failed", e)
} finally { } finally {
lastUpdate = Date.now()
updateInProgress.value = false; updateInProgress.value = false;
} }
@ -386,7 +414,8 @@ const privacyLink = computed(() => {
<ConfigModal v-if="config" v-model="config" id="configModal" /> <ConfigModal v-if="config" v-model="config" id="configModal" />
<footer> <footer>
<button class="btn btn-link text-muted" @click="toggleTheme(); false">[{{ config?.theme == "dark" ? "Light" : "Dark" }} mode]</button> <button class="btn btn-link text-muted" @click="toggleTheme(); false">[{{ config?.theme == "dark" ? "Light" : "Dark"
}} mode]</button>
<button class="btn btn-link text-muted" data-bs-toggle="modal" data-bs-target="#configModal">[Customize]</button> <button class="btn btn-link text-muted" data-bs-toggle="modal" data-bs-target="#configModal">[Customize]</button>
<div> <div>
<a :href="privacyLink" target="_blank" class="mx-1">Privacy policy</a> <a :href="privacyLink" target="_blank" class="mx-1">Privacy policy</a>