(conn)
| 155 | } |
| 156 | |
| 157 | function onConnection(conn) { |
| 158 | if (params.get("sink") === "hash") { |
| 159 | hashSink(conn); |
| 160 | return; |
| 161 | } |
| 162 | const li = document.createElement("li"); |
| 163 | const btn = document.createElement("button"); |
| 164 | btn.textContent = "Save incoming file…"; |
| 165 | const textBtn = document.createElement("button"); |
| 166 | textBtn.textContent = "Show as text"; |
| 167 | const progress = document.createElement("span"); |
| 168 | li.append(btn, " ", textBtn, " ", progress); |
| 169 | $("incoming").append(li); |
| 170 | const chose = () => { |
| 171 | btn.disabled = true; |
| 172 | textBtn.disabled = true; |
| 173 | }; |
| 174 | textBtn.onclick = () => { |
| 175 | chose(); |
| 176 | receiveText(conn, li, progress); |
| 177 | }; |
| 178 | btn.onclick = async () => { |
| 179 | chose(); |
| 180 | try { |
| 181 | // Stream to disk. The pull-based conn.read means the sender |
| 182 | // stalls on TCP backpressure while the user picks a file, and |
| 183 | // while the disk keeps up; nothing is buffered in memory. |
| 184 | const handle = await showSaveFilePicker({ suggestedName: "tailcat-download" }); |
| 185 | const w = await handle.createWritable(); |
| 186 | let n = 0; |
| 187 | for (let chunk; (chunk = await conn.read()) !== null; ) { |
| 188 | await w.write(chunk); |
| 189 | n += chunk.length; |
| 190 | progress.textContent = `${n} bytes`; |
| 191 | } |
| 192 | await w.close(); |
| 193 | conn.close(); |
| 194 | progress.textContent = `done, ${n} bytes`; |
| 195 | } catch (err) { |
| 196 | conn.close(); |
| 197 | progress.textContent = "failed: " + err.message; |
| 198 | window.tcTest.errors.push(String(err)); |
| 199 | } |
| 200 | }; |
| 201 | } |
| 202 | |
| 203 | // receiveText reads the whole incoming stream into memory and shows |
| 204 | // it as copyable text under the connection's list item. |
nothing calls this directly
no test coverage detected