diff --git a/components/app.js b/components/app.js index 01799f1..31529e0 100644 --- a/components/app.js +++ b/components/app.js @@ -98,6 +98,7 @@ export default class App extends Component { status: Status.DISCONNECTED, buffers: new Map(), activeBuffer: null, + error: null, }; pendingHistory = Promise.resolve(null); endOfHistory = new Map(); @@ -115,6 +116,7 @@ export default class App extends Component { this.handleJoinClick = this.handleJoinClick.bind(this); this.autocomplete = this.autocomplete.bind(this); this.handleBufferScrollTop = this.handleBufferScrollTop.bind(this); + this.dismissError = this.dismissError.bind(this); this.saveReceipts = debounce(this.saveReceipts.bind(this), 500); @@ -157,6 +159,11 @@ export default class App extends Component { } } + dismissError(event) { + event.preventDefault(); + this.setState({ error: null }); + } + setBufferState(name, updater, callback) { this.setState((state) => { var buf = state.buffers.get(name); @@ -353,6 +360,12 @@ export default class App extends Component { this.handleMessage(event.detail.message); }); + this.client.addEventListener("error", (event) => { + this.setState({ + error: event.detail, + }); + }); + this.createBuffer(SERVER_BUFFER); this.switchBuffer(SERVER_BUFFER); } @@ -461,7 +474,7 @@ export default class App extends Component { var after = receipt; var before = { time: msg.tags.time || irc.formatDate(new Date()) }; this.fetchHistoryBetween(channel, after, before, CHATHISTORY_MAX_SIZE).catch((err) => { - console.error("Failed to fetch history:", err); + this.setState({ error: "Failed to fetch history: " + err }); this.receipts.delete(channel); this.saveReceipts(); }); @@ -605,20 +618,20 @@ export default class App extends Component { var cmd = commands[name]; if (!cmd) { - console.error("Unknwon command '" + name + "'"); + this.setState({ error: "Unknown command '" + name + "'" }); return; } try { cmd(this, args); - } catch (err) { - console.error(err); + } catch (error) { + this.setState({ error }); } } privmsg(target, text) { if (target == SERVER_BUFFER) { - console.error("Cannot send message in server buffer"); + this.setState({ error: "Cannot send message in server buffer" }); return; } @@ -787,7 +800,7 @@ export default class App extends Component { if (this.state.status != Status.REGISTERED) { return html`
- <${Connect} params=${this.state.connectParams} disabled=${this.state.status != Status.DISCONNECTED} onSubmit=${this.handleConnectSubmit}/> + <${Connect} error=${this.state.error} params=${this.state.connectParams} disabled=${this.state.status != Status.DISCONNECTED} onSubmit=${this.handleConnectSubmit}/>
`; } @@ -833,6 +846,9 @@ export default class App extends Component { ${memberList} <${Composer} ref=${this.composer} readOnly=${this.state.activeBuffer == SERVER_BUFFER} onSubmit=${this.handleComposerSubmit} autocomplete=${this.autocomplete}/> + ${this.state.error ? html` +

${this.state.error} ×

+ ` : null} `; } } diff --git a/components/connect.js b/components/connect.js index 97589e0..94ea120 100644 --- a/components/connect.js +++ b/components/connect.js @@ -140,7 +140,9 @@ export default class Connect extends Component {
- + ${this.props.error ? html` +

${this.props.error || ""}

+ ` : null} `; diff --git a/lib/client.js b/lib/client.js index 01fd67b..4c093e9 100644 --- a/lib/client.js +++ b/lib/client.js @@ -36,11 +36,10 @@ export default class Client extends EventTarget { try { this.ws = new WebSocket(params.url); } catch (err) { - console.error("Failed to create connection:", err); + this.dispatchEvent(new CustomEvent("error", { detail: "Failed to create connection: " + err })); setTimeout(() => this.dispatchEvent(new CustomEvent("close")), 0); return; } - this.ws.addEventListener("open", this.handleOpen.bind(this)); this.ws.addEventListener("message", this.handleMessage.bind(this)); @@ -50,7 +49,7 @@ export default class Client extends EventTarget { }); this.ws.addEventListener("error", () => { - console.error("Connection error"); + this.dispatchEvent(new CustomEvent("error", { detail: "Connection Error" })); }); } @@ -95,7 +94,7 @@ export default class Client extends EventTarget { this.registered = true; break; case irc.ERR_PASSWDMISMATCH: - console.error("Password mismatch"); + this.dispatchEvent(new CustomEvent("error", { detail: "Password mismatch" })); this.close(); break; case "CAP": @@ -121,7 +120,7 @@ export default class Client extends EventTarget { case irc.ERR_SASLTOOLONG: case irc.ERR_SASLABORTED: case irc.ERR_SASLALREADY: - console.error("SASL error:", msg); + this.dispatchEvent(new CustomEvent("error", { detail: "SASL error: " + msg })); this.close(); break; case "PING": @@ -258,7 +257,7 @@ export default class Client extends EventTarget { // For now only PLAIN is supported if (challengeStr != "+") { - console.error("Expected an empty challenge, got:", challengeStr); + this.dispatchEvent(new CustomEvent("error", { detail: "Expected an empty challenge, got: " + challengeStr })); this.send({ command: "AUTHENTICATE", params: ["*"] }); return; } diff --git a/style.css b/style.css index 3d1166f..c1fdd5a 100644 --- a/style.css +++ b/style.css @@ -234,3 +234,22 @@ details summary { #buffer .nick-16 { color: #ec273e; } + +#error-msg { + color: white; + background-color: red; + position: fixed; + bottom: 2rem; + right: 0; + padding: 0.5rem; + margin: 0.5rem; +} + +#error-msg a { + color: white; + text-decoration: none; +} + +.error-text { + color: red; +}