feat: add demo data for emails, files, filters, identities, mailboxes, vacation responses, and JMAP client interface
- Created demo emails with various states (inbox, sent, drafts, trash, etc.) in `emails.ts`. - Added demo file nodes representing directories and files in `files.ts`. - Implemented demo Sieve capabilities and scripts in `filters.ts`. - Defined demo identities for users in `identities.ts`. - Established demo mailboxes with permissions and counts in `mailboxes.ts`. - Created a demo vacation response in `vacation.ts`. - Introduced a comprehensive JMAP client interface in `client-interface.ts` to standardize interactions with the JMAP API.
This commit is contained in:
+102
-2
@@ -1,6 +1,7 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { JMAPClient } from '@/lib/jmap/client';
|
||||
import type { IJMAPClient } from '@/lib/jmap/client-interface';
|
||||
import { useIdentityStore } from './identity-store';
|
||||
import { useContactStore } from './contact-store';
|
||||
import { useVacationStore } from './vacation-store';
|
||||
@@ -21,7 +22,7 @@ interface AuthState {
|
||||
error: string | null;
|
||||
serverUrl: string | null;
|
||||
username: string | null;
|
||||
client: JMAPClient | null;
|
||||
client: IJMAPClient | null;
|
||||
identities: Identity[];
|
||||
primaryIdentity: Identity | null;
|
||||
authMode: 'basic' | 'oauth';
|
||||
@@ -30,9 +31,11 @@ interface AuthState {
|
||||
tokenExpiresAt: number | null;
|
||||
connectionLost: boolean;
|
||||
activeAccountId: string | null;
|
||||
isDemoMode: boolean;
|
||||
|
||||
login: (serverUrl: string, username: string, password: string, totp?: string, rememberMe?: boolean) => Promise<boolean>;
|
||||
loginWithOAuth: (serverUrl: string, code: string, codeVerifier: string, redirectUri: string) => Promise<boolean>;
|
||||
loginDemo: () => Promise<boolean>;
|
||||
refreshAccessToken: () => Promise<string | null>;
|
||||
logout: () => Promise<void>;
|
||||
logoutAll: () => void;
|
||||
@@ -140,7 +143,7 @@ function markSessionExpired(): void {
|
||||
saveRedirectAfterLogin();
|
||||
}
|
||||
|
||||
function initializeFeatureStores(client: JMAPClient): void {
|
||||
function initializeFeatureStores(client: IJMAPClient): void {
|
||||
if (client.supportsContacts()) {
|
||||
const contactStore = useContactStore.getState();
|
||||
contactStore.setSupportsSync(true);
|
||||
@@ -242,6 +245,7 @@ export const useAuthStore = create<AuthState>()(
|
||||
tokenExpiresAt: null,
|
||||
connectionLost: false,
|
||||
activeAccountId: null,
|
||||
isDemoMode: false,
|
||||
|
||||
login: async (serverUrl, username, password, totp, rememberMe) => {
|
||||
const effectivePassword = totp ? `${password}$${totp}` : password;
|
||||
@@ -355,6 +359,68 @@ export const useAuthStore = create<AuthState>()(
|
||||
}
|
||||
},
|
||||
|
||||
loginDemo: async () => {
|
||||
set({ isLoading: true, error: null });
|
||||
try {
|
||||
// Clear all store data before re-initializing with fresh demo data
|
||||
clearAllStores();
|
||||
|
||||
const { DemoJMAPClient } = await import('@/lib/demo/demo-client');
|
||||
const client = new DemoJMAPClient();
|
||||
await client.connect();
|
||||
|
||||
const username = client.getUsername();
|
||||
const { identities, primaryIdentity } = loadIdentities(await client.getIdentities(), username);
|
||||
initializeFeatureStores(client);
|
||||
|
||||
// Register a demo account entry so the account-switcher shows
|
||||
// proper avatar/name instead of a "?" placeholder.
|
||||
const accountStore = useAccountStore.getState();
|
||||
const demoAccountId = accountStore.addAccount({
|
||||
label: primaryIdentity?.name || 'Demo User',
|
||||
serverUrl: 'https://demo.example.com',
|
||||
username,
|
||||
authMode: 'basic',
|
||||
rememberMe: false,
|
||||
displayName: primaryIdentity?.name || 'Demo User',
|
||||
email: primaryIdentity?.email || username,
|
||||
lastLoginAt: Date.now(),
|
||||
isConnected: true,
|
||||
hasError: false,
|
||||
isDefault: true,
|
||||
});
|
||||
accountStore.setActiveAccount(demoAccountId);
|
||||
|
||||
set({
|
||||
isAuthenticated: true,
|
||||
isLoading: false,
|
||||
serverUrl: 'demo.example.com',
|
||||
username,
|
||||
client,
|
||||
identities,
|
||||
primaryIdentity,
|
||||
authMode: 'basic',
|
||||
rememberMe: false,
|
||||
accessToken: null,
|
||||
tokenExpiresAt: null,
|
||||
connectionLost: false,
|
||||
error: null,
|
||||
activeAccountId: demoAccountId,
|
||||
isDemoMode: true,
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
debug.error('Demo login error:', error);
|
||||
set({
|
||||
isLoading: false,
|
||||
error: 'generic',
|
||||
isAuthenticated: false,
|
||||
client: null,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
loginWithOAuth: async (serverUrl, code, codeVerifier, redirectUri) => {
|
||||
set({ isLoading: true, error: null });
|
||||
|
||||
@@ -512,12 +578,39 @@ export const useAuthStore = create<AuthState>()(
|
||||
|
||||
logout: async () => {
|
||||
const state = get();
|
||||
const wasDemoMode = state.isDemoMode;
|
||||
const wasOAuth = state.authMode === 'oauth';
|
||||
const accountId = state.activeAccountId;
|
||||
const accountStore = useAccountStore.getState();
|
||||
const account = accountId ? accountStore.getAccountById(accountId) : null;
|
||||
const slot = account?.cookieSlot ?? 0;
|
||||
|
||||
// Demo mode: simple cleanup, no network calls
|
||||
if (wasDemoMode) {
|
||||
set({ client: null });
|
||||
state.client?.disconnect();
|
||||
set({
|
||||
isAuthenticated: false,
|
||||
serverUrl: null,
|
||||
username: null,
|
||||
client: null,
|
||||
identities: [],
|
||||
primaryIdentity: null,
|
||||
authMode: 'basic',
|
||||
rememberMe: false,
|
||||
accessToken: null,
|
||||
tokenExpiresAt: null,
|
||||
connectionLost: false,
|
||||
error: null,
|
||||
activeAccountId: null,
|
||||
isDemoMode: false,
|
||||
});
|
||||
localStorage.removeItem('auth-storage');
|
||||
clearAllStores();
|
||||
redirectToLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
clearRefreshTimer(accountId ?? undefined);
|
||||
|
||||
// Null out the client BEFORE disconnecting so the page doesn't fire
|
||||
@@ -902,6 +995,13 @@ export const useAuthStore = create<AuthState>()(
|
||||
const accountStore = useAccountStore.getState();
|
||||
const accounts = accountStore.accounts;
|
||||
|
||||
// If the only account is the demo account, re-initialize demo mode
|
||||
// instead of trying to restore a server session (which doesn't exist).
|
||||
if (accounts.length === 1 && accounts[0].serverUrl === 'https://demo.example.com') {
|
||||
await get().loginDemo();
|
||||
return;
|
||||
}
|
||||
|
||||
// Multi-account restoration: restore all registered accounts
|
||||
if (accounts.length > 0) {
|
||||
// Null out client so the page doesn't fire data-loading effects
|
||||
|
||||
+16
-16
@@ -1,6 +1,6 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import type { JMAPClient } from '@/lib/jmap/client';
|
||||
import type { IJMAPClient } from '@/lib/jmap/client-interface';
|
||||
import type { Calendar, CalendarEvent, CalendarParticipant } from '@/lib/jmap/types';
|
||||
import { debug } from '@/lib/debug';
|
||||
import { normalizeAllDayDuration } from '@/lib/calendar-utils';
|
||||
@@ -37,17 +37,17 @@ interface CalendarStore {
|
||||
dateRange: { start: string; end: string } | null;
|
||||
|
||||
setSupported: (supported: boolean) => void;
|
||||
fetchCalendars: (client: JMAPClient) => Promise<void>;
|
||||
fetchEvents: (client: JMAPClient, start: string, end: string) => Promise<void>;
|
||||
createEvent: (client: JMAPClient, event: Partial<CalendarEvent>, sendSchedulingMessages?: boolean) => Promise<CalendarEvent | null>;
|
||||
updateEvent: (client: JMAPClient, id: string, updates: Partial<CalendarEvent>, sendSchedulingMessages?: boolean) => Promise<void>;
|
||||
deleteEvent: (client: JMAPClient, id: string, sendSchedulingMessages?: boolean) => Promise<void>;
|
||||
rsvpEvent: (client: JMAPClient, eventId: string, participantId: string, status: string, replyTo?: Record<string, string> | null) => Promise<void>;
|
||||
importEvents: (client: JMAPClient, events: Partial<CalendarEvent>[], calendarId: string) => Promise<number>;
|
||||
updateCalendar: (client: JMAPClient, calendarId: string, updates: Partial<Calendar>) => Promise<void>;
|
||||
createCalendar: (client: JMAPClient, calendar: Partial<Calendar>) => Promise<Calendar | null>;
|
||||
removeCalendar: (client: JMAPClient, calendarId: string) => Promise<void>;
|
||||
clearCalendarEvents: (client: JMAPClient, calendarId: string) => Promise<number>;
|
||||
fetchCalendars: (client: IJMAPClient) => Promise<void>;
|
||||
fetchEvents: (client: IJMAPClient, start: string, end: string) => Promise<void>;
|
||||
createEvent: (client: IJMAPClient, event: Partial<CalendarEvent>, sendSchedulingMessages?: boolean) => Promise<CalendarEvent | null>;
|
||||
updateEvent: (client: IJMAPClient, id: string, updates: Partial<CalendarEvent>, sendSchedulingMessages?: boolean) => Promise<void>;
|
||||
deleteEvent: (client: IJMAPClient, id: string, sendSchedulingMessages?: boolean) => Promise<void>;
|
||||
rsvpEvent: (client: IJMAPClient, eventId: string, participantId: string, status: string, replyTo?: Record<string, string> | null) => Promise<void>;
|
||||
importEvents: (client: IJMAPClient, events: Partial<CalendarEvent>[], calendarId: string) => Promise<number>;
|
||||
updateCalendar: (client: IJMAPClient, calendarId: string, updates: Partial<Calendar>) => Promise<void>;
|
||||
createCalendar: (client: IJMAPClient, calendar: Partial<Calendar>) => Promise<Calendar | null>;
|
||||
removeCalendar: (client: IJMAPClient, calendarId: string) => Promise<void>;
|
||||
clearCalendarEvents: (client: IJMAPClient, calendarId: string) => Promise<number>;
|
||||
setSelectedDate: (date: Date) => void;
|
||||
setViewMode: (mode: CalendarViewMode) => void;
|
||||
toggleCalendarVisibility: (calendarId: string) => void;
|
||||
@@ -56,10 +56,10 @@ interface CalendarStore {
|
||||
|
||||
// iCal subscriptions
|
||||
icalSubscriptions: ICalSubscription[];
|
||||
addICalSubscription: (client: JMAPClient, url: string, name: string, color: string, refreshInterval?: number) => Promise<ICalSubscription | null>;
|
||||
removeICalSubscription: (client: JMAPClient, subscriptionId: string) => Promise<void>;
|
||||
refreshICalSubscription: (client: JMAPClient, subscriptionId: string) => Promise<void>;
|
||||
refreshAllSubscriptions: (client: JMAPClient) => Promise<void>;
|
||||
addICalSubscription: (client: IJMAPClient, url: string, name: string, color: string, refreshInterval?: number) => Promise<ICalSubscription | null>;
|
||||
removeICalSubscription: (client: IJMAPClient, subscriptionId: string) => Promise<void>;
|
||||
refreshICalSubscription: (client: IJMAPClient, subscriptionId: string) => Promise<void>;
|
||||
refreshAllSubscriptions: (client: IJMAPClient) => Promise<void>;
|
||||
isSubscriptionCalendar: (calendarId: string) => boolean;
|
||||
}
|
||||
|
||||
|
||||
+15
-15
@@ -1,7 +1,7 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import type { ContactCard, AddressBook, ContactName } from '@/lib/jmap/types';
|
||||
import type { JMAPClient } from '@/lib/jmap/client';
|
||||
import type { IJMAPClient } from '@/lib/jmap/client-interface';
|
||||
|
||||
export function getContactDisplayName(contact: ContactCard): string {
|
||||
if (contact.name?.components) {
|
||||
@@ -47,11 +47,11 @@ interface ContactStore {
|
||||
lastSelectedContactId: string | null;
|
||||
activeTab: 'all' | 'groups';
|
||||
|
||||
fetchContacts: (client: JMAPClient) => Promise<void>;
|
||||
fetchAddressBooks: (client: JMAPClient) => Promise<void>;
|
||||
createContact: (client: JMAPClient, contact: Partial<ContactCard>) => Promise<void>;
|
||||
updateContact: (client: JMAPClient, id: string, updates: Partial<ContactCard>) => Promise<void>;
|
||||
deleteContact: (client: JMAPClient, id: string) => Promise<void>;
|
||||
fetchContacts: (client: IJMAPClient) => Promise<void>;
|
||||
fetchAddressBooks: (client: IJMAPClient) => Promise<void>;
|
||||
createContact: (client: IJMAPClient, contact: Partial<ContactCard>) => Promise<void>;
|
||||
updateContact: (client: IJMAPClient, id: string, updates: Partial<ContactCard>) => Promise<void>;
|
||||
deleteContact: (client: IJMAPClient, id: string) => Promise<void>;
|
||||
|
||||
addLocalContact: (contact: ContactCard) => void;
|
||||
updateLocalContact: (id: string, updates: Partial<ContactCard>) => void;
|
||||
@@ -68,21 +68,21 @@ interface ContactStore {
|
||||
getGroups: () => ContactCard[];
|
||||
getIndividuals: () => ContactCard[];
|
||||
getGroupMembers: (groupId: string) => ContactCard[];
|
||||
createGroup: (client: JMAPClient | null, name: string, memberIds: string[]) => Promise<void>;
|
||||
updateGroup: (client: JMAPClient | null, groupId: string, name: string) => Promise<void>;
|
||||
addMembersToGroup: (client: JMAPClient | null, groupId: string, memberIds: string[]) => Promise<void>;
|
||||
removeMembersFromGroup: (client: JMAPClient | null, groupId: string, memberIds: string[]) => Promise<void>;
|
||||
deleteGroup: (client: JMAPClient | null, groupId: string) => Promise<void>;
|
||||
createGroup: (client: IJMAPClient | null, name: string, memberIds: string[]) => Promise<void>;
|
||||
updateGroup: (client: IJMAPClient | null, groupId: string, name: string) => Promise<void>;
|
||||
addMembersToGroup: (client: IJMAPClient | null, groupId: string, memberIds: string[]) => Promise<void>;
|
||||
removeMembersFromGroup: (client: IJMAPClient | null, groupId: string, memberIds: string[]) => Promise<void>;
|
||||
deleteGroup: (client: IJMAPClient | null, groupId: string) => Promise<void>;
|
||||
|
||||
toggleContactSelection: (id: string) => void;
|
||||
selectRangeContacts: (targetId: string, sortedIds: string[]) => void;
|
||||
selectAllContacts: (ids: string[]) => void;
|
||||
clearSelection: () => void;
|
||||
bulkDeleteContacts: (client: JMAPClient | null, ids: string[]) => Promise<void>;
|
||||
bulkAddToGroup: (client: JMAPClient | null, groupId: string, contactIds: string[]) => Promise<void>;
|
||||
moveContactToAddressBook: (client: JMAPClient, contactIds: string[], addressBook: AddressBook) => Promise<void>;
|
||||
bulkDeleteContacts: (client: IJMAPClient | null, ids: string[]) => Promise<void>;
|
||||
bulkAddToGroup: (client: IJMAPClient | null, groupId: string, contactIds: string[]) => Promise<void>;
|
||||
moveContactToAddressBook: (client: IJMAPClient, contactIds: string[], addressBook: AddressBook) => Promise<void>;
|
||||
|
||||
importContacts: (client: JMAPClient | null, contacts: ContactCard[]) => Promise<number>;
|
||||
importContacts: (client: IJMAPClient | null, contacts: ContactCard[]) => Promise<number>;
|
||||
}
|
||||
|
||||
export const useContactStore = create<ContactStore>()(
|
||||
|
||||
+32
-32
@@ -1,6 +1,6 @@
|
||||
import { create } from "zustand";
|
||||
import { Email, Mailbox, StateChange } from "@/lib/jmap/types";
|
||||
import { JMAPClient } from "@/lib/jmap/client";
|
||||
import type { IJMAPClient } from "@/lib/jmap/client-interface";
|
||||
import { useSettingsStore } from "@/stores/settings-store";
|
||||
import { useCalendarStore } from "@/stores/calendar-store";
|
||||
import { SearchFilters, DEFAULT_SEARCH_FILTERS, buildJMAPFilter, isFilterEmpty } from "@/lib/jmap/search-utils";
|
||||
@@ -48,7 +48,7 @@ interface EmailStore {
|
||||
setSearchQuery: (query: string) => void;
|
||||
setQuota: (quota: { used: number; total: number } | null) => void;
|
||||
selectKeyword: (keyword: string | null) => void;
|
||||
fetchTagCounts: (client: JMAPClient) => Promise<void>;
|
||||
fetchTagCounts: (client: IJMAPClient) => Promise<void>;
|
||||
toggleEmailSelection: (emailId: string) => void;
|
||||
selectRangeEmails: (targetEmailId: string) => void;
|
||||
lastSelectedEmailId: string | null;
|
||||
@@ -56,54 +56,54 @@ interface EmailStore {
|
||||
clearSelection: () => void;
|
||||
|
||||
// JMAP operations
|
||||
fetchMailboxes: (client: JMAPClient) => Promise<void>;
|
||||
fetchEmails: (client: JMAPClient, mailboxId?: string) => Promise<void>;
|
||||
loadMoreEmails: (client: JMAPClient) => Promise<void>;
|
||||
fetchEmailContent: (client: JMAPClient, emailId: string) => Promise<Email | null>;
|
||||
fetchQuota: (client: JMAPClient) => Promise<void>;
|
||||
sendEmail: (client: JMAPClient, to: string[], subject: string, body: string, cc?: string[], bcc?: string[], identityId?: string, fromEmail?: string, draftId?: string, fromName?: string, htmlBody?: string, attachments?: Array<{ blobId: string; name: string; type: string; size: number }>) => Promise<void>;
|
||||
sendRawEmail: (client: JMAPClient, rawMimeBlob: Blob, identityId: string) => Promise<void>;
|
||||
deleteEmail: (client: JMAPClient, emailId: string, forceDelete?: boolean) => Promise<void>;
|
||||
markAsRead: (client: JMAPClient, emailId: string, read: boolean) => Promise<void>;
|
||||
moveToMailbox: (client: JMAPClient, emailId: string, mailboxId: string) => Promise<void>;
|
||||
searchEmails: (client: JMAPClient, query: string) => Promise<void>;
|
||||
advancedSearch: (client: JMAPClient) => Promise<void>;
|
||||
fetchMailboxes: (client: IJMAPClient) => Promise<void>;
|
||||
fetchEmails: (client: IJMAPClient, mailboxId?: string) => Promise<void>;
|
||||
loadMoreEmails: (client: IJMAPClient) => Promise<void>;
|
||||
fetchEmailContent: (client: IJMAPClient, emailId: string) => Promise<Email | null>;
|
||||
fetchQuota: (client: IJMAPClient) => Promise<void>;
|
||||
sendEmail: (client: IJMAPClient, to: string[], subject: string, body: string, cc?: string[], bcc?: string[], identityId?: string, fromEmail?: string, draftId?: string, fromName?: string, htmlBody?: string, attachments?: Array<{ blobId: string; name: string; type: string; size: number }>) => Promise<void>;
|
||||
sendRawEmail: (client: IJMAPClient, rawMimeBlob: Blob, identityId: string) => Promise<void>;
|
||||
deleteEmail: (client: IJMAPClient, emailId: string, forceDelete?: boolean) => Promise<void>;
|
||||
markAsRead: (client: IJMAPClient, emailId: string, read: boolean) => Promise<void>;
|
||||
moveToMailbox: (client: IJMAPClient, emailId: string, mailboxId: string) => Promise<void>;
|
||||
searchEmails: (client: IJMAPClient, query: string) => Promise<void>;
|
||||
advancedSearch: (client: IJMAPClient) => Promise<void>;
|
||||
setSearchFilters: (filters: Partial<SearchFilters>) => void;
|
||||
clearSearchFilters: () => void;
|
||||
toggleAdvancedSearch: () => void;
|
||||
toggleStar: (client: JMAPClient, emailId: string) => Promise<void>;
|
||||
toggleStar: (client: IJMAPClient, emailId: string) => Promise<void>;
|
||||
|
||||
// Batch operations
|
||||
batchMarkAsRead: (client: JMAPClient, read: boolean) => Promise<void>;
|
||||
batchDelete: (client: JMAPClient) => Promise<void>;
|
||||
batchMoveToMailbox: (client: JMAPClient, mailboxId: string) => Promise<void>;
|
||||
batchMarkAsRead: (client: IJMAPClient, read: boolean) => Promise<void>;
|
||||
batchDelete: (client: IJMAPClient) => Promise<void>;
|
||||
batchMoveToMailbox: (client: IJMAPClient, mailboxId: string) => Promise<void>;
|
||||
|
||||
// Spam operations
|
||||
spamUndoCache: Map<string, { emailId: string; originalMailboxId: string; accountId?: string }>;
|
||||
markAsSpam: (client: JMAPClient, emailId: string) => Promise<void>;
|
||||
undoSpam: (client: JMAPClient, emailId: string) => Promise<void>;
|
||||
batchMarkAsSpam: (client: JMAPClient, emailIds: string[]) => Promise<void>;
|
||||
batchUndoSpam: (client: JMAPClient, emailIds: string[]) => Promise<void>;
|
||||
markAsSpam: (client: IJMAPClient, emailId: string) => Promise<void>;
|
||||
undoSpam: (client: IJMAPClient, emailId: string) => Promise<void>;
|
||||
batchMarkAsSpam: (client: IJMAPClient, emailIds: string[]) => Promise<void>;
|
||||
batchUndoSpam: (client: IJMAPClient, emailIds: string[]) => Promise<void>;
|
||||
|
||||
// Push notification handlers
|
||||
setPushConnected: (connected: boolean) => void;
|
||||
handleStateChange: (change: StateChange, client: JMAPClient) => Promise<void>;
|
||||
refreshCurrentMailbox: (client: JMAPClient) => Promise<void>;
|
||||
handleStateChange: (change: StateChange, client: IJMAPClient) => Promise<void>;
|
||||
refreshCurrentMailbox: (client: IJMAPClient) => Promise<void>;
|
||||
handleNewEmailNotification: (email: Email) => void;
|
||||
clearNewEmailNotification: () => void;
|
||||
|
||||
// Thread expansion actions
|
||||
toggleThreadExpansion: (threadId: string) => void;
|
||||
fetchThreadEmails: (client: JMAPClient, threadId: string) => Promise<Email[]>;
|
||||
fetchThreadEmails: (client: IJMAPClient, threadId: string) => Promise<Email[]>;
|
||||
collapseAllThreads: () => void;
|
||||
updateThreadCache: (threadId: string, emails: Email[]) => void;
|
||||
|
||||
// Mailbox management
|
||||
createMailbox: (client: JMAPClient, name: string, parentId?: string) => Promise<void>;
|
||||
renameMailbox: (client: JMAPClient, mailboxId: string, name: string) => Promise<void>;
|
||||
deleteMailbox: (client: JMAPClient, mailboxId: string) => Promise<void>;
|
||||
setMailboxRole: (client: JMAPClient, mailboxId: string, role: string | null) => Promise<void>;
|
||||
emptyMailbox: (client: JMAPClient, mailboxId: string) => Promise<void>;
|
||||
createMailbox: (client: IJMAPClient, name: string, parentId?: string) => Promise<void>;
|
||||
renameMailbox: (client: IJMAPClient, mailboxId: string, name: string) => Promise<void>;
|
||||
deleteMailbox: (client: IJMAPClient, mailboxId: string) => Promise<void>;
|
||||
setMailboxRole: (client: IJMAPClient, mailboxId: string, role: string | null) => Promise<void>;
|
||||
emptyMailbox: (client: IJMAPClient, mailboxId: string) => Promise<void>;
|
||||
|
||||
// Mock data for demo
|
||||
loadMockData: () => void;
|
||||
@@ -1033,7 +1033,7 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
}
|
||||
},
|
||||
|
||||
batchUndoSpam: async (client: JMAPClient, emailIds: string[]) => {
|
||||
batchUndoSpam: async (client: IJMAPClient, emailIds: string[]) => {
|
||||
const { mailboxes, selectedMailbox } = get();
|
||||
|
||||
// Find inbox (batch operations don't preserve original mailboxes)
|
||||
@@ -1521,4 +1521,4 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
mailboxes: mockMailboxes,
|
||||
});
|
||||
},
|
||||
}));
|
||||
}));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { create } from 'zustand';
|
||||
import type { JMAPClient } from '@/lib/jmap/client';
|
||||
import type { IJMAPClient } from '@/lib/jmap/client-interface';
|
||||
import type { FileNode } from '@/lib/jmap/types';
|
||||
|
||||
export interface FileResource {
|
||||
@@ -47,7 +47,7 @@ interface FileState {
|
||||
supportsFiles: boolean | null;
|
||||
selectedResources: Set<string>;
|
||||
uploadProgress: UploadProgress | null;
|
||||
client: JMAPClient | null;
|
||||
client: IJMAPClient | null;
|
||||
clipboard: ClipboardState | null;
|
||||
uploadAbortController: AbortController | null;
|
||||
favorites: string[];
|
||||
@@ -55,7 +55,7 @@ interface FileState {
|
||||
lastAction: UndoAction | null;
|
||||
|
||||
// Actions
|
||||
initClient: (client: JMAPClient) => void;
|
||||
initClient: (client: IJMAPClient) => void;
|
||||
checkSupport: () => Promise<boolean>;
|
||||
navigate: (parentId: string | null, name?: string) => Promise<void>;
|
||||
navigateByPath: (path: string) => Promise<void>;
|
||||
@@ -178,7 +178,7 @@ export const useFileStore = create<FileState>((set, get) => ({
|
||||
try { return JSON.parse(localStorage.getItem('files-recent-files') || '[]'); } catch { return []; }
|
||||
})(),
|
||||
|
||||
initClient: (client: JMAPClient) => {
|
||||
initClient: (client: IJMAPClient) => {
|
||||
set({ client });
|
||||
},
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { create } from 'zustand';
|
||||
import type { JMAPClient } from '@/lib/jmap/client';
|
||||
import type { IJMAPClient } from '@/lib/jmap/client-interface';
|
||||
import type { FilterRule, SieveCapabilities } from '@/lib/jmap/sieve-types';
|
||||
import { parseScript } from '@/lib/sieve/parser';
|
||||
import { generateScript } from '@/lib/sieve/generator';
|
||||
@@ -17,9 +17,9 @@ interface FilterStore {
|
||||
rawScript: string;
|
||||
|
||||
setSupported: (supported: boolean) => void;
|
||||
fetchFilters: (client: JMAPClient) => Promise<void>;
|
||||
saveFilters: (client: JMAPClient) => Promise<void>;
|
||||
validateScript: (client: JMAPClient, content: string) => Promise<{ isValid: boolean; errors?: string[] }>;
|
||||
fetchFilters: (client: IJMAPClient) => Promise<void>;
|
||||
saveFilters: (client: IJMAPClient) => Promise<void>;
|
||||
validateScript: (client: IJMAPClient, content: string) => Promise<{ isValid: boolean; errors?: string[] }>;
|
||||
addRule: (rule: FilterRule) => void;
|
||||
updateRule: (ruleId: string, updates: Partial<FilterRule>) => void;
|
||||
deleteRule: (ruleId: string) => void;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { create } from 'zustand';
|
||||
import type { JMAPClient } from '@/lib/jmap/client';
|
||||
import type { IJMAPClient } from '@/lib/jmap/client-interface';
|
||||
|
||||
interface VacationStore {
|
||||
isEnabled: boolean;
|
||||
@@ -13,8 +13,8 @@ interface VacationStore {
|
||||
error: string | null;
|
||||
isSupported: boolean;
|
||||
|
||||
fetchVacationResponse: (client: JMAPClient) => Promise<void>;
|
||||
updateVacationResponse: (client: JMAPClient, updates: {
|
||||
fetchVacationResponse: (client: IJMAPClient) => Promise<void>;
|
||||
updateVacationResponse: (client: IJMAPClient, updates: {
|
||||
isEnabled?: boolean;
|
||||
fromDate?: string | null;
|
||||
toDate?: string | null;
|
||||
|
||||
Reference in New Issue
Block a user