diff --git a/apps/desktop/src/components/network-health-indicator.tsx b/apps/desktop/src/components/network-health-indicator.tsx new file mode 100644 index 00000000000..b0450f2a4ba --- /dev/null +++ b/apps/desktop/src/components/network-health-indicator.tsx @@ -0,0 +1,146 @@ +import { cx } from "cva"; +import { Show } from "solid-js"; +import Tooltip from "~/components/Tooltip"; +import { + isRecordingInProgress, + networkHealth, + runSpeedTest, + runUploadHealthCheck, + speedTest, +} from "~/utils/network-health"; +import IconLucideAlertTriangle from "~icons/lucide/alert-triangle"; +import IconLucideRefreshCw from "~icons/lucide/refresh-cw"; +import IconLucideWifi from "~icons/lucide/wifi"; + +interface NetworkHealthIndicatorProps { + class?: string; +} + +export default function NetworkHealthIndicator( + props: NetworkHealthIndicatorProps, +) { + const isUnhealthy = () => networkHealth().status === "unhealthy"; + const isChecking = () => + networkHealth().status === "checking" || speedTest().status === "running"; + const isRecording = isRecordingInProgress; + + const handleRefresh = async (e: MouseEvent) => { + e.stopPropagation(); + if (isRecording() || isChecking()) return; + await runUploadHealthCheck(); + await runSpeedTest(); + }; + + const speedLabel = () => { + if (speedTest().status === "running") return "Testing..."; + if (speedTest().status === "error" && speedTest().speedMbps === null) + return "Failed"; + if (speedTest().speedMbps !== null) return `${speedTest().speedMbps} Mbps`; + if (networkHealth().status === "checking") return "Checking..."; + if (isUnhealthy()) return "Offline"; + return "Ready"; + }; + + const qualityLabel = () => { + const label = speedTest().recommendedLabel; + return label ? `${label}` : "1080p"; + }; + + const dotColor = () => { + if (isUnhealthy()) return "bg-red-9"; + if (speedTest().status === "running") return "bg-blue-9 animate-pulse"; + if (speedTest().status === "error" && speedTest().speedMbps === null) + return "bg-red-9"; + const speed = speedTest().speedMbps; + if (speed === null) return "bg-gray-8"; + if (speed >= 15) return "bg-green-9"; + if (speed >= 5) return "bg-blue-9"; + return "bg-amber-9"; + }; + + const tooltipContent = () => ( +
+
+ Network Health + + {isUnhealthy() || + (speedTest().status === "error" && speedTest().speedMbps === null) + ? "Error" + : "Operational"} + +
+
+ Upload Speed: + {speedLabel()} +
+
+ Recommended Quality: + {qualityLabel()} +
+ +
+ {networkHealth().error ?? speedTest().error} +
+
+
+ +
+
+ ); + + return ( +
+ + + +
+ ); +} +export { NetworkHealthIndicator }; diff --git a/apps/desktop/src/routes/(window-chrome)/new-main/index.tsx b/apps/desktop/src/routes/(window-chrome)/new-main/index.tsx index b0981c60573..a72db044b92 100644 --- a/apps/desktop/src/routes/(window-chrome)/new-main/index.tsx +++ b/apps/desktop/src/routes/(window-chrome)/new-main/index.tsx @@ -37,6 +37,7 @@ import { createStore, produce, reconcile } from "solid-js/store"; import toast from "solid-toast"; import { Transition } from "solid-transition-group"; import Mode from "~/components/Mode"; +import NetworkHealthIndicator from "~/components/network-health-indicator"; import { RecoveryToast } from "~/components/RecoveryToast"; import Tooltip from "~/components/Tooltip"; import { Input } from "~/routes/editor/ui"; @@ -59,6 +60,10 @@ import { importVideoFromPicker, showImportError, } from "~/utils/importMedia"; +import { + initNetworkHealthMonitoring, + setRecordingState, +} from "~/utils/network-health"; import { createCameraMutation, createCleanCaptureQuery, @@ -1846,6 +1851,16 @@ function Page() { const isRecording = () => !!currentRecording.data; const isActivelyRecording = () => currentRecording.data?.status === "recording"; + + createEffect(() => { + setRecordingState(isRecording()); + }); + + onMount(() => { + const cleanup = initNetworkHealthMonitoring(); + onCleanup(cleanup); + }); + const auth = authStore.createQuery(); const recordingSettingsQuery = recordingDeviceSettingsStore.createQuery(); const generalSettings = generalSettingsStore.createQuery(); @@ -2984,6 +2999,9 @@ function Page() { +
+ +
); @@ -3153,6 +3171,7 @@ function Page() { ? new URL("/dashboard", serverUrl()).toString() : serverUrl() } + rel="noopener" >