mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-07 16:37:02 +03:00
Revamp mailbox management so that reloading of the whole folder tree is not needed
This commit is contained in:
parent
5fdc38a40d
commit
2719f08e26
8 changed files with 67 additions and 60 deletions
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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({
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue