Improved Contacts to check if changes should be saved

This commit is contained in:
the-djmaze 2022-09-06 19:22:06 +02:00
parent c9ad0ef170
commit 2627a16c36
43 changed files with 102 additions and 63 deletions

View file

@ -2,12 +2,6 @@
* Inspired by https://github.com/mcpar-land/vcfer * Inspired by https://github.com/mcpar-land/vcfer
*/ */
const
camelCase = str =>
str.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) =>
index === 0 ? word.toLowerCase() : word.toUpperCase()
).replace(/\s+/g, '');
export class VCardProperty { export class VCardProperty {
/** /**
@ -68,7 +62,7 @@ export class VCardProperty {
parseFromJCardProperty(jCardProp) parseFromJCardProperty(jCardProp)
{ {
jCardProp = JSON.parse(JSON.stringify(jCardProp)); jCardProp = JSON.parse(JSON.stringify(jCardProp));
this.field = camelCase(jCardProp[0]); this.field = jCardProp[0].toLowerCase();
this.params = jCardProp[1]; this.params = jCardProp[1];
this.type = jCardProp[2]; this.type = jCardProp[2];
this.value = jCardProp[3]; this.value = jCardProp[3];

View file

@ -93,7 +93,7 @@ export class ContactModel extends AbstractModel {
constructor() { constructor() {
super(); super();
this.jCard = new JCard(); this.jCard = ['vcard',[]];
this.addObservables({ this.addObservables({
focused: false, focused: false,
@ -122,14 +122,13 @@ export class ContactModel extends AbstractModel {
this.addComputables({ this.addComputables({
hasValidName: () => !!(this.givenName() || this.surName()), hasValidName: () => !!(this.givenName() || this.surName()),
fullName: () => (this.givenName() + ' ' + this.surName()).trim(), fullName: () => [this.namePrefix(), this.givenName(), this.middleName(), this.surName()].join(' ').trim(),
display: () => { display: () => {
let a = this.jCard.getOne('fn')?.value, let a = this.fullName(),
b = this.fullName(), b = this.email()?.[0]?.value,
c = this.jCard.getOne('email')?.value, c = this.nickname();
d = this.nickname(); return a || b || c;
return a || b || c || d;
} }
/* /*
fullName: { fullName: {
@ -200,12 +199,12 @@ export class ContactModel extends AbstractModel {
}); });
}); });
props = jCard.getOne('x-Crypto'); props = jCard.getOne('x-crypto');
contact.signpref(props?.params.signpref || 'Ask'); contact.signpref(props?.params.signpref || 'Ask');
contact.encryptpref(props?.params.encryptpref || 'Ask'); contact.encryptpref(props?.params.encryptpref || 'Ask');
// contact.encryptpref(props?.params.allowed || 'PGP/INLINE,PGP/MIME,S/MIME,S/MIMEOpaque'); // contact.encryptpref(props?.params.allowed || 'PGP/INLINE,PGP/MIME,S/MIME,S/MIMEOpaque');
contact.jCard = jCard; contact.jCard = json.jCard;
} }
return contact; return contact;
} }
@ -246,9 +245,14 @@ export class ContactModel extends AbstractModel {
this.nickname() || this.nickname(''); this.nickname() || this.nickname('');
} }
hasChanges()
{
return this.toJSON().jCard != JSON.stringify(this.jCard);
}
toJSON() toJSON()
{ {
let jCard = this.jCard; let jCard = new JCard(this.jCard);
jCard.set('n', [ jCard.set('n', [
this.surName(), this.surName(),
this.givenName(), this.givenName(),
@ -273,7 +277,7 @@ export class ContactModel extends AbstractModel {
values.forEach(value => value && jCard.add(field, value)); values.forEach(value => value && jCard.add(field, value));
}); });
jCard.set('x-Crypto', '', { jCard.set('x-crypto', '', {
allowed: 'PGP/INLINE,PGP/MIME,S/MIME,S/MIMEOpaque', allowed: 'PGP/INLINE,PGP/MIME,S/MIME,S/MIMEOpaque',
signpref: this.signpref(), signpref: this.signpref(),
encryptpref: this.encryptpref() encryptpref: this.encryptpref()

View file

@ -142,13 +142,6 @@
text-align: right; text-align: right;
border-top: 1px solid rgba(128,128,128,0.4); border-top: 1px solid rgba(128,128,128,0.4);
.button-save-contact {
&.dirty:enabled {
color: #51a351;
font-weight: bold;
}
}
.dropdown-menu.right-edge { .dropdown-menu.right-edge {
top: auto; top: auto;
bottom: 100%; bottom: 100%;

View file

@ -15,7 +15,7 @@ import Remote from 'Remote/User/Fetch';
import { EmailModel } from 'Model/Email'; import { EmailModel } from 'Model/Email';
import { ContactModel } from 'Model/Contact'; import { ContactModel } from 'Model/Contact';
import { decorateKoCommands, showScreenPopup } from 'Knoin/Knoin'; import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews'; import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { AskPopupView } from 'View/Popup/Ask'; import { AskPopupView } from 'View/Popup/Ask';
@ -43,8 +43,6 @@ export class ContactsPopupView extends AbstractViewPopup {
isSaving: false, isSaving: false,
hasChanges: false,
contact: null contact: null
}); });
@ -61,9 +59,7 @@ export class ContactsPopupView extends AbstractViewPopup {
'.e-contact-item.focused' '.e-contact-item.focused'
); );
this.selector.on('ItemSelect', contact => { this.selector.on('ItemSelect', contact => this.populateViewContact(contact));
this.populateViewContact(contact);
});
this.selector.on('ItemGetUid', contact => contact ? contact.generateUid() : ''); this.selector.on('ItemGetUid', contact => contact ? contact.generateUid() : '');
@ -95,13 +91,10 @@ export class ContactsPopupView extends AbstractViewPopup {
this.saveCommand = this.saveCommand.bind(this); this.saveCommand = this.saveCommand.bind(this);
// this.hasChanges(!!contact()?.toJSON().jCard);
decorateKoCommands(this, { decorateKoCommands(this, {
// 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.isSaving() && !self.hasChanges(), saveCommand: self => !self.isSaving(),
syncCommand: self => !self.contacts.syncing() && !self.contacts.importing() syncCommand: self => !self.contacts.syncing() && !self.contacts.importing()
}); });
} }
@ -171,22 +164,29 @@ export class ContactsPopupView extends AbstractViewPopup {
} }
saveCommand() { saveCommand() {
this.isSaving(true); this.saveContact(this.contact());
const contact = this.contact(); }
Remote.request('ContactSave',
(iError, oData) => { saveContact(contact) {
this.isSaving(false); const data = contact.toJSON();
if (!iError && oData.Result.ResultID) { if (data.jCard != JSON.stringify(contact.jCard)) {
contact.id(oData.Result.ResultID); this.isSaving(true);
this.reloadContactList(); // TODO: remove when e-contact-foreach is dynamic Remote.request('ContactSave',
this.hasChanges(false); (iError, oData) => {
} this.isSaving(false);
}, { if (iError) {
Contact: contact alert(oData?.ErrorMessage || getNotification(iError));
// Uid: contact.id(), } else if (oData.Result.ResultID) {
// jCard: contact.jCard 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
}
}
}, data
);
}
} }
syncCommand() { syncCommand() {
@ -252,8 +252,14 @@ export class ContactsPopupView extends AbstractViewPopup {
* @param {?ContactModel} contact * @param {?ContactModel} contact
*/ */
populateViewContact(contact) { populateViewContact(contact) {
const oldContact = this.contact();
if (oldContact?.hasChanges()) {
AskPopupView.showModal([
i18n('GLOBAL/SAVE_CHANGES'),
() => this.saveContact(oldContact)
]);
}
this.contact(contact || new ContactModel); this.contact(contact || new ContactModel);
this.hasChanges(false);
} }
/** /**
@ -349,10 +355,16 @@ export class ContactsPopupView extends AbstractViewPopup {
} }
} }
tryToClose() {
(false === this.onClose()) || this.close();
}
onClose() { onClose() {
if (this.hasChanges() && AskPopupView.hidden()) { const contact = this.contact();
showScreenPopup(AskPopupView, [ if (AskPopupView.hidden() && contact?.hasChanges()) {
i18n('POPUPS_ASK/DESC_WANT_CLOSE_THIS_WINDOW'), AskPopupView.showModal([
i18n('GLOBAL/SAVE_CHANGES'),
() => this.close() | this.saveContact(contact),
() => this.close() () => this.close()
]); ]);
return false; return false;

View file

@ -97,15 +97,14 @@ trait Contacts
$bResult = false; $bResult = false;
$oAddressBookProvider = $this->AddressBookProvider($oAccount); if ($this->HasActionParam('Uid') && $this->HasActionParam('jCard')) {
if ($oAddressBookProvider && $oAddressBookProvider->IsActive()) { $oAddressBookProvider = $this->AddressBookProvider($oAccount);
$aContact = $this->GetActionParam('Contact'); if ($oAddressBookProvider && $oAddressBookProvider->IsActive()) {
if (\is_array($aContact) && isset($aContact['Uid'], $aContact['jCard'])) { $vCard = \Sabre\VObject\Reader::readJson($this->GetActionParam('jCard'));
$vCard = \Sabre\VObject\Reader::readJson($aContact['jCard']);
if ($vCard && $vCard instanceof \Sabre\VObject\Component\VCard) { if ($vCard && $vCard instanceof \Sabre\VObject\Component\VCard) {
$vCard->REV = \gmdate('Ymd\\THis\\Z'); $vCard->REV = \gmdate('Ymd\\THis\\Z');
$vCard->PRODID = 'SnappyMail-'.APP_VERSION; $vCard->PRODID = 'SnappyMail-'.APP_VERSION;
$sUid = \trim($aContact['Uid']); $sUid = \trim($this->GetActionParam('Uid'));
$oContact = $sUid ? $oAddressBookProvider->GetContactByID($sUid) : null; $oContact = $sUid ? $oAddressBookProvider->GetContactByID($sUid) : null;
if (!$oContact) { if (!$oContact) {
$oContact = new \RainLoop\Providers\AddressBook\Classes\Contact(); $oContact = new \RainLoop\Providers\AddressBook\Classes\Contact();

View file

@ -24,6 +24,7 @@
"PASSWORD": "كلمة السر", "PASSWORD": "كلمة السر",
"REPLY_TO": "الرد-الى", "REPLY_TO": "الرد-الى",
"SAVE": "حفظ", "SAVE": "حفظ",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "لم يتم ايجاد جهة الإتصال", "SEARCH": "لم يتم ايجاد جهة الإتصال",
"SPAM": "الإبلاغ عن الرسائل غير المرغوب فيها", "SPAM": "الإبلاغ عن الرسائل غير المرغوب فيها",
"SUBJECT": "الموضوع", "SUBJECT": "الموضوع",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Парола", "PASSWORD": "Парола",
"REPLY_TO": "Отговори на", "REPLY_TO": "Отговори на",
"SAVE": "Запази", "SAVE": "Запази",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Не са намерени контакти", "SEARCH": "Не са намерени контакти",
"SPAM": "Спам", "SPAM": "Спам",
"SUBJECT": "Заглавие", "SUBJECT": "Заглавие",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Heslo", "PASSWORD": "Heslo",
"REPLY_TO": "Adresa odpovědi", "REPLY_TO": "Adresa odpovědi",
"SAVE": "Uložit", "SAVE": "Uložit",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Nebyly nalezeny žádné kontakty", "SEARCH": "Nebyly nalezeny žádné kontakty",
"SPAM": "Je to spam", "SPAM": "Je to spam",
"SUBJECT": "Předmět", "SUBJECT": "Předmět",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Adgangskode", "PASSWORD": "Adgangskode",
"REPLY_TO": "Svar til", "REPLY_TO": "Svar til",
"SAVE": "Gem", "SAVE": "Gem",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Ingen kontakter fundet", "SEARCH": "Ingen kontakter fundet",
"SPAM": "Uønsket", "SPAM": "Uønsket",
"SUBJECT": "Emne", "SUBJECT": "Emne",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Passwort", "PASSWORD": "Passwort",
"REPLY_TO": "Antwort an", "REPLY_TO": "Antwort an",
"SAVE": "Speichern", "SAVE": "Speichern",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Suchen", "SEARCH": "Suchen",
"SPAM": "Spam", "SPAM": "Spam",
"SUBJECT": "Betreff", "SUBJECT": "Betreff",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Κωδικός πρόσβασης", "PASSWORD": "Κωδικός πρόσβασης",
"REPLY_TO": "Απάντηση στον", "REPLY_TO": "Απάντηση στον",
"SAVE": "Αποθήκευση", "SAVE": "Αποθήκευση",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Δεν βρέθηκαν επαφές", "SEARCH": "Δεν βρέθηκαν επαφές",
"SPAM": "Ανεπιθύμητο", "SPAM": "Ανεπιθύμητο",
"SUBJECT": "Θέμα", "SUBJECT": "Θέμα",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Password", "PASSWORD": "Password",
"REPLY_TO": "Reply-To", "REPLY_TO": "Reply-To",
"SAVE": "Save", "SAVE": "Save",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Search", "SEARCH": "Search",
"SPAM": "Spam", "SPAM": "Spam",
"SUBJECT": "Subject", "SUBJECT": "Subject",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Password", "PASSWORD": "Password",
"REPLY_TO": "Reply-To", "REPLY_TO": "Reply-To",
"SAVE": "Save", "SAVE": "Save",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Search", "SEARCH": "Search",
"SPAM": "Spam", "SPAM": "Spam",
"SUBJECT": "Subject", "SUBJECT": "Subject",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Contraseña", "PASSWORD": "Contraseña",
"REPLY_TO": "Responder", "REPLY_TO": "Responder",
"SAVE": "Guardar", "SAVE": "Guardar",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Buscar", "SEARCH": "Buscar",
"SPAM": "Es correo deseado", "SPAM": "Es correo deseado",
"SUBJECT": "Asunto", "SUBJECT": "Asunto",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Salasõna", "PASSWORD": "Salasõna",
"REPLY_TO": "Vastuse saaja", "REPLY_TO": "Vastuse saaja",
"SAVE": "Salvesta", "SAVE": "Salvesta",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Kontakte ei leitud", "SEARCH": "Kontakte ei leitud",
"SPAM": "Märgi rämpspostiks", "SPAM": "Märgi rämpspostiks",
"SUBJECT": "Pealkiri", "SUBJECT": "Pealkiri",

View file

@ -24,6 +24,7 @@
"PASSWORD": "گذرواژه", "PASSWORD": "گذرواژه",
"REPLY_TO": "پاسخ به", "REPLY_TO": "پاسخ به",
"SAVE": "ذخیره کن", "SAVE": "ذخیره کن",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "هیچ تماسی پیدا نشد", "SEARCH": "هیچ تماسی پیدا نشد",
"SPAM": "هرزنامه", "SPAM": "هرزنامه",
"SUBJECT": "موضوع", "SUBJECT": "موضوع",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Salasana", "PASSWORD": "Salasana",
"REPLY_TO": "Vastaa", "REPLY_TO": "Vastaa",
"SAVE": "Tallenna", "SAVE": "Tallenna",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Yhtään yhteystietoa ei löytynyt", "SEARCH": "Yhtään yhteystietoa ei löytynyt",
"SPAM": "Merkitse roskapostiksi", "SPAM": "Merkitse roskapostiksi",
"SUBJECT": "Aihe", "SUBJECT": "Aihe",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Mot de passe", "PASSWORD": "Mot de passe",
"REPLY_TO": "Répondre à", "REPLY_TO": "Répondre à",
"SAVE": "Enregistrer", "SAVE": "Enregistrer",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Rechercher", "SEARCH": "Rechercher",
"SPAM": "Indésirable", "SPAM": "Indésirable",
"SUBJECT": "Sujet", "SUBJECT": "Sujet",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Jelszó", "PASSWORD": "Jelszó",
"REPLY_TO": "Válaszcím", "REPLY_TO": "Válaszcím",
"SAVE": "Mentés", "SAVE": "Mentés",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Nem találtam névjegyeket", "SEARCH": "Nem találtam névjegyeket",
"SPAM": "Levélszemét", "SPAM": "Levélszemét",
"SUBJECT": "Tárgy", "SUBJECT": "Tárgy",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Sandi", "PASSWORD": "Sandi",
"REPLY_TO": "Balas-Ke", "REPLY_TO": "Balas-Ke",
"SAVE": "Simpan", "SAVE": "Simpan",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Tidak ditemukan kontak", "SEARCH": "Tidak ditemukan kontak",
"SPAM": "Spam", "SPAM": "Spam",
"SUBJECT": "Judul", "SUBJECT": "Judul",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Lykilorð", "PASSWORD": "Lykilorð",
"REPLY_TO": "Svara-til", "REPLY_TO": "Svara-til",
"SAVE": "Vista", "SAVE": "Vista",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Engir tengiliðir fundust", "SEARCH": "Engir tengiliðir fundust",
"SPAM": "Ruslpóstur", "SPAM": "Ruslpóstur",
"SUBJECT": "Viðfangsefni", "SUBJECT": "Viðfangsefni",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Parola d'ordine", "PASSWORD": "Parola d'ordine",
"REPLY_TO": "Rispondi a", "REPLY_TO": "Rispondi a",
"SAVE": "Salva", "SAVE": "Salva",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Cerca", "SEARCH": "Cerca",
"SPAM": "Segnala come spam", "SPAM": "Segnala come spam",
"SUBJECT": "Oggetto", "SUBJECT": "Oggetto",

View file

@ -24,6 +24,7 @@
"PASSWORD": "パスワード", "PASSWORD": "パスワード",
"REPLY_TO": "Reply-To", "REPLY_TO": "Reply-To",
"SAVE": "保存", "SAVE": "保存",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "検索", "SEARCH": "検索",
"SPAM": "迷惑メール", "SPAM": "迷惑メール",
"SUBJECT": "件名", "SUBJECT": "件名",

View file

@ -24,6 +24,7 @@
"PASSWORD": "비밀번호", "PASSWORD": "비밀번호",
"REPLY_TO": "회신 주소", "REPLY_TO": "회신 주소",
"SAVE": "저장", "SAVE": "저장",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "연락처를 찾지 못했습니다.", "SEARCH": "연락처를 찾지 못했습니다.",
"SPAM": "스팸", "SPAM": "스팸",
"SUBJECT": "제목", "SUBJECT": "제목",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Slaptažodis", "PASSWORD": "Slaptažodis",
"REPLY_TO": "Kam atsakyti", "REPLY_TO": "Kam atsakyti",
"SAVE": "Išsaugoti", "SAVE": "Išsaugoti",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Kontaktų nerasta", "SEARCH": "Kontaktų nerasta",
"SPAM": "Žymėti kaip šlamštas", "SPAM": "Žymėti kaip šlamštas",
"SUBJECT": "Tema", "SUBJECT": "Tema",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Parole", "PASSWORD": "Parole",
"REPLY_TO": "Reply-To", "REPLY_TO": "Reply-To",
"SAVE": "Saglabāt", "SAVE": "Saglabāt",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Kontakti nav atrasti", "SEARCH": "Kontakti nav atrasti",
"SPAM": "Spams", "SPAM": "Spams",
"SUBJECT": "Tēma", "SUBJECT": "Tēma",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Passord", "PASSWORD": "Passord",
"REPLY_TO": "Svar til", "REPLY_TO": "Svar til",
"SAVE": "Lagre", "SAVE": "Lagre",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Fant ingen kontakter", "SEARCH": "Fant ingen kontakter",
"SPAM": "Søppelpost", "SPAM": "Søppelpost",
"SUBJECT": "Emne", "SUBJECT": "Emne",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Wachtwoord", "PASSWORD": "Wachtwoord",
"REPLY_TO": "Antwoordadres", "REPLY_TO": "Antwoordadres",
"SAVE": "Opslaan", "SAVE": "Opslaan",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Zoeken", "SEARCH": "Zoeken",
"SPAM": "Ongewenste e-mail", "SPAM": "Ongewenste e-mail",
"SUBJECT": "Onderwerp", "SUBJECT": "Onderwerp",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Hasło", "PASSWORD": "Hasło",
"REPLY_TO": "Odpisz do", "REPLY_TO": "Odpisz do",
"SAVE": "Zapisz", "SAVE": "Zapisz",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Nie znaleziono żadnych kontaktów", "SEARCH": "Nie znaleziono żadnych kontaktów",
"SPAM": "Oznacz jako SPAM", "SPAM": "Oznacz jako SPAM",
"SUBJECT": "Temat", "SUBJECT": "Temat",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Senha", "PASSWORD": "Senha",
"REPLY_TO": "Responder para", "REPLY_TO": "Responder para",
"SAVE": "Salvar", "SAVE": "Salvar",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Nenhum contato encontrado", "SEARCH": "Nenhum contato encontrado",
"SPAM": "Lixo Eletrônico", "SPAM": "Lixo Eletrônico",
"SUBJECT": "Assunto", "SUBJECT": "Assunto",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Palavra-passe", "PASSWORD": "Palavra-passe",
"REPLY_TO": "Resp.-Para", "REPLY_TO": "Resp.-Para",
"SAVE": "Guardar", "SAVE": "Guardar",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Pesquisar", "SEARCH": "Pesquisar",
"SPAM": "Spam", "SPAM": "Spam",
"SUBJECT": "Assunto", "SUBJECT": "Assunto",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Palavra-passe", "PASSWORD": "Palavra-passe",
"REPLY_TO": "Resp.-Para", "REPLY_TO": "Resp.-Para",
"SAVE": "Guardar", "SAVE": "Guardar",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Pesquisar", "SEARCH": "Pesquisar",
"SPAM": "Spam", "SPAM": "Spam",
"SUBJECT": "Assunto", "SUBJECT": "Assunto",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Parolă", "PASSWORD": "Parolă",
"REPLY_TO": "Reply-To", "REPLY_TO": "Reply-To",
"SAVE": "Salvează", "SAVE": "Salvează",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Nu am găsit nimic.", "SEARCH": "Nu am găsit nimic.",
"SPAM": "SPAM", "SPAM": "SPAM",
"SUBJECT": "Subiect", "SUBJECT": "Subiect",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Пароль", "PASSWORD": "Пароль",
"REPLY_TO": "Ответить-на", "REPLY_TO": "Ответить-на",
"SAVE": "Сохранить", "SAVE": "Сохранить",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Контакты не найдены", "SEARCH": "Контакты не найдены",
"SPAM": "В спам", "SPAM": "В спам",
"SUBJECT": "Тема", "SUBJECT": "Тема",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Heslo", "PASSWORD": "Heslo",
"REPLY_TO": "Adresa pre odpoveď", "REPLY_TO": "Adresa pre odpoveď",
"SAVE": "Uložiť", "SAVE": "Uložiť",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Nenašli sa žiadne kontakty", "SEARCH": "Nenašli sa žiadne kontakty",
"SPAM": "Spam", "SPAM": "Spam",
"SUBJECT": "Predmet", "SUBJECT": "Predmet",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Geslo", "PASSWORD": "Geslo",
"REPLY_TO": "Odgovor na", "REPLY_TO": "Odgovor na",
"SAVE": "Shrani", "SAVE": "Shrani",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Ni ujemajočih stikov", "SEARCH": "Ni ujemajočih stikov",
"SPAM": "Vsiljena pošta", "SPAM": "Vsiljena pošta",
"SUBJECT": "Zadeva", "SUBJECT": "Zadeva",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Lösenord", "PASSWORD": "Lösenord",
"REPLY_TO": "Svara till", "REPLY_TO": "Svara till",
"SAVE": "Spara", "SAVE": "Spara",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Inga kontakter funna", "SEARCH": "Inga kontakter funna",
"SPAM": "Spam", "SPAM": "Spam",
"SUBJECT": "Ämne", "SUBJECT": "Ämne",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Şifre", "PASSWORD": "Şifre",
"REPLY_TO": "Reply-To", "REPLY_TO": "Reply-To",
"SAVE": "Kaydet", "SAVE": "Kaydet",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Kişi bulunamadı", "SEARCH": "Kişi bulunamadı",
"SPAM": "Spam", "SPAM": "Spam",
"SUBJECT": "Konu", "SUBJECT": "Konu",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Пароль", "PASSWORD": "Пароль",
"REPLY_TO": "Reply-To", "REPLY_TO": "Reply-To",
"SAVE": "Зберегти", "SAVE": "Зберегти",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Контакти не знайдено", "SEARCH": "Контакти не знайдено",
"SPAM": "В спам", "SPAM": "В спам",
"SUBJECT": "Тема", "SUBJECT": "Тема",

View file

@ -24,6 +24,7 @@
"PASSWORD": "Mật khẩu", "PASSWORD": "Mật khẩu",
"REPLY_TO": "Nhận thư phản hồi về", "REPLY_TO": "Nhận thư phản hồi về",
"SAVE": "Lưu", "SAVE": "Lưu",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "Tìm kiếm", "SEARCH": "Tìm kiếm",
"SPAM": "Thư rác", "SPAM": "Thư rác",
"SUBJECT": "Tiêu đề", "SUBJECT": "Tiêu đề",

View file

@ -24,6 +24,7 @@
"PASSWORD": "密码", "PASSWORD": "密码",
"REPLY_TO": "回复", "REPLY_TO": "回复",
"SAVE": "保存", "SAVE": "保存",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "搜索", "SEARCH": "搜索",
"SPAM": "这是垃圾邮件", "SPAM": "这是垃圾邮件",
"SUBJECT": "主题", "SUBJECT": "主题",

View file

@ -24,6 +24,7 @@
"PASSWORD": "密碼", "PASSWORD": "密碼",
"REPLY_TO": "Reply-To", "REPLY_TO": "Reply-To",
"SAVE": "保存", "SAVE": "保存",
"SAVE_CHANGES": "Save changes?",
"SEARCH": "沒找到連絡人", "SEARCH": "沒找到連絡人",
"SPAM": "垃圾郵件", "SPAM": "垃圾郵件",
"SUBJECT": "主題", "SUBJECT": "主題",

View file

@ -1,6 +1,6 @@
<header class="b-header-toolbar g-ui-user-select-none"> <header class="b-header-toolbar g-ui-user-select-none">
<a href="#" class="close" data-bind="click: close">×</a> <a href="#" class="close" data-bind="click: tryToClose">×</a>
<div class="btn-toolbar"> <div class="btn-toolbar">
@ -228,7 +228,7 @@
<div class="b-view-content-toolbar btn-toolbar" data-bind="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="visible: !readOnly(), command: $root.saveCommand, css: {'dirty': $root.hasChanges}"> <button class="btn button-save-contact" data-bind="visible: !readOnly(), command: $root.saveCommand">
<i data-bind="css: {'icon-ok': !$root.isSaving(), 'icon-spinner': $root.isSaving()}"></i> <i data-bind="css: {'icon-ok': !$root.isSaving(), 'icon-spinner': $root.isSaving()}"></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>