Some cleanups for new AddressBook

This commit is contained in:
djmaze 2022-07-22 22:59:50 +02:00
parent c162a3acc6
commit 052f34a9f5
4 changed files with 43 additions and 72 deletions

View file

@ -15,7 +15,6 @@ export class ContactModel extends AbstractModel {
constructor() { constructor() {
super(); super();
this.properties = [];
this.jCard = new JCard(); this.jCard = new JCard();
this.addObservables({ this.addObservables({
@ -30,8 +29,7 @@ export class ContactModel extends AbstractModel {
surName: '', // LastName surName: '', // LastName
middleName: '', // MiddleName middleName: '', // MiddleName
namePrefix: '', // NamePrefix namePrefix: '', // NamePrefix
nameSuffix: '', // NameSuffix nameSuffix: '' // NameSuffix
nickname: null
}); });
// this.email = koArrayWithDestroy(); // this.email = koArrayWithDestroy();
this.email = ko.observableArray(); this.email = ko.observableArray();
@ -43,6 +41,15 @@ export class ContactModel extends AbstractModel {
fullName: () => (this.givenName() + ' ' + this.surName()).trim(), fullName: () => (this.givenName() + ' ' + this.surName()).trim(),
nickname: {
read: () => {
let prop = this.jCard.getOne('nickname');
return prop && prop.value;
},
write: value =>
value ? this.jCard.set('nickname', value/*, params, group*/) : this.jCard.remove('nickname')
},
display: () => { display: () => {
let a = this.jCard.getOne('fn')?.value, let a = this.jCard.getOne('fn')?.value,
b = this.fullName(), b = this.fullName(),
@ -85,17 +92,12 @@ export class ContactModel extends AbstractModel {
static reviveFromJson(json) { static reviveFromJson(json) {
const contact = super.reviveFromJson(json); const contact = super.reviveFromJson(json);
if (contact) { if (contact) {
let list = [];
let jCard = new JCard(json.jCard), let jCard = new JCard(json.jCard),
props = jCard.getOne('n')?.value; props = jCard.getOne('n')?.value;
props && props.forEach((value, index) => props && props.forEach((value, index) =>
value && contact[nProps[index]](value) value && contact[nProps[index]](value)
); );
props = jCard.getOne('nickname');
props && contact.nickname(props.value);
['email', 'tel', 'url'].forEach(field => { ['email', 'tel', 'url'].forEach(field => {
props = jCard.get(field); props = jCard.get(field);
props && props.forEach(prop => { props && props.forEach(prop => {
@ -106,7 +108,6 @@ export class ContactModel extends AbstractModel {
}); });
}); });
contact.properties = list;
contact.jCard = jCard; contact.jCard = jCard;
} }
return contact; return contact;
@ -116,7 +117,7 @@ export class ContactModel extends AbstractModel {
* @returns {string} * @returns {string}
*/ */
generateUid() { generateUid() {
return ''+this.id; return '' + this.id;
} }
addEmail() { addEmail() {
@ -160,8 +161,6 @@ export class ContactModel extends AbstractModel {
]/*, params, group*/); ]/*, params, group*/);
// jCard.parseFullName({set:true}); // jCard.parseFullName({set:true});
this.nickname() ? jCard.set('nickname', this.nickname()/*, params, group*/) : jCard.remove('nickname');
['email', 'tel', 'url'].forEach(field => { ['email', 'tel', 'url'].forEach(field => {
let values = this[field].map(item => item.value()); let values = this[field].map(item => item.value());
jCard.get(field).forEach(prop => { jCard.get(field).forEach(prop => {

View file

@ -148,10 +148,6 @@
font-weight: bold; font-weight: bold;
} }
} }
&.read-only .button-save-contact {
display: none;
}
} }
.b-view-content { .b-view-content {

View file

@ -23,7 +23,6 @@ import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { AskPopupView } from 'View/Popup/Ask'; import { AskPopupView } from 'View/Popup/Ask';
const const
viewProperties = koArrayWithDestroy(),
CONTACTS_PER_PAGE = 50, CONTACTS_PER_PAGE = 50,
ScopeContacts = 'Contacts'; ScopeContacts = 'Contacts';
@ -38,20 +37,15 @@ export class ContactsPopupView extends AbstractViewPopup {
search: '', search: '',
contactsCount: 0, contactsCount: 0,
currentContact: null, selectorContact: null,
importUploaderButton: null, importButton: null,
contactsPage: 1, contactsPage: 1,
viewClearSearch: false,
viewReadOnly: false,
viewSaving: false, viewSaving: false,
watchDirty: false, hasChanges: false,
watchHash: false,
contact: null contact: null
}); });
@ -62,7 +56,7 @@ export class ContactsPopupView extends AbstractViewPopup {
this.selector = new Selector( this.selector = new Selector(
ContactUserStore, ContactUserStore,
this.currentContact, this.selectorContact,
null, null,
'.e-contact-item .actionHandle', '.e-contact-item .actionHandle',
'.e-contact-item .checkboxItem', '.e-contact-item .checkboxItem',
@ -87,7 +81,7 @@ export class ContactsPopupView extends AbstractViewPopup {
contactsCheckedOrSelected: () => { contactsCheckedOrSelected: () => {
const checked = ContactUserStore.filter(item => item.checked && item.checked()), const checked = ContactUserStore.filter(item => item.checked && item.checked()),
selected = this.currentContact(); selected = this.selectorContact();
return selected return selected
? [...checked, selected].unique() ? [...checked, selected].unique()
@ -96,34 +90,27 @@ export class ContactsPopupView extends AbstractViewPopup {
contactsCheckedOrSelectedUids: () => this.contactsCheckedOrSelected().map(contact => contact.id()), contactsCheckedOrSelectedUids: () => this.contactsCheckedOrSelected().map(contact => contact.id()),
contactsSyncEnabled: () => ContactUserStore.allowSync() && ContactUserStore.syncMode(), contactsSyncEnabled: () => ContactUserStore.allowSync() && ContactUserStore.syncMode()
viewHash: () => '' + viewProperties.map(property => property.value && property.value()).join('')
}); });
this.search.subscribe(() => this.reloadContactList()); this.search.subscribe(() => this.reloadContactList());
this.viewHash.subscribe(() => {
if (this.watchHash() && !this.viewReadOnly() && !this.watchDirty()) {
this.watchDirty(true);
}
});
this.saveCommand = this.saveCommand.bind(this); this.saveCommand = this.saveCommand.bind(this);
// this.hasChanges(!!contact()?.toJSON().jCard);
decorateKoCommands(this, { decorateKoCommands(this, {
// close: self => !self.watchDirty(), // close: self => !self.hasChanges(),
deleteCommand: self => 0 < self.contactsCheckedOrSelected().length, deleteCommand: self => 0 < self.contactsCheckedOrSelected().length,
newMessageCommand: self => 0 < self.contactsCheckedOrSelected().length, newMessageCommand: self => 0 < self.contactsCheckedOrSelected().length,
saveCommand: self => !self.viewSaving() && !self.viewReadOnly() saveCommand: self => !self.viewSaving() && !self.hasChanges(),
&& (self.contact()?.hasValidName() || self.contact()?.email().length),
syncCommand: self => !self.contacts.syncing() && !self.contacts.importing() syncCommand: self => !self.contacts.syncing() && !self.contacts.importing()
}); });
} }
newContact() { newContact() {
this.populateViewContact(null); this.populateViewContact(null);
this.currentContact(null); this.selectorContact(null);
} }
deleteCommand() { deleteCommand() {
@ -207,7 +194,7 @@ export class ContactsPopupView extends AbstractViewPopup {
} }
if (res) { if (res) {
this.watchDirty(false); this.hasChanges(false);
} }
}, { }, {
RequestUid: requestUid, RequestUid: requestUid,
@ -237,14 +224,14 @@ export class ContactsPopupView extends AbstractViewPopup {
removeCheckedOrSelectedContactsFromList() { removeCheckedOrSelectedContactsFromList() {
const contacts = this.contactsCheckedOrSelected(); const contacts = this.contactsCheckedOrSelected();
let currentContact = this.currentContact(), let selectorContact = this.selectorContact(),
count = ContactUserStore.length; count = ContactUserStore.length;
if (contacts.length) { if (contacts.length) {
contacts.forEach(contact => { contacts.forEach(contact => {
if (currentContact && currentContact.id() === contact.id()) { if (selectorContact && selectorContact.id() === contact.id()) {
currentContact = null; selectorContact = null;
this.currentContact(null); this.selectorContact(null);
} }
contact.deleted(true); contact.deleted(true);
@ -282,18 +269,12 @@ export class ContactsPopupView extends AbstractViewPopup {
* @param {?ContactModel} contact * @param {?ContactModel} contact
*/ */
populateViewContact(contact) { populateViewContact(contact) {
this.watchHash(false);
if (!contact) { if (!contact) {
contact = new ContactModel; contact = new ContactModel;
} }
this.viewReadOnly(contact.readOnly());
this.contact(contact); this.contact(contact);
viewProperties(contact.properties); this.hasChanges(false);
this.watchDirty(false);
this.watchHash(true);
} }
/** /**
@ -330,7 +311,6 @@ export class ContactsPopupView extends AbstractViewPopup {
ContactUserStore(list); ContactUserStore(list);
ContactUserStore.loading(false); ContactUserStore.loading(false);
this.viewClearSearch(!!this.search());
}, },
{ {
Offset: offset, Offset: offset,
@ -368,12 +348,12 @@ export class ContactsPopupView extends AbstractViewPopup {
// initUploader // initUploader
if (this.importUploaderButton()) { if (this.importButton()) {
const j = new Jua({ const j = new Jua({
action: serverRequest('UploadContacts'), action: serverRequest('UploadContacts'),
limit: 1, limit: 1,
disableDocumentDropPrevent: true, disableDocumentDropPrevent: true,
clickElement: this.importUploaderButton() clickElement: this.importButton()
}); });
if (j) { if (j) {
@ -391,7 +371,7 @@ export class ContactsPopupView extends AbstractViewPopup {
} }
onClose() { onClose() {
if (this.watchDirty() && AskPopupView.hidden()) { if (this.hasChanges() && AskPopupView.hidden()) {
showScreenPopup(AskPopupView, [ showScreenPopup(AskPopupView, [
i18n('POPUPS_ASK/DESC_WANT_CLOSE_THIS_WINDOW'), i18n('POPUPS_ASK/DESC_WANT_CLOSE_THIS_WINDOW'),
() => this.close() () => this.close()
@ -408,17 +388,15 @@ export class ContactsPopupView extends AbstractViewPopup {
onHide() { onHide() {
this.contact(null); this.contact(null);
this.currentContact(null); this.selectorContact(null);
this.search(''); this.search('');
this.contactsCount(0); this.contactsCount(0);
ContactUserStore([]); ContactUserStore([]);
this.sLastComposeFocusedField = ''; this.sLastComposeFocusedField = '';
if (this.bBackToCompose) { if (this.bBackToCompose) {
this.bBackToCompose = false; this.bBackToCompose = false;
showMessageComposer(); showMessageComposer();
} }
} }

View file

@ -14,7 +14,7 @@
<a id="contacts-more-dropdown-id" class="btn dropdown-toggle fontastic" href="#" tabindex="-1"></a> <a id="contacts-more-dropdown-id" class="btn dropdown-toggle fontastic" href="#" tabindex="-1"></a>
<menu class="dropdown-menu right-edge" role="menu" aria-labelledby="contacts-more-dropdown-id"> <menu class="dropdown-menu right-edge" role="menu" aria-labelledby="contacts-more-dropdown-id">
<li role="presentation"> <li role="presentation">
<a href="#" tabindex="-1" data-bind="initDom: importUploaderButton"> <a href="#" tabindex="-1" data-bind="initDom: importButton">
<i data-bind="css: {'icon-import': !contacts.importing(), 'icon-spinner': contacts.importing}"></i> <i data-bind="css: {'icon-import': !contacts.importing(), 'icon-spinner': contacts.importing}"></i>
<span data-i18n="CONTACTS/BUTTON_IMPORT"></span> <span data-i18n="CONTACTS/BUTTON_IMPORT"></span>
</a> </a>
@ -48,7 +48,7 @@
</div> </div>
<div class="b-list-content g-ui-user-select-none" data-bind="css: {'hideContactListCheckbox': !useCheckboxesInList()}"> <div class="b-list-content g-ui-user-select-none" data-bind="css: {'hideContactListCheckbox': !useCheckboxesInList()}">
<div class="content"> <div class="content">
<div class="listClear" data-bind="visible: viewClearSearch() && '' !== search()"> <div class="listClear" data-bind="visible: !!search()">
<span class="g-ui-link" data-i18n="CONTACTS/CLEAR_SEARCH" data-bind="click: clearSearch"></span> <span class="g-ui-link" data-i18n="CONTACTS/CLEAR_SEARCH" data-bind="click: clearSearch"></span>
</div> </div>
<div class="listEmptyList" data-bind="visible: 0 === contacts().length && '' === search() && !contacts.loading()" <div class="listEmptyList" data-bind="visible: 0 === contacts().length && '' === search() && !contacts.loading()"
@ -72,16 +72,16 @@
<!-- ko template: { name: 'Paginator', data: contactsPaginator } --><!-- /ko --> <!-- ko template: { name: 'Paginator', data: contactsPaginator } --><!-- /ko -->
</div> </div>
</div> </div>
<div class="b-view-content-toolbar btn-toolbar" data-bind="css: {'read-only': viewReadOnly}, i18nUpdate: contact"> <div class="b-view-content-toolbar btn-toolbar" data-bind="i18nUpdate: contact">
<!-- ko with: contact --> <!-- ko with: contact -->
<div class="btn-group"> <div class="btn-group">
<button class="btn button-save-contact" data-bind="command: $root.saveCommand, css: {'dirty': $root.watchDirty}"> <button class="btn button-save-contact" data-bind="visible: !readOnly(), command: $root.saveCommand, css: {'dirty': $root.hasChanges}">
<i data-bind="css: {'icon-ok': !$root.viewSaving(), 'icon-spinner': $root.viewSaving()}"></i> <i data-bind="css: {'icon-ok': !$root.viewSaving(), 'icon-spinner': $root.viewSaving()}"></i>
<span data-i18n="CONTACTS/BUTTON_CREATE_CONTACT" data-bind="visible: !id"></span> <span data-i18n="CONTACTS/BUTTON_CREATE_CONTACT" data-bind="visible: !id()"></span>
<span data-i18n="CONTACTS/BUTTON_UPDATE_CONTACT" data-bind="visible: id"></span> <span data-i18n="CONTACTS/BUTTON_UPDATE_CONTACT" data-bind="visible: id"></span>
</button> </button>
</div> </div>
<div class="btn-group dropdown" data-bind="registerBootstrapDropdown: true"> <div class="btn-group dropdown" data-bind="visible: !readOnly(), registerBootstrapDropdown: true">
<a id="button-add-prop-dropdown-id" href="#" tabindex="-1" class="btn dropdown-toggle" data-i18n="CONTACTS/ADD_MENU_LABEL"></a> <a id="button-add-prop-dropdown-id" href="#" tabindex="-1" class="btn dropdown-toggle" data-i18n="CONTACTS/ADD_MENU_LABEL"></a>
<menu class="dropdown-menu right-edge" style="text-align: left" tabindex="-1" role="menu" aria-labelledby="button-add-prop-dropdown-id"> <menu class="dropdown-menu right-edge" style="text-align: left" tabindex="-1" role="menu" aria-labelledby="button-add-prop-dropdown-id">
<li role="presentation"> <li role="presentation">
@ -118,15 +118,15 @@
</div> </div>
<!-- /ko --> <!-- /ko -->
</div> </div>
<div class="b-view-content" data-bind="css: {'read-only': viewReadOnly}"> <div class="b-view-content">
<div class="b-contact-view-desc" data-bind="visible: !contact" <div class="b-contact-view-desc" data-bind="visible: !contact"
data-i18n="CONTACTS/CONTACT_VIEW_DESC"></div> data-i18n="CONTACTS/CONTACT_VIEW_DESC"></div>
<div data-bind="visible: contact, i18nUpdate: contact"> <div data-bind="visible: contact, i18nUpdate: contact">
<div class="form-horizontal top-part"> <!-- ko with: contact -->
<div class="control-group" data-bind="visible: !viewReadOnly() || hasValidName()"> <div class="form-horizontal top-part" data-bind="css: {'read-only': readOnly}">
<div class="control-group" data-bind="visible: !readOnly() || hasValidName()">
<label class="fontastic iconsize24">👤</label> <label class="fontastic iconsize24">👤</label>
<div> <div>
<!-- ko with: contact -->
<div class="property-line"> <div class="property-line">
<span data-bind="text: givenName"></span> <span data-bind="text: givenName"></span>
<input type="text" <input type="text"
@ -145,10 +145,8 @@
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: nickname" data-i18n="[placeholder]CONTACTS/PLACEHOLDER_ENTER_NICK_NAME"> data-bind="value: nickname" data-i18n="[placeholder]CONTACTS/PLACEHOLDER_ENTER_NICK_NAME">
</div> </div>
<!-- /ko -->
</div> </div>
</div> </div>
<!-- ko with: contact -->
<div class="control-group" data-bind="email().length"> <div class="control-group" data-bind="email().length">
<label class="fontastic iconsize24" data-i18n="[title]GLOBAL/EMAIL">@</label> <label class="fontastic iconsize24" data-i18n="[title]GLOBAL/EMAIL">@</label>
<div> <div>
@ -191,8 +189,8 @@
<a href="#" class="btn fontastic" data-bind="visible: !readOnly(), click: $root.addUrl"></a> <a href="#" class="btn fontastic" data-bind="visible: !readOnly(), click: $root.addUrl"></a>
</div> </div>
</div> </div>
<!-- /ko -->
</div> </div>
<!-- /ko -->
<!-- <!--
<div class="e-read-only-sign fontastic iconsize24" data-i18n="[title]CONTACTS/LABEL_READ_ONLY">🔒</div> <div class="e-read-only-sign fontastic iconsize24" data-i18n="[title]CONTACTS/LABEL_READ_ONLY">🔒</div>
--> -->