refactor(tailshare): address feedback
Checks / lint (pull_request) Successful in 1m37s
Checks / format (pull_request) Successful in 1m41s
Checks / typetest (pull_request) Successful in 8m27s
Checks / typecheck (pull_request) Successful in 8m50s
Checks / build (pull_request) Successful in 9m14s
Node Tests / node-tests (pull_request) Successful in 8m15s
Browser Tests / browser-tests (pull_request) Successful in 9m29s
Checks / lint (pull_request) Successful in 1m37s
Checks / format (pull_request) Successful in 1m41s
Checks / typetest (pull_request) Successful in 8m27s
Checks / typecheck (pull_request) Successful in 8m50s
Checks / build (pull_request) Successful in 9m14s
Node Tests / node-tests (pull_request) Successful in 8m15s
Browser Tests / browser-tests (pull_request) Successful in 9m29s
- Extract useSharedWorkerAvailable() into @webnet/react - Add explicit defaults (useWorker: true, fileOps: "memory") in parseConfig so consumers don't repeat them - Restore markAutostart tri-state: undefined → latches to true on first Running, false → never autostart, true → autostart Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
export { useClient } from "./useClient.js"
|
||||
export { useLocalStorage } from "./useLocalStorage.js"
|
||||
export { useSharedWorkerAvailable } from "./useSharedWorkerAvailable.js"
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { useSyncExternalStore } from "react"
|
||||
|
||||
const sub = () => () => void 0
|
||||
const getClient = () => typeof SharedWorker !== "undefined"
|
||||
const getServer = () => false
|
||||
|
||||
export function useSharedWorkerAvailable(): boolean {
|
||||
return useSyncExternalStore(sub, getClient, getServer)
|
||||
}
|
||||
@@ -25,11 +25,10 @@ import {
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
useSyncExternalStore,
|
||||
} from "react"
|
||||
import { initIPN, InMemoryFileOps, InMemoryState } from "@webnet/tsconnect"
|
||||
import type { IpnClient } from "@webnet/tsconnect"
|
||||
import { useLocalStorage } from "@webnet/react"
|
||||
import { useLocalStorage, useSharedWorkerAvailable } from "@webnet/react"
|
||||
import wasmUrl from "@webnet/tsconnect/main.wasm"
|
||||
import type { WorkerConfig } from "@webnet/tsconnect-worker"
|
||||
|
||||
@@ -52,10 +51,10 @@ export type TailshareConfig = {
|
||||
}
|
||||
|
||||
function parseConfig(raw: string): TailshareConfig {
|
||||
const cfg: TailshareConfig = { useWorker: true, fileOps: "memory" }
|
||||
try {
|
||||
const parsed = JSON.parse(raw)
|
||||
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {}
|
||||
const cfg: TailshareConfig = {}
|
||||
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return cfg
|
||||
if (typeof parsed.hostname === "string") cfg.hostname = parsed.hostname
|
||||
if (typeof parsed.controlURL === "string") cfg.controlURL = parsed.controlURL
|
||||
if (typeof parsed.authKey === "string") cfg.authKey = parsed.authKey
|
||||
@@ -63,16 +62,12 @@ function parseConfig(raw: string): TailshareConfig {
|
||||
if (typeof parsed.autostart === "boolean") cfg.autostart = parsed.autostart
|
||||
if (typeof parsed.useWorker === "boolean") cfg.useWorker = parsed.useWorker
|
||||
if (parsed.fileOps === "memory" || parsed.fileOps === "opfs") cfg.fileOps = parsed.fileOps
|
||||
return cfg
|
||||
} catch {
|
||||
return {}
|
||||
// return defaults
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
const workerAvailableSub = () => () => void 0
|
||||
const workerAvailableClient = () => typeof SharedWorker !== "undefined"
|
||||
const workerAvailableServer = () => false
|
||||
|
||||
export const IpnPrepareContext = createContext<{
|
||||
config: TailshareConfig
|
||||
setConfig: (patch: Partial<TailshareConfig>) => void
|
||||
@@ -89,11 +84,7 @@ export function IpnProvider({ children }: { children: ReactNode }) {
|
||||
}
|
||||
const localStore = storeRef.current
|
||||
|
||||
const workerAvailable = useSyncExternalStore(
|
||||
workerAvailableSub,
|
||||
workerAvailableClient,
|
||||
workerAvailableServer,
|
||||
)
|
||||
const workerAvailable = useSharedWorkerAvailable()
|
||||
|
||||
const [configRaw, setConfigRaw] = useLocalStorage("tailshare:config")
|
||||
const config = useMemo(() => parseConfig(configRaw), [configRaw])
|
||||
@@ -104,7 +95,7 @@ export function IpnProvider({ children }: { children: ReactNode }) {
|
||||
[setConfigRaw],
|
||||
)
|
||||
|
||||
const useWorker = config.useWorker !== false && workerAvailable
|
||||
const useWorker = !!config.useWorker && workerAvailable
|
||||
|
||||
const [willBuild, setWillBuild] = useState(false)
|
||||
const build = useCallback(() => setWillBuild(true), [])
|
||||
@@ -184,7 +175,7 @@ export function IpnProvider({ children }: { children: ReactNode }) {
|
||||
<AutoExitNode ipn={ipn} config={config} setConfig={setConfig} />
|
||||
</>
|
||||
)}
|
||||
<AutoInit build={build} config={config} />
|
||||
<AutoInit build={build} config={config} setConfig={setConfig} />
|
||||
<IpnContext value={ipn}>
|
||||
<IpnPrepareContext value={prepare}>{children}</IpnPrepareContext>
|
||||
</IpnContext>
|
||||
@@ -237,7 +228,24 @@ function AutoExitNode({
|
||||
return null
|
||||
}
|
||||
|
||||
function AutoInit({ build, config }: { build: () => void; config: TailshareConfig }) {
|
||||
function AutoInit({
|
||||
build,
|
||||
config,
|
||||
setConfig,
|
||||
}: {
|
||||
build: () => void
|
||||
config: TailshareConfig
|
||||
setConfig: (patch: Partial<TailshareConfig>) => void
|
||||
}) {
|
||||
const state = useIpnSelector(getIpnState)
|
||||
|
||||
const markAutostart = useEffectEvent(() => {
|
||||
if (config.autostart !== false) setConfig({ autostart: true })
|
||||
})
|
||||
useEffect(() => {
|
||||
if (state === "Running") markAutostart()
|
||||
}, [state])
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.autostart) return
|
||||
const timeout = window.setTimeout(build, 1000)
|
||||
|
||||
@@ -171,7 +171,7 @@ function TailscaleConfig() {
|
||||
? "OPFS: files persist across reloads. Requires SharedWorker mode."
|
||||
: "Memory: files are lost on reload."
|
||||
}
|
||||
value={config.fileOps ?? "memory"}
|
||||
value={config.fileOps}
|
||||
onChange={(e) => setConfig({ fileOps: e.target.value as "memory" | "opfs" })}
|
||||
data={[
|
||||
{ label: "Memory (volatile)", value: "memory" },
|
||||
@@ -192,7 +192,7 @@ function TailscaleConfig() {
|
||||
<Checkbox
|
||||
mt="md"
|
||||
label="Use SharedWorker (enables multi-tab support)"
|
||||
checked={config.useWorker !== false}
|
||||
checked={!!config.useWorker}
|
||||
onChange={(e) => setConfig({ useWorker: e.target.checked })}
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user