webmail: add button to mark a mailbox and its children as read

this sets the seen flag on all messages in the mailbox and its children.
This commit is contained in:
Mechiel Lukkien
2025-01-30 11:50:52 +01:00
parent c8fd9ca664
commit ad26fd265d
9 changed files with 134 additions and 1 deletions

View File

@ -1195,6 +1195,16 @@ func (Webmail) FlagsClear(ctx context.Context, messageIDs []int64, flaglist []st
xops.MessageFlagsClear(ctx, log, acc, messageIDs, flaglist)
}
// MailboxesMarkRead marks all messages in mailboxes as read. Child mailboxes are
// not automatically included, they must explicitly be included in the list of IDs.
func (Webmail) MailboxesMarkRead(ctx context.Context, mailboxIDs []int64) {
reqInfo := ctx.Value(requestInfoCtxKey).(requestInfo)
acc := reqInfo.Account
log := reqInfo.Log
xops.MailboxesMarkRead(ctx, log, acc, mailboxIDs)
}
// MailboxCreate creates a new mailbox.
func (Webmail) MailboxCreate(ctx context.Context, name string) {
reqInfo := ctx.Value(requestInfoCtxKey).(requestInfo)

View File

@ -247,6 +247,20 @@
],
"Returns": []
},
{
"Name": "MailboxesMarkRead",
"Docs": "MailboxesMarkRead marks all messages in mailboxes as read. Child mailboxes are\nnot automatically included, they must explicitly be included in the list of IDs.",
"Params": [
{
"Name": "mailboxIDs",
"Typewords": [
"[]",
"int64"
]
}
],
"Returns": []
},
{
"Name": "MailboxCreate",
"Docs": "MailboxCreate creates a new mailbox.",

View File

@ -875,6 +875,16 @@ export class Client {
return await _sherpaCall(this.baseURL, this.authState, { ...this.options }, paramTypes, returnTypes, fn, params) as void
}
// MailboxesMarkRead marks all messages in mailboxes as read. Child mailboxes are
// not automatically included, they must explicitly be included in the list of IDs.
async MailboxesMarkRead(mailboxIDs: number[] | null): Promise<void> {
const fn: string = "MailboxesMarkRead"
const paramTypes: string[][] = [["[]","int64"]]
const returnTypes: string[][] = []
const params: any[] = [mailboxIDs]
return await _sherpaCall(this.baseURL, this.authState, { ...this.options }, paramTypes, returnTypes, fn, params) as void
}
// MailboxCreate creates a new mailbox.
async MailboxCreate(name: string): Promise<void> {
const fn: string = "MailboxCreate"

View File

@ -227,6 +227,11 @@ func TestAPI(t *testing.T) {
api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: sent.ID, SpecialUse: store.SpecialUse{Sent: true}}) // Sent, for sending mail later.
tneedError(t, func() { api.MailboxSetSpecialUse(ctx, store.Mailbox{ID: 0}) })
// MailboxesMarkRead
api.FlagsClear(ctx, []int64{inboxText.ID, inboxMinimal.ID}, []string{`\seen`})
api.MailboxesMarkRead(ctx, []int64{inbox.ID, archive.ID, sent.ID})
tneedError(t, func() { api.MailboxesMarkRead(ctx, []int64{inbox.ID + 999}) }) // Does not exist.
// MailboxRename
api.MailboxRename(ctx, testbox1.ID, "Testbox2")
api.MailboxRename(ctx, testbox1.ID, "Test/A/B/Box1")

View File

@ -556,6 +556,15 @@ var api;
const params = [messageIDs, flaglist];
return await _sherpaCall(this.baseURL, this.authState, { ...this.options }, paramTypes, returnTypes, fn, params);
}
// MailboxesMarkRead marks all messages in mailboxes as read. Child mailboxes are
// not automatically included, they must explicitly be included in the list of IDs.
async MailboxesMarkRead(mailboxIDs) {
const fn = "MailboxesMarkRead";
const paramTypes = [["[]", "int64"]];
const returnTypes = [];
const params = [mailboxIDs];
return await _sherpaCall(this.baseURL, this.authState, { ...this.options }, paramTypes, returnTypes, fn, params);
}
// MailboxCreate creates a new mailbox.
async MailboxCreate(name) {
const fn = "MailboxCreate";

View File

@ -556,6 +556,15 @@ var api;
const params = [messageIDs, flaglist];
return await _sherpaCall(this.baseURL, this.authState, { ...this.options }, paramTypes, returnTypes, fn, params);
}
// MailboxesMarkRead marks all messages in mailboxes as read. Child mailboxes are
// not automatically included, they must explicitly be included in the list of IDs.
async MailboxesMarkRead(mailboxIDs) {
const fn = "MailboxesMarkRead";
const paramTypes = [["[]", "int64"]];
const returnTypes = [];
const params = [mailboxIDs];
return await _sherpaCall(this.baseURL, this.authState, { ...this.options }, paramTypes, returnTypes, fn, params);
}
// MailboxCreate creates a new mailbox.
async MailboxCreate(name) {
const fn = "MailboxCreate";

View File

@ -556,6 +556,15 @@ var api;
const params = [messageIDs, flaglist];
return await _sherpaCall(this.baseURL, this.authState, { ...this.options }, paramTypes, returnTypes, fn, params);
}
// MailboxesMarkRead marks all messages in mailboxes as read. Child mailboxes are
// not automatically included, they must explicitly be included in the list of IDs.
async MailboxesMarkRead(mailboxIDs) {
const fn = "MailboxesMarkRead";
const paramTypes = [["[]", "int64"]];
const returnTypes = [];
const params = [mailboxIDs];
return await _sherpaCall(this.baseURL, this.authState, { ...this.options }, paramTypes, returnTypes, fn, params);
}
// MailboxCreate creates a new mailbox.
async MailboxCreate(name) {
const fn = "MailboxCreate";
@ -5486,7 +5495,11 @@ const newMailboxView = (xmb, mailboxlistView, otherMailbox) => {
let actionBtn;
const cmdOpenActions = async () => {
const trashmb = mailboxlistView.mailboxes().find(mb => mb.Trash);
const remove = popover(actionBtn, { transparent: true }, dom.div(style({ display: 'flex', flexDirection: 'column', gap: '.5ex' }), dom.div(dom.clickbutton('Move to trash', attr.title('Move mailbox, its messages and its mailboxes to the trash.'), async function click() {
const remove = popover(actionBtn, { transparent: true }, dom.div(style({ display: 'flex', flexDirection: 'column', gap: '.5ex' }), dom.div(dom.clickbutton('Mark as read', attr.title('Mark all messages in the mailbox and its sub mailboxes as read.'), async function click() {
remove();
const mailboxIDs = [mbv.mailbox.ID, ...mailboxlistView.mailboxes().filter(mb => mb.Name.startsWith(mbv.mailbox.Name + '/')).map(mb => mb.ID)];
await withStatus('Marking mailboxes as read', client.MailboxesMarkRead(mailboxIDs));
})), dom.div(dom.clickbutton('Move to trash', attr.title('Move mailbox, its messages and its mailboxes to the trash.'), async function click() {
if (!trashmb) {
window.alert('No mailbox configured for trash yet.');
return;

View File

@ -5149,6 +5149,13 @@ const newMailboxView = (xmb: api.Mailbox, mailboxlistView: MailboxlistView, othe
const remove = popover(actionBtn, {transparent: true},
dom.div(style({display: 'flex', flexDirection: 'column', gap: '.5ex'}),
dom.div(
dom.clickbutton('Mark as read', attr.title('Mark all messages in the mailbox and its sub mailboxes as read.'), async function click() {
remove()
const mailboxIDs = [mbv.mailbox.ID, ...mailboxlistView.mailboxes().filter(mb => mb.Name.startsWith(mbv.mailbox.Name+'/')).map(mb => mb.ID)]
await withStatus('Marking mailboxes as read', client.MailboxesMarkRead(mailboxIDs))
}),
),
dom.div(
dom.clickbutton('Move to trash', attr.title('Move mailbox, its messages and its mailboxes to the trash.'), async function click() {
if (!trashmb) {