Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: NetworkStatus #117

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
176 changes: 176 additions & 0 deletions packages/runed/src/lib/utilities/NetworkStatus/NetworkStatus.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { onMount } from "svelte";
import { IsSupported, useEventListener } from "$lib/utilities/index.js";

/**
* @desc The `NetworkInformation` interface of the Network Information API
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation
*/
export type NetworkInformation = {
/**
* @desc Effective bandwidth estimate in megabits per second, rounded to the
* nearest multiple of 25 kilobits per seconds.
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/downlink
*/
readonly downlink: number;
/**
* @desc Maximum downlink speed, in megabits per second (Mbps), for the
* underlying connection technology
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/downlinkMax
*/
readonly downlinkMax: number;
/**
* @desc Effective type of the connection meaning one of 'slow-2g', '2g', '3g', or '4g'.
* This value is determined using a combination of recently observed round-trip time
* and downlink values.
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType
*/
readonly effectiveType: "slow-2g" | "2g" | "3g" | "4g";
/**
* @desc Estimated effective round-trip time of the current connection, rounded
* to the nearest multiple of 25 milliseconds
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/rtt
*/
readonly rtt: number;
/**
* @desc {true} if the user has set a reduced data usage option on the user agent.
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/saveData
*/
readonly saveData: boolean;
/**
* @desc The type of connection a device is using to communicate with the network.
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/type
*/
readonly type:
| "bluetooth"
| "cellular"
| "ethernet"
| "none"
| "wifi"
| "wimax"
| "other"
| "unknown";
} & EventTarget;

type NavigatorWithConnection = Navigator & { connection: NetworkInformation };

/**
* Tracks the state of browser's network connection.
*
* @see {@link https://runed.dev/docs/utilities/network-status}
*/
export class NetworkStatus {
#isSupported = new IsSupported(() => "navigator" in window);
#navigator: Navigator | undefined;
#connection?: NetworkInformation = $derived.by(() =>
this.#navigator && "connection" in this.#navigator
? (this.#navigator as NavigatorWithConnection).connection
: undefined
);
#online: boolean = $state(false);
#updatedAt: Date = $state(new Date());

constructor() {
onMount(() => this.#updateStatus());
if (this.#isSupported.current) {
this.#navigator = window.navigator;
} else {
this.#navigator = undefined;
}

if (this.#connection) {
useEventListener(this.#connection, "change", this.#updateStatus, { passive: true });
} else {
useEventListener(window, "online", this.#updateStatus, { passive: true });
useEventListener(window, "offline", this.#updateStatus, { passive: true });
}
}

#updateStatus() {
if (!this.#navigator) return;
this.#online = this.#navigator.onLine;
this.#updatedAt = new Date();
}

/**
* @desc Whether the network status API is supported in the environment.
*/
get isSupported(): boolean {
return this.#isSupported.current;
}

/**
* @desc The `NetworkInformation` interface of the Network Information API
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation
*/
get connection(): NetworkInformation | undefined {
return this.#connection;
}

/**
* @desc Returns the online status of the browser.
* @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine
*/
get online(): boolean {
return this.#online;
}

/**
* @desc The {Date} object pointing to the moment when state update occurred.
*/
get updatedAt(): Date {
return this.#updatedAt;
}

/**
* @desc Effective bandwidth estimate in megabits per second, rounded to the
* nearest multiple of 25 kilobits per seconds.
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/downlink
*/
get downlink(): NetworkInformation["downlink"] | undefined {
return this.#connection?.downlink;
}

/**
* @desc Maximum downlink speed, in megabits per second (Mbps), for the
* underlying connection technology
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/downlinkMax
*/
get downlinkMax(): NetworkInformation["downlinkMax"] | undefined {
return this.#connection?.downlinkMax;
}

/**
* @desc Effective type of the connection meaning one of 'slow-2g', '2g', '3g', or '4g'.
* This value is determined using a combination of recently observed round-trip time
* and downlink values.
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType
*/
get effectiveType(): NetworkInformation["effectiveType"] | undefined {
return this.#connection?.effectiveType;
}

/**
* @desc Estimated effective round-trip time of the current connection, rounded
* to the nearest multiple of 25 milliseconds
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/rtt
*/
get rtt(): NetworkInformation["rtt"] | undefined {
return this.#connection?.rtt;
}

/**
* @desc {true} if the user has set a reduced data usage option on the user agent.
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/saveData
*/
get saveData(): NetworkInformation["saveData"] | undefined {
return this.#connection?.saveData;
}

/**
* @desc The type of connection a device is using to communicate with the network.
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/type
*/
get type(): NetworkInformation["type"] | undefined {
return this.#connection?.type;
}
}
1 change: 1 addition & 0 deletions packages/runed/src/lib/utilities/NetworkStatus/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./NetworkStatus.svelte.js";
1 change: 1 addition & 0 deletions packages/runed/src/lib/utilities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export * from "./AnimationFrames/index.js";
export * from "./useIntersectionObserver/index.js";
export * from "./IsFocusWithin/index.js";
export * from "./FiniteStateMachine/index.js";
export * from "./NetworkStatus/index.js";
export * from "./PersistedState/index.js";
export * from "./useGeolocation/index.js";
export * from "./Context/index.js";
Expand Down
31 changes: 26 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sites/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"runed": "workspace:^",
"svelte": "^5.11.0",
"svelte-check": "^4.1.1",
"svelte-sonner": "^0.3.28",
"tailwindcss": "4.0.0-beta.5",
"typescript": "^5.7.2",
"velite": "^0.2.1",
Expand Down
111 changes: 111 additions & 0 deletions sites/docs/src/content/utilities/network-status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
---
title: NetworkStatus
description: Watch for network status changes.
category: Sensors
---

<script>
import Demo from "$lib/components/demos/network-status.svelte";
import { Callout } from "@svecodocs/kit";
</script>

## Demo

To see it in action, try disabling and then re-enabling your Internet connection. If you're using
Google Chrome, you can also
[artificially throttle the network](https://developer.chrome.com/docs/devtools/settings/throttling)
to test its behavior under different conditions.

<Demo />

## Usage

You can use `NetworkStatus` in alongside [Previous](https://runed.dev/docs/utilities/previous) to
get both current and previous network status.

<Callout type="warning">

This utility must be used within a `browser` context. An `isSupported` property is available to
check if the API is supported on the current environment/device.

</Callout>

```svelte
<script lang="ts">
import { NetworkStatus } from "runed";
// checkout svelte-sonner for toasts (https://github.com/wobsoriano/svelte-sonner)
import { toast } from "svelte-sonner";

const networkStatus = new NetworkStatus();
const previousNetworkStatus = new Previous(() => ({
online: networkStatus.online,
updatedAt: networkStatus.updatedAt
}));

$effect(() => {
if (!networkStatus.isSupported) return;

if (networkStatus.online === false) {
toast.error("No internet connection.");
}
if (networkStatus.effectiveType === "2g") {
toast.warning("You are experiencing a slow connection.");
}
if (networkStatus.online === true && previousNetworkStatus.current?.online === false) {
toast.success("You are back online!");
}
});
</script>

{#if networkStatus.isSupported}
<p>online: {networkStatus.online}</p>
{:else}
<p>Network Status is currently not available.</p>
{/if}
```

### `isSupported`

It's important to check if the API is supported on the current environment/device before using it.

```ts
const networkStatus = new NetworkStatus();

if (networkStatus.isSupported) {
// do something with the network status
}
```

### Getting the previous status

It's often useful to get the previous status of the network connection. You can use the
[Previous](https://runed.dev/docs/utilities/previous) utility to track the previous state.

```ts
const networkStatus = new NetworkStatus();
const previousNetworkStatus = new Previous(() => ({
online: networkStatus.online,
updatedAt: networkStatus.updatedAt
}));
```

## Reference

The returned status always includes `online` and `updatedAt`. Other properties are returned based on
the
[NetworkInformation](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation#instance_properties)
interface and depend on
[your browser's compatibility](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation#browser_compatibility).

```typescript
interface NetworkStatus {
online: boolean;
updatedAt: Date;
downlink?: number;
downlinkMax?: number;
effectiveType?: "slow-2g" | "2g" | "3g" | "4g";
rtt?: number;
saveData?: boolean;
type?: "bluetooth" | "cellular" | "ethernet" | "none" | "wifi" | "wimax" | "other" | "unknown";
}
```
Loading