Implement support for replies
This commit is contained in:
@@ -15,8 +15,21 @@ export function buildThreadAddressMatcher(): RegExp {
|
||||
// In the config, the thread ID is represented with the placeholder: %ID%
|
||||
|
||||
const idPrefix = escapeRexString(config.mail.threads.idPrefix)
|
||||
const idCapture = `(${idPrefix}[^@]+)` // first rule of email: don't be as strict as the RFC
|
||||
const idCapture = `(${idPrefix}[^@.]+)` // first rule of email: don't be as strict as the RFC (special case: IDs after the prefix cannot contain a period)
|
||||
|
||||
const replyPrefix = escapeRexString(config.mail.threads.replyPrefix)
|
||||
const replyCapture = `(${replyPrefix}[^@.]+)?`
|
||||
|
||||
const template = escapeRexString(config.mail.threads.template)
|
||||
.replace('%ID%', idCapture)
|
||||
.replace('%REPLY%', replyCapture)
|
||||
|
||||
const template = escapeRexString(config.mail.threads.template).replace('%ID%', idCapture)
|
||||
return new RegExp(`^${template}$`)
|
||||
}
|
||||
|
||||
export function formatThreadAddress(thread: string, replyToHash?: string): string {
|
||||
let reply = replyToHash ? `${config.mail.threads.replyPrefix}${replyToHash}` : ''
|
||||
return config.mail.threads.template
|
||||
.replace('%ID%', thread)
|
||||
.replace('%REPLY%', reply)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import {getMailboxesToSearch, MailboxIterable} from "../mail/read.ts";
|
||||
import {withClient} from "../mail/client.ts";
|
||||
import type {Message, RootData, ThreadData} from "../types.ts";
|
||||
import type {Message, RootData, ThreadComment, ThreadData} from "../types.ts";
|
||||
import {AsyncCollection} from "../bones/collection/AsyncCollection.ts";
|
||||
import {sha256} from "../bones/crypto.ts";
|
||||
import {config} from "../config.ts";
|
||||
import { marked } from "marked";
|
||||
import {sanitizeHtml} from "../mail/sanitize.ts";
|
||||
import {formatThreadAddress} from "./id.ts";
|
||||
|
||||
export async function refreshThreadsEntirely(): Promise<void> {
|
||||
await withClient(async client => {
|
||||
@@ -41,6 +42,9 @@ export async function refreshThreadsEntirely(): Promise<void> {
|
||||
comments: [],
|
||||
}
|
||||
|
||||
// 3 passes required to make this work (works because objects are by-reference)
|
||||
// Pass 1: create all the ThreadComment instances and make a map by their ID
|
||||
const commentsByHash: {[hash: string]: ThreadComment} = {}
|
||||
const messages = messagesByThread[threadId]
|
||||
for ( const message of messages ) {
|
||||
if (
|
||||
@@ -50,7 +54,10 @@ export async function refreshThreadsEntirely(): Promise<void> {
|
||||
threadData.refresh.markers[message.mailbox] = message.modseq
|
||||
}
|
||||
|
||||
threadData.comments.push({
|
||||
commentsByHash[message.idHash] = {
|
||||
idHash: message.idHash,
|
||||
replyToHash: message.replyToHash,
|
||||
replyAddress: formatThreadAddress(threadId, message.idHash),
|
||||
user: {
|
||||
name: message.from.name || '(anonymous)',
|
||||
mailId: sha256(message.from.address!.toLowerCase()),
|
||||
@@ -60,7 +67,30 @@ export async function refreshThreadsEntirely(): Promise<void> {
|
||||
subject: message.subject,
|
||||
text: message.content,
|
||||
rendered: message.html || sanitizeHtml(await marked(message.content)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2: Push replies into the replies array of their immediate parent
|
||||
for ( const message of messages ) {
|
||||
if ( !message.replyToHash ) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ( !commentsByHash[message.replyToHash].replies ) {
|
||||
commentsByHash[message.replyToHash].replies = []
|
||||
}
|
||||
|
||||
commentsByHash[message.replyToHash].replies!.push(commentsByHash[message.idHash])
|
||||
}
|
||||
|
||||
// Pass 3: push all top-level comments into the ThreadData
|
||||
for ( const idHash of Object.keys(commentsByHash) ) {
|
||||
const comment = commentsByHash[idHash]
|
||||
if ( comment.replyToHash ) {
|
||||
continue
|
||||
}
|
||||
|
||||
threadData.comments.push(comment)
|
||||
}
|
||||
|
||||
const json = JSON.stringify(
|
||||
|
||||
Reference in New Issue
Block a user