feat(tailshare): download incoming taildrop files

This commit is contained in:
2026-06-20 16:32:23 +00:00
parent f566a98b95
commit ed9cd5ea3e
2 changed files with 69 additions and 1 deletions
+49 -1
View File
@@ -11,6 +11,8 @@ import {
Loader,
Modal,
NativeSelect,
Space,
Stack,
Text,
TextInput,
Title,
@@ -35,14 +37,18 @@ import {
getLoginUrl,
getPeers,
getWaitingFileCount,
getWaitingFiles,
} from "@webnet/tsconnect-redux"
import { IpnContext, useIpnSelector } from "../contexts/IpnContext"
import { use } from "react"
import styles from "./tailscale.scss"
import { CopyIcon } from "@phosphor-icons/react"
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 { fmtSize } from "../utils/format"
import { download } from "../utils/download"
function ConnectTailscale() {
const willBuild = useTailshareSelector(getIpnPrepareWillBuild)
@@ -289,7 +295,43 @@ function TailscaleSelfNodeStatus() {
)
}
function TailscaleWaitingFiles({
waitingFiles,
ipn,
}: {
waitingFiles: Record<string, IPNWaitingFile>
ipn: IPN
}) {
return (
<>
<Title order={2}>Taildrop received files</Title>
<Stack>
{Object.values(waitingFiles).map((file) => (
<Group key={file.name}>
<Text>{file.name}</Text>
<Space flex={1} />
<Text>{fmtSize(file.size)}</Text>
<ActionIcon
onClick={() => {
ipn.openWaitingFile(file.name).then((data) => {
download(data, file.name)
})
}}
>
<DownloadIcon />
</ActionIcon>
<ActionIcon bg="red" onClick={() => ipn.deleteWaitingFile(file.name)}>
<TrashIcon />
</ActionIcon>
</Group>
))}
</Stack>
</>
)
}
export default function TailscaleRoute() {
const waitingFiles = useIpnSelector(getWaitingFiles)
const ipn = use(IpnContext)
return (
@@ -307,6 +349,12 @@ export default function TailscaleRoute() {
{!ipn ? <ConnectTailscale /> : <TailscaleState />}
<Divider m={"md"} />
<TailscaleConfig />
{!!Object.keys(waitingFiles).length && !!ipn && (
<>
<Divider m={"md"} />
<TailscaleWaitingFiles waitingFiles={waitingFiles} ipn={ipn} />
</>
)}
</>
)
}
+20
View File
@@ -0,0 +1,20 @@
export function download(data: Uint8Array | Blob, name: string) {
if (data instanceof Uint8Array) {
if (data.buffer instanceof ArrayBuffer) {
data = new Blob([data as Uint8Array<ArrayBuffer>])
} else {
const next = new Uint8Array<ArrayBuffer>(new ArrayBuffer(data.length))
next.set(data)
data = new Blob([next])
}
}
const a = document.createElement("a")
const url = URL.createObjectURL(data)
a.href = url
a.download = name
a.hidden = true
document.body.append(a)
a.click()
a.remove()
URL.revokeObjectURL(url)
}