Rip apart client, build a wrapper.

The goal here is to be able to augment the client with additional data
as we get it. At this point I'm now augmenting with the mailbox data
that the MailboxList is requesting and showing that.

That's progress.

There may be significant issues with making multiple requests in a
single round-trip because my client library appears to do things like
hard-coding the position of specific requests. I may have to work around
this.
This commit is contained in:
Eli Ribble 2024-08-27 22:49:48 -07:00
parent c17e8b9ad0
commit bab5d421d4
7 changed files with 195 additions and 141 deletions

107
src/client/Client.tsx Normal file
View file

@ -0,0 +1,107 @@
/*
* Contains all of the logic for interacting with JMAP
* None of the dependencies should leak, in types or otherwise
*/
import * as base64 from "base-64";
import * as jmapclient from "jmap-client-ts";
import { FetchTransport } from "jmap-client-ts/lib/utils/fetch-transport";
import { IAccount, ISession } from "./types";
type Callback = () => void;
export interface IAuth {
email: string;
password: string;
}
export interface ClientState {
session: ISession | null;
}
export default class Client {
// Get the currently active account
account(accountId: string): IAccount | null {
if (!(this.state.session && this.state.session.accounts)) return null;
return this.state.session.accounts[accountId];
}
// All objects which currently are listening for changes
callbacks: Array<Callback> = [];
jclient: jmapclient.Client | null = null;
state: ClientState = {
session: null,
};
onChange(f: Callback) {
this.callbacks.push(f);
}
// Ensure that login happens. If this is called many times, only login once.
ensureLogin(auth: IAuth) {
if (this.jclient != null) return;
this.doLogin(auth);
}
// Make the request to get system metadata
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);
this.jclient = new jmapclient.Client({
accessToken: "fake token",
httpHeaders: { Authorization: basic_auth },
sessionUrl: well_known_url,
transport: new FetchTransport(fetch.bind(window)),
});
this.jclient
.fetchSession()
.then(() => {
this._onSession();
})
.catch((error) => console.error(error));
return;
}
mailboxList(accountId: string, ids: Array<string>) {
if (this.jclient == null) return;
this.jclient
.mailbox_get({
accountId: accountId,
ids: null,
})
.then((response) => {
if (this.state.session == null) return;
const account = this.state.session.accounts[response.accountId];
account.mailboxes = response.list;
this._triggerChange();
});
}
_triggerChange() {
this.callbacks.forEach((c) => {
c();
});
}
_onSession() {
console.log("Session received");
// For the type checker
if (!this.jclient) return;
const session = this.jclient.getSession();
this.state.session = {
...session,
accounts: Object.fromEntries(
Object.entries(session.accounts).map(([key, account]) => [
key,
{ ...account, id: key.toString(), mailboxes: [] },
]),
),
};
if (!this.state.session) return;
this._triggerChange();
}
}