Revamp mailbox management so that reloading of the whole folder tree is not needed

This commit is contained in:
djmaze 2021-12-08 11:06:42 +01:00
parent 5fdc38a40d
commit 2719f08e26
8 changed files with 67 additions and 60 deletions

View file

@ -346,26 +346,6 @@ class AppUser extends AbstractApp {
return false; return false;
} }
/**
* @param {Function=} callback = null
*/
foldersReload(callback = null) {
Remote.foldersReload(callback);
}
foldersPromisesActionHelper(promise, errorDefCode) {
Remote.abort('Folders')
.fastResolve(true)
.then(() => promise)
.then(
() => Remote.foldersReloadWithTimeout(),
error => {
FolderUserStore.folderListError(getNotification(error.code, '', errorDefCode) + '.\n' + error.message);
Remote.foldersReloadWithTimeout();
}
);
}
reloadOpenPgpKeys() { reloadOpenPgpKeys() {
if (PgpUserStore.capaOpenPGP()) { if (PgpUserStore.capaOpenPGP()) {
const keys = [], const keys = [],
@ -720,7 +700,7 @@ class AppUser extends AbstractApp {
SettingsUserStore.init(); SettingsUserStore.init();
ContactUserStore.init(); ContactUserStore.init();
this.foldersReload(value => { Remote.foldersReload(value => {
try { try {
if (value) { if (value) {
startScreens([ startScreens([
@ -738,9 +718,6 @@ class AppUser extends AbstractApp {
this.folderInformationMultiply(); this.folderInformationMultiply();
}, refreshFolders); }, refreshFolders);
// Every 15 minutes
setInterval(()=>this.foldersReload(), 900000);
ContactUserStore.init(); ContactUserStore.init();
this.accountsAndIdentities(); this.accountsAndIdentities();

View file

@ -20,6 +20,8 @@ import { i18n, trigger as translatorTrigger } from 'Common/Translator';
import { AbstractModel } from 'Knoin/AbstractModel'; import { AbstractModel } from 'Knoin/AbstractModel';
//import { mailBox } from 'Common/Links';
const const
normalizeFolder = sFolderFullName => ('' === sFolderFullName normalizeFolder = sFolderFullName => ('' === sFolderFullName
|| UNUSED_OPTION_VALUE === sFolderFullName || UNUSED_OPTION_VALUE === sFolderFullName
@ -236,7 +238,10 @@ export class FolderModel extends AbstractModel {
static reviveFromJson(json) { static reviveFromJson(json) {
const folder = super.reviveFromJson(json); const folder = super.reviveFromJson(json);
if (folder) { if (folder) {
folder.deep = json.FullName.split(folder.delimiter).length - 1; const path = folder.fullName.split(folder.delimiter);
folder.deep = path.length - 1;
path.pop();
folder.parentName = path.join(folder.delimiter);
let type = (folder.metadata[FolderMetadataKeys.KolabFolderType] let type = (folder.metadata[FolderMetadataKeys.KolabFolderType]
|| folder.metadata[FolderMetadataKeys.KolabFolderTypeShared] || folder.metadata[FolderMetadataKeys.KolabFolderTypeShared]
@ -360,6 +365,8 @@ export class FolderModel extends AbstractModel {
!!folder.subFolders().find( !!folder.subFolders().find(
folder => folder.hasUnreadMessages() | folder.hasSubscribedUnreadMessagesSubfolders() folder => folder.hasUnreadMessages() | folder.hasSubscribedUnreadMessagesSubfolders()
) )
// ,href: () => folder.canBeSelected() && mailBox(folder.fullNameHash)
}); });
folder.addSubscribables({ folder.addSubscribables({

View file

@ -194,10 +194,6 @@ export class AbstractFetchRemote
this.request('GetPublicKey', fCallback); this.request('GetPublicKey', fCallback);
} }
fastResolve(mData) {
return Promise.resolve(mData);
}
setTrigger(trigger, value) { setTrigger(trigger, value) {
if (trigger) { if (trigger) {
value = !!value; value = !!value;

View file

@ -207,21 +207,17 @@ class RemoteUserFetch extends AbstractFetchRemote {
* @param {?Function} fCallback * @param {?Function} fCallback
*/ */
foldersReload(fCallback) { foldersReload(fCallback) {
clearTimeout(this.foldersTimeout);
this.abort('Folders') this.abort('Folders')
.post('Folders', FolderUserStore.foldersLoading) .post('Folders', FolderUserStore.foldersLoading)
.then(data => { .then(data => {
data = FolderCollectionModel.reviveFromJson(data.Result); data = FolderCollectionModel.reviveFromJson(data.Result);
data && data.storeIt(); data && data.storeIt();
fCallback && fCallback(true); fCallback && fCallback(true);
// Repeat every 15 minutes
this.foldersTimeout = setTimeout(() => this.foldersReload(), 900000);
}) })
.catch(() => fCallback && setTimeout(() => fCallback(false), 1)); .catch(() => fCallback && setTimeout(fCallback, 1, false));
}
foldersReloadWithTimeout() {
this.setTrigger(FolderUserStore.foldersLoading, true);
clearTimeout(this.foldersTimeout);
this.foldersTimeout = setTimeout(() => this.foldersReload(), 500);
} }
/* /*

View file

@ -5,7 +5,7 @@ import { ClientSideKeyName, FolderMetadataKeys } from 'Common/EnumsUser';
import { Settings } from 'Common/Globals'; import { Settings } from 'Common/Globals';
import { getNotification } from 'Common/Translator'; import { getNotification } from 'Common/Translator';
import { setFolder, removeFolderFromCacheList } from 'Common/Cache'; import { setFolder, getFolderFromCacheList, removeFolderFromCacheList } from 'Common/Cache';
import { Capa } from 'Common/Enums'; import { Capa } from 'Common/Enums';
import { defaultOptionsAfterRender } from 'Common/Utils'; import { defaultOptionsAfterRender } from 'Common/Utils';
import { initOnStartOrLangChange, i18n } from 'Common/Translator'; import { initOnStartOrLangChange, i18n } from 'Common/Translator';
@ -71,21 +71,27 @@ export class FoldersUserSettings /*extends AbstractViewSettings*/ {
if (nameToEdit && folder.name() !== nameToEdit) { if (nameToEdit && folder.name() !== nameToEdit) {
Local.set(ClientSideKeyName.FoldersLashHash, ''); Local.set(ClientSideKeyName.FoldersLashHash, '');
Remote Remote.abort('Folders').post('FolderRename', FolderUserStore.foldersRenaming, {
.post('FolderRename', FolderUserStore.foldersRenaming, {
Folder: folder.fullName, Folder: folder.fullName,
NewFolderName: nameToEdit NewFolderName: nameToEdit
}) })
.then(data => { .then(data => {
folder.name(nameToEdit/*data.Name*/); folder.name(nameToEdit/*data.Name*/);
if (folder.subFolders.length) { if (folder.subFolders.length) {
Remote.foldersReloadWithTimeout(); Remote.setTrigger(FolderUserStore.foldersLoading, true);
// rename all subfolders folder.delimiter clearTimeout(Remote.foldersTimeout);
Remote.foldersTimeout = setTimeout(() => Remote.foldersReload(), 500);
// TODO: rename all subfolders with folder.delimiter to prevent reload?
} else { } else {
removeFolderFromCacheList(folder.fullName); removeFolderFromCacheList(folder.fullName);
data = data.Result; data = data.Result;
folder.fullName = data.FullName; folder.fullName = data.FullName;
setFolder(folder); setFolder(folder);
/*
const folder = getFolderFromCacheList(folder.parentName);
var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
console.log((folder ? folder.subFolders : FolderUserStore.folderList).sort(collator.compare));
*/
} }
}) })
.catch(error => { .catch(error => {
@ -131,14 +137,19 @@ export class FoldersUserSettings /*extends AbstractViewSettings*/ {
if (folderToRemove) { if (folderToRemove) {
Local.set(ClientSideKeyName.FoldersLashHash, ''); Local.set(ClientSideKeyName.FoldersLashHash, '');
// rl.app.foldersPromisesActionHelper
Remote.abort('Folders').post('FolderDelete', FolderUserStore.foldersDeleting, { Remote.abort('Folders').post('FolderDelete', FolderUserStore.foldersDeleting, {
Folder: folderToRemove.fullName Folder: folderToRemove.fullName
}).then( }).then(
() => { () => {
folderToRemove.selectable(false) // folderToRemove.flags.push('\\nonexistent');
removeFolderFromCacheList(folderToRemove.fullName); folderToRemove.selectable(false);
FolderUserStore.folderList(FolderUserStore.folderList.filter(folder => folder !== folderToRemove)); // folderToRemove.subscribed(false);
// folderToRemove.checkable(false);
if (!folderToRemove.subFolders.length) {
removeFolderFromCacheList(folderToRemove.fullName);
const folder = getFolderFromCacheList(folderToRemove.parentName);
(folder ? folder.subFolders : FolderUserStore.folderList).remove(folderToRemove);
}
}, },
error => { error => {
FolderUserStore.folderListError( FolderUserStore.folderListError(

View file

@ -4,6 +4,7 @@ import { Notification } from 'Common/Enums';
import { UNUSED_OPTION_VALUE } from 'Common/Consts'; import { UNUSED_OPTION_VALUE } from 'Common/Consts';
import { defaultOptionsAfterRender } from 'Common/Utils'; import { defaultOptionsAfterRender } from 'Common/Utils';
import { folderListOptionsBuilder } from 'Common/UtilsUser'; import { folderListOptionsBuilder } from 'Common/UtilsUser';
import { getNotification } from 'Common/Translator';
import { FolderUserStore } from 'Stores/User/Folder'; import { FolderUserStore } from 'Stores/User/Folder';
@ -12,6 +13,9 @@ import Remote from 'Remote/User/Fetch';
import { decorateKoCommands } from 'Knoin/Knoin'; import { decorateKoCommands } from 'Knoin/Knoin';
import { AbstractViewPopup } from 'Knoin/AbstractViews'; import { AbstractViewPopup } from 'Knoin/AbstractViews';
import { setFolder, getFolderFromCacheList } from 'Common/Cache';
import { FolderModel } from 'Model/FolderCollection';
class FolderCreatePopupView extends AbstractViewPopup { class FolderCreatePopupView extends AbstractViewPopup {
constructor() { constructor() {
super('FolderCreate'); super('FolderCreate');
@ -49,13 +53,27 @@ class FolderCreatePopupView extends AbstractViewPopup {
parentFolderName = FolderUserStore.namespace.substr(0, FolderUserStore.namespace.length - 1); parentFolderName = FolderUserStore.namespace.substr(0, FolderUserStore.namespace.length - 1);
} }
rl.app.foldersPromisesActionHelper( Remote.abort('Folders').post('FolderCreate', FolderUserStore.foldersCreating, {
Remote.post('FolderCreate', FolderUserStore.foldersCreating, {
Folder: this.folderName(), Folder: this.folderName(),
Parent: parentFolderName Parent: parentFolderName
}), })
Notification.CantCreateFolder .then(
); data => {
const folder = getFolderFromCacheList(parentFolderName),
subFolder = FolderModel.reviveFromJson(data.Result);
setFolder(subFolder);
(folder ? folder.subFolders : FolderUserStore.folderList).push(subFolder);
/*
var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
console.log((folder ? folder.subFolders : FolderUserStore.folderList).sort(collator.compare));
*/
},
error => {
FolderUserStore.folderListError(
getNotification(error.code, '', Notification.CantCreateFolder)
+ '.\n' + error.message);
}
);
this.cancelCommand(); this.cancelCommand();
} }

View file

@ -1309,7 +1309,7 @@ class MailClient
/** /**
* @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Base\Exceptions\InvalidArgumentException
*/ */
public function FolderCreate(string $sFolderNameInUtf8, string $sFolderParentFullName = '', bool $bSubscribeOnCreation = true, string $sDelimiter = '') : self public function FolderCreate(string $sFolderNameInUtf8, string $sFolderParentFullName = '', bool $bSubscribeOnCreation = true, string $sDelimiter = '') : ?Folder
{ {
$sFolderNameInUtf8 = \trim($sFolderNameInUtf8); $sFolderNameInUtf8 = \trim($sFolderNameInUtf8);
$sFolderParentFullName = \trim($sFolderParentFullName); $sFolderParentFullName = \trim($sFolderParentFullName);
@ -1353,7 +1353,11 @@ class MailClient
$this->oImapClient->FolderSubscribe($sFullNameToCreate); $this->oImapClient->FolderSubscribe($sFullNameToCreate);
} }
return $this; $aFolders = $this->oImapClient->IsSupported('LIST-STATUS')
? $this->oImapClient->FolderStatusList($sFullNameToCreate, '')
: $this->oImapClient->FolderList($sFullNameToCreate, '');
$oImapFolder = $aFolders[$sFullNameToCreate];
return $oImapFolder ? new Folder($oImapFolder, $bSubscribeOnCreation) : null;
} }
/** /**

View file

@ -119,10 +119,7 @@ trait Folders
{ {
try try
{ {
if ($this->MailClient()->FolderCreate($mFolderNameToCreate, $sParent, true, $sDelimiter)) $this->MailClient()->FolderCreate($mFolderNameToCreate, $sParent, true, $sDelimiter);
{
$bDoItAgain = true;
}
} }
catch (\Throwable $oException) catch (\Throwable $oException)
{ {
@ -198,15 +195,16 @@ trait Folders
$sFolderNameInUtf = $this->GetActionParam('Folder', ''); $sFolderNameInUtf = $this->GetActionParam('Folder', '');
$sFolderParentFullName = $this->GetActionParam('Parent', ''); $sFolderParentFullName = $this->GetActionParam('Parent', '');
$this->MailClient()->FolderCreate($sFolderNameInUtf, $sFolderParentFullName, $oFolder = $this->MailClient()->FolderCreate($sFolderNameInUtf, $sFolderParentFullName,
!!$this->Config()->Get('labs', 'use_imap_list_subscribe', true)); !!$this->Config()->Get('labs', 'use_imap_list_subscribe', true));
// FolderInformation(string $sFolderName, int $iPrevUidNext = 0, array $aUids = array())
return $this->DefaultResponse(__FUNCTION__, $oFolder);
} }
catch (\Throwable $oException) catch (\Throwable $oException)
{ {
throw new ClientException(Notifications::CantCreateFolder, $oException); throw new ClientException(Notifications::CantCreateFolder, $oException);
} }
return $this->TrueResponse(__FUNCTION__);
} }
public function DoFolderSetMetadata() : array public function DoFolderSetMetadata() : array