Compare commits
No commits in common. "1e9dae15f13dc81288c438acd436b87c08610db1" and "d8ee3d5f0f37c6e182d5f853b1b9a4c0c4759d27" have entirely different histories.
1e9dae15f1
...
d8ee3d5f0f
|
@ -21,7 +21,6 @@
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-bootstrap": "^2.10.4",
|
"react-bootstrap": "^2.10.4",
|
||||||
"react-bootstrap-icons": "^1.11.4",
|
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"sass": "^1.77.8",
|
"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": {
|
"node_modules/react-dev-utils": {
|
||||||
"version": "12.0.1",
|
"version": "12.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-bootstrap": "^2.10.4",
|
"react-bootstrap": "^2.10.4",
|
||||||
"react-bootstrap-icons": "^1.11.4",
|
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"sass": "^1.77.8",
|
"sass": "^1.77.8",
|
||||||
|
|
|
@ -88,10 +88,6 @@ class EmailContent extends React.Component<
|
||||||
Received
|
Received
|
||||||
</Form.Label>
|
</Form.Label>
|
||||||
<Col sm={10}>{email.receivedAt}</Col>
|
<Col sm={10}>{email.receivedAt}</Col>
|
||||||
<Form.Label column sm={2}>
|
|
||||||
Sent
|
|
||||||
</Form.Label>
|
|
||||||
<Col sm={10}>{email.sentAt}</Col>
|
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
</Form>
|
</Form>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
|
@ -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 Placeholder from "react-bootstrap/Placeholder";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
@ -59,21 +55,6 @@ class EmailSummary extends React.Component<
|
||||||
stub.subject}
|
stub.subject}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,19 +3,10 @@
|
||||||
* None of the dependencies should leak, in types or otherwise
|
* None of the dependencies should leak, in types or otherwise
|
||||||
*/
|
*/
|
||||||
import * as base64 from "base-64";
|
import * as base64 from "base-64";
|
||||||
import * as jmaptypes from "./jmap-client-ts/src/types";
|
|
||||||
import * as jmapclient from "./jmap-client-ts/src";
|
import * as jmapclient from "./jmap-client-ts/src";
|
||||||
import { FetchTransport } from "./jmap-client-ts/src/utils/fetch-transport";
|
import { FetchTransport } from "./jmap-client-ts/src/utils/fetch-transport";
|
||||||
|
|
||||||
import {
|
import { IAccount, IEmail, IEmailStub, IMailbox, ISession } from "./types";
|
||||||
IAccount,
|
|
||||||
IEmail,
|
|
||||||
IEmailStub,
|
|
||||||
IMailbox,
|
|
||||||
ISession,
|
|
||||||
MailboxRole,
|
|
||||||
PushMessage,
|
|
||||||
} from "./types";
|
|
||||||
|
|
||||||
type Callback = () => void;
|
type Callback = () => void;
|
||||||
|
|
||||||
|
@ -30,40 +21,9 @@ export interface ClientState {
|
||||||
emailStubs: Set<string>;
|
emailStubs: Set<string>;
|
||||||
mailboxes: Set<string>;
|
mailboxes: Set<string>;
|
||||||
};
|
};
|
||||||
mailboxes: {
|
|
||||||
trash: IMailbox;
|
|
||||||
} | null;
|
|
||||||
session: ISession | 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 {
|
export default class Client {
|
||||||
// All objects which currently are listening for changes
|
// All objects which currently are listening for changes
|
||||||
callbacks: Array<Callback> = [];
|
callbacks: Array<Callback> = [];
|
||||||
|
@ -74,7 +34,6 @@ export default class Client {
|
||||||
emailStubs: new Set(),
|
emailStubs: new Set(),
|
||||||
mailboxes: new Set(),
|
mailboxes: new Set(),
|
||||||
},
|
},
|
||||||
mailboxes: null,
|
|
||||||
session: null,
|
session: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -108,43 +67,6 @@ export default class Client {
|
||||||
return;
|
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
|
// Ensure we have the full email content
|
||||||
ensureEmailContent(accountId: string, emailId: string) {
|
ensureEmailContent(accountId: string, emailId: string) {
|
||||||
if (this.state.session == null) return;
|
if (this.state.session == null) return;
|
||||||
|
@ -176,9 +98,7 @@ export default class Client {
|
||||||
|
|
||||||
email(emailId: string): IEmail | null {
|
email(emailId: string): IEmail | null {
|
||||||
if (this.state.session == null) return null;
|
if (this.state.session == null) return null;
|
||||||
const result = this.state.session.emails[emailId];
|
return this.state.session.emails[emailId];
|
||||||
if (result === undefined) return null;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emailGetContent(accountId: string, emailId: string) {
|
emailGetContent(accountId: string, emailId: string) {
|
||||||
|
@ -228,7 +148,7 @@ export default class Client {
|
||||||
.email_get({
|
.email_get({
|
||||||
accountId: accountId,
|
accountId: accountId,
|
||||||
ids: [emailId],
|
ids: [emailId],
|
||||||
properties: ["from", "mailboxIds", "receivedAt", "subject"],
|
properties: ["from", "receivedAt", "subject"],
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
console.log(msg, "response", response);
|
console.log(msg, "response", response);
|
||||||
|
@ -243,7 +163,6 @@ export default class Client {
|
||||||
this.state.session.emailStubs[e.id] = {
|
this.state.session.emailStubs[e.id] = {
|
||||||
from: e.from,
|
from: e.from,
|
||||||
id: e.id,
|
id: e.id,
|
||||||
mailboxIds: e.mailboxIds,
|
|
||||||
receivedAt: e.receivedAt,
|
receivedAt: e.receivedAt,
|
||||||
subject: e.subject,
|
subject: e.subject,
|
||||||
};
|
};
|
||||||
|
@ -344,16 +263,12 @@ export default class Client {
|
||||||
if (!this.jclient) return;
|
if (!this.jclient) return;
|
||||||
|
|
||||||
const session = this.jclient.getSession();
|
const session = this.jclient.getSession();
|
||||||
// Subscribe to server-pushed events
|
|
||||||
if (session.eventSourceUrl) {
|
|
||||||
this._subscribeToEventSource(session.eventSourceUrl);
|
|
||||||
}
|
|
||||||
this.state.session = {
|
this.state.session = {
|
||||||
...session,
|
...session,
|
||||||
accounts: Object.fromEntries(
|
accounts: Object.fromEntries(
|
||||||
Object.entries(session.accounts).map(([key, account]) => [
|
Object.entries(session.accounts).map(([key, account]) => [
|
||||||
key,
|
key,
|
||||||
new Account(key.toString(), account),
|
{ ...account, id: key.toString(), mailboxes: null },
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
emails: {},
|
emails: {},
|
||||||
|
@ -362,16 +277,4 @@ export default class Client {
|
||||||
if (!this.state.session) return;
|
if (!this.state.session) return;
|
||||||
this._triggerChange("Session");
|
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
|
|
@ -1,10 +1,8 @@
|
||||||
import * as client from "./jmap-client-ts/src/types";
|
import * as client from "./jmap-client-ts/src/types";
|
||||||
|
|
||||||
export type MailboxIdMap = { [mailboxId: string]: boolean };
|
|
||||||
export interface IEmailStub {
|
export interface IEmailStub {
|
||||||
from: Array<client.IEmailAddress> | null;
|
from: Array<client.IEmailAddress> | null;
|
||||||
id: string;
|
id: string;
|
||||||
mailboxIds: MailboxIdMap;
|
|
||||||
receivedAt: string;
|
receivedAt: string;
|
||||||
subject: string;
|
subject: string;
|
||||||
}
|
}
|
||||||
|
@ -20,20 +18,8 @@ export interface IEmail extends client.IEmailProperties {
|
||||||
export interface IAccount extends client.IAccount {
|
export interface IAccount extends client.IAccount {
|
||||||
id: string;
|
id: string;
|
||||||
mailboxes: Array<IMailbox> | null;
|
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 AccountIdMap = { [accountId: string]: IAccount };
|
||||||
export type EmailStubIdMap = { [emailId: string]: IEmailStub };
|
export type EmailStubIdMap = { [emailId: string]: IEmailStub };
|
||||||
export type EmailIdMap = { [emailId: string]: IEmail };
|
export type EmailIdMap = { [emailId: string]: IEmail };
|
||||||
|
|
Loading…
Reference in New Issue