-
-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add .idea to gitignore chore: clean up chore: clean up
- Loading branch information
1 parent
296c835
commit e529bf5
Showing
5 changed files
with
295 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./useNetworkStatus.svelte.js"; |
145 changes: 145 additions & 0 deletions
145
packages/runed/src/lib/utilities/useNetworkStatus/useNetworkStatus.svelte.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import { addEventListener } from "$lib/internal/utils/event.js"; | ||
import { Previous } from "$lib/utilities/index.js"; | ||
import { browser } from "$app/environment"; | ||
|
||
/** | ||
* @desc The `NetworkInformation` interface of the Network Information API | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation | ||
*/ | ||
type NetworkInformation = { | ||
readonly downlink: number; | ||
readonly downlinkMax: number; | ||
readonly effectiveType: "slow-2g" | "2g" | "3g" | "4g"; | ||
readonly rtt: number; | ||
readonly saveData: boolean; | ||
readonly type: | ||
| "bluetooth" | ||
| "cellular" | ||
| "ethernet" | ||
| "none" | ||
| "wifi" | ||
| "wimax" | ||
| "other" | ||
| "unknown"; | ||
} & EventTarget; | ||
|
||
type NavigatorWithConnection = Navigator & { connection: NetworkInformation }; | ||
|
||
interface NetworkStatus { | ||
/** | ||
* @desc Returns the online status of the browser. | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine | ||
*/ | ||
online: boolean; | ||
/** | ||
* @desc The {Date} object pointing to the moment when state update occurred. | ||
*/ | ||
updatedAt: Date; | ||
/** | ||
* @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 | ||
*/ | ||
downlink?: NetworkInformation["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 | ||
*/ | ||
downlinkMax?: NetworkInformation["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 | ||
*/ | ||
effectiveType?: NetworkInformation["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 | ||
*/ | ||
rtt?: NetworkInformation["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 | ||
*/ | ||
saveData?: NetworkInformation["saveData"]; | ||
/** | ||
* @desc The type of connection a device is using to communicate with the network. | ||
* It will be one of the following values: | ||
* - bluetooth | ||
* - cellular | ||
* - ethernet | ||
* - none | ||
* - wifi | ||
* - wimax | ||
* - other | ||
* - unknown | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/type | ||
*/ | ||
type?: NetworkInformation["type"]; | ||
} | ||
|
||
/** | ||
* Tracks the state of browser's network connection. | ||
* @returns null if the function is not run in the browser | ||
*/ | ||
export function useNetworkStatus() { | ||
if (!browser) { | ||
return { | ||
get current() { | ||
return null; | ||
}, | ||
get previous() { | ||
return null; | ||
}, | ||
}; | ||
} | ||
const navigator = window.navigator; | ||
const connection = | ||
"connection" in navigator ? (navigator as NavigatorWithConnection).connection : undefined; | ||
const getConnectionStatus = (): NetworkStatus => { | ||
return { | ||
online: navigator.onLine, | ||
updatedAt: new Date(), | ||
downlink: connection?.downlink, | ||
downlinkMax: connection?.downlinkMax, | ||
effectiveType: connection?.effectiveType, | ||
rtt: connection?.rtt, | ||
saveData: connection?.saveData, | ||
type: connection?.type, | ||
}; | ||
}; | ||
|
||
let current = $state(getConnectionStatus()); | ||
const previous = new Previous(() => current); | ||
const handleStatusChange = () => { | ||
current = getConnectionStatus(); | ||
}; | ||
|
||
$effect(() => { | ||
// The connection event handler also manages online and offline states. | ||
if (connection) { | ||
addEventListener(connection, "change", handleStatusChange, { passive: true }); | ||
} else { | ||
addEventListener(window, "online", handleStatusChange, { passive: true }); | ||
addEventListener(window, "offline", handleStatusChange, { passive: true }); | ||
} | ||
|
||
return () => { | ||
window.removeEventListener("online", handleStatusChange); | ||
window.removeEventListener("online", handleStatusChange); | ||
connection?.removeEventListener("change", handleStatusChange); | ||
}; | ||
}); | ||
|
||
return { | ||
get current() { | ||
return current; | ||
}, | ||
get previous() { | ||
return previous.current; | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
--- | ||
title: useNetworkStatus | ||
description: Watch for network status changes. | ||
category: Browser | ||
--- | ||
|
||
<script> | ||
import Demo from '$lib/components/demos/use-network-status.svelte'; | ||
import { Callout } from '$lib/components' | ||
</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 `useNetworkStatus()` to retrieve both current and previous network status. | ||
It must be used within the `browser` context, otherwise it will return `null`. | ||
|
||
```svelte | ||
<script lang="ts"> | ||
import { useNetworkStatus } from "runed"; | ||
import { toast } from "svelte-sonner"; | ||
const networkStatus = useNetworkStatus(); | ||
$effect(() => { | ||
if (!networkStatus.current) { | ||
return; | ||
} | ||
if (networkStatus.current.online === false) { | ||
toast.error("No internet connection."); | ||
} | ||
if (networkStatus.current.effectiveType === "2g") { | ||
toast.warning("You are experiencing a slow connection."); | ||
} | ||
if (networkStatus.current.online === true && networkStatus.previous?.online === false) { | ||
toast.success("You are back online!"); | ||
} | ||
}); | ||
</script> | ||
{#if networkStatus.current} | ||
<p>online: {networkStatus.current.online}</p> | ||
{/if} | ||
``` | ||
|
||
### Current | ||
|
||
You can get the current status by calling the `current` method. | ||
|
||
|
||
```ts | ||
const networkStatus = useNetworkStatus(); | ||
|
||
networkStatus.current; | ||
``` | ||
|
||
### Previous | ||
|
||
You can get the previous status by calling the `previous` method. | ||
It defaults to `undefined` if the network status hasn't been updated since the component mounted. | ||
|
||
```ts | ||
const networkStatus = useNetworkStatus(); | ||
|
||
networkStatus.previous; | ||
``` | ||
|
||
## 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"; | ||
} | ||
``` | ||
|
||
|
||
|
54 changes: 54 additions & 0 deletions
54
sites/docs/src/lib/components/demos/use-network-status.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<script lang="ts"> | ||
import { useNetworkStatus } from "runed"; | ||
import { toast } from "svelte-sonner"; | ||
import DemoContainer from "$lib/components/demo-container.svelte"; | ||
const networkStatus = useNetworkStatus(); | ||
const timeOffline = $derived(() => { | ||
if (networkStatus && networkStatus.previous) { | ||
const now = networkStatus.current.updatedAt; | ||
const prev = networkStatus.previous.updatedAt; | ||
if (!now || !prev) { | ||
return 0; | ||
} | ||
const differenceMs = now.getTime() - prev.getTime(); | ||
if (differenceMs < 0) { | ||
return 0; | ||
} | ||
const differenceSeconds = differenceMs / 1000; | ||
return Math.round(differenceSeconds); | ||
} | ||
return 0; | ||
}); | ||
$effect(() => { | ||
if (!networkStatus.current) { | ||
return; | ||
} | ||
if (networkStatus.current.online === false) { | ||
toast.error("No internet connection. Reconnect to continue using the app."); | ||
} | ||
if ( | ||
networkStatus.current.effectiveType === "3g" || | ||
networkStatus.current.effectiveType === "2g" || | ||
networkStatus.current.effectiveType === "slow-2g" | ||
) { | ||
toast.warning( | ||
"You are experiencing a slow connection. Some features may take longer to load." | ||
); | ||
} | ||
if (networkStatus.current.online === true && networkStatus.previous?.online === false) { | ||
toast.success( | ||
`You are back online! Catch up with what you missed during your ${timeOffline()} seconds offline.` | ||
); | ||
} | ||
}); | ||
</script> | ||
|
||
<DemoContainer> | ||
{#if networkStatus.current} | ||
<pre>{JSON.stringify(networkStatus.current, null, 2)}</pre> | ||
{/if} | ||
</DemoContainer> |