parent
01760496ed
commit
10522dc389
17 changed files with 1333 additions and 352 deletions
@ -0,0 +1,49 @@ |
||||
IMAP |
||||
> Boxes |
||||
> Messages (ordered by uid) |
||||
|
||||
AETHER |
||||
> Conversations |
||||
> (Parsed) Messages (indexed by MessageID) |
||||
|
||||
MONGO |
||||
> Box |
||||
> BoxID |
||||
> UIDValidity |
||||
> UIDNext |
||||
> SeqNext |
||||
> ... |
||||
|
||||
> MsgUUID |
||||
> MessageID |
||||
> Box |
||||
> UID |
||||
|
||||
> Participant |
||||
Name |
||||
Image |
||||
Addresses |
||||
... |
||||
|
||||
> Messages |
||||
> MsgUUID |
||||
> Subject |
||||
> Participant[] |
||||
> Time |
||||
> ParsedContent |
||||
|
||||
> Conversations |
||||
> Title |
||||
> Archived |
||||
> LatestMessageTime |
||||
> Participant[] |
||||
> Messages[] |
||||
|
||||
Box |
||||
SEQNO / UID / SUBJECT |
||||
|
||||
1 / 1: aaa |
||||
2 / 3: bbb |
||||
--- 3 / 7: ccc |
||||
--- |
||||
--- 3 / 8: ddd |
After Width: | Height: | Size: 8.6 KiB |
@ -1,148 +1,372 @@ |
||||
import Message from './Message'; |
||||
import Conversation from './Conversation'; |
||||
import Imap, { ConnectionProperties, Message as RawMessage, MailboxType } from './Imap'; |
||||
import Imap from 'imap'; |
||||
import { ObjectID } from 'mongodb' |
||||
|
||||
interface Contact { |
||||
name: string; |
||||
addresses: Set<string>; |
||||
} |
||||
// import Message from './Message';
|
||||
// import Conversation from './Conversation';
|
||||
import Log from './Log'; |
||||
import * as DB from './data/Data'; |
||||
import ImapController from './imap/ImapController'; |
||||
|
||||
export default class Account { |
||||
private imap: Imap; |
||||
private contacts: Contact[] = []; |
||||
private conversations: Conversation[] = []; |
||||
// interface Contact {
|
||||
// name: string;
|
||||
// addresses: Set<string>;
|
||||
// }
|
||||
|
||||
private name: string; |
||||
private image: string; |
||||
export default class Account { |
||||
private address: string; |
||||
private unread: boolean; |
||||
private accountID: ObjectID; |
||||
private conn: ImapController; |
||||
// private contacts: Contact[] = [];
|
||||
// private conversations: Conversation[] = [];
|
||||
|
||||
constructor(name: string, image: string, connection: ConnectionProperties) { |
||||
this.name = name; |
||||
this.image = image; |
||||
this.address = connection.username; |
||||
this.unread = true; |
||||
constructor(data: DB.Account) { |
||||
this.accountID = data._id; |
||||
this.address = data.address; |
||||
Log.info('Created account %s', data.address); |
||||
|
||||
this.imap = new Imap(connection); |
||||
this.conn = new ImapController({ |
||||
user: data.address, |
||||
password: data.password, |
||||
host: data.host, |
||||
port: data.port, |
||||
tls: data.tls |
||||
}); |
||||
} |
||||
|
||||
async connect() { |
||||
await this.imap.connect(); |
||||
async init() { |
||||
await this.conn.connect(); |
||||
|
||||
Log.perfStart('Synchronizing ' + this.address); |
||||
const remoteBoxes = await this.getBoxes(); |
||||
await this.synchronizeBoxes(remoteBoxes); |
||||
await this.synchronizeMessages(remoteBoxes); |
||||
Log.perfEnd('Synchronizing ' + this.address); |
||||
|
||||
const messages = await this.fetchAllMessages(); |
||||
this.conversations = this.createConversations(messages).filter(c => c.active); |
||||
this.contacts = this.createContacts(messages); |
||||
|
||||
// Log.info('Connected to %s', this.data.address);
|
||||
// await this.synchronizeData();
|
||||
// Log.perfEnd('Synchronizing ' + this.data.address);
|
||||
|
||||
// const messages = await this.fetchAllMessages();
|
||||
// this.conversations = this.createConversations(messages).filter(c => c.active);
|
||||
// this.contacts = this.createContacts(messages);
|
||||
} |
||||
|
||||
getName() { |
||||
return this.name; |
||||
async synchronizeBoxes(remoteBoxes: Map<string, Imap.Box>): Promise<void> { |
||||
const currentBoxes = await DB.MailboxModel.find({ account: this.accountID }); |
||||
await Promise.all([ ...remoteBoxes.values() ].map(async box => { |
||||
const existing = currentBoxes.filter(b => b.path === box.name)[0]; |
||||
if (!existing) await this.addNewBox(box); |
||||
else await this.refreshExistingBox(box, existing); |
||||
})); |
||||
} |
||||
|
||||
getAddress() { |
||||
return this.address; |
||||
private async addNewBox(box: Imap.Box) { |
||||
await DB.MailboxModel.create({ |
||||
account: this.accountID, |
||||
name: box.name, // TODO: This
|
||||
path: box.name, |
||||
delimiter: '.', // TODO: and this
|
||||
type: DB.MailboxType.Inbox, |
||||
treeTypes: new Set([ DB.MailboxType.Inbox ]), |
||||
parent: undefined, // and this
|
||||
uidValidity: box.uidvalidity, |
||||
uidNext: 1, |
||||
} as DB.Create<DB.Mailbox>); |
||||
} |
||||
|
||||
getImage() { |
||||
return this.image; |
||||
private async refreshExistingBox(remote: Imap.Box, _existing: DB.Mailbox) { |
||||
Log.debug('existing box ' + remote.name); |
||||
} |
||||
|
||||
hasUnreads() { |
||||
return this.unread; |
||||
async synchronizeMessages(remoteBoxes: Map<string, Imap.Box>): Promise<void> { |
||||
const currentBoxes = await DB.MailboxModel.find({ account: this.accountID }); |
||||
await Promise.all(currentBoxes.map(async box => { |
||||
const remote = remoteBoxes.get(box.path)!; |
||||
console.log(box.uidNext, remote.uidnext); |
||||
// if (box.uidValidity !== remote.uidvalidity) {
|
||||
// // Reacquire existing messages
|
||||
// }
|
||||
if (box.uidNext !== remote.uidnext) { |
||||
// Get new messages
|
||||
const messages = await (await this.conn.get(box.path)).fetchMessagesByUID(`${box.uidNext}:*`); |
||||
await DB.MailboxModel.updateOne({ _id: box._id }, { uidNext: remote.uidnext }); |
||||
if (messages.size > 0) { |
||||
console.log('adding ' + messages.size + ' messages.'); |
||||
await DB.MessageModel.insertMany([ ...messages.keys() ].map(uid => { |
||||
const message = messages.get(uid)!; |
||||
const headers = this.parseHeaders(message.headers); |
||||
return { |
||||
account: this.accountID, |
||||
box: box._id, |
||||
uid: uid, |
||||
messageId: headers.get('MESSAGE-ID') ?? '[!DATE:' + (+message.attrs.date) + ']', |
||||
subject: this.cleanSubject(headers.get('SUBJECT')), |
||||
date: message.attrs.date, |
||||
} as DB.Message; |
||||
})); |
||||
} |
||||
} |
||||
})); |
||||
} |
||||
|
||||
getConversations() { |
||||
return this.conversations; |
||||
private async getBoxes(): Promise<Map<string, Imap.Box>> { |
||||
Log.perfStart('Getting boxes for ' + this.address); |
||||
|
||||
const remoteBoxes = await (await this.conn.get()).getBoxes(); |
||||
const boxReqs: Promise<void>[] = []; |
||||
const boxes: Map<string, Imap.Box> = new Map(); |
||||
|
||||
const reqBoxesRecursively = (tree: Imap.MailBoxes, path: string = '') => { |
||||
Object.keys(tree).forEach(name => { |
||||
boxReqs.push((async () => { |
||||
const box = (await this.conn.get(path + name)).getOpenBoxProps(); |
||||
boxes.set(path + name, box); |
||||
})()); |
||||
if (tree[name].children) reqBoxesRecursively(tree[name].children, |
||||
path + name + tree[name].delimiter); |
||||
}); |
||||
} |
||||
|
||||
reqBoxesRecursively(remoteBoxes); |
||||
await Promise.all(boxReqs); |
||||
|
||||
Log.perfEnd('Getting boxes for ' + this.address); |
||||
return boxes; |
||||
} |
||||
|
||||
getContacts() { |
||||
return this.contacts; |
||||
private parseHeaders(rawHeaders: string): Map<string, string> { |
||||
const headers: Map<string, string> = new Map(); |
||||
rawHeaders |
||||
.split(/\r?\n(?=[A-z:\-_]+)/g) |
||||
.map(h => h.trim()) |
||||
.filter(h => h) |
||||
.forEach(h => { |
||||
const delimiter = h.indexOf(':'); |
||||
const name = h.substr(0, delimiter).trim(); |
||||
const value = h.substr(delimiter + 1).trim(); |
||||
headers.set(name.toUpperCase(), value); |
||||
}); |
||||
return headers; |
||||
} |
||||
|
||||
getMessages(_messages: string[]): Message[] { |
||||
return [{ |
||||
id: 'AOUEOAEu', |
||||
date: new Date(), |
||||
from: 'me@auri.xyz', |
||||
to: [ 'nicole@aurailus.design' ], |
||||
content: '<p>Lorem ipsum dolor sit amet.</p>' |
||||
}]; |
||||
private cleanSubject(subject: string = '') { |
||||
return subject.replace(/^((re|fwd?|b?cc)(:| ) *)*/gi, '').trim(); |
||||
} |
||||
|
||||
async fetchAllMessages(): Promise<RawMessage[]> { |
||||
const boxes = (await this.imap.listBoxes()).filter(box => |
||||
!box.treeTypes.has(MailboxType.Spam) && !box.treeTypes.has(MailboxType.Trash)); |
||||
// async synchronizeData(): Promise<void> {
|
||||
// const existingBoxes = await DB.MailboxModel.find({ account: this.data._id });
|
||||
// const remoteBoxes = (await this.imap.listBoxes());
|
||||
|
||||
let allMeta: RawMessage[] = []; |
||||
// const newBoxes: (Mailbox & { uidValidity: number })[] = [];
|
||||
// const boxValidityChanged: { _id: ObjectID, uidValidity: number }[] = [];
|
||||
// const removedBoxes: Set<ObjectID> = new Set(existingBoxes.map(box => box._id));
|
||||
|
||||
for (let box of boxes) { |
||||
await this.imap.openBox(box.path); |
||||
const meta = await this.imap.fetchMessages('1:*'); |
||||
Object.keys(meta).forEach(id => allMeta.push(meta[id])); |
||||
} |
||||
// for (let remote of remoteBoxes) {
|
||||
// if (!remote.treeTypes.has(DB.MailboxType.Inbox) &&
|
||||
// !remote.treeTypes.has(DB.MailboxType.Sent) &&
|
||||
// !remote.treeTypes.has(DB.MailboxType.Archives)) continue;
|
||||
|
||||
allMeta = allMeta.sort((a, b) => +a.date - +b.date); |
||||
return allMeta; |
||||
} |
||||
// const existing = existingBoxes.filter(box => box.path === remote.path)[0];
|
||||
// let uidValidity = (await this.imap.openBox(remote.path)).uidvalidity;
|
||||
// // Log.debug('Opened %s', remote.path);
|
||||
// if (!existing) newBoxes.push({ ...remote, uidValidity });
|
||||
// else {
|
||||
// removedBoxes.delete(existing._id);
|
||||
// if (existing.uidValidity != uidValidity) boxValidityChanged.push({ _id: existing._id, uidValidity });
|
||||
// }
|
||||
// };
|
||||
|
||||
private createConversations(messages: RawMessage[]): Conversation[] { |
||||
const conversations: Conversation[] = []; |
||||
|
||||
messages.forEach(message => { |
||||
if (message.replyTo) { |
||||
for (let conversation of conversations) { |
||||
for (let reference of [ ...message.references, message.replyTo ]) { |
||||
if (conversation.messages.has(reference)) { |
||||
conversation.date = message.date; |
||||
conversation.messages.add(message.messageId); |
||||
conversation.title = this.cleanSubjectLine(message.subject); |
||||
conversation.active = conversation.active || message.active; |
||||
message.to.forEach(p => conversation.participants.add(p.address)); |
||||
conversation.participants.add(message.from.address); |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
// let createdIDs: Map<string, ObjectID> = new Map();
|
||||
|
||||
conversations.push({ |
||||
title: this.cleanSubjectLine(message.subject), |
||||
messages: new Set([ message.messageId ]), |
||||
date: message.date, |
||||
active: message.active, |
||||
participants: new Set([ message.from.address, ...message.to.map(p => p.address) ]) |
||||
}); |
||||
}); |
||||
// for (let box of newBoxes) {
|
||||
// createdIDs.set(box.path, (await DB.MailboxModel.create({
|
||||
// name: box.name,
|
||||
// path: box.path,
|
||||
// account: this.data._id,
|
||||
// delimiter: box.delimiter,
|
||||
// type: box.type,
|
||||
// treeTypes: box.treeTypes,
|
||||
// parent: (await DB.MailboxModel.findOne({ path: box.parent }))?._id,
|
||||
// uidValidity: box.uidValidity,
|
||||
// uidNext: 1,
|
||||
// } as DB.Create<DB.Mailbox>)).id);
|
||||
// }
|
||||
|
||||
conversations.forEach(conversation => { |
||||
conversation.participants.delete(this.address); |
||||
}); |
||||
// await Promise.all(boxValidityChanged.map(({ _id, uidValidity }) =>
|
||||
// DB.MailboxModel.updateOne({ _id }, { uidValidity, uidNext: 1 })));
|
||||
|
||||
return conversations.sort((a, b) => +a.date - +b.date); |
||||
} |
||||
// await DB.MailboxModel.deleteMany({ _id: { $in: [ ...removedBoxes ] } });
|
||||
|
||||
private createContacts(messages: RawMessage[]): Contact[] { |
||||
const contacts: Contact[] = []; |
||||
// for (let box of await DB.MailboxModel.find({ account: this.data._id })) {
|
||||
// await this.imap.openBox(box.path);
|
||||
// const meta = Object.values(await this.imap.fetchMessages(box.uidNext + ':*'));
|
||||
|
||||
for (let message of messages) { |
||||
[ message.from, ...message.to ].forEach(participant => { |
||||
for (let contact of contacts) { |
||||
if (contact.addresses.has(participant.address)) { |
||||
if (participant.name) contact.name = participant.name; |
||||
return; |
||||
} |
||||
} |
||||
// const contacts: { name: string; addresses: Set<string> }[] = [];
|
||||
|
||||
contacts.push({ |
||||
name: participant.name ?? participant.address, |
||||
addresses: new Set([ participant.address ]) |
||||
}); |
||||
}); |
||||
} |
||||
// for (let m of meta) {
|
||||
// [ m.from, ...m.to ].forEach(participant => {
|
||||
// for (let contact of contacts) {
|
||||
// if (contact.addresses.has(participant.address)) {
|
||||
// if (participant.name) contact.name = participant.name;
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
return contacts; |
||||
} |
||||
// contacts.push({
|
||||
// name: participant.name ?? participant.address,
|
||||
// addresses: new Set([ participant.address ])
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
// await Promise.all(contacts.map(async contact => {
|
||||
// const addresses = [ ...contact.addresses ];
|
||||
// await DB.ContactModel.updateOne(
|
||||
// { addresses: { $elemMatch: { $in: addresses } as any } },
|
||||
// {
|
||||
// $set: { name: contact.name },
|
||||
// $addToSet: { addresses }
|
||||
// },
|
||||
// { upsert: true });
|
||||
// }));
|
||||
|
||||
// await Promise.all(meta.map(async meta => {
|
||||
// await DB.MessageModel.updateOne({ messageId: meta.messageId }, {
|
||||
// box: box._id,
|
||||
// uid: meta.boxId,
|
||||
// account: this.data._id,
|
||||
// $setOnInsert: {
|
||||
// subject: meta.subject,
|
||||
// date: meta.date,
|
||||
// from: (await DB.ContactModel.findOne({ addresses: meta.from.address }))!._id
|
||||
// }
|
||||
// } as any as DB.Message,
|
||||
// { upsert: true });
|
||||
// }));
|
||||
|
||||
// // await DB.MessageIDModel.insertMany(Object.values(meta).map(meta =>
|
||||
// // ({ messageId: meta.messageId, uid: meta.boxId, account: this.data._id, box: box._id })));
|
||||
// }
|
||||
|
||||
// const boxes = (await this.imap.listBoxes()).filter(box =>
|
||||
// !box.treeTypes.has(MailboxType.Spam) && !box.treeTypes.has(MailboxType.Trash));
|
||||
|
||||
// let allMeta: RawMessage[] = [];
|
||||
|
||||
// for (let box of boxes) {
|
||||
// await this.imap.openBox(box.path);
|
||||
// const meta = await this.imap.fetchMessages('1:*');
|
||||
// Object.keys(meta).forEach(id => allMeta.push(meta[id]));
|
||||
// }
|
||||
// }
|
||||
|
||||
// getName() {
|
||||
// return this.name;
|
||||
// }
|
||||
|
||||
// getAddress() {
|
||||
// return this.address;
|
||||
// }
|
||||
|
||||
// getImage() {
|
||||
// return this.image;
|
||||
// }
|
||||
|
||||
// hasUnreads() {
|
||||
// return this.unread;
|
||||
// }
|
||||
|
||||
// getConversations() {
|
||||
// return this.conversations;
|
||||
// }
|
||||
|
||||
// getContacts() {
|
||||
// return this.contacts;
|
||||
// }
|
||||
|
||||
// getMessages(_messages: string[]): Message[] {
|
||||
// return [{
|
||||
// id: 'AOUEOAEu',
|
||||
// date: new Date(),
|
||||
// from: 'me@auri.xyz',
|
||||
// to: [ 'nicole@aurailus.design' ],
|
||||
// content: '<p>Lorem ipsum dolor sit amet.</p>'
|
||||
// }];
|
||||
// }
|
||||
|
||||
// async fetchAllMessages(): Promise<RawMessage[]> {
|
||||
// const boxes = (await this.imap.listBoxes()).filter(box =>
|
||||
// !box.treeTypes.has(MailboxType.Spam) && !box.treeTypes.has(MailboxType.Trash));
|
||||
|
||||
// let allMeta: RawMessage[] = [];
|
||||
|
||||
// for (let box of boxes) {
|
||||
// await this.imap.openBox(box.path);
|
||||
// const meta = await this.imap.fetchMessages('1:*');
|
||||
// Object.keys(meta).forEach(id => allMeta.push(meta[id]));
|
||||
// }
|
||||
|
||||
// allMeta = allMeta.sort((a, b) => +a.date - +b.date);
|
||||
// return allMeta;
|
||||
// }
|
||||
|
||||
// private createConversations(messages: RawMessage[]): Conversation[] {
|
||||
// const conversations: Conversation[] = [];
|
||||
|
||||
// messages.forEach(message => {
|
||||
// if (message.replyTo) {
|
||||
// for (let conversation of conversations) {
|
||||
// for (let reference of [ ...message.references, message.replyTo ]) {
|
||||
// if (conversation.messages.has(reference)) {
|
||||
// conversation.date = message.date;
|
||||
// conversation.messages.add(message.messageId);
|
||||
// conversation.title = this.cleanSubjectLine(message.subject);
|
||||
// conversation.active = conversation.active || message.active;
|
||||
// message.to.forEach(p => conversation.participants.add(p.address));
|
||||
// conversation.participants.add(message.from.address);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// conversations.push({
|
||||
// title: this.cleanSubjectLine(message.subject),
|
||||
// messages: new Set([ message.messageId ]),
|
||||
// date: message.date,
|
||||
// active: message.active,
|
||||
// participants: new Set([ message.from.address, ...message.to.map(p => p.address) ])
|
||||
// });
|
||||
// });
|
||||
|
||||
// conversations.forEach(conversation => {
|
||||
// conversation.participants.delete(this.address);
|
||||
// });
|
||||
|
||||
// return conversations.sort((a, b) => +a.date - +b.date);
|
||||
// }
|
||||
|
||||
// private createContacts(messages: RawMessage[]): Contact[] {
|
||||
// const contacts: Contact[] = [];
|
||||
|
||||
// for (let message of messages) {
|
||||
// [ message.from, ...message.to ].forEach(participant => {
|
||||
// for (let contact of contacts) {
|
||||
// if (contact.addresses.has(participant.address)) {
|
||||
// if (participant.name) contact.name = participant.name;
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// contacts.push({
|
||||
// name: participant.name ?? participant.address,
|
||||
// addresses: new Set([ participant.address ])
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
// return contacts;
|
||||
// }
|
||||
|
||||
private cleanSubjectLine(subject: string = '') { |
||||
return subject.replace(/^((re|fwd?|b?cc)(:| ) *)*/gi, '').trim(); |
||||
} |
||||
}; |
||||
|
@ -1,61 +1,61 @@ |
||||
import { buildSchema } from 'graphql'; |
||||
|
||||
import { Type, SCHEMA } from 'common/graph'; |
||||
|
||||
import Message from './Message'; |
||||
import Account from './Account'; |
||||
import Conversation from './Conversation'; |
||||
|
||||
export interface Context { |
||||
accounts: Record<Type.ID, Account>; |
||||
} |
||||
|
||||
export const Schema = buildSchema(SCHEMA); |
||||
|
||||
function messageResolver(message: Message) { |
||||
return { |
||||
id: message.id, |
||||
date: message.date, |
||||
from: message.from, |
||||
to: message.to, |
||||
|
||||
html: () => message.content, |
||||
markdown: () => message.content |
||||
}; |
||||
} |
||||
|
||||
function conversationResolver(conversation: Conversation, id: string) { |
||||
return { |
||||
id: id, |
||||
unread: false, |
||||
title: conversation.title, |
||||
lastMessage: conversation.date, |
||||
messages: conversation.messages, |
||||
participants: conversation.participants |
||||
}; |
||||
} |
||||
|
||||
function accountResolver(account: Account, id: string) { |
||||
return { |
||||
id: id, |
||||
name: account.getName(), |
||||
image: account.getImage(), |
||||
address: account.getAddress(), |
||||
unread: account.hasUnreads(), |
||||
|
||||
messages: [], |
||||
contacts: () => account.getContacts(), |
||||
conversations: () => { |
||||
const conversations = account.getConversations(); |
||||
for (let key in conversations) if (!conversations[key].active) delete conversations[key]; |
||||
return Object.keys(conversations).map(id => conversationResolver(conversations[id as any], id)); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
export const Resolver = { |
||||
accounts: (_: any, ctx: Context) => Object.keys(ctx.accounts).map(id => accountResolver(ctx.accounts[id], id)), |
||||
account: ({ account: id }: { account: string }, ctx: Context) => accountResolver(ctx.accounts[id], id), |
||||
messages: async ({ account, ids }: { account: string; ids: string[] }, ctx: Context) => |
||||
(await ctx.accounts[account].getMessages(ids)).map(msg => messageResolver(msg)) |
||||
}; |
||||
// import { buildSchema } from 'graphql';
|
||||
|
||||
// import { Type, SCHEMA } from 'common/graph';
|
||||
|
||||
// import Message from './Message';
|
||||
// import Account from './Account';
|
||||
// import Conversation from './Conversation';
|
||||
|
||||
// export interface Context {
|
||||
// accounts: Record<Type.ID, Account>;
|
||||
// }
|
||||
|
||||
// export const Schema = buildSchema(SCHEMA);
|
||||
|
||||
// function messageResolver(message: Message) {
|
||||
// return {
|
||||
// id: message.id,
|
||||
// date: message.date,
|
||||
// from: message.from,
|
||||
// to: message.to,
|
||||
|
||||
// html: () => message.content,
|
||||
// markdown: () => message.content
|
||||
// };
|
||||
// }
|
||||
|
||||
// function conversationResolver(conversation: Conversation, id: string) {
|
||||
// return {
|
||||
// id: id,
|
||||
// unread: false,
|
||||
// title: conversation.title,
|
||||
// lastMessage: conversation.date,
|
||||
// messages: conversation.messages,
|
||||
// participants: conversation.participants
|
||||
// };
|
||||
// }
|
||||
|
||||
// function accountResolver(account: Account, id: string) {
|
||||
// return {
|
||||
// id: id,
|
||||
// name: account.getName(),
|
||||
// image: account.getImage(),
|
||||
// address: account.getAddress(),
|
||||
// unread: account.hasUnreads(),
|
||||
|
||||
// messages: [],
|
||||
// contacts: () => account.getContacts(),
|
||||
// conversations: () => {
|
||||
// const conversations = account.getConversations();
|
||||
// for (let key in conversations) if (!conversations[key].active) delete conversations[key];
|
||||
// return Object.keys(conversations).map(id => conversationResolver(conversations[id as any], id));
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
// export const Resolver = {
|
||||
// accounts: (_: any, ctx: Context) => Object.keys(ctx.accounts).map(id => accountResolver(ctx.accounts[id], id)),
|
||||
// account: ({ account: id }: { account: string }, ctx: Context) => accountResolver(ctx.accounts[id], id),
|
||||
// messages: async ({ account, ids }: { account: string; ids: string[] }, ctx: Context) =>
|
||||
// (await ctx.accounts[account].getMessages(ids)).map(msg => messageResolver(msg))
|
||||
// };
|
||||
|