From 5eb2e66a151049c2aad63bc8730e2389fd4ddccd Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Sun, 14 Jul 2019 21:30:17 +0200 Subject: [PATCH] Various structural refactoring --- src/app.js | 37 ++++--- src/components/Login.js | 233 ++++++++++++++++++++++------------------ src/components/input.js | 70 ++++++------ src/lib/api-request.js | 27 +++++ 4 files changed, 215 insertions(+), 152 deletions(-) create mode 100644 src/lib/api-request.js diff --git a/src/app.js b/src/app.js index 6850f18..a997386 100644 --- a/src/app.js +++ b/src/app.js @@ -53,31 +53,35 @@ let App = create({ this.setState({ client: client }); + this.startClient(client); }, + + updateRooms: function (client) { + let rooms = {}; + + client.getRooms().forEach((room) => { + rooms[room.roomId] = room; + }); + + this.setState({rooms: rooms}); + }, startClient: function(client) { console.log(client); + client.on("sync", (state, _prevState, _data) => { if (state == "ERROR") { /* FIXME: Implement? */ } else if (state == "SYNCING") { - let rooms = {}; - client.getRooms().forEach((room) => { - rooms[room.roomId] = room; - }); - this.setState({rooms: rooms}); + this.updateRooms(client); } else if (state == "PREPARED") { /* FIXME: Implement? */ } }); client.on("Room.localEchoUpdated", (_event) => { - let rooms = {}; - client.getRooms().forEach((room) => { - rooms[room.roomId] = room; - }); - this.setState({rooms: rooms}); + this.updateRooms(client); }); client.startClient(); @@ -87,13 +91,14 @@ let App = create({ if (this.state.client == undefined) { //Login screen return ; + } else { + return ( + <> + {this.setState({roomId: roomId});}}/> + + + ); } - return ( - <> - {this.setState({roomId: roomId});}}/> - - - ); } }); diff --git a/src/components/Login.js b/src/components/Login.js index ded3b57..62ad6a0 100644 --- a/src/components/Login.js +++ b/src/components/Login.js @@ -4,6 +4,8 @@ const create = require('create-react-class'); const Promise = require('bluebird'); const urllib = require('url'); +const createApiRequester = require("../lib/api-request"); + let login = create({ displayName: "Login", @@ -24,67 +26,82 @@ let login = create({ }, login: function() { - this.setState({error: ""}); + return Promise.try(() => { + this.setState({error: ""}); + + if (this.state.hs.valid) { + return this.doLogin(); + } + + let parts = this.state.formState.user.split(':'); + if (parts.length != 2) { + return this.setState({error: "Please enter a full mxid, like username:homeserver.tld"}); + } + + let hostname = urllib.parse("https://" + parts[1]); + + return Promise.try(() => { + return getApiServer(hostname); + }).then((homeserverUrl) => { + console.log("Using API server", homeserverUrl); - if (this.state.hs.valid) { - return this.doLogin(); - } + this.setState({ + apiUrl: homeserverUrl, + apiRequest: createApiRequester(homeserverUrl), + formState: Object.assign(this.state.formState, { + user: parts[0], + hs: homeserverUrl + }), + hs: Object.assign(this.state.hs, { + valid: true + }) + }); - let parts = this.state.formState.user.split(':'); - if (parts.length != 2) { - return this.setState({error: "Please enter a full mxid, like username:homeserver.tld"}); - } + return this.doLogin(); + }).catch((error) => { + /* FIXME: Error filtering */ + console.log("ERROR fetching homeserver url", error); - let hostname = urllib.parse("https://" + parts[1]); - getApiServer(hostname).then((hs) => { - console.log("Using API server", hs); - let formState = this.state.formState; - formState.user = parts[0]; - formState.hs = hs; - let hsState = Object.assign(this.state.hs, {valid: true}); - this.setState({apiUrl: hs, formState: formState, hs: hsState}); - this.doLogin(); - }).catch((error) => { - console.log("ERROR fetching homeserver url", error); - let hsState = Object.assign(this.state.hs, {error: error, valid: false, prompt: true}); - this.setState({hs: hsState}); + this.setState({ + hs: Object.assign(this.state.hs, { + error: error, + valid: false, + prompt: true + }) + }); + }); }); }, doLogin: function() { - console.log("Logging in"); - let user = this.state.formState.user.replace('@', ''); - let password = this.state.formState.pass; - let hs = this.state.apiUrl; - - let data = { - user: user, - password: password, - type: "m.login.password", - initial_device_display_name: "Neo v4", - }; + return Promise.try(() => { + console.log("Logging in"); + let user = this.state.formState.user.replace('@', ''); + let password = this.state.formState.pass; + let homeserverUrl = this.state.apiUrl; + + return Promise.try(() => { + return this.state.apiRequest("/_matrix/client/r0/login", { + user: user, + password: password, + type: "m.login.password", + initial_device_display_name: "Neo v4", + }); + }).then((responseJson) => { + console.log("got access token", responseJson); - let url = hs + "/_matrix/client/r0/login"; + this.setState({ json: responseJson }); - fetch(url, { - body: JSON.stringify(data), - headers: { - 'content-type': 'application/json' - }, - method: 'POST', - }).then((response) => response.json()) - .then((responseJson) => { - console.log("got access token", responseJson); - this.setState({json: responseJson}); if(responseJson.access_token != undefined) { - this.props.callback(responseJson.user_id, responseJson.access_token, hs); + this.props.callback(responseJson.user_id, responseJson.access_token, homeserverUrl); } else { - this.setState({error: responseJson.error}); + this.setState({ error: responseJson.error }); } - }) - .catch((error) => { - console.error(url, error); + }).catch((error) => { + /* FIXME: Why are errors being swallowed here? */ + console.error(error); }); + }); }, handleUserChange: function(e) { @@ -106,7 +123,7 @@ let login = create({ this.setState({formState: formState}); }, - handleHsChange: function(e) { + handleHomeserverChange: function(e) { let formState = this.state.formState; formState.hs = e.target.value; this.setState({formState: formState}); @@ -138,9 +155,9 @@ let login = create({ {this.state.hs.prompt ? ( - <> - - + <> + + ) : ( {this.state.formState["hs"]} )} @@ -152,39 +169,44 @@ let login = create({ } }); -function getApiServer(hostname) { - /* FIXME: Promise.try */ - return new Promise((resolve, reject) => { - console.log("Checking for api server from mxid", urllib.format(hostname)); - checkApi(hostname).then(() => { - // Hostname is a valid api server - hostname.pathname = ""; - resolve(urllib.format(hostname)); - }).catch(() => { - console.log("trying .well-known"); - tryWellKnown(hostname).then((hostname) => { - console.log("got .well-known host", hostname); - resolve(hostname); - }).catch((_err) => { - /* FIXME: Error chaining */ - reject(new Error("Fatal error trying to get API host")); - }); +function getApiServer(parsedUrl) { + return Promise.try(() => { + console.log("Checking for api server from mxid", urllib.format(parsedUrl)); + + return checkApi(parsedUrl); + }).then(() => { + // Hostname is a valid api server + return buildUrl(parsedUrl, ""); + }).catch(() => { + /* FIXME: Error filtering */ + console.log("trying .well-known"); + + return Promise.try(() => { + return tryWellKnown(parsedUrl); + }).then((hostname) => { + console.log("got .well-known host", hostname); + + return hostname; + }).catch((_err) => { + /* FIXME: Error chaining */ + throw new Error("Fatal error trying to get API host"); }); }); } function checkApi(host) { - let versionUrl = buildUrl(host, "/_matrix/client/versions"); - return new Promise((resolve, reject) => { - fetch(versionUrl).then((response) => { + return Promise.try(() => { + let versionUrl = buildUrl(host, "/_matrix/client/versions"); + + return Promise.try(() => { + return fetch(versionUrl); + }).then((response) => { if (response.status != 200) { console.log("Invalid homeserver url", versionUrl); + /* FIXME: Error types */ - return reject(new Error("Invalid homeserver URL")); + throw new Error("Invalid homeserver URL"); } - resolve(); - }).catch((err) => { - reject(err); }); }); } @@ -193,36 +215,43 @@ function tryWellKnown(host) { let wellKnownUrl = urllib.format(Object.assign(host, { pathname: "/.well-known/matrix/client" })); + console.log("Trying", wellKnownUrl, "for .well-known"); - return new Promise((resolve, reject) => { - return fetch(wellKnownUrl) - .then((response) => { - if (response.status != 200) { - console.log("no well-known in use"); - reject(new Error("No homeserver found")); - } - return response; - }).catch((_error) => { - /* FIXME: Error chaining */ - reject(new Error("can't fetch .well-known")); - }) - .then((response) => response.json()) - .then((json) => { - console.log("Parsed json", json); - if (json['m.homeserver'] != undefined && json['m.homeserver'].base_url != undefined) { - resolve(json['m.homeserver'].base_url); - } - }) - .catch((err) => { - console.log("Error in json", err); - /* FIXME: Error chaining */ - reject(new Error("Error while parsing .well-known")); - }); + + return Promise.try(() => { + return fetch(wellKnownUrl); + }).tap((response) => { + if (response.status != 200) { + console.log("no well-known in use"); + + /* FIXME: Error type */ + throw new Error("No homeserver found"); + } + }).catch((_error) => { + /* FIXME: Error chaining */ + throw new Error("can't fetch .well-known"); + }).then((response) => { + return response.json(); + }).then((json) => { + console.log("Parsed json", json); + + if (json['m.homeserver'] != null && json['m.homeserver'].base_url != null) { + return json['m.homeserver'].base_url; + } else { + /* FIXME: Error type */ + throw new Error("No homeserver specified in .well-known"); + } + }).catch((err) => { + /* FIXME: Error filtering? */ + console.log("Error in json", err); + + /* FIXME: Error chaining */ + throw new Error("Error while parsing .well-known"); }); } -function buildUrl(host, path) { - return urllib.format(Object.assign(host, { +function buildUrl(parsedUrl, path) { + return urllib.format(Object.assign(parsedUrl, { pathname: path })); } diff --git a/src/components/input.js b/src/components/input.js index 28c1da4..ba41e15 100644 --- a/src/components/input.js +++ b/src/components/input.js @@ -96,7 +96,8 @@ let input = create({ uploadFiles: function(uploads) { let client = this.props.client; - Promise.map(uploads, (upload) => { + + return Promise.map(uploads, (upload) => { let fileUploadPromise = client.uploadContent(upload.file, {onlyContentUri: false}).then((response) => { return response.content_uri; @@ -107,28 +108,25 @@ let input = create({ let additionalPromise; if (mimeType.startsWith("image/") || mimeType.startsWith("video/")) { function elementToThumbnail(element) { - /* FIXME: Get rid of `new Promise`, replace with Promise.try */ - return new Promise((resolve, reject) => { - riot.createThumbnail(element, + return Promise.try(() => { + return riot.createThumbnail(element, element.width, element.height, thumbnailType - ) - .catch((error) => { - console.error("neo: error getting thumbnail", error); - reject(error); - }) - .then((thumbResult) => { - return client.uploadContent(thumbResult.thumbnail, {onlyContentUri: false}); - }).then((response) => { - return resolve({ - thumbnail_url: response.content_uri, - thumbnail_info: { - mimetype: thumbnailType - } - }); - }); + ); + }).catch((error) => { + console.error("neo: error getting thumbnail", error); + throw error; + }).then((thumbResult) => { + return client.uploadContent(thumbResult.thumbnail, {onlyContentUri: false}); + }).then((response) => { + return { + thumbnail_url: response.content_uri, + thumbnail_info: { + mimetype: thumbnailType + } + }; }); } if (mimeType.startsWith("image/")) { @@ -142,6 +140,7 @@ let input = create({ } // create and upload thumbnail let thumbnailType = "image/png"; + if (mimeType == "image/jpeg") { thumbnailType = mimeType; } @@ -150,15 +149,18 @@ let input = create({ } else { // m.file } - Promise.all([fileUploadPromise, additionalPromise]).then((result) => { + + return Promise.all([fileUploadPromise, additionalPromise]).then((result) => { console.log(result); let info = { mimetype: mimeType }; + if (result[1] != undefined) { info = Object.assign(info, result[1]); } - client.sendEvent(this.props.roomId, "m.room.message", { + + return client.sendEvent(this.props.roomId, "m.room.message", { body: upload.file.name, msgtype: eventType, info: info, @@ -213,21 +215,21 @@ let input = create({ render: function() { return
{this.props.replyEvent && -
this.props.onReplyClick()}> - {this.props.replyEvent.plaintext()} -
+
this.props.onReplyClick()}> + {this.props.replyEvent.plaintext()} +
} {this.state.uploads.length > 0 && -
- {this.state.uploads.map((upload, key) => { - return ( -
- - this.removeUpload(key)}>X -
- ); - })} -
+
+ {this.state.uploads.map((upload, key) => { + return ( +
+ + this.removeUpload(key)}>X +
+ ); + })} +
}
diff --git a/src/lib/api-request.js b/src/lib/api-request.js new file mode 100644 index 0000000..a5a5fed --- /dev/null +++ b/src/lib/api-request.js @@ -0,0 +1,27 @@ +"use strict"; + +const Promise = require("bluebird"); + +module.exports = function createApiRequester(apiUrl) { + return function apiRequest(path, body) { + return Promise.try(() => { + let targetUrl = apiUrl + path; + + return Promise.try(() => { + return fetch(targetUrl, { + body: JSON.stringify(body), + headers: { + 'content-type': 'application/json' + }, + method: 'POST', + }); + }).then((response) => { + if (response.status >= 200 && response.status < 400) { + return response.json(); + } else { + throw new Error(`Non-200 response code for ${targetUrl}: ${response.status}`); + } + }); + }); + } +};