---
components/app.js | 41 +++++++++++++++++++++++++++++++++++++
components/buffer-header.js | 25 +++++++++++++++++++---
lib/irc.js | 4 ++++
3 files changed, 67 insertions(+), 3 deletions(-)
diff --git a/components/app.js b/components/app.js
index a27c915..db3d713 100644
--- a/components/app.js
+++ b/components/app.js
@@ -226,6 +226,7 @@ export default class App extends Component {
this.handleVerifyClick = this.handleVerifyClick.bind(this);
this.handleVerifySubmit = this.handleVerifySubmit.bind(this);
this.handleOpenSettingsClick = this.handleOpenSettingsClick.bind(this);
+ this.handleUploadClick = this.handleUploadClick.bind(this);
this.handleSettingsChange = this.handleSettingsChange.bind(this);
this.handleSettingsDisconnect = this.handleSettingsDisconnect.bind(this);
this.handleSwitchSubmit = this.handleSwitchSubmit.bind(this);
@@ -1904,6 +1905,43 @@ export default class App extends Component {
this.openDialog("settings", { showProtocolHandler });
}
+ async handleUploadClick(event) {
+ const file = event.target.files && event.target.files[0];
+ if (!file) {
+ return;
+ }
+ event.target.value = null;
+ let serverID = State.getActiveServerID(this.state);
+ let client = this.clients.get(serverID);
+ const url = client.isupport.filehost();
+ if (!url) {
+ return;
+ }
+ let authHeaderVal;
+ if (this.config.server.auth === "oauth2") {
+ authHeaderVal = "Bearer " + this.state.connectParams.saslOauthBearer.token;
+ } else {
+ authHeaderVal = "Basic " + btoa(this.state.connectParams.saslPlain.username
+ + ":" +
+ this.state.connectParams.saslPlain.password);
+ }
+ let response = await fetch(url, {
+ method: "POST",
+ body: file,
+ headers: {
+ "Authorization": authHeaderVal,
+ "Content-Disposition": "attachment; filename=" + file.name
+ }
+ });
+ let location = response.headers.get("location");
+ if (location) {
+ let fileUrl = new URL(url)
+ fileUrl.pathname = location
+ this.composer.current.setState({text: fileUrl.toString()});
+ this.composer.current.focus()
+ }
+ }
+
handleSettingsChange(settings) {
store.settings.put(settings);
this.setState({ settings });
@@ -1990,6 +2028,8 @@ export default class App extends Component {
activeUser = activeServer.users.get(activeBuffer.name);
}
+ let client = this.clients.get(activeBuffer.server);
+
bufferHeader = html`
<section id="buffer-header">
<${BufferHeader}
@@ -2004,6 +2044,7 @@ export default class App extends Component {
onAddNetwork=${this.handleAddNetworkClick}
onManageNetwork=${() => this.handleManageNetworkClick(activeBuffer.server)}
onOpenSettings=${this.handleOpenSettingsClick}
+ onUpload=${client.isupport.filehost() ? this.handleUploadClick : null}
/>
</section>
`;
diff --git a/components/buffer-header.js b/components/buffer-header.js
index 5b2b56a..01ced0f 100644
--- a/components/buffer-header.js
@@ -1,4 +1,4 @@
-import { html, Component } from "../lib/index.js";
+import { html, Component, createRef } from "../lib/index.js";
import linkify from "../lib/linkify.js";
import { strip as stripANSI } from "../lib/ansi.js";
import { BufferType, ServerStatus, getServerName } from "../state.js";
@@ -26,6 +26,8 @@ export default function BufferHeader(props) {
fullyConnected = fullyConnected && props.bouncerNetwork.state === "connected";
}
+ let uploadRef = createRef();
+
let description = null, actions = [];
switch (props.buffer.type) {
case BufferType.SERVER:
@@ -124,6 +126,14 @@ export default function BufferHeader(props) {
description = linkify(stripANSI(props.buffer.topic), props.onChannelClick);
}
if (props.buffer.joined) {
+ if (props.onUpload) {
+ actions.push(html`
+ <input type="file" accept="*" style="display: none;" ref=${uploadRef} onChange=${props.onUpload}></input>
+ <button onClick=${() => {uploadRef.current.click();}}>
+ Upload
+ </button>
+ `);
+ }
actions.push(html`
<button
key="part"
@@ -203,13 +213,22 @@ export default function BufferHeader(props) {
description = html`<${NickStatus} status=${status}/> ${realname} ${details}`;
}
- actions = html`
+ if (props.onUpload) {
+ actions.push(html`
+ <input type="file" accept="*" style="display: none;" ref=${uploadRef} onChange=${props.onUpload}></input>
+ <button onClick=${() => {uploadRef.current.click();}}>
+ Upload
+ </button>
+ `);
+ }
+
+ actions.push(html`
<button
key="close"
class="danger"
onClick=${props.onClose}
>Close</button>
- `;
+ `);
break;
}
diff --git a/lib/irc.js b/lib/irc.js
index 86fa65a..3fa81e0 100644
--- a/lib/irc.js
+++ b/lib/irc.js
@@ -500,6 +500,10 @@ export class Isupport {
}
return parseInt(this.raw.get("LINELEN"), 10);
}
+
+ filehost() {
+ return this.raw.get("SOJU.IM/FILEHOST");
+ }
}
export function getMaxPrivmsgLen(isupport, nick, target) {
--
2.43.1
Thanks for the patch! I haven't looked closely at the code, but as a more
general comment: I think I'd like to support this feature, but I'm not
sure what's the best way to expose it without being intrusive. I'd say
that a prominent button in the top bar is a bit too intrusive. I don't
have great alternative ideas currently.
I also wonder how to give feedback to the user that an upload is in
progress. Maybe we can keep this for later.
Additionally, would be nicer to not completely throw away any previous
text in the input field -- maybe append the URL with a space separator
if it's not empty.