import { addObservablesTo, addComputablesTo } from 'External/ko'; import { ComposeType } from 'Common/EnumsUser'; import { registerShortcut } from 'Common/Globals'; import { arrayLength, pInt } from 'Common/Utils'; import { download, computedPaginatorHelper, showMessageComposer } from 'Common/UtilsUser'; import { Selector } from 'Common/Selector'; import { serverRequestRaw, serverRequest } from 'Common/Links'; import { i18n, getNotification } from 'Common/Translator'; import { SettingsUserStore } from 'Stores/User/Settings'; import { ContactUserStore } from 'Stores/User/Contact'; import Remote from 'Remote/User/Fetch'; import { EmailModel } from 'Model/Email'; import { ContactModel } from 'Model/Contact'; import { decorateKoCommands } from 'Knoin/Knoin'; import { AbstractViewPopup } from 'Knoin/AbstractViews'; import { AskPopupView } from 'View/Popup/Ask'; const CONTACTS_PER_PAGE = 50, ScopeContacts = 'Contacts'; let bOpenCompose = false, sComposeRecipientsField = ''; export class ContactsPopupView extends AbstractViewPopup { constructor() { super('Contacts'); addObservablesTo(this, { search: '', contactsCount: 0, selectorContact: null, importButton: null, contactsPage: 1, isSaving: false, contact: null }); this.contacts = ContactUserStore; this.useCheckboxesInList = SettingsUserStore.useCheckboxesInList; this.selector = new Selector( ContactUserStore, this.selectorContact, null, '.e-contact-item .actionHandle', '.e-contact-item .checkboxItem', '.e-contact-item.focused' ); this.selector.on('ItemSelect', contact => this.populateViewContact(contact)); this.selector.on('ItemGetUid', contact => contact ? contact.generateUid() : ''); addComputablesTo(this, { contactsPaginator: computedPaginatorHelper( this.contactsPage, () => Math.max(1, Math.ceil(this.contactsCount() / CONTACTS_PER_PAGE)) ), contactsCheckedOrSelected: () => { const checked = ContactUserStore.filter(item => item.checked()), selected = this.selectorContact(); return checked.length ? checked : (selected ? [selected] : []); }, contactsSyncEnabled: () => ContactUserStore.allowSync() && ContactUserStore.syncMode(), isBusy: () => ContactUserStore.syncing() | ContactUserStore.importing() | ContactUserStore.loading() | this.isSaving() }); this.search.subscribe(() => this.reloadContactList()); this.saveCommand = this.saveCommand.bind(this); decorateKoCommands(this, { deleteCommand: self => !self.isBusy() && 0 < self.contactsCheckedOrSelected().length, newMessageCommand: self => !self.isBusy() && 0 < self.contactsCheckedOrSelected().length, saveCommand: self => !self.isBusy(), syncCommand: self => !self.isBusy() }); } newContact() { this.populateViewContact(new ContactModel); this.selectorContact(null); } deleteCommand() { const contacts = this.contactsCheckedOrSelected(); if (contacts.length) { let selectorContact = this.selectorContact(), Uids = [], count = 0; contacts.forEach(contact => { Uids.push(contact.id()); if (selectorContact && selectorContact.id() === contact.id()) { this.selectorContact(selectorContact = null); } contact.deleted(true); ++count; }); Remote.request('ContactsDelete', (iError, oData) => { if (iError) { alert(oData?.ErrorMessage || getNotification(iError)); } else { const page = this.contactsPage(); if (page > Math.max(1, Math.ceil((this.contactsCount() - count) / CONTACTS_PER_PAGE))) { this.contactsPage(page - 1); } // contacts.forEach(contact => ContactUserStore.remove(contact)); } this.reloadContactList(); }, { Uids: Uids.join(',') } ); } } newMessageCommand() { let aE = [], recipients = {to:null,cc:null,bcc:null}; this.contactsCheckedOrSelected().forEach(oContact => { const data = oContact?.getNameAndEmailHelper(), email = data ? new EmailModel(data[0], data[1]) : null; email?.validate() && aE.push(email); }); if (arrayLength(aE)) { bOpenCompose = false; this.close(); recipients[sComposeRecipientsField] = aE; showMessageComposer([ComposeType.Empty, null, recipients.to, recipients.cc, recipients.bcc]) } } clearSearch() { this.search(''); } saveCommand() { this.saveContact(this.contact()); } saveContact(contact) { const data = contact.toJSON(); if (data.jCard != JSON.stringify(contact.jCard)) { this.isSaving(true); Remote.request('ContactSave', (iError, oData) => { if (iError) { alert(oData?.ErrorMessage || getNotification(iError)); } else if (oData.Result.ResultID) { if (contact.id()) { contact.id(oData.Result.ResultID); contact.jCard = JSON.parse(data.jCard); } else { this.reloadContactList(); // TODO: remove when e-contact-foreach is dynamic } } this.isSaving(false); }, data ); } } syncCommand() { ContactUserStore.sync(iError => { iError && alert(getNotification(iError)); this.reloadContactList(true); }); } exportVcf() { download(serverRequestRaw('ContactsVcf'), 'contacts.vcf'); } exportCsv() { download(serverRequestRaw('ContactsCsv'), 'contacts.csv'); } /** * @param {?ContactModel} contact */ populateViewContact(contact) { const oldContact = this.contact(), fn = () => this.contact(contact); if (oldContact?.hasChanges()) { AskPopupView.showModal([ i18n('GLOBAL/SAVE_CHANGES'), () => this.saveContact(oldContact) | fn(), fn ]); } else fn(); } /** * @param {boolean=} dropPagePosition = false */ reloadContactList(dropPagePosition = false) { let offset = (this.contactsPage() - 1) * CONTACTS_PER_PAGE; if (dropPagePosition) { this.contactsPage(1); offset = 0; } ContactUserStore.loading(true); Remote.abort('Contacts').request('Contacts', (iError, data) => { let count = 0, list = []; if (iError) { // console.error(data); alert(data?.ErrorMessage || getNotification(iError)); } else if (arrayLength(data.Result.List)) { data.Result.List.forEach(item => { item = ContactModel.reviveFromJson(item); item && list.push(item); }); count = pInt(data.Result.Count); } this.contactsCount(0 < count ? count : 0); ContactUserStore(list); ContactUserStore.loading(false); }, { Offset: offset, Limit: CONTACTS_PER_PAGE, Search: this.search() } ); } onBuild(dom) { this.selector.init(dom.querySelector('.b-list-content'), ScopeContacts); registerShortcut('delete', '', ScopeContacts, () => { this.deleteCommand(); return false; }); registerShortcut('c,w', '', ScopeContacts, () => { this.newMessageCommand(); return false; }); const self = this; dom.addEventListener('click', event => { let el = event.target.closestWithin('.e-paginator a', dom); if (el && (el = pInt(ko.dataFor(el)?.value))) { self.contactsPage(el); self.reloadContactList(); } }); // initUploader if (this.importButton()) { const j = new Jua({ action: serverRequest('UploadContacts'), limit: 1, clickElement: this.importButton() }); if (j) { j.on('onStart', () => { ContactUserStore.importing(true); }).on('onComplete', (id, result, data) => { ContactUserStore.importing(false); this.reloadContactList(); if (!id || !result || !data || !data.Result) { alert(i18n('CONTACTS/ERROR_IMPORT_FILE')); } }); } } } tryToClose() { (false === this.onClose()) || this.close(); } onClose() { const contact = this.contact(); if (AskPopupView.hidden() && contact?.hasChanges()) { AskPopupView.showModal([ i18n('GLOBAL/SAVE_CHANGES'), () => this.close() | this.saveContact(contact), () => this.close() ]); return false; } } onShow(bBackToCompose, sRecipientsField) { bOpenCompose = !!bBackToCompose; sComposeRecipientsField = ['to','cc','bcc'].includes(sRecipientsField) ? sRecipientsField : 'to'; this.reloadContactList(true); } onHide() { this.contact(null); this.selectorContact(null); this.search(''); this.contactsCount(0); ContactUserStore([]); bOpenCompose && showMessageComposer(); } }