feat: manage shared/group account settings from Accounts page
This commit is contained in:
+26
-11
@@ -1,5 +1,5 @@
|
||||
import type { IJMAPClient } from '@/lib/jmap/client-interface';
|
||||
import type { Email, Mailbox, StateChange, AccountStates, Thread, Identity, EmailAddress, ContactCard, AddressBook, VacationResponse, Calendar, CalendarEvent, CalendarEventFilter, CalendarTask, FileNode, ScheduledEmail, SendEmailResult } from '@/lib/jmap/types';
|
||||
import type { Email, Mailbox, StateChange, AccountStates, Thread, Identity, EmailAddress, ContactCard, AddressBook, VacationResponse, Calendar, CalendarEvent, CalendarEventFilter, CalendarTask, FileNode, ScheduledEmail, SendEmailResult, SharedAccount } from '@/lib/jmap/types';
|
||||
import type { SieveScript, SieveCapabilities } from '@/lib/jmap/sieve-types';
|
||||
import { getDemoData, type DemoData } from './demo-data';
|
||||
import { generateDemoId } from './demo-utils';
|
||||
@@ -118,7 +118,7 @@ export class DemoJMAPClient implements IJMAPClient {
|
||||
|
||||
// ── Mailboxes ─────────────────────────────────────────────────
|
||||
|
||||
async getMailboxes(): Promise<Mailbox[]> { return [...this.data.mailboxes]; }
|
||||
async getMailboxes(_accountId?: string): Promise<Mailbox[]> { return [...this.data.mailboxes]; }
|
||||
async getAllMailboxes(): Promise<Mailbox[]> { return [...this.data.mailboxes]; }
|
||||
|
||||
async createMailbox(name: string, parentId?: string): Promise<Mailbox> {
|
||||
@@ -603,9 +603,9 @@ export class DemoJMAPClient implements IJMAPClient {
|
||||
|
||||
// ── Vacation ──────────────────────────────────────────────────
|
||||
|
||||
async getVacationResponse(): Promise<VacationResponse> { return { ...this.data.vacationResponse }; }
|
||||
async getVacationResponse(_accountId?: string): Promise<VacationResponse> { return { ...this.data.vacationResponse }; }
|
||||
|
||||
async setVacationResponse(updates: Partial<VacationResponse>): Promise<void> {
|
||||
async setVacationResponse(updates: Partial<VacationResponse>, _accountId?: string): Promise<void> {
|
||||
Object.assign(this.data.vacationResponse, updates);
|
||||
}
|
||||
|
||||
@@ -849,19 +849,34 @@ export class DemoJMAPClient implements IJMAPClient {
|
||||
|
||||
// ── Sieve / Filters ──────────────────────────────────────────
|
||||
|
||||
getSharedAccounts(): SharedAccount[] {
|
||||
return [{
|
||||
id: 'demo-account',
|
||||
name: 'Demo',
|
||||
isPrimary: true,
|
||||
capabilities: { mail: true, sieve: true, calendars: true, contacts: true, filenode: true },
|
||||
}];
|
||||
}
|
||||
|
||||
getSieveAccountId(): string { return 'demo-account'; }
|
||||
|
||||
getSieveCapabilities(): SieveCapabilities | null {
|
||||
getSieveAccounts(): { id: string; name: string; isPrimary: boolean }[] {
|
||||
return this.getSharedAccounts()
|
||||
.filter((a) => a.isPrimary || a.capabilities.sieve)
|
||||
.map(({ id, name, isPrimary }) => ({ id, name, isPrimary }));
|
||||
}
|
||||
|
||||
getSieveCapabilities(_accountId?: string): SieveCapabilities | null {
|
||||
return { ...this.data.sieveCapabilities };
|
||||
}
|
||||
|
||||
async getSieveScripts(): Promise<SieveScript[]> { return [...this.data.sieveScripts]; }
|
||||
async getSieveScripts(_accountId?: string): Promise<SieveScript[]> { return [...this.data.sieveScripts]; }
|
||||
|
||||
async getSieveScriptContent(blobId: string): Promise<string> {
|
||||
async getSieveScriptContent(blobId: string, _accountId?: string): Promise<string> {
|
||||
return this.data.sieveContent[blobId] ?? '';
|
||||
}
|
||||
|
||||
async createSieveScript(name: string, content: string, activate?: boolean): Promise<SieveScript> {
|
||||
async createSieveScript(name: string, content: string, activate?: boolean, _accountId?: string): Promise<SieveScript> {
|
||||
const blobId = generateDemoId('sieve-blob');
|
||||
const script: SieveScript = { id: generateDemoId('sieve'), name, blobId, isActive: activate ?? false };
|
||||
this.data.sieveScripts.push(script);
|
||||
@@ -874,7 +889,7 @@ export class DemoJMAPClient implements IJMAPClient {
|
||||
return script;
|
||||
}
|
||||
|
||||
async updateSieveScript(scriptId: string, content: string, activate?: boolean): Promise<void> {
|
||||
async updateSieveScript(scriptId: string, content: string, activate?: boolean, _accountId?: string): Promise<void> {
|
||||
const script = this.data.sieveScripts.find(s => s.id === scriptId);
|
||||
if (!script) return;
|
||||
const blobId = generateDemoId('sieve-blob');
|
||||
@@ -890,11 +905,11 @@ export class DemoJMAPClient implements IJMAPClient {
|
||||
}
|
||||
}
|
||||
|
||||
async deleteSieveScript(scriptId: string): Promise<void> {
|
||||
async deleteSieveScript(scriptId: string, _accountId?: string): Promise<void> {
|
||||
this.data.sieveScripts = this.data.sieveScripts.filter(s => s.id !== scriptId);
|
||||
}
|
||||
|
||||
async validateSieveScript(): Promise<{ isValid: boolean; errors?: string[] }> {
|
||||
async validateSieveScript(_content?: string, _accountId?: string): Promise<{ isValid: boolean; errors?: string[] }> {
|
||||
return { isValid: true };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Email, Mailbox, StateChange, AccountStates, Thread, Identity, EmailAddress, ContactCard, AddressBook, AddressBookRights, VacationResponse, Calendar, CalendarRights, CalendarEvent, CalendarEventFilter, CalendarTask, FileNode, FileNodeRights, Principal, PushSubscription, ScheduledEmail, SendEmailResult } from "./types";
|
||||
import type { Email, Mailbox, StateChange, AccountStates, Thread, Identity, EmailAddress, ContactCard, AddressBook, AddressBookRights, VacationResponse, Calendar, CalendarRights, CalendarEvent, CalendarEventFilter, CalendarTask, FileNode, FileNodeRights, Principal, PushSubscription, ScheduledEmail, SendEmailResult, SharedAccount } from "./types";
|
||||
import type { SieveScript, SieveCapabilities } from "./sieve-types";
|
||||
|
||||
/**
|
||||
@@ -71,7 +71,7 @@ export interface IJMAPClient {
|
||||
getQuota(): Promise<{ used: number; total: number } | null>;
|
||||
|
||||
// ── Mailboxes ─────────────────────────────────────────────────
|
||||
getMailboxes(): Promise<Mailbox[]>;
|
||||
getMailboxes(accountId?: string): Promise<Mailbox[]>;
|
||||
getAllMailboxes(): Promise<Mailbox[]>;
|
||||
createMailbox(name: string, parentId?: string): Promise<Mailbox>;
|
||||
updateMailbox(mailboxId: string, changes: { name?: string; parentId?: string | null; role?: string | null; sortOrder?: number }): Promise<void>;
|
||||
@@ -239,8 +239,8 @@ export interface IJMAPClient {
|
||||
deleteIdentity(identityId: string): Promise<void>;
|
||||
|
||||
// ── Vacation ──────────────────────────────────────────────────
|
||||
getVacationResponse(): Promise<VacationResponse>;
|
||||
setVacationResponse(updates: Partial<VacationResponse>): Promise<void>;
|
||||
getVacationResponse(accountId?: string): Promise<VacationResponse>;
|
||||
setVacationResponse(updates: Partial<VacationResponse>, accountId?: string): Promise<void>;
|
||||
|
||||
// ── Contacts ──────────────────────────────────────────────────
|
||||
getContactsAccountId(): string;
|
||||
@@ -294,15 +294,19 @@ export interface IJMAPClient {
|
||||
setAddressBookShare(addressBookId: string, principalId: string, rights: AddressBookRights | null, targetAccountId?: string): Promise<void>;
|
||||
setFileNodeShare(fileNodeId: string, principalId: string, rights: FileNodeRights | null, targetAccountId?: string): Promise<void>;
|
||||
|
||||
// ── Accounts (primary + shared/group) ────────────────────────
|
||||
getSharedAccounts(): SharedAccount[];
|
||||
|
||||
// ── Sieve / Filters ──────────────────────────────────────────
|
||||
getSieveAccountId(): string;
|
||||
getSieveCapabilities(): SieveCapabilities | null;
|
||||
getSieveScripts(): Promise<SieveScript[]>;
|
||||
getSieveScriptContent(blobId: string): Promise<string>;
|
||||
createSieveScript(name: string, content: string, activate?: boolean): Promise<SieveScript>;
|
||||
updateSieveScript(scriptId: string, content: string, activate?: boolean): Promise<void>;
|
||||
deleteSieveScript(scriptId: string): Promise<void>;
|
||||
validateSieveScript(content: string): Promise<{ isValid: boolean; errors?: string[] }>;
|
||||
getSieveAccounts(): { id: string; name: string; isPrimary: boolean }[];
|
||||
getSieveCapabilities(accountId?: string): SieveCapabilities | null;
|
||||
getSieveScripts(accountId?: string): Promise<SieveScript[]>;
|
||||
getSieveScriptContent(blobId: string, accountId?: string): Promise<string>;
|
||||
createSieveScript(name: string, content: string, activate?: boolean, accountId?: string): Promise<SieveScript>;
|
||||
updateSieveScript(scriptId: string, content: string, activate?: boolean, accountId?: string): Promise<void>;
|
||||
deleteSieveScript(scriptId: string, accountId?: string): Promise<void>;
|
||||
validateSieveScript(content: string, accountId?: string): Promise<{ isValid: boolean; errors?: string[] }>;
|
||||
|
||||
// ── Files (WebDAV / FileNode) ─────────────────────────────────
|
||||
getFilesAccountId(): string;
|
||||
|
||||
+106
-45
@@ -1,4 +1,4 @@
|
||||
import type { Email, Mailbox, StateChange, AccountStates, Thread, Identity, EmailAddress, ContactCard, AddressBook, AddressBookRights, VacationResponse, Calendar, CalendarRights, CalendarEvent, CalendarEventFilter, CalendarTask, FileNode, FileNodeFilter, FileNodeRights, Principal, PushSubscription, EmailSubmission, ScheduledEmail, SendEmailResult } from "./types";
|
||||
import type { Email, Mailbox, StateChange, AccountStates, Thread, Identity, EmailAddress, ContactCard, AddressBook, AddressBookRights, VacationResponse, Calendar, CalendarRights, CalendarEvent, CalendarEventFilter, CalendarTask, FileNode, FileNodeFilter, FileNodeRights, Principal, PushSubscription, EmailSubmission, ScheduledEmail, SendEmailResult, SharedAccount } from "./types";
|
||||
import type { SieveScript, SieveCapabilities } from "./sieve-types";
|
||||
import type { IJMAPClient } from "./client-interface";
|
||||
import { toWildcardQuery } from "./search-utils";
|
||||
@@ -897,16 +897,17 @@ export class JMAPClient implements IJMAPClient {
|
||||
}
|
||||
}
|
||||
|
||||
async getMailboxes(): Promise<Mailbox[]> {
|
||||
async getMailboxes(accountId?: string): Promise<Mailbox[]> {
|
||||
const acctId = accountId || this.accountId;
|
||||
try {
|
||||
const response = await this.request([
|
||||
["Mailbox/get", { accountId: this.accountId }, "0"]
|
||||
["Mailbox/get", { accountId: acctId }, "0"]
|
||||
]);
|
||||
|
||||
if (response.methodResponses?.[0]?.[0] === "Mailbox/get") {
|
||||
const rawMailboxes = (response.methodResponses[0][1].list || []) as JMAPMailbox[];
|
||||
|
||||
debug.log('jmap', `[JMAP Mailbox] getMailboxes returned ${rawMailboxes.length} mailboxes for account ${this.accountId}`);
|
||||
debug.log('jmap', `[JMAP Mailbox] getMailboxes returned ${rawMailboxes.length} mailboxes for account ${acctId}`);
|
||||
|
||||
// Warn if response might be truncated
|
||||
const maxObjects = this.getMaxObjectsInGet();
|
||||
@@ -940,9 +941,9 @@ export class JMAPClient implements IJMAPClient {
|
||||
unreadThreads: mb.unreadThreads ?? 0,
|
||||
myRights: mb.myRights || DEFAULT_MAILBOX_RIGHTS,
|
||||
isSubscribed: mb.isSubscribed ?? true,
|
||||
accountId: this.accountId,
|
||||
accountName: this.accounts[this.accountId]?.name || this.username,
|
||||
isShared: false,
|
||||
accountId: acctId,
|
||||
accountName: this.accounts[acctId]?.name || this.username,
|
||||
isShared: acctId !== this.accountId,
|
||||
}) as Mailbox);
|
||||
}
|
||||
|
||||
@@ -2082,10 +2083,10 @@ export class JMAPClient implements IJMAPClient {
|
||||
return ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:vacationresponse"];
|
||||
}
|
||||
|
||||
async getVacationResponse(): Promise<VacationResponse> {
|
||||
async getVacationResponse(accountId?: string): Promise<VacationResponse> {
|
||||
const response = await this.request([
|
||||
["VacationResponse/get", {
|
||||
accountId: this.accountId,
|
||||
accountId: accountId || this.accountId,
|
||||
ids: ["singleton"],
|
||||
}, "0"]
|
||||
], this.vacationUsing());
|
||||
@@ -2109,10 +2110,10 @@ export class JMAPClient implements IJMAPClient {
|
||||
throw new Error("Failed to fetch vacation response: unexpected server response");
|
||||
}
|
||||
|
||||
async setVacationResponse(updates: Partial<VacationResponse>): Promise<void> {
|
||||
async setVacationResponse(updates: Partial<VacationResponse>, accountId?: string): Promise<void> {
|
||||
const response = await this.request([
|
||||
["VacationResponse/set", {
|
||||
accountId: this.accountId,
|
||||
accountId: accountId || this.accountId,
|
||||
update: {
|
||||
"singleton": updates,
|
||||
},
|
||||
@@ -3421,18 +3422,79 @@ export class JMAPClient implements IJMAPClient {
|
||||
return ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:sieve"];
|
||||
}
|
||||
|
||||
getSieveCapabilities(): SieveCapabilities | null {
|
||||
const sieveAccountId = this.getSieveAccountId();
|
||||
/**
|
||||
* Accounts (primary + shared/group) visible in this session, each tagged with
|
||||
* the capabilities it advertises. Unifies the per-feature enumeration that
|
||||
* getCalendarCapableAccountIds()/getContactCapableAccountIds()/
|
||||
* getFilesCapableAccountIds() do: a non-primary account is included when it
|
||||
* advertises a capability OR is a non-personal (shared/group) account, since
|
||||
* Stalwart doesn't always advertise capabilities on those even when they
|
||||
* support the feature. Primary account first; name from the session account
|
||||
* (primary falls back to the username). Drives the settings "Shared with me"
|
||||
* list and the scoped-settings tab gating.
|
||||
*/
|
||||
getSharedAccounts(): SharedAccount[] {
|
||||
const primaryId = this.getSieveAccountId();
|
||||
const toEntry = (id: string, isPrimary: boolean): SharedAccount => {
|
||||
const account = this.accounts[id];
|
||||
const caps = account?.accountCapabilities;
|
||||
const shared = !account?.isPersonal;
|
||||
return {
|
||||
id,
|
||||
name: account?.name || (isPrimary ? (this.username || id) : id),
|
||||
isPrimary,
|
||||
capabilities: {
|
||||
mail: !!caps?.["urn:ietf:params:jmap:mail"] || shared,
|
||||
sieve: !!caps?.["urn:ietf:params:jmap:sieve"] || shared,
|
||||
calendars: !!caps?.["urn:ietf:params:jmap:calendars"] || shared,
|
||||
contacts: !!caps?.["urn:ietf:params:jmap:contacts"] || shared,
|
||||
filenode: !!caps?.["urn:ietf:params:jmap:filenode"] || shared,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const result = [toEntry(primaryId, true)];
|
||||
for (const id of Object.keys(this.accounts)) {
|
||||
if (id === primaryId) continue;
|
||||
const account = this.accounts[id];
|
||||
// Mirror the per-feature helpers: include any non-personal account, plus
|
||||
// accounts that advertise at least one editable capability.
|
||||
const caps = account.accountCapabilities;
|
||||
const advertisesAny =
|
||||
!!caps?.["urn:ietf:params:jmap:mail"] ||
|
||||
!!caps?.["urn:ietf:params:jmap:sieve"] ||
|
||||
!!caps?.["urn:ietf:params:jmap:calendars"] ||
|
||||
!!caps?.["urn:ietf:params:jmap:contacts"] ||
|
||||
!!caps?.["urn:ietf:params:jmap:filenode"];
|
||||
if (!account.isPersonal || advertisesAny) {
|
||||
result.push(toEntry(id, false));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sieve-capable accounts (primary + shared/group), for the filters UI.
|
||||
* Thin wrapper over getSharedAccounts() filtered to the sieve capability.
|
||||
*/
|
||||
getSieveAccounts(): { id: string; name: string; isPrimary: boolean }[] {
|
||||
return this.getSharedAccounts()
|
||||
.filter((a) => a.isPrimary || a.capabilities.sieve)
|
||||
.map(({ id, name, isPrimary }) => ({ id, name, isPrimary }));
|
||||
}
|
||||
|
||||
getSieveCapabilities(accountId?: string): SieveCapabilities | null {
|
||||
const sieveAccountId = accountId || this.getSieveAccountId();
|
||||
const accountInfo = this.accounts[sieveAccountId];
|
||||
if (!accountInfo?.accountCapabilities) return null;
|
||||
const caps = accountInfo.accountCapabilities["urn:ietf:params:jmap:sieve"];
|
||||
return (caps as SieveCapabilities) || null;
|
||||
}
|
||||
|
||||
async getSieveScripts(): Promise<SieveScript[]> {
|
||||
async getSieveScripts(accountId?: string): Promise<SieveScript[]> {
|
||||
const response = await this.request([
|
||||
["SieveScript/get", {
|
||||
accountId: this.getSieveAccountId(),
|
||||
accountId: accountId || this.getSieveAccountId(),
|
||||
}, "0"]
|
||||
], this.sieveUsing());
|
||||
|
||||
@@ -3442,21 +3504,26 @@ export class JMAPClient implements IJMAPClient {
|
||||
throw new Error('Failed to fetch Sieve scripts');
|
||||
}
|
||||
|
||||
async getSieveScriptContent(blobId: string): Promise<string> {
|
||||
const url = this.getBlobDownloadUrl(blobId, 'script.sieve', 'application/sieve');
|
||||
async getSieveScriptContent(blobId: string, accountId?: string): Promise<string> {
|
||||
// Blobs are scoped per account, so a shared/group account's script must be
|
||||
// downloaded against that owner's accountId, not the primary one.
|
||||
const url = this.getBlobDownloadUrl(
|
||||
blobId, 'script.sieve', 'application/sieve', accountId || this.getSieveAccountId(),
|
||||
);
|
||||
const response = await this.authenticatedFetch(url, {});
|
||||
if (!response.ok) throw new Error(`Failed to download script: ${response.status}`);
|
||||
return response.text();
|
||||
}
|
||||
|
||||
private async uploadSieveBlob(content: string): Promise<string> {
|
||||
private async uploadSieveBlob(content: string, accountId?: string): Promise<string> {
|
||||
if (!this.session?.uploadUrl) {
|
||||
throw new Error('Upload URL not available');
|
||||
}
|
||||
|
||||
const targetAccountId = accountId || this.getSieveAccountId();
|
||||
const uploadUrl = this.session.uploadUrl.replace(
|
||||
'{accountId}',
|
||||
encodeURIComponent(this.getSieveAccountId())
|
||||
encodeURIComponent(targetAccountId)
|
||||
);
|
||||
|
||||
const response = await this.authenticatedFetch(uploadUrl, {
|
||||
@@ -3474,17 +3541,17 @@ export class JMAPClient implements IJMAPClient {
|
||||
|
||||
const result = await response.json();
|
||||
if (result.blobId) return result.blobId;
|
||||
const blobInfo = result[this.getSieveAccountId()];
|
||||
const blobInfo = result[targetAccountId];
|
||||
if (blobInfo?.blobId) return blobInfo.blobId;
|
||||
throw new Error('Invalid upload response: blobId not found');
|
||||
}
|
||||
|
||||
async createSieveScript(name: string, content: string, activate?: boolean): Promise<SieveScript> {
|
||||
const blobId = await this.uploadSieveBlob(content);
|
||||
const accountId = this.getSieveAccountId();
|
||||
async createSieveScript(name: string, content: string, activate?: boolean, accountId?: string): Promise<SieveScript> {
|
||||
const targetAccountId = accountId || this.getSieveAccountId();
|
||||
const blobId = await this.uploadSieveBlob(content, targetAccountId);
|
||||
|
||||
const setArgs: Record<string, unknown> = {
|
||||
accountId,
|
||||
accountId: targetAccountId,
|
||||
create: {
|
||||
"new-script": { name, blobId }
|
||||
},
|
||||
@@ -3505,7 +3572,7 @@ export class JMAPClient implements IJMAPClient {
|
||||
}
|
||||
const createdId = result.created?.["new-script"]?.id;
|
||||
if (createdId) {
|
||||
const scripts = await this.getSieveScripts();
|
||||
const scripts = await this.getSieveScripts(targetAccountId);
|
||||
const script = scripts.find(s => s.id === createdId);
|
||||
if (script) return script;
|
||||
}
|
||||
@@ -3513,12 +3580,12 @@ export class JMAPClient implements IJMAPClient {
|
||||
throw new Error("Failed to create sieve script");
|
||||
}
|
||||
|
||||
async updateSieveScript(scriptId: string, content: string, activate?: boolean): Promise<void> {
|
||||
const blobId = await this.uploadSieveBlob(content);
|
||||
const accountId = this.getSieveAccountId();
|
||||
async updateSieveScript(scriptId: string, content: string, activate?: boolean, accountId?: string): Promise<void> {
|
||||
const targetAccountId = accountId || this.getSieveAccountId();
|
||||
const blobId = await this.uploadSieveBlob(content, targetAccountId);
|
||||
|
||||
const setArgs: Record<string, unknown> = {
|
||||
accountId,
|
||||
accountId: targetAccountId,
|
||||
update: {
|
||||
[scriptId]: { blobId }
|
||||
},
|
||||
@@ -3542,12 +3609,10 @@ export class JMAPClient implements IJMAPClient {
|
||||
throw new Error("Failed to update sieve script");
|
||||
}
|
||||
|
||||
async deleteSieveScript(scriptId: string): Promise<void> {
|
||||
const accountId = this.getSieveAccountId();
|
||||
|
||||
async deleteSieveScript(scriptId: string, accountId?: string): Promise<void> {
|
||||
const response = await this.request([
|
||||
["SieveScript/set", {
|
||||
accountId,
|
||||
accountId: accountId || this.getSieveAccountId(),
|
||||
destroy: [scriptId]
|
||||
}, "0"]
|
||||
], this.sieveUsing());
|
||||
@@ -3563,12 +3628,10 @@ export class JMAPClient implements IJMAPClient {
|
||||
throw new Error("Failed to delete sieve script");
|
||||
}
|
||||
|
||||
async activateSieveScript(scriptId: string): Promise<void> {
|
||||
const accountId = this.getSieveAccountId();
|
||||
|
||||
async activateSieveScript(scriptId: string, accountId?: string): Promise<void> {
|
||||
const response = await this.request([
|
||||
["SieveScript/set", {
|
||||
accountId,
|
||||
accountId: accountId || this.getSieveAccountId(),
|
||||
onSuccessActivateScript: scriptId,
|
||||
}, "0"]
|
||||
], this.sieveUsing());
|
||||
@@ -3582,12 +3645,10 @@ export class JMAPClient implements IJMAPClient {
|
||||
}
|
||||
}
|
||||
|
||||
async deactivateSieveScript(): Promise<void> {
|
||||
const accountId = this.getSieveAccountId();
|
||||
|
||||
async deactivateSieveScript(accountId?: string): Promise<void> {
|
||||
const response = await this.request([
|
||||
["SieveScript/set", {
|
||||
accountId,
|
||||
accountId: accountId || this.getSieveAccountId(),
|
||||
onSuccessActivateScript: null,
|
||||
}, "0"]
|
||||
], this.sieveUsing());
|
||||
@@ -3601,13 +3662,13 @@ export class JMAPClient implements IJMAPClient {
|
||||
}
|
||||
}
|
||||
|
||||
async validateSieveScript(content: string): Promise<{ isValid: boolean; errors?: string[] }> {
|
||||
const blobId = await this.uploadSieveBlob(content);
|
||||
const accountId = this.getSieveAccountId();
|
||||
async validateSieveScript(content: string, accountId?: string): Promise<{ isValid: boolean; errors?: string[] }> {
|
||||
const targetAccountId = accountId || this.getSieveAccountId();
|
||||
const blobId = await this.uploadSieveBlob(content, targetAccountId);
|
||||
|
||||
const response = await this.request([
|
||||
["SieveScript/validate", {
|
||||
accountId,
|
||||
accountId: targetAccountId,
|
||||
blobId,
|
||||
}, "0"]
|
||||
], this.sieveUsing());
|
||||
|
||||
@@ -3,6 +3,25 @@ export interface EmailHeader {
|
||||
value: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A session-visible account (the user's own primary account plus any
|
||||
* shared/group accounts delegated to them), tagged with the capabilities it
|
||||
* advertises. Produced by client.getSharedAccounts(); drives the settings
|
||||
* "Shared with me" list and the scoped-settings tab gating.
|
||||
*/
|
||||
export interface SharedAccount {
|
||||
id: string;
|
||||
name: string;
|
||||
isPrimary: boolean;
|
||||
capabilities: {
|
||||
mail: boolean;
|
||||
sieve: boolean;
|
||||
calendars: boolean;
|
||||
contacts: boolean;
|
||||
filenode: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Email {
|
||||
id: string;
|
||||
threadId: string;
|
||||
|
||||
Reference in New Issue
Block a user