diff --git a/.gitignore b/.gitignore index c2c04c1..8cfe93e 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,7 @@ src/LiveRecorder.WebApi/live-recorder.db* *.m4s *.m3u8 *.zip + +# Frontend source/config TypeScript files must stay trackable. +!frontend/**/*.ts +!frontend/**/*.tsx diff --git a/frontend/src/composables/useBackendStatus.ts b/frontend/src/composables/useBackendStatus.ts new file mode 100644 index 0000000..79273f1 --- /dev/null +++ b/frontend/src/composables/useBackendStatus.ts @@ -0,0 +1,35 @@ +import { computed, reactive } from "vue"; + +const backendStatusState = reactive({ + unavailable: false, + message: "", + lastChangedAt: "" +}); + +function setLastChangedNow() { + backendStatusState.lastChangedAt = new Date().toLocaleTimeString(); +} + +export function markBackendUnavailable(message: string) { + backendStatusState.unavailable = true; + backendStatusState.message = message; + setLastChangedNow(); +} + +export function markBackendAvailable() { + if (!backendStatusState.unavailable && !backendStatusState.message) { + return; + } + + backendStatusState.unavailable = false; + backendStatusState.message = ""; + setLastChangedNow(); +} + +export function useBackendStatus() { + return { + backendUnavailable: computed(() => backendStatusState.unavailable), + backendMessage: computed(() => backendStatusState.message), + backendLastChangedAt: computed(() => backendStatusState.lastChangedAt) + }; +} diff --git a/frontend/src/composables/useViewport.ts b/frontend/src/composables/useViewport.ts new file mode 100644 index 0000000..0558918 --- /dev/null +++ b/frontend/src/composables/useViewport.ts @@ -0,0 +1,27 @@ +import { computed, onBeforeUnmount, onMounted, ref } from "vue"; + +const MOBILE_BREAKPOINT = 768; + +export function useViewport() { + const width = ref(typeof window === "undefined" ? 1280 : window.innerWidth); + + function syncViewport() { + width.value = window.innerWidth; + } + + onMounted(() => { + syncViewport(); + window.addEventListener("resize", syncViewport, { passive: true }); + }); + + onBeforeUnmount(() => { + window.removeEventListener("resize", syncViewport); + }); + + const isMobile = computed(() => width.value <= MOBILE_BREAKPOINT); + + return { + width, + isMobile + }; +}