Compare commits

..

No commits in common. "1e9dae15f13dc81288c438acd436b87c08610db1" and "d8ee3d5f0f37c6e182d5f853b1b9a4c0c4759d27" have entirely different histories.

7 changed files with 6 additions and 153 deletions

12
package-lock.json generated
View File

@ -21,7 +21,6 @@
"bootstrap": "^5.3.3",
"react": "^18.3.1",
"react-bootstrap": "^2.10.4",
"react-bootstrap-icons": "^1.11.4",
"react-dom": "^18.3.1",
"react-scripts": "5.0.1",
"sass": "^1.77.8",
@ -14747,17 +14746,6 @@
}
}
},
"node_modules/react-bootstrap-icons": {
"version": "1.11.4",
"resolved": "https://registry.npmjs.org/react-bootstrap-icons/-/react-bootstrap-icons-1.11.4.tgz",
"integrity": "sha512-lnkOpNEZ/Zr7mNxvjA9efuarCPSgtOuGA55XiRj7ASJnBjb1wEAdtJOd2Aiv9t07r7FLI1IgyZPg9P6jqWD/IA==",
"dependencies": {
"prop-types": "^15.7.2"
},
"peerDependencies": {
"react": ">=16.8.6"
}
},
"node_modules/react-dev-utils": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",

View File

@ -16,7 +16,6 @@
"bootstrap": "^5.3.3",
"react": "^18.3.1",
"react-bootstrap": "^2.10.4",
"react-bootstrap-icons": "^1.11.4",
"react-dom": "^18.3.1",
"react-scripts": "5.0.1",
"sass": "^1.77.8",

View File

@ -88,10 +88,6 @@ class EmailContent extends React.Component<
Received
</Form.Label>
<Col sm={10}>{email.receivedAt}</Col>
<Form.Label column sm={2}>
Sent
</Form.Label>
<Col sm={10}>{email.sentAt}</Col>
</Form.Group>
</Form>
</Row>

View File

@ -1,7 +1,3 @@
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";
@ -59,21 +55,6 @@ class EmailSummary extends React.Component<
stub.subject}
</span>
</a>
<ButtonToolbar>
<ButtonGroup className="me-2">
<Button
onClick={() => {
this.props.client.emailMoveTrash(
this.props.account,
this.props.emailId,
);
}}
>
<Trash />
</Button>
<Button>2</Button>
</ButtonGroup>
</ButtonToolbar>
</div>
);
}

View File

@ -3,19 +3,10 @@
* 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,
MailboxRole,
PushMessage,
} from "./types";
import { IAccount, IEmail, IEmailStub, IMailbox, ISession } from "./types";
type Callback = () => void;
@ -30,40 +21,9 @@ export interface ClientState {
emailStubs: Set<string>;
mailboxes: Set<string>;
};
mailboxes: {
trash: IMailbox;
} | null;
session: ISession | null;
}
class Account implements IAccount {
id: string;
accountCapabilities: { [key: string]: jmaptypes.IMailCapabilities };
isPersonal: boolean;
isReadOnly: boolean;
mailboxes: Array<IMailbox> | 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<Callback> = [];
@ -74,7 +34,6 @@ export default class Client {
emailStubs: new Set(),
mailboxes: new Set(),
},
mailboxes: null,
session: null,
};
@ -108,43 +67,6 @@ 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<keyof typeof email.mailboxIds, boolean>,
);
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;
@ -176,9 +98,7 @@ export default class Client {
email(emailId: string): IEmail | null {
if (this.state.session == null) return null;
const result = this.state.session.emails[emailId];
if (result === undefined) return null;
return result;
return this.state.session.emails[emailId];
}
emailGetContent(accountId: string, emailId: string) {
@ -228,7 +148,7 @@ export default class Client {
.email_get({
accountId: accountId,
ids: [emailId],
properties: ["from", "mailboxIds", "receivedAt", "subject"],
properties: ["from", "receivedAt", "subject"],
})
.then((response) => {
console.log(msg, "response", response);
@ -243,7 +163,6 @@ 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,
};
@ -344,16 +263,12 @@ export default class Client {
if (!this.jclient) return;
const session = this.jclient.getSession();
// Subscribe to server-pushed events
if (session.eventSourceUrl) {
this._subscribeToEventSource(session.eventSourceUrl);
}
this.state.session = {
...session,
accounts: Object.fromEntries(
Object.entries(session.accounts).map(([key, account]) => [
key,
new Account(key.toString(), account),
{ ...account, id: key.toString(), mailboxes: null },
]),
),
emails: {},
@ -362,16 +277,4 @@ export default class Client {
if (!this.state.session) return;
this._triggerChange("Session");
}
_subscribeToEventSource(url: string) {
// For typechecker
if (this.jclient === null) return;
const eventSourceUrl = url
.replace("{types}", "*")
.replace("{closeafter}", "no")
.replace("{ping}", "60");
this.jclient.subscribeToEvents(eventSourceUrl, (e) => {
console.log("Got an event!", e);
});
}
}

@ -1 +1 @@
Subproject commit 2ef5f5b7fa0a22a499bd32831ac24622f17e10e6
Subproject commit fdab37996a4709b13962b67d1aa49e85a8305755

View File

@ -1,10 +1,8 @@
import * as client from "./jmap-client-ts/src/types";
export type MailboxIdMap = { [mailboxId: string]: boolean };
export interface IEmailStub {
from: Array<client.IEmailAddress> | null;
id: string;
mailboxIds: MailboxIdMap;
receivedAt: string;
subject: string;
}
@ -20,20 +18,8 @@ export interface IEmail extends client.IEmailProperties {
export interface IAccount extends client.IAccount {
id: string;
mailboxes: Array<IMailbox> | 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 };