Allow selection of the account.
This was surprisingly complex because I tried to use react-router and found no easily and reasonable way to do it, so instead I hooked the location change event myself and plumbed it through. This required me to switch to a class-style component rather than a functional one, which required translating a bunch of coding styles through React. Overall I'm happy, it works pretty well and simply.
This commit is contained in:
parent
1d7d96de4a
commit
c5afd9f895
|
@ -5,19 +5,20 @@ import { IAccount } from "jmap-client-ts/lib/types";
|
|||
|
||||
type AccountIdMap = { [accountId: string]: IAccount };
|
||||
type AccountListProps = {
|
||||
account: IAccount | null;
|
||||
accounts: AccountIdMap;
|
||||
};
|
||||
|
||||
const AccountList: React.FC<AccountListProps> = ({ accounts }) => {
|
||||
const AccountList: React.FC<AccountListProps> = ({ account, accounts }) => {
|
||||
return (
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle variant="success" id="dropdown-basic">
|
||||
Dropdown Button
|
||||
{account ? account.name : "select account"}
|
||||
</Dropdown.Toggle>
|
||||
|
||||
<Dropdown.Menu>
|
||||
{Object.keys(accounts).map((key: keyof AccountIdMap) => (
|
||||
<Dropdown.Item href={"#/" + key}>{accounts[key].name}</Dropdown.Item>
|
||||
<Dropdown.Item href={"#" + key}>{accounts[key].name}</Dropdown.Item>
|
||||
))}
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
|
|
153
src/App.tsx
153
src/App.tsx
|
@ -2,98 +2,147 @@ import "./App.css";
|
|||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import * as base64 from "base-64";
|
||||
import { Client } from "jmap-client-ts";
|
||||
import { ISession } from "jmap-client-ts/lib/types";
|
||||
import { IAccount, ISession } from "jmap-client-ts/lib/types";
|
||||
import { FetchTransport } from "jmap-client-ts/lib/utils/fetch-transport";
|
||||
|
||||
import AccountList from "./AccountList";
|
||||
import AuthModal from "./AuthModal";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
|
||||
interface IAuth {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
interface IAppState {
|
||||
auth: IAuth;
|
||||
client: Client | null;
|
||||
session: ISession | null;
|
||||
interface ILocation {
|
||||
accountId: string;
|
||||
}
|
||||
|
||||
const App = () => {
|
||||
const [state, setInternalState] = useState<IAppState>({
|
||||
auth: { email: "", password: "" },
|
||||
client: null,
|
||||
session: null,
|
||||
});
|
||||
type AppState = {
|
||||
auth: IAuth;
|
||||
location: ILocation;
|
||||
session: ISession | null;
|
||||
};
|
||||
|
||||
// When the user provides credentials
|
||||
const onLogin = (email: string, password: string) => {
|
||||
// Store the provided credentials for now
|
||||
state.auth.email = email;
|
||||
state.auth.password = password;
|
||||
state.client = null;
|
||||
setInternalState(state);
|
||||
localStorage.setItem("auth", JSON.stringify(state.auth));
|
||||
doLogin(state.auth);
|
||||
type AppProps = {};
|
||||
|
||||
class App extends React.Component<AppProps, AppState> {
|
||||
account(): IAccount | null {
|
||||
if (!(this.state.session && this.state.session.accounts)) return null;
|
||||
return this.state.session.accounts[this.state.location.accountId];
|
||||
}
|
||||
client: Client | null = null;
|
||||
state: AppState = {
|
||||
auth: { email: "", password: "" },
|
||||
location: { accountId: "" },
|
||||
session: null,
|
||||
};
|
||||
|
||||
// Make the request to get system metadata
|
||||
const doLogin = (auth: IAuth) => {
|
||||
doLogin(auth: IAuth) {
|
||||
const domain = auth.email.split("@")[1];
|
||||
const well_known_url = "https://" + domain + "/.well-known/jmap";
|
||||
const basic_auth =
|
||||
"Basic " + base64.encode(auth.email + ":" + auth.password);
|
||||
|
||||
state.client = new Client({
|
||||
this.client = new Client({
|
||||
accessToken: "fake token",
|
||||
httpHeaders: { Authorization: basic_auth },
|
||||
sessionUrl: well_known_url,
|
||||
transport: new FetchTransport(fetch.bind(window)),
|
||||
});
|
||||
|
||||
state.client
|
||||
this.client
|
||||
.fetchSession()
|
||||
.then(() => {
|
||||
console.log("Session recieved");
|
||||
if (state.client) {
|
||||
state.session = state.client.getSession();
|
||||
setInternalState({
|
||||
...state,
|
||||
session: state.client.getSession(),
|
||||
});
|
||||
}
|
||||
console.log("Session received");
|
||||
|
||||
// For the type checker
|
||||
if (!this.client) return;
|
||||
|
||||
const session = this.client.getSession();
|
||||
this.setState({
|
||||
...this.state,
|
||||
session: session,
|
||||
});
|
||||
})
|
||||
.catch((error) => console.error(error));
|
||||
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
const loadAuth = () => {
|
||||
onHashChange() {
|
||||
console.log(window.location.hash);
|
||||
const hash = window.location.hash.substring(1);
|
||||
this.setState({
|
||||
...this.state,
|
||||
location: { accountId: hash },
|
||||
});
|
||||
}
|
||||
// When the user provides credentials
|
||||
onLogin(email: string, password: string) {
|
||||
// Store the provided credentials for now
|
||||
this.setState({
|
||||
...this.state,
|
||||
auth: {
|
||||
email: email,
|
||||
password: password,
|
||||
},
|
||||
});
|
||||
localStorage.setItem("auth", JSON.stringify(this.state.auth));
|
||||
this.doLogin({ email, password });
|
||||
}
|
||||
|
||||
loadAuth() {
|
||||
const data = localStorage.getItem("auth");
|
||||
if (!data) return;
|
||||
const auth = JSON.parse(data);
|
||||
state.auth = auth;
|
||||
if (state.client == null) {
|
||||
console.log("NULL STATE.client");
|
||||
doLogin(state.auth);
|
||||
this.setState({
|
||||
...this.state,
|
||||
auth: auth,
|
||||
});
|
||||
if (this.client == null) {
|
||||
this.doLogin(auth);
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadAuth();
|
||||
}, []);
|
||||
componentDidMount() {
|
||||
window.addEventListener(
|
||||
"hashchange",
|
||||
() => {
|
||||
this.onHashChange();
|
||||
},
|
||||
false,
|
||||
);
|
||||
this.loadAuth();
|
||||
this.onHashChange();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
{state && state.auth ? (
|
||||
<AccountList accounts={state.session ? state.session.accounts : {}} />
|
||||
) : (
|
||||
<AuthModal onLogin={onLogin}></AuthModal>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener(
|
||||
"hashchange",
|
||||
() => {
|
||||
this.onHashChange();
|
||||
},
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="App">
|
||||
{this.state && this.state.auth ? (
|
||||
<AccountList
|
||||
account={this.account()}
|
||||
accounts={this.state.session ? this.state.session.accounts : {}}
|
||||
/>
|
||||
) : (
|
||||
<AuthModal onLogin={this.onLogin}></AuthModal>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
|
Loading…
Reference in New Issue