feat(tsconnect): add outgoing file transfer progress notifications

- Export UpdateOutgoingFiles on taildrop.Extension so it can be called
  from outside the package (wasm bridge, package main).
- Wrap sendFile's PUT body with progresstracking.NewReader so bytes-sent
  is sampled roughly once per second during transfer.
- Create an OutgoingFile entry (with UUID, peer ID, name, declared size)
  before the PUT and call UpdateOutgoingFiles on each progress tick and
  on completion (setting Finished/Succeeded). This flows into the IPN
  notify stream as OutgoingFiles notifications.
- Add jsOutgoingFile struct and wire n.OutgoingFiles into a new
  notifyOutgoingFiles callback in run(), mirroring notifyIncomingFiles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-14 23:01:30 +00:00
parent 4ef06f2498
commit 705eebe5fc
3 changed files with 74 additions and 4 deletions
+33
View File
@@ -419,6 +419,26 @@ func (i *jsIPN) run(jsCallbacks js.Value) {
log.Printf("could not marshal IncomingFiles: %v", err)
}
}
if n.OutgoingFiles != nil {
files := make([]jsOutgoingFile, len(n.OutgoingFiles))
for i, f := range n.OutgoingFiles {
files[i] = jsOutgoingFile{
ID: f.ID,
PeerID: string(f.PeerID),
Name: f.Name,
Started: f.Started.UnixMilli(),
DeclaredSize: f.DeclaredSize,
Sent: f.Sent,
Finished: f.Finished,
Succeeded: f.Succeeded,
}
}
if b, err := json.Marshal(files); err == nil {
jsCallbacks.Call("notifyOutgoingFiles", string(b))
} else {
log.Printf("could not marshal OutgoingFiles: %v", err)
}
}
})
go func() {
@@ -955,6 +975,19 @@ type jsIncomingFile struct {
Received int64 `json:"received"` // bytes received so far
}
// jsOutgoingFile is the JSON representation of an outgoing file transfer
// sent to the notifyOutgoingFiles callback.
type jsOutgoingFile struct {
ID string `json:"id"`
PeerID string `json:"peerID"`
Name string `json:"name"`
Started int64 `json:"started"` // Unix milliseconds
DeclaredSize int64 `json:"declaredSize"` // -1 if unknown
Sent int64 `json:"sent"` // bytes sent so far
Finished bool `json:"finished"`
Succeeded bool `json:"succeeded"` // only meaningful when finished
}
type jsNetMap struct {
Self jsNetMapSelfNode `json:"self"`
Peers []jsNetMapPeerNode `json:"peers"`