import window from 'window'; import _ from '_'; import $ from '$'; import ko from 'ko'; import key from 'key'; import Jua from 'Jua'; import { SaveSettingsStep, ContactPropertyType, ComposeType, Capa, Magics, StorageResultType, Notification, KeyState } from 'Common/Enums'; import { delegateRunOnDestroy, computedPagenatorHelper, inArray, trim, windowResizeCallback, createCommand, isNonEmptyArray, fakeMd5, pInt, isUnd } from 'Common/Utils'; import {CONTACTS_PER_PAGE} from 'Common/Consts'; import {bMobileDevice} from 'Common/Globals'; import {Selector} from 'Common/Selector'; import {exportContactsVcf, exportContactsCsv, uploadContacts} from 'Common/Links'; import {i18n, getNotification} from 'Common/Translator'; import SettingsStore from 'Stores/User/Settings'; import ContactStore from 'Stores/User/Contact'; import Remote from 'Remote/User/Ajax'; import * as Settings from 'Storage/Settings'; import {EmailModel} from 'Model/Email'; import {ContactModel} from 'Model/Contact'; import {ContactPropertyModel} from 'Model/ContactProperty'; import {getApp} from 'Helper/Apps/User'; import {view, ViewType, showScreenPopup, hideScreenPopup, routeOn, routeOff} from 'Knoin/Knoin'; import {AbstractViewNext} from 'Knoin/AbstractViewNext'; @view({ name: 'View/Popup/Contacts', type: ViewType.Popup, templateID: 'PopupsContacts' }) class ContactsPopupView extends AbstractViewNext { constructor() { super(); const fFastClearEmptyListHelper = (list) => { if (list && 0 < list.length) { this.viewProperties.removeAll(list); delegateRunOnDestroy(list); } }; this.bBackToCompose = false; this.sLastComposeFocusedField = ''; this.allowContactsSync = ContactStore.allowContactsSync; this.enableContactsSync = ContactStore.enableContactsSync; this.allowExport = !bMobileDevice; this.search = ko.observable(''); this.contactsCount = ko.observable(0); this.contacts = ContactStore.contacts; this.currentContact = ko.observable(null); this.importUploaderButton = ko.observable(null); this.contactsPage = ko.observable(1); this.contactsPageCount = ko.computed(() => { const iPage = window.Math.ceil(this.contactsCount() / CONTACTS_PER_PAGE); return 0 >= iPage ? 1 : iPage; }); this.contactsPagenator = ko.computed(computedPagenatorHelper(this.contactsPage, this.contactsPageCount)); this.emptySelection = ko.observable(true); this.viewClearSearch = ko.observable(false); this.viewID = ko.observable(''); this.viewReadOnly = ko.observable(false); this.viewProperties = ko.observableArray([]); this.viewSaveTrigger = ko.observable(SaveSettingsStep.Idle); this.viewPropertiesNames = this.viewProperties.filter( (property) => -1 < inArray(property.type(), [ContactPropertyType.FirstName, ContactPropertyType.LastName]) ); this.viewPropertiesOther = this.viewProperties.filter( (property) => -1 < inArray(property.type(), [ContactPropertyType.Note]) ); this.viewPropertiesOther = ko.computed(() => { const list = _.filter(this.viewProperties(), (property) => -1 < inArray(property.type(), [ContactPropertyType.Nick]) ); return _.sortBy(list, (property) => property.type()); }); this.viewPropertiesEmails = this.viewProperties.filter( (property) => ContactPropertyType.Email === property.type() ); this.viewPropertiesWeb = this.viewProperties.filter( (property) => ContactPropertyType.Web === property.type() ); this.viewHasNonEmptyRequaredProperties = ko.computed(() => { const names = this.viewPropertiesNames(), emails = this.viewPropertiesEmails(), fFilter = (property) => '' !== trim(property.value()); return !!(_.find(names, fFilter) || _.find(emails, fFilter)); }); this.viewPropertiesPhones = this.viewProperties.filter( (property) => ContactPropertyType.Phone === property.type() ); this.viewPropertiesEmailsNonEmpty = this.viewPropertiesNames.filter( (property) => '' !== trim(property.value()) ); this.viewPropertiesEmailsEmptyAndOnFocused = this.viewPropertiesEmails.filter((property) => { const foc = property.focused(); return '' === trim(property.value()) && !foc; }); this.viewPropertiesPhonesEmptyAndOnFocused = this.viewPropertiesPhones.filter((property) => { const foc = property.focused(); return '' === trim(property.value()) && !foc; }); this.viewPropertiesWebEmptyAndOnFocused = this.viewPropertiesWeb.filter((property) => { const foc = property.focused(); return '' === trim(property.value()) && !foc; }); this.viewPropertiesOtherEmptyAndOnFocused = ko.computed( () => _.filter(this.viewPropertiesOther(), (property) => { const foc = property.focused(); return '' === trim(property.value()) && !foc; }) ); this.viewPropertiesEmailsEmptyAndOnFocused.subscribe((list) => { fFastClearEmptyListHelper(list); }); this.viewPropertiesPhonesEmptyAndOnFocused.subscribe((list) => { fFastClearEmptyListHelper(list); }); this.viewPropertiesWebEmptyAndOnFocused.subscribe((list) => { fFastClearEmptyListHelper(list); }); this.viewPropertiesOtherEmptyAndOnFocused.subscribe((list) => { fFastClearEmptyListHelper(list); }); this.viewSaving = ko.observable(false); this.useCheckboxesInList = SettingsStore.useCheckboxesInList; this.search.subscribe(() => { this.reloadContactList(); }); this.contacts.subscribe(windowResizeCallback); this.viewProperties.subscribe(windowResizeCallback); this.contactsChecked = ko.computed( () => _.filter(this.contacts(), (item) => item.checked()) ); this.contactsCheckedOrSelected = ko.computed(() => { const checked = this.contactsChecked(), selected = this.currentContact(); return _.union(checked, selected ? [selected] : []); }); this.contactsCheckedOrSelectedUids = ko.computed( () => _.map(this.contactsCheckedOrSelected(), (contact) => contact.idContact) ); this.selector = new Selector( this.contacts, this.currentContact, null, '.e-contact-item .actionHandle', '.e-contact-item.selected', '.e-contact-item .checkboxItem', '.e-contact-item.focused'); this.selector.on('onItemSelect', (contact) => { this.populateViewContact(contact ? contact : null); if (!contact) { this.emptySelection(true); } }); this.selector.on('onItemGetUid', (contact) => (contact ? contact.generateUid() : '')); this.newCommand = createCommand(() => { this.populateViewContact(null); this.currentContact(null); }); this.deleteCommand = createCommand(() => { this.deleteSelectedContacts(); this.emptySelection(true); }, () => 0 < this.contactsCheckedOrSelected().length); this.newMessageCommand = createCommand(() => { if (!Settings.capa(Capa.Composer)) { return false; } let aE = [], toEmails = null, ccEmails = null, bccEmails = null; const aC = this.contactsCheckedOrSelected(); if (isNonEmptyArray(aC)) { aE = _.map(aC, (oItem) => { if (oItem) { const data = oItem.getNameAndEmailHelper(), email = data ? new EmailModel(data[0], data[1]) : null; if (email && email.validate()) { return email; } } return null; }); aE = _.compact(aE); } if (isNonEmptyArray(aE)) { this.bBackToCompose = false; hideScreenPopup(require('View/Popup/Contacts')); switch (this.sLastComposeFocusedField) { case 'cc': ccEmails = aE; break; case 'bcc': bccEmails = aE; break; case 'to': default: toEmails = aE; break; } this.sLastComposeFocusedField = ''; _.delay(() => { showScreenPopup(require('View/Popup/Compose'), [ComposeType.Empty, null, toEmails, ccEmails, bccEmails]); }, Magics.Time200ms); } return true; }, () => 0 < this.contactsCheckedOrSelected().length); this.clearCommand = createCommand(() => { this.search(''); }); this.saveCommand = createCommand(() => { this.viewSaving(true); this.viewSaveTrigger(SaveSettingsStep.Animate); const requestUid = fakeMd5(), properties = []; _.each(this.viewProperties(), (oItem) => { if (oItem.type() && '' !== trim(oItem.value())) { properties.push([oItem.type(), oItem.value(), oItem.typeStr()]); } }); Remote.contactSave((sResult, oData) => { let res = false; this.viewSaving(false); if (StorageResultType.Success === sResult && oData && oData.Result && oData.Result.RequestUid === requestUid && 0 < pInt(oData.Result.ResultID)) { if ('' === this.viewID()) { this.viewID(pInt(oData.Result.ResultID)); } this.reloadContactList(); res = true; } _.delay(() => { this.viewSaveTrigger(res ? SaveSettingsStep.TrueResult : SaveSettingsStep.FalseResult); }, Magics.Time350ms); if (res) { this.watchDirty(false); _.delay(() => { this.viewSaveTrigger(SaveSettingsStep.Idle); }, Magics.Time1s); } }, requestUid, this.viewID(), properties); }, () => { const bV = this.viewHasNonEmptyRequaredProperties(), bReadOnly = this.viewReadOnly(); return !this.viewSaving() && bV && !bReadOnly; }); this.syncCommand = createCommand(() => { getApp().contactsSync((result, data) => { if (StorageResultType.Success !== result || !data || !data.Result) { window.alert(getNotification( data && data.ErrorCode ? data.ErrorCode : Notification.ContactsSyncError)); } this.reloadContactList(true); }); }, () => !this.contacts.syncing() && !this.contacts.importing()); this.bDropPageAfterDelete = false; this.watchDirty = ko.observable(false); this.watchHash = ko.observable(false); this.viewHash = ko.computed(() => '' + _.map(this.viewProperties(), (oItem) => oItem.value()).join('')); // this.saveCommandDebounce = _.debounce(_.bind(this.saveCommand, this), 1000); this.viewHash.subscribe(() => { if (this.watchHash() && !this.viewReadOnly() && !this.watchDirty()) { this.watchDirty(true); } }); this.sDefaultKeyScope = KeyState.ContactList; } getPropertyPlaceholder(type) { let result = ''; switch (type) { case ContactPropertyType.LastName: result = 'CONTACTS/PLACEHOLDER_ENTER_LAST_NAME'; break; case ContactPropertyType.FirstName: result = 'CONTACTS/PLACEHOLDER_ENTER_FIRST_NAME'; break; case ContactPropertyType.Nick: result = 'CONTACTS/PLACEHOLDER_ENTER_NICK_NAME'; break; // no default } return result; } addNewProperty(type, typeStr) { this.viewProperties.push(new ContactPropertyModel(type, typeStr || '', '', true, this.getPropertyPlaceholder(type))); } addNewOrFocusProperty(type, typeStr) { const item = _.find(this.viewProperties(), (prop) => type === prop.type()); if (item) { item.focused(true); } else { this.addNewProperty(type, typeStr); } } addNewEmail() { this.addNewProperty(ContactPropertyType.Email, 'Home'); } addNewPhone() { this.addNewProperty(ContactPropertyType.Phone, 'Mobile'); } addNewWeb() { this.addNewProperty(ContactPropertyType.Web); } addNewNickname() { this.addNewOrFocusProperty(ContactPropertyType.Nick); } addNewNotes() { this.addNewOrFocusProperty(ContactPropertyType.Note); } addNewBirthday() { this.addNewOrFocusProperty(ContactPropertyType.Birthday); } exportVcf() { getApp().download(exportContactsVcf()); } exportCsv() { getApp().download(exportContactsCsv()); } initUploader() { if (this.importUploaderButton()) { const j = new Jua({ 'action': uploadContacts(), 'name': 'uploader', 'queueSize': 1, 'multipleSizeLimit': 1, 'disableDragAndDrop': true, 'disableMultiple': true, 'disableDocumentDropPrevent': true, 'clickElement': this.importUploaderButton() }); if (j) { j .on('onStart', () => { this.contacts.importing(true); }) .on('onComplete', (id, result, data) => { this.contacts.importing(false); this.reloadContactList(); if (!id || !result || !data || !data.Result) { window.alert(i18n('CONTACTS/ERROR_IMPORT_FILE')); } }); } } } removeCheckedOrSelectedContactsFromList() { const koContacts = this.contacts, contacts = this.contactsCheckedOrSelected(); let currentContact = this.currentContact(), count = this.contacts().length; if (0 < contacts.length) { _.each(contacts, (contact) => { if (currentContact && currentContact.idContact === contact.idContact) { currentContact = null; this.currentContact(null); } contact.deleted(true); count -= 1; }); if (0 >= count) { this.bDropPageAfterDelete = true; } _.delay(() => { _.each(contacts, (contact) => { koContacts.remove(contact); delegateRunOnDestroy(contact); }); }, Magics.Time500ms); } } deleteSelectedContacts() { if (0 < this.contactsCheckedOrSelected().length) { Remote.contactsDelete( _.bind(this.deleteResponse, this), this.contactsCheckedOrSelectedUids() ); this.removeCheckedOrSelectedContactsFromList(); } } /** * @param {string} sResult * @param {AjaxJsonDefaultResponse} oData */ deleteResponse(sResult, oData) { if (Magics.Time500ms < (StorageResultType.Success === sResult && oData && oData.Time ? pInt(oData.Time) : 0)) { this.reloadContactList(this.bDropPageAfterDelete); } else { _.delay(() => { this.reloadContactList(this.bDropPageAfterDelete); }, Magics.Time500ms); } } removeProperty(oProp) { this.viewProperties.remove(oProp); delegateRunOnDestroy(oProp); } /** * @param {?ContactModel} contact */ populateViewContact(contact) { let id = '', lastName = '', firstName = ''; const list = []; this.watchHash(false); this.emptySelection(false); this.viewReadOnly(false); if (contact) { id = contact.idContact; if (isNonEmptyArray(contact.properties)) { _.each(contact.properties, (property) => { if (property && property[0]) { if (ContactPropertyType.LastName === property[0]) { lastName = property[1]; } else if (ContactPropertyType.FirstName === property[0]) { firstName = property[1]; } else { list.push(new ContactPropertyModel(property[0], property[2] || '', property[1])); } } }); } this.viewReadOnly(!!contact.readOnly); } list.unshift(new ContactPropertyModel(ContactPropertyType.LastName, '', lastName, false, this.getPropertyPlaceholder(ContactPropertyType.LastName))); list.unshift(new ContactPropertyModel(ContactPropertyType.FirstName, '', firstName, !contact, this.getPropertyPlaceholder(ContactPropertyType.FirstName))); this.viewID(id); delegateRunOnDestroy(this.viewProperties()); this.viewProperties([]); this.viewProperties(list); this.watchDirty(false); this.watchHash(true); } /** * @param {boolean=} dropPagePosition = false */ reloadContactList(dropPagePosition = false) { let offset = (this.contactsPage() - 1) * CONTACTS_PER_PAGE; this.bDropPageAfterDelete = false; if (dropPagePosition) { this.contactsPage(1); offset = 0; } this.contacts.loading(true); Remote.contacts((result, data) => { let count = 0, list = []; if (StorageResultType.Success === result && data && data.Result && data.Result.List) { if (isNonEmptyArray(data.Result.List)) { list = _.map(data.Result.List, (item) => { const contact = new ContactModel(); return contact.parse(item) ? contact : null; }); list = _.compact(list); count = pInt(data.Result.Count); count = 0 < count ? count : 0; } } this.contactsCount(count); delegateRunOnDestroy(this.contacts()); this.contacts(list); this.contacts.loading(false); this.viewClearSearch('' !== this.search()); }, offset, CONTACTS_PER_PAGE, this.search()); } onBuild(dom) { this.oContentVisible = $('.b-list-content', dom); this.oContentScrollable = $('.content', this.oContentVisible); this.selector.init(this.oContentVisible, this.oContentScrollable, KeyState.ContactList); key('delete', KeyState.ContactList, () => { this.deleteCommand(); return false; }); key('c, w', KeyState.ContactList, () => { this.newMessageCommand(); return false; }); const self = this; dom .on('click', '.e-pagenator .e-page', function() { // eslint-disable-line prefer-arrow-callback const page = ko.dataFor(this); // eslint-disable-line no-invalid-this if (page) { self.contactsPage(pInt(page.value)); self.reloadContactList(); } }); this.initUploader(); } onShow(bBackToCompose, sLastComposeFocusedField) { this.bBackToCompose = isUnd(bBackToCompose) ? false : !!bBackToCompose; this.sLastComposeFocusedField = isUnd(sLastComposeFocusedField) ? '' : sLastComposeFocusedField; routeOff(); this.reloadContactList(true); } onHide() { routeOn(); this.currentContact(null); this.emptySelection(true); this.search(''); this.contactsCount(0); delegateRunOnDestroy(this.contacts()); this.contacts([]); this.sLastComposeFocusedField = ''; if (this.bBackToCompose) { this.bBackToCompose = false; if (Settings.capa(Capa.Composer)) { showScreenPopup(require('View/Popup/Compose')); } } } } module.exports = ContactsPopupView;