feat(tailshare): start implementing the new file sharing app #28

Merged
codinget merged 27 commits from feat/tailshare into main 2026-07-25 23:31:14 +02:00
4 changed files with 14 additions and 65 deletions
Showing only changes of commit 52dd50c4b8 - Show all commits
@@ -24,7 +24,6 @@ import {
setIpnPrepareAuthKey,
setIpnPrepareControlURL,
setIpnPrepareHostname,
setIpnPrepareTaildrop,
triggerIpnBuild,
type IpnPrepare,
} from "../store/slices/ipnPrepare"
@@ -60,7 +59,7 @@ export function IpnProvider({ children }: { children: ReactNode }) {
const builderParams: Parameters<IpnBuilder>[0] = {}
if (!willBuild) return builderParams
if (willBuild.taildrop === "memory") builderParams.fileOps = new InMemoryFileOps()
builderParams.fileOps = new InMemoryFileOps()
if (client && localStorage) {
builderParams.stateStorage = new WebStorageState(localStorage, "ipn:")
@@ -159,7 +158,6 @@ function AutoInit() {
function AutoConfig() {
const ipnPrepare = useTailshareSelector(selectIpnPrepareSlice)
const dispatch = useTailshareDispatch()
const [taildrop, setTaildrop] = useLocalStorage("ipn#taildrop")
const [hostname, setHostname, clearHostname] = useLocalStorage("ipn#hostname")
const [controlURL, setControlURL, clearControlURL] = useLocalStorage("ipn#controlURL")
const [authKey, setAuthKey, clearAuthKey] = useLocalStorage("ipn#authKey")
@@ -167,7 +165,6 @@ function AutoConfig() {
const [hasPrepared, setHasPrepared] = useState(false)
const updateLocalStorage = useEffectEvent((ipnPrepare: IpnPrepare) => {
setTaildrop(ipnPrepare.taildrop)
if (ipnPrepare.hostname) setHostname(ipnPrepare.hostname)
else clearHostname()
if (ipnPrepare.controlURL) setControlURL(ipnPrepare.controlURL)
@@ -181,7 +178,6 @@ function AutoConfig() {
}, [ipnPrepare, hasPrepared])
const setPrepare = useEffectEvent(() => {
if (taildrop === "memory" || taildrop === "disable") dispatch(setIpnPrepareTaildrop(taildrop))
if (hostname) dispatch(setIpnPrepareHostname(hostname))
if (controlURL) dispatch(setIpnPrepareControlURL(controlURL))
if (authKey) dispatch(setIpnPrepareAuthKey(authKey))
@@ -20,8 +20,3 @@
align-items: center;
justify-content: space-between;
}
.willRestart {
position: relative;
top: -0.25em;
}
+12 -39
View File
@@ -27,7 +27,6 @@ import {
setIpnPrepareAuthKey,
setIpnPrepareControlURL,
setIpnPrepareHostname,
setIpnPrepareTaildrop,
triggerIpnBuild,
} from "../store/slices/ipnPrepare"
import {
@@ -46,7 +45,7 @@ import { CopyIcon, DownloadIcon, TrashIcon } from "@phosphor-icons/react"
import { useDisclosure } from "@mantine/hooks"
import clsx from "clsx"
import { useLocalStorage } from "../hooks/useLocalStorage"
import type { IPN, IPNWaitingFile } from "@webnet/tsconnect"
import type { IpnClient, IPNWaitingFile } from "@webnet/tsconnect"
import { fmtSize } from "../utils/format"
import { download } from "../utils/download"
import { setTailnetDrawerOpen } from "../store/slices/states"
@@ -114,8 +113,7 @@ function TailscaleConfig() {
const requireRestart =
ipnPrepare.willBuild &&
(ipnPrepare.taildrop !== ipnPrepare.willBuild.taildrop ||
ipnPrepare.authKey !== ipnPrepare.willBuild.authKey ||
(ipnPrepare.authKey !== ipnPrepare.willBuild.authKey ||
ipnPrepare.controlURL !== ipnPrepare.willBuild.controlURL ||
ipnPrepare.hostname !== ipnPrepare.willBuild.hostname)
@@ -141,26 +139,20 @@ function TailscaleConfig() {
)}
<TextInput
mt="xs"
label={
<>
Hostname
<Text unstyled className={styles.willRestart} component="span" c="red">
*
</Text>
</>
}
withAsterisk
label={<>Hostname</>}
placeholder="(Autogen on startup)"
value={ipnPrepare.hostname ?? ""}
onChange={(e) => dispatch(setIpnPrepareHostname(e.target.value))}
/>
<TextInput
mt="xs"
label={
withAsterisk
label={<>Pregenerated authkey</>}
description={
<>
Pregenerated authkey
<Text unstyled className={styles.willRestart} component="span" c="red">
*
</Text>
This is a soft credential that will be stored in the browser - only set this if you
must, logging in manually is more secure
</>
}
placeholder="(Manual login flow)"
@@ -169,14 +161,8 @@ function TailscaleConfig() {
/>
<TextInput
mt="xs"
label={
<>
Control plane URL
<Text unstyled className={styles.willRestart} component="span" c="red">
*
</Text>
</>
}
withAsterisk
label={<>Control plane URL</>}
placeholder="(Default Tailscale control plane)"
value={ipnPrepare.controlURL ?? ""}
onChange={(e) => dispatch(setIpnPrepareControlURL(e.target.value))}
@@ -187,19 +173,6 @@ function TailscaleConfig() {
checked={autostart === "1"}
onChange={(e) => setAutostart("" + +e.target.checked)}
/>
<Checkbox
mt="xs"
label={
<>
Enable Taildrop
<Text unstyled className={styles.willRestart} component="span" c="red">
*
</Text>
</>
}
checked={ipnPrepare.taildrop === "memory"}
onChange={(e) => dispatch(setIpnPrepareTaildrop(e.target.checked ? "memory" : "disable"))}
/>
<Box className={clsx(styles.restartLine, requireRestart || styles.hidden)}>
<Text mt="xs" c="dimmed">
Restart required to apply the changes.
@@ -304,7 +277,7 @@ function TailscaleWaitingFiles({
ipn,
}: {
waitingFiles: Record<string, IPNWaitingFile>
ipn: IPN
ipn: IpnClient
}) {
return (
<>
@@ -1,10 +1,6 @@
import { createSlice, type PayloadAction, type Store } from "@reduxjs/toolkit"
export type TaildropOptions = "disable" | "memory"
export type IpnPrepare = {
taildrop: TaildropOptions
hostname: string | null
controlURL: string | null
authKey: string | null
@@ -13,7 +9,6 @@ export type IpnPrepare = {
alreadyRunning: boolean
}
const initialState: IpnPrepare = {
taildrop: "memory",
hostname: null,
controlURL: null,
authKey: null,
@@ -25,9 +20,6 @@ export const ipnPrepareSlice = createSlice({
name: "ipnPrepare",
initialState,
reducers: {
setIpnPrepareTaildrop: (state, action: PayloadAction<TaildropOptions>) => {
state.taildrop = action.payload
},
setIpnPrepareHostname: (state, action: PayloadAction<string | null>) => {
state.hostname = action.payload || null
},
@@ -45,7 +37,6 @@ export const ipnPrepareSlice = createSlice({
},
},
selectors: {
getIpnPrepareTaildrop: (state) => state.taildrop,
getIpnPrepareHostname: (state) => state.hostname,
getIpnPrepareControlURL: (state) => state.controlURL,
getIpnPrepareAuthKey: (state) => state.authKey,
@@ -55,17 +46,11 @@ export const ipnPrepareSlice = createSlice({
})
export const {
actions: {
setIpnPrepareAuthKey,
setIpnPrepareControlURL,
setIpnPrepareHostname,
setIpnPrepareTaildrop,
},
actions: { setIpnPrepareAuthKey, setIpnPrepareControlURL, setIpnPrepareHostname },
selectors: {
getIpnPrepareControlURL,
getIpnPrepareAuthKey,
getIpnPrepareHostname,
getIpnPrepareTaildrop,
getIpnPrepareWillBuild,
getIpnPrepareAlreadyRunning,
},