diff --git a/src/EmailSummary.tsx b/src/EmailSummary.tsx
index a9514ea..66d3517 100644
--- a/src/EmailSummary.tsx
+++ b/src/EmailSummary.tsx
@@ -1,3 +1,7 @@
+import { Trash } from "react-bootstrap-icons";
+import Button from "react-bootstrap/Button";
+import ButtonGroup from "react-bootstrap/ButtonGroup";
+import ButtonToolbar from "react-bootstrap/ButtonToolbar";
import Placeholder from "react-bootstrap/Placeholder";
import React from "react";
@@ -55,6 +59,21 @@ class EmailSummary extends React.Component<
stub.subject}
+
+
+
+
+
+
);
}
diff --git a/src/client/Client.tsx b/src/client/Client.tsx
index b85c74d..25eb9ab 100644
--- a/src/client/Client.tsx
+++ b/src/client/Client.tsx
@@ -3,10 +3,19 @@
* None of the dependencies should leak, in types or otherwise
*/
import * as base64 from "base-64";
+import * as jmaptypes from "./jmap-client-ts/src/types";
import * as jmapclient from "./jmap-client-ts/src";
import { FetchTransport } from "./jmap-client-ts/src/utils/fetch-transport";
-import { IAccount, IEmail, IEmailStub, IMailbox, ISession } from "./types";
+import {
+ IAccount,
+ IEmail,
+ IEmailStub,
+ IMailbox,
+ ISession,
+ MailboxRole,
+ PushMessage,
+} from "./types";
type Callback = () => void;
@@ -21,9 +30,40 @@ export interface ClientState {
emailStubs: Set;
mailboxes: Set;
};
+ mailboxes: {
+ trash: IMailbox;
+ } | null;
session: ISession | null;
}
+class Account implements IAccount {
+ id: string;
+
+ accountCapabilities: { [key: string]: jmaptypes.IMailCapabilities };
+ isPersonal: boolean;
+ isReadOnly: boolean;
+ mailboxes: Array | null;
+ name: string;
+
+ constructor(id: string, props: jmaptypes.IAccount) {
+ this.id = id;
+ this.accountCapabilities = props.accountCapabilities;
+ this.isPersonal = props.isPersonal;
+ this.isReadOnly = props.isReadOnly;
+ this.mailboxes = null;
+ this.name = props.name;
+ }
+ mailboxByRole(role: MailboxRole): IMailbox | null {
+ if (this.mailboxes === null) return null;
+ for (let i = 0; i < this.mailboxes.length; i++) {
+ const m = this.mailboxes[i];
+ if (m.role === role) {
+ return m;
+ }
+ }
+ return null;
+ }
+}
export default class Client {
// All objects which currently are listening for changes
callbacks: Array = [];
@@ -34,6 +74,7 @@ export default class Client {
emailStubs: new Set(),
mailboxes: new Set(),
},
+ mailboxes: null,
session: null,
};
@@ -67,6 +108,43 @@ export default class Client {
return;
}
+ emailMoveTrash(account: IAccount, emailId: string) {
+ if (this.jclient === null) return;
+ console.log("Trashing", emailId);
+ const email = this.emailStub(emailId);
+ if (email === null) return;
+ const trashMailbox = account.mailboxByRole("trash");
+ if (trashMailbox === null) {
+ console.error(
+ "Cannot trash ",
+ emailId,
+ " because ",
+ account.id,
+ " does not have a 'trash' mailbox",
+ );
+ return;
+ }
+ let mailboxIds = Object.keys(email.mailboxIds).reduce(
+ (acc, key) => {
+ acc[key as keyof typeof email.mailboxIds] = false;
+ return acc;
+ },
+ {} as Record,
+ );
+ mailboxIds[trashMailbox.id] = true;
+ const props = {
+ accountId: account.id,
+ update: {
+ [email.id]: {
+ mailboxIds: mailboxIds,
+ },
+ },
+ };
+ this.jclient.email_set(props).then((response) => {
+ console.log("Trashed", emailId);
+ });
+ }
+
// Ensure we have the full email content
ensureEmailContent(accountId: string, emailId: string) {
if (this.state.session == null) return;
@@ -98,7 +176,9 @@ export default class Client {
email(emailId: string): IEmail | null {
if (this.state.session == null) return null;
- return this.state.session.emails[emailId];
+ const result = this.state.session.emails[emailId];
+ if (result === undefined) return null;
+ return result;
}
emailGetContent(accountId: string, emailId: string) {
@@ -148,7 +228,7 @@ export default class Client {
.email_get({
accountId: accountId,
ids: [emailId],
- properties: ["from", "receivedAt", "subject"],
+ properties: ["from", "mailboxIds", "receivedAt", "subject"],
})
.then((response) => {
console.log(msg, "response", response);
@@ -163,6 +243,7 @@ export default class Client {
this.state.session.emailStubs[e.id] = {
from: e.from,
id: e.id,
+ mailboxIds: e.mailboxIds,
receivedAt: e.receivedAt,
subject: e.subject,
};
@@ -272,7 +353,7 @@ export default class Client {
accounts: Object.fromEntries(
Object.entries(session.accounts).map(([key, account]) => [
key,
- { ...account, id: key.toString(), mailboxes: null },
+ new Account(key.toString(), account),
]),
),
emails: {},
diff --git a/src/client/types.tsx b/src/client/types.tsx
index a479d70..e8fa48c 100644
--- a/src/client/types.tsx
+++ b/src/client/types.tsx
@@ -1,8 +1,10 @@
import * as client from "./jmap-client-ts/src/types";
+export type MailboxIdMap = { [mailboxId: string]: boolean };
export interface IEmailStub {
from: Array | null;
id: string;
+ mailboxIds: MailboxIdMap;
receivedAt: string;
subject: string;
}
@@ -18,8 +20,20 @@ export interface IEmail extends client.IEmailProperties {
export interface IAccount extends client.IAccount {
id: string;
mailboxes: Array | null;
-}
+ mailboxByRole(role: MailboxRole): IMailbox | null;
+}
+export type MailboxRole =
+ | "all"
+ | "archive"
+ | "drafts"
+ | "flagged"
+ | "important"
+ | "junk"
+ | "sent"
+ | "subscribed"
+ | "trash";
+export type PushMessage = client.PushMessage;
export type AccountIdMap = { [accountId: string]: IAccount };
export type EmailStubIdMap = { [emailId: string]: IEmailStub };
export type EmailIdMap = { [emailId: string]: IEmail };