feat: use matrix-js-sdk for login

pull/8/head
c-cal 6 years ago
parent a0bebe338b
commit ea9e09c166
Signed by: watcha
GPG Key ID: 87DD78E7F7A1581D
  1. 131
      src/App.js
  2. 61
      src/Login.js
  3. 9
      src/User.css

@ -1,5 +1,6 @@
import React, { Component } from "react";
import { withNamespaces } from "react-i18next";
import sdk from "matrix-js-sdk";
import Login from "./Login.js";
import AdminHome from "./AdminHome.js";
@ -10,101 +11,85 @@ class App extends Component {
constructor(props) {
super(props);
this.state = {
accessToken: null,
homeserver: null,
client: null,
loginError: false,
clientPrepared: false,
};
}
componentDidMount() {
let accessToken = null;
const search = window.location.search;
if (search.includes("=")) {
// Retrieving the token passed from Riot
// see riot-web.git/src/components/structures/WatchaAdmin.js
const key = search.split("=")[1];
const value = localStorage.getItem("watcha-" + key);
if (value !== null) {
localStorage.removeItem("watcha-" + key);
async componentDidMount() {
this.setLanguage();
const { i18n } = this.props;
i18n.changeLanguage(value.split("|")[0]);
accessToken = value.split("|")[1];
} else {
// if the token was incorrect, or was already retrieved,
// then redirect to Riot for security
window.location =
window.location.protocol + "//" + window.location.host;
return;
}
const baseUrl =
localStorage.getItem("mx_hs_url") ||
(await this.getBaseUrlFromConfig());
const client = sdk.createClient({ baseUrl });
this.setState({ client });
const accessToken = localStorage.getItem("mx_access_token");
if (accessToken) {
await client
.loginWithToken(accessToken)
.then(() => this.setupClient(client))
.catch(error => {
this.setState({ loginError: true });
console.error(error.message);
});
} else {
this.setState({ loginError: true });
}
}
fetch("/config.json")
async getBaseUrlFromConfig() {
return await fetch("/config.json")
.then(response => response.json())
.then(data =>
this.setState({
homeserver:
data["default_server_config"]["m.homeserver"][
"base_url"
] + "/",
accessToken,
})
.then(
data =>
data["default_server_config"]["m.homeserver"]["base_url"] +
"/"
)
.catch(error => {
// should only happen in dev - without token
// should only occur in development environment when the chat
// and the administration interface do not have the same domain
const defaultHomeServer =
process.env.REACT_APP_CORE || "http://localhost:8008";
console.log("Defaulting homeserver to " + defaultHomeServer);
this.setState({
homeserver: defaultHomeServer + "/",
accessToken,
});
(process.env.REACT_APP_CORE || "http://localhost:8008") +
"/";
console.log(`Set ${defaultHomeServer} as default home server`);
return defaultHomeServer;
});
}
connection = async (userName, password) => {
try {
const path = this.state.homeserver + "_matrix/client/r0/login";
setLanguage() {
const localSettings = localStorage.getItem("mx_local_settings");
if (localSettings && localSettings.language) {
this.props.i18n.changeLanguage(localSettings.language);
}
}
const loginRequest = await fetch(path, {
method: "POST",
body: JSON.stringify({
initial_device_display_name: "Web setup account",
user: userName,
password: password,
type: "m.login.password",
}),
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});
const loginData = JSON.parse(await loginRequest.text());
if (loginData["access_token"]) {
this.setState({ accessToken: loginData["access_token"] });
return this.state.accessToken;
} else {
// TODO: to test
console.log("error: no access token");
setupClient = async client => {
await client.startClient({ initialSyncLimit: 10 });
client.on("sync", (state, prevState, response) => {
if (state === "SYNCING" && prevState === "SYNCING") {
return;
}
} catch (e) {
// TODO: is this useful ??
console.log("error: " + e);
return;
}
console.info("MatrixClient sync state => %s", state);
if (state === "PREPARED") {
this.setState({ clientPrepared: true });
}
});
};
render() {
return this.state.accessToken ? (
return this.state.clientPrepared ? (
<AdminHome
className="AdminHome"
token={this.state.accessToken}
server={this.state.homeserver}
token={this.state.client.getAccessToken()}
server={this.state.client.baseUrl + "/"}
/>
) : (
<Login connection={this.connection} />
);
) : this.state.loginError ? (
<Login client={this.state.client} setupClient={this.setupClient} />
) : null;
}
}

@ -4,6 +4,7 @@ import { withNamespaces } from "react-i18next";
import Button from "react-bootstrap/Button";
import Form from "react-bootstrap/Form";
import InputGroup from "react-bootstrap/InputGroup";
import Spinner from "react-bootstrap/Spinner";
import logo from "./images/logo.svg";
@ -11,15 +12,29 @@ class Login extends Component {
constructor(props) {
super(props);
this.state = {
userName: "",
user: "",
password: "",
submitCount: 0,
};
}
static propTypes = {
connection: PropTypes.func.isRequired,
client: PropTypes.object.isRequired,
setupClient: PropTypes.func.isRequired,
};
login() {
const { client, setupClient } = this.props;
const { user, password } = this.state;
client
.loginWithPassword(user, password)
.then(() => setupClient(client))
.catch(error => {
this.setState({ submitCount: 0 });
alert(error.message);
});
}
onChange = event =>
this.setState({ [event.target.name]: event.target.value });
@ -28,11 +43,45 @@ class Login extends Component {
onSubmit = event => {
event.preventDefault();
this.props.connection(this.state.userName, this.state.password);
this.setState(
state => ({
submitCount: state.submitCount + 1,
}),
() => {
if (this.state.submitCount === 1) {
this.login();
}
}
);
};
render() {
const { t } = this.props;
const button =
this.state.submitCount === 0 ? (
<Button variant="outline-primary" type="submit" block>
{t("Sign in")}
</Button>
) : (
<Button
className="loadingLoginButton"
variant="outline-primary"
block
disabled
>
<span className="loadingLoginText">
{t("Loading") + "..."}
</span>
<Spinner
as="span"
animation="border"
size="sm"
role="status"
aria-hidden="true"
/>
</Button>
);
return (
<div className="loginForm container mx-auto">
<Form.Control
@ -58,7 +107,7 @@ class Login extends Component {
</InputGroup.Prepend>
<Form.Control
autoComplete="username"
name="userName"
name="user"
onChange={this.onChange}
placeholder={t("Name")}
required
@ -85,9 +134,7 @@ class Login extends Component {
/>
</InputGroup>
</Form.Group>
<Button variant="outline-primary btn-block" type="submit">
{t("Sign in")}
</Button>
{button}
</Form>
</div>
);

@ -9,6 +9,15 @@
max-height: 30vh;
}
.loadingLoginButton {
display: flex;
align-items: center;
}
.loadingLoginText {
flex-grow: 1;
}
.loading {
height: stretch;
width: stretch;

Loading…
Cancel
Save