Replaced tricky delegateRunOnDestroy() with koArrayWithDestroy()

koArrayWithDestroy creates ko.observableArray and calls the desired onDestroy() on entry delete.
This commit is contained in:
the-djmaze 2022-02-17 12:48:57 +01:00
parent f4bc796fb1
commit e324e2f6b6
13 changed files with 32 additions and 55 deletions

View file

@ -1,7 +1,7 @@
import 'External/User/ko'; import 'External/User/ko';
import { isArray, arrayLength, pString, forEachObjectValue } from 'Common/Utils'; import { isArray, arrayLength, pString, forEachObjectValue } from 'Common/Utils';
import { delegateRunOnDestroy, mailToHelper, setLayoutResizer } from 'Common/UtilsUser'; import { mailToHelper, setLayoutResizer } from 'Common/UtilsUser';
import { import {
Notification, Notification,
@ -333,8 +333,6 @@ class AppUser extends AbstractApp {
if (isArray(accounts)) { if (isArray(accounts)) {
// AccountUserStore.accounts.forEach(oAccount => counts[oAccount.email] = oAccount.count()); // AccountUserStore.accounts.forEach(oAccount => counts[oAccount.email] = oAccount.count());
delegateRunOnDestroy(AccountUserStore.accounts());
AccountUserStore.accounts( AccountUserStore.accounts(
accounts.map( accounts.map(
sValue => new AccountModel(sValue/*, counts[sValue]*/) sValue => new AccountModel(sValue/*, counts[sValue]*/)
@ -345,8 +343,6 @@ class AppUser extends AbstractApp {
} }
if (isArray(oData.Result.Identities)) { if (isArray(oData.Result.Identities)) {
delegateRunOnDestroy(IdentityUserStore());
IdentityUserStore( IdentityUserStore(
oData.Result.Identities.map(identityData => { oData.Result.Identities.map(identityData => {
const identity = new IdentityModel( const identity = new IdentityModel(

View file

@ -102,17 +102,6 @@ folderListOptionsBuilder = (
return aResult; return aResult;
}, },
/**
* Call the Model/CollectionModel onDestroy() to clear knockout functions/objects
* @param {Object|Array} objectOrObjects
* @returns {void}
*/
delegateRunOnDestroy = (objectOrObjects) => {
objectOrObjects && (isArray(objectOrObjects) ? objectOrObjects : [objectOrObjects]).forEach(
obj => obj.onDestroy && obj.onDestroy()
);
},
/** /**
* @returns {function} * @returns {function}
*/ */

13
dev/External/ko.js vendored
View file

@ -21,7 +21,18 @@ export const
addSubscribablesTo = (target, subscribables) => addSubscribablesTo = (target, subscribables) =>
forEachObjectEntry(subscribables, (key, fn) => target[key].subscribe(fn)), forEachObjectEntry(subscribables, (key, fn) => target[key].subscribe(fn)),
dispose = disposable => disposable && isFunction(disposable.dispose) && disposable.dispose(); dispose = disposable => disposable && isFunction(disposable.dispose) && disposable.dispose(),
// With this we don't need delegateRunOnDestroy
koArrayWithDestroy = data => {
data = ko.observableArray(data);
data.subscribe(changes =>
changes.forEach(item =>
'deleted' === item.status && null == item.moved && item.value.onDestroy && item.value.onDestroy()
)
, data, 'arrayChange');
return data;
};
ko.bindingHandlers.tooltipErrorTip = { ko.bindingHandlers.tooltipErrorTip = {
init: (element, fValueAccessor) => { init: (element, fValueAccessor) => {

View file

@ -1,7 +1,6 @@
import ko from 'ko'; import { koArrayWithDestroy } from 'External/ko';
import { arrayLength, pString } from 'Common/Utils'; import { arrayLength, pString } from 'Common/Utils';
import { delegateRunOnDestroy } from 'Common/UtilsUser';
import { i18n } from 'Common/Translator'; import { i18n } from 'Common/Translator';
import { getFolderFromCacheList } from 'Common/Cache'; import { getFolderFromCacheList } from 'Common/Cache';
@ -65,7 +64,7 @@ export class FilterModel extends AbstractModel {
actionType: FilterAction.MoveTo actionType: FilterAction.MoveTo
}); });
this.conditions = ko.observableArray(); this.conditions = koArrayWithDestroy();
const fGetRealFolderName = (folderFullName) => { const fGetRealFolderName = (folderFullName) => {
const folder = getFolderFromCacheList(folderFullName); const folder = getFolderFromCacheList(folderFullName);
@ -211,7 +210,6 @@ export class FilterModel extends AbstractModel {
removeCondition(oConditionToDelete) { removeCondition(oConditionToDelete) {
this.conditions.remove(oConditionToDelete); this.conditions.remove(oConditionToDelete);
delegateRunOnDestroy(oConditionToDelete);
} }
setRecipients() { setRecipients() {

View file

@ -1,8 +1,7 @@
import ko from 'ko';
import { AbstractModel } from 'Knoin/AbstractModel'; import { AbstractModel } from 'Knoin/AbstractModel';
import { FilterModel } from 'Model/Filter'; import { FilterModel } from 'Model/Filter';
import { arrayLength, pString, b64EncodeJSON } from 'Common/Utils'; import { arrayLength, pString, b64EncodeJSON } from 'Common/Utils';
import { koArrayWithDestroy } from 'External/ko';
const SIEVE_FILE_NAME = 'rainloop.user'; const SIEVE_FILE_NAME = 'rainloop.user';
@ -275,7 +274,7 @@ export class SieveScriptModel extends AbstractModel
hasChanges: false hasChanges: false
}); });
this.filters = ko.observableArray(); this.filters = koArrayWithDestroy();
// this.saving = ko.observable(false).extend({ debounce: 200 }); // this.saving = ko.observable(false).extend({ debounce: 200 });
this.addSubscribables({ this.addSubscribables({

View file

@ -1,9 +1,6 @@
import ko from 'ko';
import { getNotification } from 'Common/Translator'; import { getNotification } from 'Common/Translator';
import { forEachObjectValue } from 'Common/Utils'; import { forEachObjectValue } from 'Common/Utils';
import { addObservablesTo } from 'External/ko'; import { addObservablesTo } from 'External/ko';
import { delegateRunOnDestroy } from 'Common/UtilsUser';
import { SieveUserStore } from 'Stores/User/Sieve'; import { SieveUserStore } from 'Stores/User/Sieve';
import Remote from 'Remote/User/Fetch'; import Remote from 'Remote/User/Fetch';
@ -76,7 +73,6 @@ export class FiltersUserSettings /*extends AbstractViewSettings*/ {
this.setError((data && data.ErrorMessageAdditional) || getNotification(iError)); this.setError((data && data.ErrorMessageAdditional) || getNotification(iError));
} else { } else {
this.scripts.remove(script); this.scripts.remove(script);
delegateRunOnDestroy(script);
} }
}, },
{name:script.name()} {name:script.name()}

View file

@ -1,8 +1,7 @@
import ko from 'ko'; import { addObservablesTo, koArrayWithDestroy } from 'External/ko';
import { addObservablesTo } from 'External/ko';
export const AccountUserStore = { export const AccountUserStore = {
accounts: ko.observableArray(), accounts: koArrayWithDestroy(),
loading: ko.observable(false).extend({ debounce: 100 }), loading: ko.observable(false).extend({ debounce: 100 }),
getEmailAddresses: () => AccountUserStore.accounts.map(item => item.email) getEmailAddresses: () => AccountUserStore.accounts.map(item => item.email)

View file

@ -1,10 +1,10 @@
import ko from 'ko'; import ko from 'ko';
import { SettingsGet } from 'Common/Globals'; import { SettingsGet } from 'Common/Globals';
import { pInt } from 'Common/Utils'; import { pInt } from 'Common/Utils';
import { addObservablesTo } from 'External/ko'; import { addObservablesTo, koArrayWithDestroy } from 'External/ko';
import Remote from 'Remote/User/Fetch'; import Remote from 'Remote/User/Fetch';
export const ContactUserStore = ko.observableArray(); export const ContactUserStore = koArrayWithDestroy();
ContactUserStore.loading = ko.observable(false).extend({ debounce: 200 }); ContactUserStore.loading = ko.observable(false).extend({ debounce: 200 });
ContactUserStore.importing = ko.observable(false).extend({ debounce: 200 }); ContactUserStore.importing = ko.observable(false).extend({ debounce: 200 });

View file

@ -1,6 +1,6 @@
import ko from 'ko'; import { koArrayWithDestroy } from 'External/ko';
export const IdentityUserStore = ko.observableArray(); export const IdentityUserStore = koArrayWithDestroy();
IdentityUserStore.getIDS = () => IdentityUserStore.map(item => (item ? item.id() : null)) IdentityUserStore.getIDS = () => IdentityUserStore.map(item => (item ? item.id() : null))
.filter(value => null !== value); .filter(value => null !== value);

View file

@ -1,8 +1,8 @@
import ko from 'ko'; import { koArrayWithDestroy } from 'External/ko';
export const SieveUserStore = { export const SieveUserStore = {
// capabilities // capabilities
capa: ko.observableArray(), capa: ko.observableArray(),
// Sieve scripts SieveScriptModel // Sieve scripts SieveScriptModel
scripts: ko.observableArray() scripts: koArrayWithDestroy()
} }

View file

@ -13,8 +13,9 @@ import {
} from 'Common/EnumsUser'; } from 'Common/EnumsUser';
import { inFocus, pInt, isArray, arrayLength, forEachObjectEntry } from 'Common/Utils'; import { inFocus, pInt, isArray, arrayLength, forEachObjectEntry } from 'Common/Utils';
import { delegateRunOnDestroy, initFullscreen } from 'Common/UtilsUser'; import { initFullscreen } from 'Common/UtilsUser';
import { encodeHtml, HtmlEditor, htmlToPlain } from 'Common/Html'; import { encodeHtml, HtmlEditor, htmlToPlain } from 'Common/Html';
import { koArrayWithDestroy } from 'External/ko';
import { UNUSED_OPTION_VALUE } from 'Common/Consts'; import { UNUSED_OPTION_VALUE } from 'Common/Consts';
import { serverRequest } from 'Common/Links'; import { serverRequest } from 'Common/Links';
@ -237,7 +238,7 @@ class ComposePopupView extends AbstractViewPopup {
this.bcc.focused = ko.observable(false); this.bcc.focused = ko.observable(false);
this.bcc.focused.subscribe(value => value && (this.sLastFocusedField = 'bcc')); this.bcc.focused.subscribe(value => value && (this.sLastFocusedField = 'bcc'));
this.attachments = ko.observableArray(); this.attachments = koArrayWithDestroy();
this.dragAndDropOver = ko.observable(false).extend({ debounce: 1 }); this.dragAndDropOver = ko.observable(false).extend({ debounce: 1 });
this.dragAndDropVisible = ko.observable(false).extend({ debounce: 1 }); this.dragAndDropVisible = ko.observable(false).extend({ debounce: 1 });
@ -1380,7 +1381,6 @@ class ComposePopupView extends AbstractViewPopup {
const attachment = this.getAttachmentById(id); const attachment = this.getAttachmentById(id);
if (attachment) { if (attachment) {
this.attachments.remove(attachment); this.attachments.remove(attachment);
delegateRunOnDestroy(attachment);
oJua && oJua.cancel(id); oJua && oJua.cancel(id);
} }
}; };
@ -1535,7 +1535,6 @@ class ComposePopupView extends AbstractViewPopup {
this.pgpSign(false); this.pgpSign(false);
this.pgpEncrypt(false); this.pgpEncrypt(false);
delegateRunOnDestroy(this.attachments());
this.attachments([]); this.attachments([]);
this.dragAndDropOver(false); this.dragAndDropOver(false);

View file

@ -1,4 +1,4 @@
import ko from 'ko'; import { koArrayWithDestroy } from 'External/ko';
import { import {
SaveSettingsStep, SaveSettingsStep,
@ -8,7 +8,7 @@ import {
import { ComposeType } from 'Common/EnumsUser'; import { ComposeType } from 'Common/EnumsUser';
import { arrayLength, pInt } from 'Common/Utils'; import { arrayLength, pInt } from 'Common/Utils';
import { download, delegateRunOnDestroy, computedPaginatorHelper, showMessageComposer } from 'Common/UtilsUser'; import { download, computedPaginatorHelper, showMessageComposer } from 'Common/UtilsUser';
import { Selector } from 'Common/Selector'; import { Selector } from 'Common/Selector';
import { serverRequestRaw, serverRequest } from 'Common/Links'; import { serverRequestRaw, serverRequest } from 'Common/Links';
@ -66,7 +66,7 @@ class ContactsPopupView extends AbstractViewPopup {
this.contacts = ContactUserStore; this.contacts = ContactUserStore;
this.viewProperties = ko.observableArray(); this.viewProperties = koArrayWithDestroy();
this.useCheckboxesInList = SettingsUserStore.useCheckboxesInList; this.useCheckboxesInList = SettingsUserStore.useCheckboxesInList;
@ -346,10 +346,7 @@ class ContactsPopupView extends AbstractViewPopup {
} }
setTimeout(() => { setTimeout(() => {
contacts.forEach(contact => { contacts.forEach(contact => ContactUserStore.remove(contact));
ContactUserStore.remove(contact);
delegateRunOnDestroy(contact);
});
}, 500); }, 500);
} }
} }
@ -373,7 +370,6 @@ class ContactsPopupView extends AbstractViewPopup {
removeProperty(oProp) { removeProperty(oProp) {
this.viewProperties.remove(oProp); this.viewProperties.remove(oProp);
delegateRunOnDestroy(oProp);
} }
/** /**
@ -397,7 +393,6 @@ class ContactsPopupView extends AbstractViewPopup {
this.viewID(id); this.viewID(id);
// delegateRunOnDestroy(this.viewProperties());
// this.viewProperties([]); // this.viewProperties([]);
this.viewProperties(contact.properties); this.viewProperties(contact.properties);
@ -436,7 +431,6 @@ class ContactsPopupView extends AbstractViewPopup {
this.contactsCount(count); this.contactsCount(count);
delegateRunOnDestroy(ContactUserStore());
ContactUserStore(list); ContactUserStore(list);
ContactUserStore.loading(false); ContactUserStore.loading(false);
@ -516,7 +510,6 @@ class ContactsPopupView extends AbstractViewPopup {
this.search(''); this.search('');
this.contactsCount(0); this.contactsCount(0);
delegateRunOnDestroy(ContactUserStore());
ContactUserStore([]); ContactUserStore([]);
this.sLastComposeFocusedField = ''; this.sLastComposeFocusedField = '';

View file

@ -2,7 +2,6 @@ import ko from 'ko';
import { getNotification, i18nToNodes } from 'Common/Translator'; import { getNotification, i18nToNodes } from 'Common/Translator';
import { addObservablesTo } from 'External/ko'; import { addObservablesTo } from 'External/ko';
import { delegateRunOnDestroy } from 'Common/UtilsUser';
import Remote from 'Remote/User/Fetch'; import Remote from 'Remote/User/Fetch';
import { FilterModel } from 'Model/Filter'; import { FilterModel } from 'Model/Filter';
@ -78,7 +77,6 @@ class SieveScriptPopupView extends AbstractViewPopup {
deleteFilter(filter) { deleteFilter(filter) {
this.script().filters.remove(filter); this.script().filters.remove(filter);
delegateRunOnDestroy(filter);
} }
addFilter() { addFilter() {
@ -103,7 +101,6 @@ class SieveScriptPopupView extends AbstractViewPopup {
filters = script.filters(), filters = script.filters(),
index = filters.indexOf(filter); index = filters.indexOf(filter);
if (-1 < index) { if (-1 < index) {
delegateRunOnDestroy(filters[index]);
filters[index] = clonedFilter; filters[index] = clonedFilter;
script.filters(filters); script.filters(filters);
} }