diff --git a/src/EmailList.tsx b/src/EmailList.tsx index 34c44be..4f5a20e 100644 --- a/src/EmailList.tsx +++ b/src/EmailList.tsx @@ -3,6 +3,7 @@ import Stack from "react-bootstrap/Stack"; import Client from "./client/Client"; import { IAccount, IMailbox } from "./client/types"; +import EmailSummary from "./EmailSummary"; type EmailListProps = { account: IAccount | null; @@ -17,37 +18,31 @@ class EmailList extends React.Component { if (this.props.account == null) return; if (this.props.client == null) return; if (this.props.mailbox == null) return; - this.props.client.emailList( + this.props.client.ensureEmailList( this.props.account.id, this.props.mailbox.id, - [], ); } render() { if ( this.props.account == null || + this.props.client == null || this.props.mailbox == null || this.props.mailbox.emailIds == null ) { return ; - } else if (this.props.mailbox.emails == null) { - return ( - - {this.props.mailbox.emailIds.map((e) => ( -
- Email {e} -
- ))} -
- ); } else { return ( - {this.props.mailbox.emails.map((m) => ( -
- {m.subject} -
+ {this.props.mailbox.emailIds.slice(0, 5).map((e) => ( + ))}
); diff --git a/src/EmailSummary.tsx b/src/EmailSummary.tsx new file mode 100644 index 0000000..afffa7a --- /dev/null +++ b/src/EmailSummary.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import Client from "./client/Client"; +import { IAccount, IEmail } from "./client/types"; + +type EmailSummaryProps = { + account: IAccount | null; + client: Client | null; + email: IEmail | null; + emailId: string; +}; +type EmailSummaryState = {}; + +class EmailSummary extends React.Component< + EmailSummaryProps, + EmailSummaryState +> { + componentDidMount() { + this.ensureData(); + } + componentDidUpdate() { + this.ensureData(); + } + + ensureData() { + if (this.props.account == null) return; + if (this.props.client == null) return; + this.props.client.ensureEmailGet(this.props.account.id, this.props.emailId); + } + + render() { + return ( +
+ {this.props.email != null + ? this.props.email.subject + : this.props.emailId} +
+ ); + } +} +export default EmailSummary; diff --git a/src/client/Client.tsx b/src/client/Client.tsx index d002d1c..fde5d63 100644 --- a/src/client/Client.tsx +++ b/src/client/Client.tsx @@ -6,7 +6,7 @@ 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, IMailbox, ISession } from "./types"; +import { IAccount, IEmail, IMailbox, ISession } from "./types"; type Callback = () => void; @@ -36,11 +36,27 @@ export default class Client { this.callbacks.push(f); } + // Ensure we have the fully-fleshed email + ensureEmailGet(accountId: string, emailId: string) { + if (this.state.session == null) return; + const existing = this.state.session.emails[emailId]; + if (existing != null) return; + this.emailGet(accountId, emailId); + } + + // Ensure we have the list of emails for the provided account and mailbox + ensureEmailList(accountId: string, mailboxId: string) { + const mailbox = this.mailbox(accountId, mailboxId); + if (mailbox != null && mailbox.emailIds != null) return; + this.emailList(accountId, mailboxId, []); + } + // 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]; @@ -65,13 +81,41 @@ export default class Client { return; } + email(emailId: string): IEmail | null { + if (this.state.session == null) return null; + return this.state.session.emails[emailId]; + } + + emailGet(accountId: string, emailId: string) { + if (this.jclient == null) return; + + this.jclient + .email_get({ + accountId: accountId, + ids: [emailId], + properties: ["mailboxIds", "subject"], + }) + .then((response) => { + console.log("Email response", response); + response.list.forEach((e) => { + if (this.state.session == null) return; + const existing = this.state.session.emails[e.id]; + if (existing !== undefined && existing.subject !== e.subject) { + console.error( + "Expectations violation: email ID is not unique within the server!", + ); + } + this.state.session.emails[e.id] = e; + this._triggerChange(); + }); + }) + .catch((x) => { + console.error("Failed to get email", emailId, x); + }); + } + emailList(accountId: string, mailboxId: string, ids: Array) { if (this.jclient == null) return; - /*this.jclient.email_get({ - accountId: accountId, - ids: [], - properties: ["threadId"] - });*/ this.jclient .email_query({ accountId: accountId, @@ -84,7 +128,7 @@ export default class Client { this._triggerChange(); }) .catch(() => { - console.error("OH NOES"); + console.error("Failed to get email list from mailbox", mailboxId); }); } @@ -145,6 +189,7 @@ export default class Client { { ...account, id: key.toString(), mailboxes: null }, ]), ), + emails: {}, }; if (!this.state.session) return; this._triggerChange(); diff --git a/src/client/types.tsx b/src/client/types.tsx index a4a6b3f..08b9cf9 100644 --- a/src/client/types.tsx +++ b/src/client/types.tsx @@ -5,13 +5,17 @@ export interface IMailbox extends client.IMailboxProperties { emails: Array | null; } +export interface IEmail extends client.IEmailProperties {} + export interface IAccount extends client.IAccount { id: string; mailboxes: Array | null; } export type AccountIdMap = { [accountId: string]: IAccount }; +export type EmailIdMap = { [emailId: string]: IEmail }; export interface ISession extends client.ISession { accounts: AccountIdMap; + emails: EmailIdMap; }