Execute oxfmt (#1166)
Ran pnpm oxfmt . This should get the web repo aligned so that we can better enforce oxfmt going forward.
This commit is contained in:
@@ -8,7 +8,10 @@ export { MeshRegistryContext } from "./src/provider/MeshRegistryContext.ts";
|
||||
export { useClient } from "./src/adapters/useClient.ts";
|
||||
export { useClientById } from "./src/adapters/useClientById.ts";
|
||||
export { useActiveClient } from "./src/adapters/useActiveClient.ts";
|
||||
export { useMeshRegistry, useOptionalMeshRegistry } from "./src/adapters/useMeshRegistry.ts";
|
||||
export {
|
||||
useMeshRegistry,
|
||||
useOptionalMeshRegistry,
|
||||
} from "./src/adapters/useMeshRegistry.ts";
|
||||
export { useSignal } from "./src/adapters/useSignal.ts";
|
||||
export { useSignalValue } from "./src/adapters/useSignalValue.ts";
|
||||
|
||||
@@ -22,14 +25,26 @@ export { useDirectChat } from "./src/hooks/useDirectChat.ts";
|
||||
export type { UseDirectChatResult } from "./src/hooks/useDirectChat.ts";
|
||||
export { useDraft } from "./src/hooks/useDraft.ts";
|
||||
export type { UseDraftResult } from "./src/hooks/useDraft.ts";
|
||||
export { useHasNodeError, useNodeError, useNodeErrors } from "./src/hooks/useNodeError.ts";
|
||||
export {
|
||||
useHasNodeError,
|
||||
useNodeError,
|
||||
useNodeErrors,
|
||||
} from "./src/hooks/useNodeError.ts";
|
||||
export { useNodes } from "./src/hooks/useNodes.ts";
|
||||
export { useNode } from "./src/hooks/useNode.ts";
|
||||
export { useChannels, useChannel } from "./src/hooks/useChannels.ts";
|
||||
export { useConfig, useIsRegionUnset, useModuleConfig } from "./src/hooks/useConfig.ts";
|
||||
export {
|
||||
useConfig,
|
||||
useIsRegionUnset,
|
||||
useModuleConfig,
|
||||
} from "./src/hooks/useConfig.ts";
|
||||
export { useConfigEditor } from "./src/hooks/useConfigEditor.ts";
|
||||
export { useConnectionProgress } from "./src/hooks/useConnectionProgress.ts";
|
||||
export { useTotalUnread, useUnreadByKey, useUnreadCount } from "./src/hooks/useUnread.ts";
|
||||
export {
|
||||
useTotalUnread,
|
||||
useUnreadByKey,
|
||||
useUnreadCount,
|
||||
} from "./src/hooks/useUnread.ts";
|
||||
export { useTelemetry } from "./src/hooks/useTelemetry.ts";
|
||||
export type { UseTelemetryResult } from "./src/hooks/useTelemetry.ts";
|
||||
export { usePosition } from "./src/hooks/usePosition.ts";
|
||||
|
||||
@@ -2,51 +2,25 @@
|
||||
"name": "@meshtastic/sdk-react",
|
||||
"version": "1.0.0",
|
||||
"description": "React hooks + provider for @meshtastic/sdk. Signal-backed reactive state exposed as idiomatic hooks.",
|
||||
"exports": {
|
||||
".": "./mod.ts"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "./dist/mod.js",
|
||||
"module": "./dist/mod.js",
|
||||
"types": "./dist/mod.d.ts",
|
||||
"license": "GPL-3.0-only",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/meshtastic/web.git",
|
||||
"directory": "packages/sdk-react"
|
||||
},
|
||||
"tsdown": {
|
||||
"entry": "mod.ts",
|
||||
"platform": "neutral",
|
||||
"target": "esnext",
|
||||
"dts": true,
|
||||
"format": [
|
||||
"esm"
|
||||
],
|
||||
"splitting": false,
|
||||
"sourcemap": false,
|
||||
"minify": false,
|
||||
"treeshake": true,
|
||||
"report": false,
|
||||
"clean": true
|
||||
},
|
||||
"jsrInclude": [
|
||||
"mod.ts",
|
||||
"src",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"jsrExclude": [
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"tests"
|
||||
],
|
||||
"files": [
|
||||
"package.json",
|
||||
"README.md",
|
||||
"LICENSE",
|
||||
"dist"
|
||||
],
|
||||
"type": "module",
|
||||
"main": "./dist/mod.js",
|
||||
"module": "./dist/mod.js",
|
||||
"types": "./dist/mod.d.ts",
|
||||
"exports": {
|
||||
".": "./mod.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"prepack": "cp ../../LICENSE ./LICENSE",
|
||||
@@ -59,14 +33,40 @@
|
||||
"@meshtastic/sdk": "workspace:*",
|
||||
"better-result": "^2.9.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18 || ^19"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/react": "^16.0.0",
|
||||
"@types/react": "^19.2.17",
|
||||
"jsdom": "^29.1.1",
|
||||
"react": "^19.2.7",
|
||||
"react-dom": "^19.2.7"
|
||||
}
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18 || ^19"
|
||||
},
|
||||
"tsdown": {
|
||||
"clean": true,
|
||||
"dts": true,
|
||||
"entry": "mod.ts",
|
||||
"format": [
|
||||
"esm"
|
||||
],
|
||||
"minify": false,
|
||||
"platform": "neutral",
|
||||
"report": false,
|
||||
"sourcemap": false,
|
||||
"splitting": false,
|
||||
"target": "esnext",
|
||||
"treeshake": true
|
||||
},
|
||||
"jsrExclude": [
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"tests"
|
||||
],
|
||||
"jsrInclude": [
|
||||
"mod.ts",
|
||||
"src",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -15,7 +15,11 @@ export function useClient(): MeshClient {
|
||||
const direct = useContext(MeshContext);
|
||||
const registry = useContext(MeshRegistryContext);
|
||||
const active = useSignal(
|
||||
registry?.active ?? { value: undefined, peek: () => undefined, subscribe: () => () => {} },
|
||||
registry?.active ?? {
|
||||
value: undefined,
|
||||
peek: () => undefined,
|
||||
subscribe: () => () => {},
|
||||
},
|
||||
);
|
||||
const client = direct ?? active;
|
||||
if (!client) {
|
||||
|
||||
@@ -5,7 +5,9 @@ import { MeshRegistryContext } from "../provider/MeshRegistryContext.ts";
|
||||
export function useMeshRegistry(): MeshRegistry {
|
||||
const registry = useContext(MeshRegistryContext);
|
||||
if (!registry) {
|
||||
throw new Error("useMeshRegistry must be called inside a <MeshRegistryProvider>.");
|
||||
throw new Error(
|
||||
"useMeshRegistry must be called inside a <MeshRegistryProvider>.",
|
||||
);
|
||||
}
|
||||
return registry;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,14 @@ import { useCallback, useSyncExternalStore } from "react";
|
||||
* returning. The selector should be stable; memoize it with `useCallback` in
|
||||
* the caller when it closes over changing values.
|
||||
*/
|
||||
export function useSignalValue<T, U>(sig: ReadonlySignal<T>, select: (value: T) => U): U {
|
||||
export function useSignalValue<T, U>(
|
||||
sig: ReadonlySignal<T>,
|
||||
select: (value: T) => U,
|
||||
): U {
|
||||
const getSnapshot = useCallback(() => select(sig.value), [sig, select]);
|
||||
const getServerSnapshot = useCallback(() => select(sig.peek()), [sig, select]);
|
||||
const getServerSnapshot = useCallback(
|
||||
() => select(sig.peek()),
|
||||
[sig, select],
|
||||
);
|
||||
return useSyncExternalStore(sig.subscribe, getSnapshot, getServerSnapshot);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,10 @@ export function useChat(channel: ChannelNumber): UseChatResult {
|
||||
const client = useClient();
|
||||
const sig = useMemo(() => client.chat.messages(channel), [client, channel]);
|
||||
const messages = useSignal(sig);
|
||||
const send = useCallback((input: SendTextInput) => client.chat.send(input), [client]);
|
||||
const send = useCallback(
|
||||
(input: SendTextInput) => client.chat.send(input),
|
||||
[client],
|
||||
);
|
||||
const loadOlder = useCallback(
|
||||
(before: Date, limit?: number) => {
|
||||
const conv: ConversationKey = { kind: "channel", channel };
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import type { ConversationKey, Message, SendTextError, SendTextInput } from "@meshtastic/sdk";
|
||||
import type {
|
||||
ConversationKey,
|
||||
Message,
|
||||
SendTextError,
|
||||
SendTextInput,
|
||||
} from "@meshtastic/sdk";
|
||||
import type { ResultType } from "better-result";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useClient } from "../adapters/useClient.ts";
|
||||
@@ -20,7 +25,10 @@ export function useDirectChat(peer: number): UseDirectChatResult {
|
||||
const client = useClient();
|
||||
const sig = useMemo(() => client.chat.direct(peer), [client, peer]);
|
||||
const messages = useSignal(sig);
|
||||
const send = useCallback((input: SendTextInput) => client.chat.send(input), [client]);
|
||||
const send = useCallback(
|
||||
(input: SendTextInput) => client.chat.send(input),
|
||||
[client],
|
||||
);
|
||||
const loadOlder = useCallback(
|
||||
(before: Date, limit?: number) => {
|
||||
const conv: ConversationKey = { kind: "direct", peer };
|
||||
|
||||
@@ -15,13 +15,19 @@ export interface UseDraftResult {
|
||||
*/
|
||||
export function useDraft(conv: ConversationKey): UseDraftResult {
|
||||
const client = useClient();
|
||||
const sig = useMemo(() => client.chat.drafts.get(conv), [client, conv.kind, keyOf(conv)]);
|
||||
const sig = useMemo(
|
||||
() => client.chat.drafts.get(conv),
|
||||
[client, conv.kind, keyOf(conv)],
|
||||
);
|
||||
const text = useSignal(sig);
|
||||
const setText = useCallback(
|
||||
(value: string) => client.chat.drafts.set(conv, value),
|
||||
[client, conv],
|
||||
);
|
||||
const clear = useCallback(() => client.chat.drafts.clear(conv), [client, conv]);
|
||||
const clear = useCallback(
|
||||
() => client.chat.drafts.clear(conv),
|
||||
[client, conv],
|
||||
);
|
||||
return { text, setText, clear };
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,13 @@ import { useClient } from "../adapters/useClient.ts";
|
||||
export function useFavoriteNode() {
|
||||
const client = useClient();
|
||||
const favorite = useCallback(
|
||||
(nodeNum: number): Promise<ResultType<number, Error>> => client.nodes.favorite(nodeNum),
|
||||
(nodeNum: number): Promise<ResultType<number, Error>> =>
|
||||
client.nodes.favorite(nodeNum),
|
||||
[client],
|
||||
);
|
||||
const unfavorite = useCallback(
|
||||
(nodeNum: number): Promise<ResultType<number, Error>> => client.nodes.unfavorite(nodeNum),
|
||||
(nodeNum: number): Promise<ResultType<number, Error>> =>
|
||||
client.nodes.unfavorite(nodeNum),
|
||||
[client],
|
||||
);
|
||||
return { favorite, unfavorite };
|
||||
|
||||
@@ -5,11 +5,13 @@ import { useClient } from "../adapters/useClient.ts";
|
||||
export function useIgnoreNode() {
|
||||
const client = useClient();
|
||||
const ignore = useCallback(
|
||||
(nodeNum: number): Promise<ResultType<number, Error>> => client.nodes.ignore(nodeNum),
|
||||
(nodeNum: number): Promise<ResultType<number, Error>> =>
|
||||
client.nodes.ignore(nodeNum),
|
||||
[client],
|
||||
);
|
||||
const unignore = useCallback(
|
||||
(nodeNum: number): Promise<ResultType<number, Error>> => client.nodes.unignore(nodeNum),
|
||||
(nodeNum: number): Promise<ResultType<number, Error>> =>
|
||||
client.nodes.unignore(nodeNum),
|
||||
[client],
|
||||
);
|
||||
return { ignore, unignore };
|
||||
|
||||
@@ -4,5 +4,7 @@ import { useSignalValue } from "../adapters/useSignalValue.ts";
|
||||
|
||||
export function useNode(nodeNum: number): Node | undefined {
|
||||
const client = useClient();
|
||||
return useSignalValue(client.nodes.list, (list) => list.find((n) => n.num === nodeNum));
|
||||
return useSignalValue(client.nodes.list, (list) =>
|
||||
list.find((n) => n.num === nodeNum),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,5 +4,7 @@ import { useSignalValue } from "../adapters/useSignalValue.ts";
|
||||
|
||||
export function usePosition(nodeNum: number): Position | undefined {
|
||||
const client = useClient();
|
||||
return useSignalValue(client.position.list, (list) => list.find((p) => p.nodeNum === nodeNum));
|
||||
return useSignalValue(client.position.list, (list) =>
|
||||
list.find((p) => p.nodeNum === nodeNum),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,8 +10,14 @@ export interface UseTelemetryResult {
|
||||
|
||||
export function useTelemetry(nodeNum: number): UseTelemetryResult {
|
||||
const client = useClient();
|
||||
const latestSig = useMemo(() => client.telemetry.latest(nodeNum), [client, nodeNum]);
|
||||
const historySig = useMemo(() => client.telemetry.history(nodeNum), [client, nodeNum]);
|
||||
const latestSig = useMemo(
|
||||
() => client.telemetry.latest(nodeNum),
|
||||
[client, nodeNum],
|
||||
);
|
||||
const historySig = useMemo(
|
||||
() => client.telemetry.history(nodeNum),
|
||||
[client, nodeNum],
|
||||
);
|
||||
const latest = useSignal(latestSig);
|
||||
const history = useSignal(historySig);
|
||||
return { latest, history };
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { MeshRegistry } from "@meshtastic/sdk";
|
||||
import { createContext } from "react";
|
||||
|
||||
export const MeshRegistryContext = createContext<MeshRegistry | undefined>(undefined);
|
||||
export const MeshRegistryContext = createContext<MeshRegistry | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
@@ -12,6 +12,13 @@ export interface MeshRegistryProviderProps {
|
||||
* more than one connected device at a time. For single-client apps, prefer
|
||||
* `<MeshProvider client={...}>`.
|
||||
*/
|
||||
export function MeshRegistryProvider({ registry, children }: MeshRegistryProviderProps) {
|
||||
return <MeshRegistryContext.Provider value={registry}>{children}</MeshRegistryContext.Provider>;
|
||||
export function MeshRegistryProvider({
|
||||
registry,
|
||||
children,
|
||||
}: MeshRegistryProviderProps) {
|
||||
return (
|
||||
<MeshRegistryContext.Provider value={registry}>
|
||||
{children}
|
||||
</MeshRegistryContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,13 @@ import { ChannelNumber, MeshRegistry } from "@meshtastic/sdk";
|
||||
import { createFakeTransport } from "@meshtastic/sdk/testing";
|
||||
import { act, renderHook, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { MeshRegistryProvider, useChannels, useMeshDevice, useNode, useNodes } from "../mod.ts";
|
||||
import {
|
||||
MeshRegistryProvider,
|
||||
useChannels,
|
||||
useMeshDevice,
|
||||
useNode,
|
||||
useNodes,
|
||||
} from "../mod.ts";
|
||||
|
||||
function makeRegistry(deviceCount: number) {
|
||||
const registry = new MeshRegistry();
|
||||
@@ -21,7 +27,9 @@ describe("sdk-react under MeshRegistryProvider", () => {
|
||||
it("useMeshDevice resolves through the registry's active client", async () => {
|
||||
const { registry, handles } = makeRegistry(1);
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MeshRegistryProvider registry={registry}>{children}</MeshRegistryProvider>
|
||||
<MeshRegistryProvider registry={registry}>
|
||||
{children}
|
||||
</MeshRegistryProvider>
|
||||
);
|
||||
const { result } = renderHook(() => useMeshDevice(), { wrapper });
|
||||
expect(result.current.myNodeNum).toBeUndefined();
|
||||
@@ -36,7 +44,9 @@ describe("sdk-react under MeshRegistryProvider", () => {
|
||||
it("useNodes lists nodes from the active client and updates on packets", async () => {
|
||||
const { registry, handles } = makeRegistry(1);
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MeshRegistryProvider registry={registry}>{children}</MeshRegistryProvider>
|
||||
<MeshRegistryProvider registry={registry}>
|
||||
{children}
|
||||
</MeshRegistryProvider>
|
||||
);
|
||||
const { result } = renderHook(() => useNodes(), { wrapper });
|
||||
expect(result.current).toEqual([]);
|
||||
@@ -53,7 +63,9 @@ describe("sdk-react under MeshRegistryProvider", () => {
|
||||
it("useNode selects by node number and re-renders on update", async () => {
|
||||
const { registry, handles } = makeRegistry(1);
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MeshRegistryProvider registry={registry}>{children}</MeshRegistryProvider>
|
||||
<MeshRegistryProvider registry={registry}>
|
||||
{children}
|
||||
</MeshRegistryProvider>
|
||||
);
|
||||
const { result } = renderHook(() => useNode(11), { wrapper });
|
||||
expect(result.current).toBeUndefined();
|
||||
@@ -73,7 +85,9 @@ describe("sdk-react under MeshRegistryProvider", () => {
|
||||
if (!client) throw new Error("expected an active client");
|
||||
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MeshRegistryProvider registry={registry}>{children}</MeshRegistryProvider>
|
||||
<MeshRegistryProvider registry={registry}>
|
||||
{children}
|
||||
</MeshRegistryProvider>
|
||||
);
|
||||
const { result } = renderHook(() => useChannels(), { wrapper });
|
||||
expect(result.current).toEqual([]);
|
||||
@@ -95,7 +109,9 @@ describe("sdk-react under MeshRegistryProvider", () => {
|
||||
const { registry, handles } = makeRegistry(2);
|
||||
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MeshRegistryProvider registry={registry}>{children}</MeshRegistryProvider>
|
||||
<MeshRegistryProvider registry={registry}>
|
||||
{children}
|
||||
</MeshRegistryProvider>
|
||||
);
|
||||
const { result } = renderHook(() => useMeshDevice(), { wrapper });
|
||||
|
||||
|
||||
@@ -32,7 +32,9 @@ describe("sdk-react hooks", () => {
|
||||
|
||||
it("useChat surfaces inbound messages", async () => {
|
||||
const { handle, wrapper } = setup();
|
||||
const { result } = renderHook(() => useChat(ChannelNumber.Primary), { wrapper });
|
||||
const { result } = renderHook(() => useChat(ChannelNumber.Primary), {
|
||||
wrapper,
|
||||
});
|
||||
expect(result.current.messages).toEqual([]);
|
||||
// Exhaustive integration coverage is in packages/sdk; this test ensures
|
||||
// the hook wiring re-renders on a signal change.
|
||||
|
||||
@@ -4,6 +4,11 @@ export default defineProject({
|
||||
test: {
|
||||
name: "@meshtastic/sdk-react",
|
||||
environment: "jsdom",
|
||||
include: ["src/**/*.test.ts", "src/**/*.test.tsx", "tests/**/*.test.ts", "tests/**/*.test.tsx"],
|
||||
include: [
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"tests/**/*.test.ts",
|
||||
"tests/**/*.test.tsx",
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user