diff --git a/app/(main)/[locale]/contacts/page.tsx b/app/(main)/[locale]/contacts/page.tsx index 2f1b7520..798297f9 100644 --- a/app/(main)/[locale]/contacts/page.tsx +++ b/app/(main)/[locale]/contacts/page.tsx @@ -84,6 +84,7 @@ export default function ContactsPage() { bulkDeleteContacts, bulkAddToGroup, moveContactToAddressBook, + createAddressBook, renameAddressBook, removeAddressBook, shareAddressBook, @@ -95,6 +96,7 @@ export default function ContactsPage() { const [activeCategory, setActiveCategory] = useState("all"); const [showImportDialog, setShowImportDialog] = useState(false); const [renamingAddressBook, setRenamingAddressBook] = useState(null); + const [creatingAddressBook, setCreatingAddressBook] = useState(false); const [sharingAddressBookId, setSharingAddressBookId] = useState(null); const [defaultBookIdForCreate, setDefaultBookIdForCreate] = useState(undefined); const [createPrefill, setCreatePrefill] = useState<{ email?: string; name?: string } | undefined>(undefined); @@ -300,6 +302,34 @@ export default function ContactsPage() { } }, [client, supportsSync, contacts, updateContact, updateLocalContact, t]); + // Refresh address books (and contacts) after a structural change, staying + // multi-account aware so a freshly created book lands in the sidebar. + const refreshAddressBooks = useCallback(async () => { + if (!client) return; + if (multiAccountEnabled && accountClients.length > 0) { + const activeId = useAuthStore.getState().activeAccountId; + if (activeId) { + const { fetchAllAccountsAddressBooks } = useContactStore.getState(); + await fetchAllAccountsAddressBooks(accountClients, activeId); + return; + } + } + await useContactStore.getState().fetchAddressBooks(client); + }, [client, multiAccountEnabled, accountClients]); + + const handleCreateAddressBook = useCallback(async (name: string) => { + if (!client) return; + try { + await createAddressBook(client, name); + await refreshAddressBooks(); + toast.success(t("address_books.created")); + setCreatingAddressBook(false); + } catch (error) { + console.error('Failed to create address book:', error); + toast.error(t("address_books.create_failed")); + } + }, [client, createAddressBook, refreshAddressBooks, t]); + const handleImportContacts = useCallback(async (importedContacts: ContactCard[]) => { return importContacts( supportsSync && client ? client : null, @@ -857,6 +887,7 @@ export default function ContactsPage() { onSelectCategory={handleSelectCategory} onCreateGroup={handleCreateGroup} onCreateContact={handleCreateNew} + onCreateAddressBook={client ? () => setCreatingAddressBook(true) : undefined} onImport={() => setShowImportDialog(true)} onEditGroup={handleEditGroupFromSidebar} onDeleteGroup={handleDeleteGroupFromSidebar} @@ -1006,6 +1037,15 @@ export default function ContactsPage() { }} /> )} + {creatingAddressBook && ( + setCreatingAddressBook(false)} + onConfirm={handleCreateAddressBook} + /> + )} {renamingAddressBook && ( void; onCreateGroup: () => void; onCreateContact: () => void; + onCreateAddressBook?: () => void; onImport?: () => void; onEditGroup?: (groupId: string) => void; onDeleteGroup?: (groupId: string) => void; @@ -91,6 +92,7 @@ export function ContactsSidebar({ onSelectCategory, onCreateGroup, onCreateContact, + onCreateAddressBook, onImport, onEditGroup, onDeleteGroup, @@ -298,6 +300,15 @@ export function ContactsSidebar({ {t("groups.create")} + {onCreateAddressBook && ( + + )} {onImport && ( + )} @@ -278,6 +308,16 @@ export function AddressBookManagementSettings() { )} + {creating && ( + setCreating(false)} + onConfirm={handleCreate} + /> + )} + {sharingId && client && (() => { const book = addressBooks.find((b) => b.id === sharingId); if (!book) return null; diff --git a/locales/cs/common.json b/locales/cs/common.json index a642b676..e6203fd8 100644 --- a/locales/cs/common.json +++ b/locales/cs/common.json @@ -2172,6 +2172,9 @@ "title": "Sdílené" }, "address_books": { + "create": "Nový adresář", + "created": "Adresář vytvořen", + "create_failed": "Nepodařilo se vytvořit adresář", "title": "Moje adresáře", "shared_prefix": "Sdílené: {name}", "moved": "Kontakt přesunut do {name}", diff --git a/locales/da/common.json b/locales/da/common.json index ac73cc9a..4cfeaa5e 100644 --- a/locales/da/common.json +++ b/locales/da/common.json @@ -2172,6 +2172,9 @@ "title": "Delt" }, "address_books": { + "create": "Ny adressebog", + "created": "Adressebog oprettet", + "create_failed": "Kunne ikke oprette adressebog", "title": "Mine adressebøger", "shared_prefix": "Delt: {name}", "moved": "Kontakt flyttet til {name}", diff --git a/locales/de/common.json b/locales/de/common.json index 0fc3f008..81f1e0ee 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -2172,6 +2172,9 @@ "title": "Geteilt" }, "address_books": { + "create": "Neues Adressbuch", + "created": "Adressbuch erstellt", + "create_failed": "Adressbuch konnte nicht erstellt werden", "title": "Meine Adressbücher", "shared_prefix": "Geteilt: {name}", "moved": "Kontakt verschoben nach {name}", diff --git a/locales/en/common.json b/locales/en/common.json index 90c4ae6b..20855cb1 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -2173,6 +2173,9 @@ "title": "Shared" }, "address_books": { + "create": "New address book", + "created": "Address book created", + "create_failed": "Failed to create address book", "title": "My Address Books", "shared_prefix": "Shared: {name}", "moved": "Contact moved to {name}", diff --git a/locales/es/common.json b/locales/es/common.json index 3e7bd844..d0e719bd 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -2172,6 +2172,9 @@ "title": "Compartidos" }, "address_books": { + "create": "Nueva libreta de direcciones", + "created": "Libreta de direcciones creada", + "create_failed": "No se pudo crear la libreta de direcciones", "title": "Mis Libretas de Direcciones", "shared_prefix": "Compartido: {name}", "moved": "Contacto movido a {name}", diff --git a/locales/fa/common.json b/locales/fa/common.json index e4065d57..391485d8 100644 --- a/locales/fa/common.json +++ b/locales/fa/common.json @@ -2173,6 +2173,9 @@ "title": "اشتراکی" }, "address_books": { + "create": "دفترچه آدرس جدید", + "created": "دفترچه آدرس ایجاد شد", + "create_failed": "ایجاد دفترچه آدرس ناموفق بود", "title": "دفترچه‌های آدرس من", "shared_prefix": "اشتراکی: {name}", "moved": "مخاطب به {name} منتقل شد", diff --git a/locales/fr/common.json b/locales/fr/common.json index 162a49c9..3c4fac6a 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -2172,6 +2172,9 @@ "title": "Partagés" }, "address_books": { + "create": "Nouveau carnet d'adresses", + "created": "Carnet d'adresses créé", + "create_failed": "Échec de la création du carnet d'adresses", "title": "Mes Carnets d'adresses", "shared_prefix": "Partagé : {name}", "moved": "Contact déplacé vers {name}", diff --git a/locales/hu/common.json b/locales/hu/common.json index 6938bfdf..cd158799 100644 --- a/locales/hu/common.json +++ b/locales/hu/common.json @@ -2173,6 +2173,9 @@ "title": "Megosztott" }, "address_books": { + "create": "Új címjegyzék", + "created": "Címjegyzék létrehozva", + "create_failed": "Nem sikerült létrehozni a címjegyzéket", "title": "Címjegyzékeim", "shared_prefix": "Megosztott: {name}", "moved": "Névjegy áthelyezve ide: {name}", diff --git a/locales/it/common.json b/locales/it/common.json index 3f462ef8..bb864c9f 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -2172,6 +2172,9 @@ "title": "Condivisi" }, "address_books": { + "create": "Nuova rubrica", + "created": "Rubrica creata", + "create_failed": "Impossibile creare la rubrica", "title": "Le mie Rubriche", "shared_prefix": "Condiviso: {name}", "moved": "Contatto spostato in {name}", diff --git a/locales/ja/common.json b/locales/ja/common.json index bf2c491a..5d36ea70 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -2172,6 +2172,9 @@ "title": "共有" }, "address_books": { + "create": "新しいアドレス帳", + "created": "アドレス帳を作成しました", + "create_failed": "アドレス帳の作成に失敗しました", "title": "マイアドレス帳", "shared_prefix": "共有: {name}", "moved": "連絡先を {name} に移動しました", diff --git a/locales/ko/common.json b/locales/ko/common.json index aa4bcfcc..60bead10 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -2172,6 +2172,9 @@ "title": "공유됨" }, "address_books": { + "create": "새 주소록", + "created": "주소록이 생성되었습니다", + "create_failed": "주소록을 만들지 못했습니다", "title": "내 주소록", "shared_prefix": "공유됨: {name}", "moved": "연락처가 {name}(으)로 이동되었어요", diff --git a/locales/lv/common.json b/locales/lv/common.json index fa19be63..a77ec36e 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -2168,6 +2168,9 @@ "title": "Koplietotie" }, "address_books": { + "create": "Jauna adrešu grāmata", + "created": "Adrešu grāmata izveidota", + "create_failed": "Neizdevās izveidot adrešu grāmatu", "title": "Manas adrešu grāmatas", "shared_prefix": "Koplietotā: {name}", "moved": "Kontakts pārvietots uz {name}", diff --git a/locales/nl/common.json b/locales/nl/common.json index 380b9419..576289af 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -2172,6 +2172,9 @@ "title": "Gedeeld" }, "address_books": { + "create": "Nieuw adresboek", + "created": "Adresboek aangemaakt", + "create_failed": "Kan adresboek niet aanmaken", "title": "Mijn Adresboeken", "shared_prefix": "Gedeeld: {name}", "moved": "Contact verplaatst naar {name}", diff --git a/locales/pl/common.json b/locales/pl/common.json index 26a75d6c..d2f9f1a2 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -2172,6 +2172,9 @@ "title": "Udostępnione" }, "address_books": { + "create": "Nowa książka adresowa", + "created": "Utworzono książkę adresową", + "create_failed": "Nie udało się utworzyć książki adresowej", "title": "Moje książki adresowe", "shared_prefix": "Udostępnione: {name}", "moved": "Kontakt przeniesiono do {name}", diff --git a/locales/pt/common.json b/locales/pt/common.json index 4d1068fc..9fb7f599 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -2172,6 +2172,9 @@ "title": "Compartilhados" }, "address_books": { + "create": "Novo livro de endereços", + "created": "Livro de endereços criado", + "create_failed": "Falha ao criar o livro de endereços", "title": "Meus Catálogos de Endereços", "shared_prefix": "Compartilhado: {name}", "moved": "Contato movido para {name}", diff --git a/locales/ro/common.json b/locales/ro/common.json index 85513e02..06916535 100644 --- a/locales/ro/common.json +++ b/locales/ro/common.json @@ -2173,6 +2173,9 @@ "title": "Partajat" }, "address_books": { + "create": "Agendă nouă", + "created": "Agendă creată", + "create_failed": "Nu s-a putut crea agenda", "title": "Agendele mele de adrese", "shared_prefix": "Partajat: {name}", "moved": "Contactul a fost mutat la {name}", diff --git a/locales/ru/common.json b/locales/ru/common.json index 070df697..d5b47ddc 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -2172,6 +2172,9 @@ "title": "Общий" }, "address_books": { + "create": "Новая адресная книга", + "created": "Адресная книга создана", + "create_failed": "Не удалось создать адресную книгу", "title": "Мои адресные книги", "shared_prefix": "Общая: {name}", "moved": "Контакт перемещён в {name}", diff --git a/locales/tr/common.json b/locales/tr/common.json index 23aadb7b..f5baf9b0 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -2172,6 +2172,9 @@ "title": "Paylaşılan" }, "address_books": { + "create": "Yeni adres defteri", + "created": "Adres defteri oluşturuldu", + "create_failed": "Adres defteri oluşturulamadı", "title": "Adres Defterlerim", "shared_prefix": "Paylaşılan: {name}", "moved": "Kişi {name} konumuna taşındı", diff --git a/locales/uk/common.json b/locales/uk/common.json index f5d75941..db78274c 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -2172,6 +2172,9 @@ "title": "Спільний доступ" }, "address_books": { + "create": "Нова адресна книга", + "created": "Адресну книгу створено", + "create_failed": "Не вдалося створити адресну книгу", "title": "Мої адресні книги", "shared_prefix": "Спільно: {name}", "moved": "Контакт переміщено до {name}", diff --git a/locales/zh/common.json b/locales/zh/common.json index 41c7b344..f4f94de3 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -2172,6 +2172,9 @@ "title": "共享" }, "address_books": { + "create": "新建通讯录", + "created": "通讯录已创建", + "create_failed": "创建通讯录失败", "title": "我的地址簿", "shared_prefix": "共享:{name}", "moved": "联系人已移至 {name}", diff --git a/stores/contact-store.ts b/stores/contact-store.ts index 0fa0fe24..f0a9cb6a 100644 --- a/stores/contact-store.ts +++ b/stores/contact-store.ts @@ -201,6 +201,7 @@ interface ContactStore { bulkDeleteContacts: (client: IJMAPClient | null, ids: string[]) => Promise; bulkAddToGroup: (client: IJMAPClient | null, groupId: string, contactIds: string[]) => Promise; moveContactToAddressBook: (client: IJMAPClient, contactIds: string[], addressBook: AddressBook) => Promise; + createAddressBook: (client: IJMAPClient, name: string) => Promise; renameAddressBook: (client: IJMAPClient, addressBook: AddressBook, newName: string) => Promise; removeAddressBook: (client: IJMAPClient, addressBook: AddressBook) => Promise; shareAddressBook: (client: IJMAPClient, addressBook: AddressBook, principalId: string, rights: AddressBookRights | null) => Promise; @@ -849,6 +850,22 @@ export const useContactStore = create()( } }, + createAddressBook: async (client, name) => { + set({ error: null }); + const trimmed = name.trim(); + if (!trimmed) throw new Error('Address book name is required'); + try { + // New books always belong to the active account; the caller refreshes + // the list afterwards (single- or multi-account aware) so the freshly + // created book lands in state with its full server-set properties. + return await client.createAddressBook(trimmed); + } catch (error) { + const msg = error instanceof Error ? error.message : 'Failed to create address book'; + set({ error: msg }); + throw error; + } + }, + renameAddressBook: async (client, addressBook, newName) => { set({ error: null }); const trimmed = newName.trim();