Add rooms, per-network file sharing, and file transfer UX
Additional rooms: - Store tracks customRooms per networkId; createRoom action slugifies and deduplicates, switches active room on creation - Sidebar: + button next to Rooms label opens an inline input; Escape dismisses; Enter creates the room Per-network share directories: - Store tracks sharedFilesByNetwork keyed by networkId instead of a single global map; setSharedFiles(files, networkId?) defaults to activeNetworkId - FolderPicker reads per-network map to display accurate file count label File transfer UX: - Incoming file-offer now emits pending_offer instead of auto-accepting; user sees Accept / Reject buttons in the sidebar Transfers section - Progress events emitted per chunk during receive; Transfers panel shows a live progress bar with received/total sizes - file-cancel protocol message: sender or receiver can cancel an in-flight transfer; DataChannel is closed and buffers discarded - PeerConn: acceptOffer(), rejectOffer(), cancelRecv(), cancelSend() - BrowserAdapter: acceptOffer(), rejectOffer(), cancelTransfer() - Store: pendingOffers, fileProgress, acceptOffer, rejectOffer, cancelTransfer - Transfers component renders in sidebar; auto-hides when no activity Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,14 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { useRef } from 'react'
|
||||
import { useWaste } from '../store'
|
||||
|
||||
export function FolderPicker() {
|
||||
const { setSharedFiles } = useWaste()
|
||||
const { setSharedFiles, activeNetworkId, sharedFilesByNetwork } = useWaste()
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const [label, setLabel] = useState<string | null>(null)
|
||||
|
||||
const current = activeNetworkId ? sharedFilesByNetwork[activeNetworkId] : undefined
|
||||
const label = current && current.size > 0
|
||||
? `${current.size} file${current.size !== 1 ? 's' : ''} shared`
|
||||
: null
|
||||
|
||||
function onChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
const fileList = e.target.files
|
||||
@@ -15,9 +19,7 @@ export function FolderPicker() {
|
||||
map.set(f.name, f)
|
||||
}
|
||||
setSharedFiles(map)
|
||||
const first = fileList[0]
|
||||
const parts = (first.webkitRelativePath || first.name).split('/')
|
||||
setLabel(`${parts[0]} (${map.size} file${map.size !== 1 ? 's' : ''})`)
|
||||
e.target.value = ''
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -34,7 +36,7 @@ export function FolderPicker() {
|
||||
<button
|
||||
className="folder-picker-btn"
|
||||
onClick={() => inputRef.current?.click()}
|
||||
title="Share a folder with peers"
|
||||
title="Share a folder with peers on this network"
|
||||
>
|
||||
{label ?? '+ Share folder'}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user