Improved observables

This commit is contained in:
djmaze 2020-10-27 11:09:24 +01:00
parent 8d7e25fc7c
commit 7db7d5545b
28 changed files with 236 additions and 234 deletions

View file

@ -399,7 +399,7 @@ class AppUser extends AbstractApp {
Remote.foldersReloadWithTimeout(FolderStore.foldersLoading); Remote.foldersReloadWithTimeout(FolderStore.foldersLoading);
}, },
(errorCode) => { (errorCode) => {
FolderStore.folderList.error(getNotification(errorCode, '', errorDefCode)); FolderStore.folderListError(getNotification(errorCode, '', errorDefCode));
Remote.foldersReloadWithTimeout(FolderStore.foldersLoading); Remote.foldersReloadWithTimeout(FolderStore.foldersLoading);
} }
); );
@ -757,7 +757,7 @@ class AppUser extends AbstractApp {
} }
this.reloadFlagsCurrentMessageListAndMessageFromCache(); this.reloadFlagsCurrentMessageListAndMessageFromCache();
MessageStore.message.viewTrigger(!MessageStore.message.viewTrigger()); MessageStore.messageViewTrigger(!MessageStore.messageViewTrigger());
} }
} }

View file

@ -287,15 +287,4 @@ ko.extenders.specialThrottle = (target, timeout) => {
return target; return target;
}; };
// functions
ko.observable.fn.validateEmail = function() {
this.hasError = ko.observable(false);
this.subscribe(value => this.hasError(value && !/^[^@\s]+@[^@\s]+$/.test(value)));
this.valueHasMutated();
return this;
};
export default ko; export default ko;

4
dev/External/ko.js vendored
View file

@ -36,7 +36,6 @@ ko.bindingHandlers.tooltip = {
ko.bindingHandlers.tooltipErrorTip = { ko.bindingHandlers.tooltipErrorTip = {
init: element => { init: element => {
doc.addEventListener('click', () => element.removeAttribute('data-rainloopErrorTip')); doc.addEventListener('click', () => element.removeAttribute('data-rainloopErrorTip'));
ko.utils.domNodeDisposal.addDisposeCallback(element, () => element.removeAttribute('data-rainloopErrorTip'));
}, },
update: (element, fValueAccessor) => { update: (element, fValueAccessor) => {
const value = koValue(fValueAccessor()); const value = koValue(fValueAccessor());
@ -258,8 +257,7 @@ ko.extenders.falseTimeout = (target, option) => {
// functions // functions
ko.observable.fn.deleteAccessHelper = function() { ko.observable.fn.deleteAccessHelper = function() {
this.extend({ falseTimeout: 3000 }).extend({ toggleSubscribeProperty: [this, 'deleteAccess'] }); return this.extend({ falseTimeout: 3000, toggleSubscribeProperty: [this, 'deleteAccess'] });
return this;
}; };
ko.addObservablesTo = (target, observables) => { ko.addObservablesTo = (target, observables) => {

View file

@ -102,7 +102,7 @@ export class FolderCollectionModel extends AbstractCollectionModel
AppStore.threadsAllowed(!!(Settings.app('useImapThread') && this.IsThreadsSupported)); AppStore.threadsAllowed(!!(Settings.app('useImapThread') && this.IsThreadsSupported));
FolderStore.folderList.optimized(!!this.Optimized); FolderStore.folderListOptimized(!!this.Optimized);
let update = false; let update = false;

View file

@ -11,8 +11,26 @@ const settingsGet = rl.settings.get;
class ContactsAdminSettings { class ContactsAdminSettings {
constructor() { constructor() {
this.defaultOptionsAfterRender = defaultOptionsAfterRender; this.defaultOptionsAfterRender = defaultOptionsAfterRender;
this.enableContacts = ko.observable(!!settingsGet('ContactsEnable'));
this.contactsSync = ko.observable(!!settingsGet('ContactsSync')); ko.addObservablesTo(this, {
enableContacts: !!settingsGet('ContactsEnable'),
contactsSync: !!settingsGet('ContactsSync'),
contactsType: '',
pdoDsn: settingsGet('ContactsPdoDsn'),
pdoUser: settingsGet('ContactsPdoUser'),
pdoPassword: settingsGet('ContactsPdoPassword'),
pdoDsnTrigger: SaveSettingsStep.Idle,
pdoUserTrigger: SaveSettingsStep.Idle,
pdoPasswordTrigger: SaveSettingsStep.Idle,
contactsTypeTrigger: SaveSettingsStep.Idle,
testing: false,
testContactsSuccess: false,
testContactsError: false,
testContactsErrorMessage: ''
});
const supportedTypes = settingsGet('supportedPdoDrivers') || [], const supportedTypes = settingsGet('supportedPdoDrivers') || [],
types = [{ types = [{
@ -30,8 +48,6 @@ class ContactsAdminSettings {
this.contactsTypesOptions = types; this.contactsTypesOptions = types;
this.contactsType = ko.observable('');
this.mainContactsType = ko this.mainContactsType = ko
.computed({ .computed({
read: this.contactsType, read: this.contactsType,
@ -55,20 +71,6 @@ class ContactsAdminSettings {
this.testContactsErrorMessage(''); this.testContactsErrorMessage('');
}); });
this.pdoDsn = ko.observable(settingsGet('ContactsPdoDsn'));
this.pdoUser = ko.observable(settingsGet('ContactsPdoUser'));
this.pdoPassword = ko.observable(settingsGet('ContactsPdoPassword'));
this.pdoDsnTrigger = ko.observable(SaveSettingsStep.Idle);
this.pdoUserTrigger = ko.observable(SaveSettingsStep.Idle);
this.pdoPasswordTrigger = ko.observable(SaveSettingsStep.Idle);
this.contactsTypeTrigger = ko.observable(SaveSettingsStep.Idle);
this.testing = ko.observable(false);
this.testContactsSuccess = ko.observable(false);
this.testContactsError = ko.observable(false);
this.testContactsErrorMessage = ko.observable('');
this.contactsType(settingsGet('ContactsPdoType')); this.contactsType(settingsGet('ContactsPdoType'));
this.onTestContactsResponse = this.onTestContactsResponse.bind(this); this.onTestContactsResponse = this.onTestContactsResponse.bind(this);

View file

@ -39,9 +39,15 @@ class GeneralAdminSettings {
this.capaAttachmentThumbnails = CapaAdminStore.attachmentThumbnails; this.capaAttachmentThumbnails = CapaAdminStore.attachmentThumbnails;
this.capaTemplates = CapaAdminStore.templates; this.capaTemplates = CapaAdminStore.templates;
this.allowLanguagesOnSettings = ko.observable(!!settingsGet('AllowLanguagesOnSettings')); ko.addObservablesTo(this, {
allowLanguagesOnSettings: !!settingsGet('AllowLanguagesOnSettings'),
newMoveToFolder: !!settingsGet('NewMoveToFolder'),
attachmentLimitTrigger: SaveSettingsStep.Idle,
languageTrigger: SaveSettingsStep.Idle,
themeTrigger: SaveSettingsStep.Idle
});
this.weakPassword = AppAdminStore.weakPassword; this.weakPassword = AppAdminStore.weakPassword;
this.newMoveToFolder = ko.observable(!!settingsGet('NewMoveToFolder'));
this.dataFolderAccess = AppAdminStore.dataFolderAccess; this.dataFolderAccess = AppAdminStore.dataFolderAccess;
@ -67,10 +73,7 @@ class GeneralAdminSettings {
this.languageFullName = ko.computed(() => convertLangName(this.language())); this.languageFullName = ko.computed(() => convertLangName(this.language()));
this.languageAdminFullName = ko.computed(() => convertLangName(this.languageAdmin())); this.languageAdminFullName = ko.computed(() => convertLangName(this.languageAdmin()));
this.attachmentLimitTrigger = ko.observable(SaveSettingsStep.Idle);
this.languageTrigger = ko.observable(SaveSettingsStep.Idle);
this.languageAdminTrigger = ko.observable(SaveSettingsStep.Idle).extend({ throttle: 100 }); this.languageAdminTrigger = ko.observable(SaveSettingsStep.Idle).extend({ throttle: 100 });
this.themeTrigger = ko.observable(SaveSettingsStep.Idle);
} }
onBuild() { onBuild() {

View file

@ -7,13 +7,14 @@ import Remote from 'Remote/Admin/Fetch';
class LoginAdminSettings { class LoginAdminSettings {
constructor() { constructor() {
const settingsGet = rl.settings.get; const settingsGet = rl.settings.get;
this.determineUserLanguage = ko.observable(!!settingsGet('DetermineUserLanguage')); ko.addObservablesTo(this, {
this.determineUserDomain = ko.observable(!!settingsGet('DetermineUserDomain')); determineUserLanguage: !!settingsGet('DetermineUserLanguage'),
determineUserDomain: !!settingsGet('DetermineUserDomain'),
allowLanguagesOnLogin: !!settingsGet('AllowLanguagesOnLogin'),
dummy: false
});
this.defaultDomain = ko.observable(settingsGet('LoginDefaultDomain')).idleTrigger(); this.defaultDomain = ko.observable(settingsGet('LoginDefaultDomain')).idleTrigger();
this.allowLanguagesOnLogin = ko.observable(!!settingsGet('AllowLanguagesOnLogin'));
this.dummy = ko.observable(false);
} }
onBuild() { onBuild() {

View file

@ -13,8 +13,6 @@ const settingsGet = rl.settings.get;
class SecurityAdminSettings { class SecurityAdminSettings {
constructor() { constructor() {
this.useLocalProxyForExternalImages = ko.observable(!!rl.settings.get('UseLocalProxyForExternalImages'));
this.weakPassword = AppAdminStore.weakPassword; this.weakPassword = AppAdminStore.weakPassword;
this.capaOpenPGP = CapaAdminStore.openPGP; this.capaOpenPGP = CapaAdminStore.openPGP;
@ -22,35 +20,39 @@ class SecurityAdminSettings {
this.capaTwoFactorAuth = CapaAdminStore.twoFactorAuth; this.capaTwoFactorAuth = CapaAdminStore.twoFactorAuth;
this.capaTwoFactorAuthForce = CapaAdminStore.twoFactorAuthForce; this.capaTwoFactorAuthForce = CapaAdminStore.twoFactorAuthForce;
ko.addObservablesTo(this, {
useLocalProxyForExternalImages: !!rl.settings.get('UseLocalProxyForExternalImages'),
verifySslCertificate: !!settingsGet('VerifySslCertificate'),
allowSelfSigned: !!settingsGet('AllowSelfSigned'),
isTwoFactorDropperShown: false,
twoFactorDropperUser: '',
twoFactorDropperUserFocused: false,
adminLogin: settingsGet('AdminLogin'),
adminLoginError: false,
adminPassword: '',
adminPasswordNew: '',
adminPasswordNew2: '',
adminPasswordNewError: false,
adminPasswordUpdateError: false,
adminPasswordUpdateSuccess: false
});
this.capaTwoFactorAuth.subscribe(value => { this.capaTwoFactorAuth.subscribe(value => {
if (!value) { if (!value) {
this.capaTwoFactorAuthForce(false); this.capaTwoFactorAuthForce(false);
} }
}); });
this.verifySslCertificate = ko.observable(!!settingsGet('VerifySslCertificate'));
this.allowSelfSigned = ko.observable(!!settingsGet('AllowSelfSigned'));
this.verifySslCertificate.subscribe(value => { this.verifySslCertificate.subscribe(value => {
if (!value) { if (!value) {
this.allowSelfSigned(true); this.allowSelfSigned(true);
} }
}); });
this.isTwoFactorDropperShown = ko.observable(false);
this.twoFactorDropperUser = ko.observable('');
this.twoFactorDropperUser.focused = ko.observable(false);
this.adminLogin = ko.observable(settingsGet('AdminLogin'));
this.adminLoginError = ko.observable(false);
this.adminPassword = ko.observable('');
this.adminPasswordNew = ko.observable('');
this.adminPasswordNew2 = ko.observable('');
this.adminPasswordNewError = ko.observable(false);
this.adminPasswordUpdateError = ko.observable(false);
this.adminPasswordUpdateSuccess = ko.observable(false);
this.adminPassword.subscribe(() => { this.adminPassword.subscribe(() => {
this.adminPasswordUpdateError(false); this.adminPasswordUpdateError(false);
this.adminPasswordUpdateSuccess(false); this.adminPasswordUpdateSuccess(false);
@ -104,7 +106,7 @@ class SecurityAdminSettings {
this.isTwoFactorDropperShown(true); this.isTwoFactorDropperShown(true);
setTimeout(() => { setTimeout(() => {
this.twoFactorDropperUser.focused(true); this.twoFactorDropperUserFocused(true);
}, 50); }, 50);
} }
@ -167,7 +169,7 @@ class SecurityAdminSettings {
this.isTwoFactorDropperShown(false); this.isTwoFactorDropperShown(false);
this.twoFactorDropperUser(''); this.twoFactorDropperUser('');
this.twoFactorDropperUser.focused(false); this.twoFactorDropperUserFocused(false);
} }
} }

View file

@ -16,12 +16,14 @@ class FiltersUserSettings {
this.modules = FilterStore.modules; this.modules = FilterStore.modules;
this.filters = FilterStore.filters; this.filters = FilterStore.filters;
this.inited = ko.observable(false); ko.addObservablesTo(this, {
this.serverError = ko.observable(false); inited: false,
this.serverErrorDesc = ko.observable(''); serverError: false,
this.haveChanges = ko.observable(false); serverErrorDesc: '',
haveChanges: false,
this.saveErrorText = ko.observable(''); saveErrorText: ''
});
this.serverError.subscribe((value) => { this.serverError.subscribe((value) => {
if (!value) { if (!value) {
@ -37,18 +39,14 @@ class FiltersUserSettings {
this.filterForDeletion = ko.observable(null).deleteAccessHelper(); this.filterForDeletion = ko.observable(null).deleteAccessHelper();
this.filters.subscribe(() => { this.filters.subscribe(() => this.haveChanges(true));
this.haveChanges(true);
});
this.filterRaw.subscribe(() => { this.filterRaw.subscribe(() => {
this.haveChanges(true); this.haveChanges(true);
this.filterRaw.error(false); this.filterRaw.error(false);
}); });
this.haveChanges.subscribe(() => { this.haveChanges.subscribe(() => this.saveErrorText(''));
this.saveErrorText('');
});
this.filterRaw.active.subscribe(() => { this.filterRaw.active.subscribe(() => {
this.haveChanges(true); this.haveChanges(true);

View file

@ -62,7 +62,7 @@ class FoldersUserSettings {
} }
onShow() { onShow() {
FolderStore.folderList.error(''); FolderStore.folderListError('');
} }
onBuild(oDom) { onBuild(oDom) {
@ -113,7 +113,7 @@ class FoldersUserSettings {
removeFolderFromCacheList(folderToRemove.fullNameRaw); removeFolderFromCacheList(folderToRemove.fullNameRaw);
} }
} else if (0 < folderToRemove.privateMessageCountAll()) { } else if (0 < folderToRemove.privateMessageCountAll()) {
FolderStore.folderList.error(getNotification(Notification.CantDeleteNonEmptyFolder)); FolderStore.folderListError(getNotification(Notification.CantDeleteNonEmptyFolder));
} }
} }

View file

@ -47,9 +47,11 @@ class GeneralUserSettings {
this.languageFullName = ko.computed(() => convertLangName(this.language())); this.languageFullName = ko.computed(() => convertLangName(this.language()));
this.languageTrigger = ko.observable(SaveSettingsStep.Idle).extend({ throttle: 100 }); this.languageTrigger = ko.observable(SaveSettingsStep.Idle).extend({ throttle: 100 });
this.mppTrigger = ko.observable(SaveSettingsStep.Idle); ko.addObservablesTo(this, {
this.editorDefaultTypeTrigger = ko.observable(SaveSettingsStep.Idle); mppTrigger: SaveSettingsStep.Idle,
this.layoutTrigger = ko.observable(SaveSettingsStep.Idle); editorDefaultTypeTrigger: SaveSettingsStep.Idle,
layoutTrigger: SaveSettingsStep.Idle
});
this.identities = IdentityStore.identities; this.identities = IdentityStore.identities;

View file

@ -3,17 +3,19 @@ import { Capa } from 'Common/Enums';
class CapaAdminStore { class CapaAdminStore {
constructor() { constructor() {
this.additionalAccounts = ko.observable(false); ko.addObservablesTo(this, {
this.identities = ko.observable(false); additionalAccounts: false,
this.attachmentThumbnails = ko.observable(false); identities: false,
this.sieve = ko.observable(false); attachmentThumbnails: false,
this.filters = ko.observable(false); sieve: false,
this.themes = ko.observable(true); filters: false,
this.userBackground = ko.observable(false); themes: true,
this.openPGP = ko.observable(false); userBackground: false,
this.twoFactorAuth = ko.observable(false); openPGP: false,
this.twoFactorAuthForce = ko.observable(false); twoFactorAuth: false,
this.templates = ko.observable(false); twoFactorAuthForce: false,
templates: false
});
} }
populate() { populate() {

View file

@ -1,15 +0,0 @@
import ko from 'ko';
class LicenseAdminStore {
constructor() {
this.licensing = ko.observable(false);
this.licensingProcess = ko.observable(false);
this.licenseValid = ko.observable(false);
this.licenseExpired = ko.observable(0);
this.licenseError = ko.observable('');
this.licenseTrigger = ko.observable(false);
}
}
export default new LicenseAdminStore();

View file

@ -5,8 +5,10 @@ class PackageAdminStore {
this.packages = ko.observableArray([]); this.packages = ko.observableArray([]);
this.packages.loading = ko.observable(false).extend({ throttle: 100 }); this.packages.loading = ko.observable(false).extend({ throttle: 100 });
this.packagesReal = ko.observable(true); ko.addObservablesTo(this, {
this.packagesMainUpdatable = ko.observable(true); packagesReal: true,
packagesMainUpdatable: true
});
} }
} }

View file

@ -7,13 +7,11 @@ class LanguageStore {
this.language = ko this.language = ko
.observable('') .observable('')
.extend({ limitedList: this.languages }) .extend({ limitedList: this.languages, reversible: true });
.extend({ reversible: true });
this.languageAdmin = ko this.languageAdmin = ko
.observable('') .observable('')
.extend({ limitedList: this.languagesAdmin }) .extend({ limitedList: this.languagesAdmin, reversible: true });
.extend({ reversible: true });
this.userLanguage = ko.observable(''); this.userLanguage = ko.observable('');
this.userLanguageAdmin = ko.observable(''); this.userLanguageAdmin = ko.observable('');

View file

@ -2,10 +2,11 @@ import ko from 'ko';
class AccountUserStore { class AccountUserStore {
constructor() { constructor() {
this.email = ko.observable(''); ko.addObservablesTo(this, {
this.parentEmail = ko.observable(''); email: '',
parentEmail: '',
this.signature = ko.observable(''); signature: ''
});
this.accounts = ko.observableArray([]); this.accounts = ko.observableArray([]);
this.accounts.loading = ko.observable(false).extend({ throttle: 100 }); this.accounts.loading = ko.observable(false).extend({ throttle: 100 });

View file

@ -7,9 +7,21 @@ const Settings = rl.settings;
class AppUserStore { class AppUserStore {
constructor() { constructor() {
this.currentAudio = ko.observable(''); ko.addObservablesTo(this, {
currentAudio: '',
this.focusedState = ko.observable(Focused.None); focusedState: Focused.None,
projectHash: '',
threadsAllowed: false,
composeInEdit: false,
contactsAutosave: false,
useLocalProxyForExternalImages: false,
contactsIsAllowed: false
});
const isMobile = Settings.app('mobile'); const isMobile = Settings.app('mobile');
@ -38,16 +50,6 @@ class AppUserStore {
} }
}); });
this.projectHash = ko.observable('');
this.threadsAllowed = ko.observable(false);
this.composeInEdit = ko.observable(false);
this.contactsAutosave = ko.observable(false);
this.useLocalProxyForExternalImages = ko.observable(false);
this.contactsIsAllowed = ko.observable(false);
this.attachmentsActions = ko.observableArray([]); this.attachmentsActions = ko.observableArray([]);
this.devEmail = ''; this.devEmail = '';

View file

@ -9,11 +9,13 @@ class ContactUserStore {
this.contacts.exportingVcf = ko.observable(false).extend({ throttle: 200 }); this.contacts.exportingVcf = ko.observable(false).extend({ throttle: 200 });
this.contacts.exportingCsv = ko.observable(false).extend({ throttle: 200 }); this.contacts.exportingCsv = ko.observable(false).extend({ throttle: 200 });
this.allowContactsSync = ko.observable(false); ko.addObservablesTo(this, {
this.enableContactsSync = ko.observable(false); allowContactsSync: false,
this.contactsSyncUrl = ko.observable(''); enableContactsSync: false,
this.contactsSyncUser = ko.observable(''); contactsSyncUrl: '',
this.contactsSyncPass = ko.observable(''); contactsSyncUser: '',
contactsSyncPass: ''
});
} }
populate() { populate() {

View file

@ -2,15 +2,16 @@ import ko from 'ko';
class FilterUserStore { class FilterUserStore {
constructor() { constructor() {
this.capa = ko.observable(''); ko.addObservablesTo(this, {
this.modules = ko.observable({}); capa: '',
modules: {},
raw: ''
});
this.filters = ko.observableArray([]); this.filters = ko.observableArray([]);
this.filters.loading = ko.observable(false).extend({ throttle: 200 }); this.filters.loading = ko.observable(false).extend({ throttle: 200 });
this.filters.saving = ko.observable(false).extend({ throttle: 200 }); this.filters.saving = ko.observable(false).extend({ throttle: 200 });
this.raw = ko.observable('');
} }
} }

View file

@ -7,26 +7,29 @@ import { getFolderInboxName, getFolderFromCacheList } from 'Common/Cache';
class FolderUserStore { class FolderUserStore {
constructor() { constructor() {
this.displaySpecSetting = ko.observable(true); ko.addObservablesTo(this, {
displaySpecSetting: true,
this.sentFolder = ko.observable(''); sentFolder: '',
this.draftFolder = ko.observable(''); draftFolder: '',
this.spamFolder = ko.observable(''); spamFolder: '',
this.trashFolder = ko.observable(''); trashFolder: '',
this.archiveFolder = ko.observable(''); archiveFolder: '',
folderListOptimized: false,
folderListError: '',
foldersLoading: false,
foldersCreating: false,
foldersDeleting: false,
foldersRenaming: false,
foldersInboxUnreadCount: 0
});
this.namespace = ''; this.namespace = '';
this.folderList = ko.observableArray([]); this.folderList = ko.observableArray([]);
this.folderList.optimized = ko.observable(false);
this.folderList.error = ko.observable('');
this.foldersLoading = ko.observable(false);
this.foldersCreating = ko.observable(false);
this.foldersDeleting = ko.observable(false);
this.foldersRenaming = ko.observable(false);
this.foldersInboxUnreadCount = ko.observable(0);
this.currentFolder = ko.observable(null).extend({ toggleSubscribeProperty: [this, 'selected'] }); this.currentFolder = ko.observable(null).extend({ toggleSubscribeProperty: [this, 'selected'] });

View file

@ -59,44 +59,47 @@ class MessageUserStore {
this.messageList = ko.observableArray([]).extend({ rateLimit: 0 }); this.messageList = ko.observableArray([]).extend({ rateLimit: 0 });
this.messageListCount = ko.observable(0); ko.addObservablesTo(this, {
this.messageListSearch = ko.observable(''); messageListCount: 0,
this.messageListThreadUid = ko.observable(''); messageListSearch: '',
this.messageListPage = ko.observable(1); messageListThreadUid: '',
this.messageListPageBeforeThread = ko.observable(1); messageListPage: 1,
this.messageListError = ko.observable(''); messageListPageBeforeThread: 1,
messageListError: '',
this.messageListEndFolder = ko.observable(''); messageListEndFolder: '',
this.messageListEndSearch = ko.observable(''); messageListEndSearch: '',
this.messageListEndThreadUid = ko.observable(''); messageListEndThreadUid: '',
this.messageListEndPage = ko.observable(1); messageListEndPage: 1,
messageListLoading: false,
messageListIsNotCompleted: false,
selectorMessageSelected: null,
selectorMessageFocused: null,
// message viewer
message: null,
messageViewTrigger: false,
messageError: '',
messageCurrentLoading: false,
messageFullScreenMode: false,
messagesBodiesDom: null,
messageActiveDom: null
});
this.messageListLoading = ko.observable(false);
this.messageListIsNotCompleted = ko.observable(false);
this.messageListCompleteLoadingThrottle = ko.observable(false).extend({ throttle: 200 }); this.messageListCompleteLoadingThrottle = ko.observable(false).extend({ throttle: 200 });
this.messageListCompleteLoadingThrottleForAnimation = ko.observable(false).extend({ specialThrottle: 700 }); this.messageListCompleteLoadingThrottleForAnimation = ko.observable(false).extend({ specialThrottle: 700 });
this.messageListDisableAutoSelect = ko.observable(false).extend({ falseTimeout: 500 }); this.messageListDisableAutoSelect = ko.observable(false).extend({ falseTimeout: 500 });
this.selectorMessageSelected = ko.observable(null);
this.selectorMessageFocused = ko.observable(null);
// message viewer
this.message = ko.observable(null);
this.message.viewTrigger = ko.observable(false);
this.messageError = ko.observable('');
this.messageCurrentLoading = ko.observable(false);
this.messageLoadingThrottle = ko.observable(false).extend({ throttle: 50 }); this.messageLoadingThrottle = ko.observable(false).extend({ throttle: 50 });
this.messageFullScreenMode = ko.observable(false);
this.messagesBodiesDom = ko.observable(null);
this.messageActiveDom = ko.observable(null);
this.messageLoading = ko.computed(() => this.messageCurrentLoading()); this.messageLoading = ko.computed(() => this.messageCurrentLoading());
this.messageListEndHash = ko.computed( this.messageListEndHash = ko.computed(

View file

@ -23,13 +23,15 @@ class SettingsUserStore {
this.messagesPerPage = ko.observable(20).extend({ limitedList: MESSAGES_PER_PAGE_VALUES }); this.messagesPerPage = ko.observable(20).extend({ limitedList: MESSAGES_PER_PAGE_VALUES });
this.showImages = ko.observable(false); ko.addObservablesTo(this, {
this.useCheckboxesInList = ko.observable(true); showImages: false,
this.allowDraftAutosave = ko.observable(true); useCheckboxesInList: true,
this.useThreads = ko.observable(false); allowDraftAutosave: true,
this.replySameFolder = ko.observable(false); useThreads: false,
replySameFolder: false,
this.autoLogout = ko.observable(30); autoLogout: 30
});
this.usePreviewPane = ko.computed(() => Layout.NoPreview !== this.layout()); this.usePreviewPane = ko.computed(() => Layout.NoPreview !== this.layout());

View file

@ -1,5 +1,3 @@
import ko from 'ko';
import { StorageResultType, Notification } from 'Common/Enums'; import { StorageResultType, Notification } from 'Common/Enums';
import { getNotification } from 'Common/Translator'; import { getNotification } from 'Common/Translator';
@ -8,14 +6,7 @@ import Remote from 'Remote/User/Fetch';
import { popup, command } from 'Knoin/Knoin'; import { popup, command } from 'Knoin/Knoin';
import { AbstractViewNext } from 'Knoin/AbstractViewNext'; import { AbstractViewNext } from 'Knoin/AbstractViewNext';
ko.observable.fn.validateEmail = function() { const reEmail = /^[^@\s]+@[^@\s]+$/;
this.hasError = ko.observable(false);
this.subscribe(value => this.hasError(value && !/^[^@\s]+@[^@\s]+$/.test(value)));
this.valueHasMutated();
return this;
};
@popup({ @popup({
name: 'View/Popup/Identity', name: 'View/Popup/Identity',
@ -29,10 +20,20 @@ class IdentityPopupView extends AbstractViewNext {
this.addObservables({ this.addObservables({
edit: false, edit: false,
owner: false, owner: false,
email: '',
emailFocused: false, emailFocused: false,
emailHasError: false,
name: '', name: '',
replyTo: '',
replyToFocused: false, replyToFocused: false,
replyToHasError: false,
bcc: '',
bccFocused: false, bccFocused: false,
bccHasError: false,
signature: '', signature: '',
signatureInsertBefore: false, signatureInsertBefore: false,
@ -44,21 +45,26 @@ class IdentityPopupView extends AbstractViewNext {
submitError: '' submitError: ''
}); });
this.email = ko.observable('').validateEmail(); this.addSubscribables({
this.replyTo = ko.observable('').validateEmail(); email: value => this.emailHasError(value && !reEmail.test(value)),
this.bcc = ko.observable('').validateEmail(); replyTo: value => {
this.replyToHasError(value && !reEmail.test(value));
this.bcc.subscribe((value) => {
if (false === this.showBcc() && value.length) {
this.showBcc(true);
}
});
this.replyTo.subscribe((value) => {
if (false === this.showReplyTo() && value.length) { if (false === this.showReplyTo() && value.length) {
this.showReplyTo(true); this.showReplyTo(true);
} }
},
bcc: value => {
this.bccHasError(value && !reEmail.test(value));
if (false === this.showBcc() && value.length) {
this.showBcc(true);
}
}
}); });
/*
this.email.valueHasMutated();
this.replyTo.valueHasMutated();
this.bcc.valueHasMutated();
*/
} }
@command((self) => !self.submitRequest()) @command((self) => !self.submitRequest())
@ -67,11 +73,11 @@ class IdentityPopupView extends AbstractViewNext {
this.signature.__fetchEditorValue(); this.signature.__fetchEditorValue();
} }
if (!this.email.hasError()) { if (!this.emailHasError()) {
this.email.hasError(!this.email().trim()); this.emailHasError(!this.email().trim());
} }
if (this.email.hasError()) { if (this.emailHasError()) {
if (!this.owner()) { if (!this.owner()) {
this.emailFocused(true); this.emailFocused(true);
} }
@ -79,12 +85,12 @@ class IdentityPopupView extends AbstractViewNext {
return false; return false;
} }
if (this.replyTo.hasError()) { if (this.replyToHasError()) {
this.replyToFocused(true); this.replyToFocused(true);
return false; return false;
} }
if (this.bcc.hasError()) { if (this.bccHasError()) {
this.bccFocused(true); this.bccFocused(true);
return false; return false;
} }
@ -129,9 +135,9 @@ class IdentityPopupView extends AbstractViewNext {
this.signature(''); this.signature('');
this.signatureInsertBefore(false); this.signatureInsertBefore(false);
this.email.hasError(false); this.emailHasError(false);
this.replyTo.hasError(false); this.replyToHasError(false);
this.bcc.hasError(false); this.bccHasError(false);
this.showBcc(false); this.showBcc(false);
this.showReplyTo(false); this.showReplyTo(false);

View file

@ -1,5 +1,3 @@
import ko from 'ko';
import AppStore from 'Stores/User/App'; import AppStore from 'Stores/User/App';
import AccountStore from 'Stores/User/Account'; import AccountStore from 'Stores/User/Account';
import MessageStore from 'Stores/User/Message'; import MessageStore from 'Stores/User/Message';
@ -32,8 +30,10 @@ class AbstractSystemDropDownUserView extends AbstractViewNext {
this.accounts = AccountStore.accounts; this.accounts = AccountStore.accounts;
this.accountsUnreadCount = AccountStore.accountsUnreadCount; this.accountsUnreadCount = AccountStore.accountsUnreadCount;
this.accountMenuDropdownTrigger = ko.observable(false); this.addObservables({
this.capaAdditionalAccounts = ko.observable(Settings.capa(Capa.AdditionalAccounts)); accountMenuDropdownTrigger: false,
capaAdditionalAccounts: Settings.capa(Capa.AdditionalAccounts)
});
this.addAccountClick = this.addAccountClick.bind(this); this.addAccountClick = this.addAccountClick.bind(this);

View file

@ -264,7 +264,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
fullScreenMode: value => $htmlCL.toggle('rl-message-fullscreen', value) fullScreenMode: value => $htmlCL.toggle('rl-message-fullscreen', value)
}); });
this.message.viewTrigger.subscribe(() => { MessageStore.messageViewTrigger.subscribe(() => {
const message = this.message(); const message = this.message();
message ? this.viewIsFlagged(message.isFlagged()) : this.viewIsFlagged(false); message ? this.viewIsFlagged(message.isFlagged()) : this.viewIsFlagged(false);
}); });

View file

@ -30,7 +30,7 @@
<div class="controls"> <div class="controls">
<div class="input-append"> <div class="input-append">
<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" <input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="textInput: twoFactorDropperUser, hasfocus: twoFactorDropperUser.focused" placeholder="Email"/> data-bind="textInput: twoFactorDropperUser, hasfocus: twoFactorDropperUserFocused" placeholder="Email"/>
<button class="btn"> <button class="btn">
<i class="icon-spinner animated" data-bind="visible: true"></i> <i class="icon-spinner animated" data-bind="visible: true"></i>
<span data-bind="visible: false">OK</span> <span data-bind="visible: false">OK</span>

View file

@ -14,7 +14,7 @@
<span data-bind="text: submitError"></span> <span data-bind="text: submitError"></span>
</div> </div>
<br /> <br />
<div class="control-group" data-bind="visible: !owner(), css: {'error': email.hasError}"> <div class="control-group" data-bind="visible: !owner(), css: {'error': emailHasError}">
<label class="i18n control-label" data-i18n="POPUPS_IDENTITY/LABEL_EMAIL"></label> <label class="i18n control-label" data-i18n="POPUPS_IDENTITY/LABEL_EMAIL"></label>
<div class="controls"> <div class="controls">
<input type="email" class="inputEmail input-xlarge" autofocus="" <input type="email" class="inputEmail input-xlarge" autofocus=""
@ -36,7 +36,7 @@
data-bind="value: name, onEnter: addOrEditIdentityCommand" /> data-bind="value: name, onEnter: addOrEditIdentityCommand" />
</div> </div>
</div> </div>
<div class="control-group" data-bind="visible: showReplyTo, css: {'error': replyTo.hasError}"> <div class="control-group" data-bind="visible: showReplyTo, css: {'error': replyToHasError}">
<label class="i18n control-label" data-i18n="POPUPS_IDENTITY/LABEL_REPLY_TO"></label> <label class="i18n control-label" data-i18n="POPUPS_IDENTITY/LABEL_REPLY_TO"></label>
<div class="controls"> <div class="controls">
<input type="text" class="inputReplyTo input-xlarge" <input type="text" class="inputReplyTo input-xlarge"
@ -44,7 +44,7 @@
data-bind="value: replyTo, onEnter: addOrEditIdentityCommand, hasfocus: replyToFocused" /> data-bind="value: replyTo, onEnter: addOrEditIdentityCommand, hasfocus: replyToFocused" />
</div> </div>
</div> </div>
<div class="control-group" data-bind="visible: showBcc, css: {'error': bcc.hasError}"> <div class="control-group" data-bind="visible: showBcc, css: {'error': bccHasError}">
<label class="i18n control-label" data-i18n="POPUPS_IDENTITY/LABEL_BCC"></label> <label class="i18n control-label" data-i18n="POPUPS_IDENTITY/LABEL_BCC"></label>
<div class="controls"> <div class="controls">
<input type="text" class="inputBcc input-xlarge" <input type="text" class="inputBcc input-xlarge"

View file

@ -18,14 +18,14 @@
&nbsp;&nbsp; &nbsp;&nbsp;
<span class="i18n" data-i18n="SETTINGS_FOLDERS/BUTTON_SYSTEM"></span> <span class="i18n" data-i18n="SETTINGS_FOLDERS/BUTTON_SYSTEM"></span>
</a> </a>
<div class="alert folders-list-error" data-bind="visible: folderList.optimized"> <div class="alert folders-list-error" data-bind="visible: folderListOptimized">
<b class="i18n" data-i18n="SETTINGS_FOLDERS/TO_MANY_FOLDERS_DESC_1"></b> <b class="i18n" data-i18n="SETTINGS_FOLDERS/TO_MANY_FOLDERS_DESC_1"></b>
<br /> <br />
<span class="i18n" data-i18n="SETTINGS_FOLDERS/TO_MANY_FOLDERS_DESC_2"></span> <span class="i18n" data-i18n="SETTINGS_FOLDERS/TO_MANY_FOLDERS_DESC_2"></span>
</div> </div>
<div class="alert folders-list-error" data-bind="visible: '' !== folderList.error()"> <div class="alert folders-list-error" data-bind="visible: '' !== folderListError()">
<button type="button" class="close" data-bind="click: function () { folderList.error(''); }">&times;</button> <button type="button" class="close" data-bind="click: function () { folderListError(''); }">&times;</button>
<span data-bind="text: folderList.error"></span> <span data-bind="text: folderListError"></span>
</div> </div>
<table class="table table-hover list-table" data-bind="i18nUpdate: folderList" style="margin-top: 40px"> <table class="table table-hover list-table" data-bind="i18nUpdate: folderList" style="margin-top: 40px">
<colgroup> <colgroup>