Speedup foldercollection

This commit is contained in:
djmaze 2021-04-23 13:01:51 +02:00
parent 29fe73d4e7
commit 8aa9b0b33f
7 changed files with 124 additions and 175 deletions

View file

@ -100,10 +100,8 @@ export class FolderCollectionModel extends AbstractCollectionModel
storeIt() { storeIt() {
const cnt = pInt(this.CountRec); const cnt = pInt(this.CountRec);
let limit = pInt(Settings.app('folderSpecLimit')); FolderUserStore.displaySpecSetting(0 >= cnt
limit = 100 < limit ? 100 : 10 > limit ? 10 : limit; || Math.max(10, Math.min(100, pInt(Settings.app('folderSpecLimit')))) < cnt);
FolderUserStore.displaySpecSetting(0 >= cnt || limit < cnt);
FolderUserStore.folderList(this); FolderUserStore.folderList(this);

View file

@ -20,7 +20,7 @@
} }
.b-folders-user { .b-folders-user {
.e-link.print-count.system .badge { a.print-count.system .badge {
display: none !important; display: none !important;
} }
} }
@ -75,7 +75,7 @@
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
.e-link { a {
display: block; display: block;
position: relative; position: relative;
z-index: 1; z-index: 1;
@ -108,9 +108,7 @@
display: inline; display: inline;
} }
padding: 0; padding: 0 @folderItemPadding;
padding-left: @folderItemPadding;
padding-right: @folderItemPadding;
text-decoration: none; text-decoration: none;
@ -203,21 +201,21 @@
font-weight: bold; font-weight: bold;
} }
.b-sub-folders .e-item .e-link { .b-sub-folders .e-item a {
padding-left: @subPadding * 1 + @folderItemPadding; padding-left: @subPadding * 1 + @folderItemPadding;
} }
.b-sub-folders .b-sub-folders .e-item .e-link { .b-sub-folders .b-sub-folders .e-item a {
padding-left: @subPadding * 2 + @folderItemPadding; padding-left: @subPadding * 2 + @folderItemPadding;
} }
.b-sub-folders .b-sub-folders .b-sub-folders .e-item .e-link { .b-sub-folders .b-sub-folders .b-sub-folders .e-item a {
padding-left: @subPadding * 3 + @folderItemPadding; padding-left: @subPadding * 3 + @folderItemPadding;
} }
.b-sub-folders .b-sub-folders .b-sub-folders .b-sub-folders .e-item .e-link { .b-sub-folders .b-sub-folders .b-sub-folders .b-sub-folders .e-item a {
padding-left: @subPadding * 4 + @folderItemPadding; padding-left: @subPadding * 4 + @folderItemPadding;
} }
/**/ /**/
&.single-root-inbox .i-am-inbox.e-link { &.single-root-inbox a.i-am-inbox {
display: none !important; display: none !important;
} }
@ -228,16 +226,16 @@
} }
&.single-root-inbox .i-am-inbox-wrapper { &.single-root-inbox .i-am-inbox-wrapper {
.b-sub-folders .e-item .e-link { .b-sub-folders .e-item a {
padding-left: @folderItemPadding; padding-left: @folderItemPadding;
} }
.b-sub-folders .b-sub-folders .e-item .e-link { .b-sub-folders .b-sub-folders .e-item a {
padding-left: @subPadding * 1 + @folderItemPadding; padding-left: @subPadding * 1 + @folderItemPadding;
} }
.b-sub-folders .b-sub-folders .b-sub-folders .e-item .e-link { .b-sub-folders .b-sub-folders .b-sub-folders .e-item a {
padding-left: @subPadding * 2 + @folderItemPadding; padding-left: @subPadding * 2 + @folderItemPadding;
} }
.b-sub-folders .b-sub-folders .b-sub-folders .b-sub-folders .e-item .e-link { .b-sub-folders .b-sub-folders .b-sub-folders .b-sub-folders .e-item a {
padding-left: @subPadding * 3 + @folderItemPadding; padding-left: @subPadding * 3 + @folderItemPadding;
} }
} }

View file

@ -108,16 +108,16 @@ export class FolderListMailBoxUserView extends AbstractViewLeft {
} }
} }
el = eqs(event, '.b-folders .e-item .e-link.selectable .inbox-star-icon'); el = eqs(event, '.b-folders .e-item a.selectable .inbox-star-icon');
el && fSelectFolder(el, event, !this.isInboxStarred()); el && fSelectFolder(el, event, !this.isInboxStarred());
el = eqs(event, '.b-folders .e-item .e-link.selectable'); el = eqs(event, '.b-folders .e-item a.selectable');
el && fSelectFolder(el, event, false); el && fSelectFolder(el, event, false);
}); });
shortcuts.add('arrowup,arrowdown', '', Scope.FolderList, event => { shortcuts.add('arrowup,arrowdown', '', Scope.FolderList, event => {
let items = [], index = 0; let items = [], index = 0;
dom.querySelectorAll('.b-folders .e-item .e-link:not(.hidden)').forEach(node => { dom.querySelectorAll('.b-folders .e-item a:not(.hidden)').forEach(node => {
if (node.offsetHeight || node.getClientRects().length) { if (node.offsetHeight || node.getClientRects().length) {
items.push(node); items.push(node);
if (node.matches('.focused')) { if (node.matches('.focused')) {
@ -140,7 +140,7 @@ export class FolderListMailBoxUserView extends AbstractViewLeft {
}); });
shortcuts.add('enter,open', '', Scope.FolderList, () => { shortcuts.add('enter,open', '', Scope.FolderList, () => {
const item = qs('.b-folders .e-item .e-link:not(.hidden).focused'); const item = qs('.b-folders .e-item a:not(.hidden).focused');
if (item) { if (item) {
AppUserStore.focusedState(Scope.MessageList); AppUserStore.focusedState(Scope.MessageList);
item.click(); item.click();
@ -150,7 +150,7 @@ export class FolderListMailBoxUserView extends AbstractViewLeft {
}); });
shortcuts.add('space', '', Scope.FolderList, () => { shortcuts.add('space', '', Scope.FolderList, () => {
const item = qs('.b-folders .e-item .e-link:not(.hidden).focused'), const item = qs('.b-folders .e-item a:not(.hidden).focused'),
folder = item && ko.dataFor(item); folder = item && ko.dataFor(item);
if (folder) { if (folder) {
const collapsed = folder.collapsed(); const collapsed = folder.collapsed();
@ -169,11 +169,11 @@ export class FolderListMailBoxUserView extends AbstractViewLeft {
}); });
AppUserStore.focusedState.subscribe(value => { AppUserStore.focusedState.subscribe(value => {
let el = qs('.b-folders .e-item .e-link.focused'); let el = qs('.b-folders .e-item a.focused');
el && qs('.b-folders .e-item .e-link.focused').classList.remove('focused'); el && qs('.b-folders .e-item a.focused').classList.remove('focused');
if (Scope.FolderList === value) { if (Scope.FolderList === value) {
el = qs('.b-folders .e-item .e-link.selected'); el = qs('.b-folders .e-item a.selected');
el && qs('.b-folders .e-item .e-link.selected').classList.add('focused'); el && qs('.b-folders .e-item a.selected').classList.add('focused');
} }
}); });
} }
@ -181,7 +181,7 @@ export class FolderListMailBoxUserView extends AbstractViewLeft {
scrollToFocused() { scrollToFocused() {
const scrollable = this.oContentScrollable; const scrollable = this.oContentScrollable;
if (scrollable) { if (scrollable) {
let block, focused = scrollable.querySelector('.e-item .e-link.focused'); let block, focused = scrollable.querySelector('.e-item a.focused');
if (focused) { if (focused) {
const fRect = focused.getBoundingClientRect(), const fRect = focused.getBoundingClientRect(),
sRect = scrollable.getBoundingClientRect(); sRect = scrollable.getBoundingClientRect();

View file

@ -20,44 +20,32 @@ class FolderCollection extends \MailSo\Base\Collection
/** /**
* @var string * @var string
*/ */
public $Namespace; public $Namespace = '';
/** /**
* @var string * @var string
*/ */
public $FoldersHash; public $FoldersHash = '';
/** /**
* @var bool * @var bool
*/ */
public $IsThreadsSupported; public $IsThreadsSupported = false;
/** /**
* @var bool * @var bool
*/ */
public $IsSortSupported; public $IsSortSupported = false;
/** /**
* @var bool * @var bool
*/ */
public $Optimized; public $Optimized = false;
/** /**
* @var array * @var array
*/ */
public $SystemFolders; public $SystemFolders = array();
function __construct($input = array())
{
parent::__construct($input);
$this->Namespace = '';
$this->FoldersHash = '';
$this->SystemFolders = array();
$this->IsThreadsSupported = false;
$this->IsSortSupported = false;
$this->Optimized = false;
}
public function append($oFolder, bool $bToTop = false) : void public function append($oFolder, bool $bToTop = false) : void
{ {
@ -118,12 +106,7 @@ class FolderCollection extends \MailSo\Base\Collection
$oFolder = $this[0]; $oFolder = $this[0];
} }
if ($oFolder) return $oFolder ? $oFolder->Delimiter() : '/';
{
$sDelimiter = $oFolder->Delimiter();
}
return $sDelimiter;
} }
public function SetNamespace(string $sNamespace) : self public function SetNamespace(string $sNamespace) : self
@ -133,92 +116,18 @@ class FolderCollection extends \MailSo\Base\Collection
return $this; return $this;
} }
public function InitByUnsortedMailFolderArray(array $aUnsortedMailFolders) : void public function AddWithPositionSearch(Folder $oMailFolder) : void
{ {
$this->Clear();
$aSortedByLenImapFolders = array();
foreach ($aUnsortedMailFolders as /* @var $oMailFolder Folder */ $oMailFolder)
{
$aSortedByLenImapFolders[$oMailFolder->FullNameRaw()] =& $oMailFolder;
unset($oMailFolder);
}
unset($aUnsortedMailFolders);
$aAddedFolders = array();
foreach ($aSortedByLenImapFolders as /* @var $oMailFolder Folder */ $oMailFolder)
{
$sDelimiter = $oMailFolder->Delimiter();
$aFolderExplode = \explode($sDelimiter, $oMailFolder->FullNameRaw());
if (1 < \count($aFolderExplode))
{
\array_pop($aFolderExplode);
$sNonExistenFolderFullNameRaw = '';
foreach ($aFolderExplode as $sFolderExplodeItem)
{
$sNonExistenFolderFullNameRaw .= (0 < \strlen($sNonExistenFolderFullNameRaw))
? $sDelimiter.$sFolderExplodeItem : $sFolderExplodeItem;
if (!isset($aSortedByLenImapFolders[$sNonExistenFolderFullNameRaw]))
{
try
{
$aAddedFolders[$sNonExistenFolderFullNameRaw] =
Folder::NewNonExistenInstance($sNonExistenFolderFullNameRaw, $sDelimiter);
}
catch (\Throwable $oExc)
{
unset($oExc);
}
}
}
}
}
$aSortedByLenImapFolders = \array_merge($aSortedByLenImapFolders, $aAddedFolders);
unset($aAddedFolders);
\uasort($aSortedByLenImapFolders, function ($oFolderA, $oFolderB) {
return \strnatcmp($oFolderA->FullNameRaw(), $oFolderB->FullNameRaw());
});
foreach ($aSortedByLenImapFolders as $oMailFolder)
{
$this->AddWithPositionSearch($oMailFolder);
unset($oMailFolder);
}
unset($aSortedByLenImapFolders);
}
public function AddWithPositionSearch(Folder $oMailFolder) : bool
{
$oItemFolder = null;
$bIsAdded = false;
foreach ($this as $oItemFolder) foreach ($this as $oItemFolder)
{ {
if ($oMailFolder instanceof Folder && if (0 === \strpos($oMailFolder->FullNameRaw(), $oItemFolder->FullNameRaw().$oItemFolder->Delimiter()))
0 === \strpos($oMailFolder->FullNameRaw(), $oItemFolder->FullNameRaw().$oItemFolder->Delimiter()))
{ {
if ($oItemFolder->SubFolders(true)->AddWithPositionSearch($oMailFolder)) $oItemFolder->SubFolders(true)->AddWithPositionSearch($oMailFolder);
{ return;
$bIsAdded = true;
}
break;
} }
} }
if (!$bIsAdded && $oMailFolder instanceof Folder) $this->append($oMailFolder);
{
$bIsAdded = true;
$this->append($oMailFolder);
}
return $bIsAdded;
} }
public function SortByCallback(callable $fCallback) : void public function SortByCallback(callable $fCallback) : void
@ -241,7 +150,7 @@ class FolderCollection extends \MailSo\Base\Collection
public function jsonSerialize() public function jsonSerialize()
{ {
return array_merge(parent::jsonSerialize(), array( return \array_merge(parent::jsonSerialize(), array(
'Namespace' => $this->GetNamespace(), 'Namespace' => $this->GetNamespace(),
'FoldersHash' => $this->FoldersHash ?: '', 'FoldersHash' => $this->FoldersHash ?: '',
'IsThreadsSupported' => $this->IsThreadsSupported, 'IsThreadsSupported' => $this->IsThreadsSupported,

View file

@ -1439,14 +1439,12 @@ class MailClient
if (0 < \count($aFetchResponse)) if (0 < \count($aFetchResponse))
{ {
$aFetchIndexArray = array(); $aFetchIndexArray = array();
$oFetchResponseItem = null;
foreach ($aFetchResponse as /* @var $oFetchResponseItem \MailSo\Imap\FetchResponse */ $oFetchResponseItem) foreach ($aFetchResponse as /* @var $oFetchResponseItem \MailSo\Imap\FetchResponse */ $oFetchResponseItem)
{ {
$aFetchIndexArray[($bIndexAsUid) $aFetchIndexArray[$bIndexAsUid
? $oFetchResponseItem->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::UID) ? $oFetchResponseItem->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::UID)
: $oFetchResponseItem->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::INDEX)] =& $oFetchResponseItem; : $oFetchResponseItem->GetFetchValue(\MailSo\Imap\Enumerations\FetchType::INDEX)
] = $oFetchResponseItem;
unset($oFetchResponseItem);
} }
foreach ($aRequestIndexOrUids as $iFUid) foreach ($aRequestIndexOrUids as $iFUid)
@ -1979,8 +1977,6 @@ class MailClient
public function Folders(string $sParent = '', string $sListPattern = '*', bool $bUseListSubscribeStatus = true, int $iOptimizationLimit = 0) : ?FolderCollection public function Folders(string $sParent = '', string $sListPattern = '*', bool $bUseListSubscribeStatus = true, int $iOptimizationLimit = 0) : ?FolderCollection
{ {
$oFolderCollection = null;
$aImapSubscribedFoldersHelper = null; $aImapSubscribedFoldersHelper = null;
if ($bUseListSubscribeStatus) if ($bUseListSubscribeStatus)
{ {
@ -2000,56 +1996,102 @@ class MailClient
} }
$aFolders = $this->oImapClient->FolderList($sParent, $sListPattern); $aFolders = $this->oImapClient->FolderList($sParent, $sListPattern);
if (!$aFolders) {
return null;
}
if ($aFolders) $aMailFoldersHelper = array();
foreach ($aFolders as /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder)
{ {
$aMailFoldersHelper = array(); $aMailFoldersHelper[] = new Folder($oImapFolder,
(null === $aImapSubscribedFoldersHelper || \in_array($oImapFolder->FullNameRaw(), $aImapSubscribedFoldersHelper)) ||
$oImapFolder->IsInbox()
);
}
foreach ($aFolders as /* @var $oImapFolder \MailSo\Imap\Folder */ $oImapFolder) $iCount = \count($aMailFoldersHelper);
$aMailFoldersHelper = $this->folderListOptimization($aMailFoldersHelper, $iOptimizationLimit);
if (!$aMailFoldersHelper) {
return null;
}
$oFolderCollection = new FolderCollection;
$oFolderCollection->IsThreadsSupported = $this->IsThreadsSupported();
$oFolderCollection->IsSortSupported = $this->oImapClient->IsSupported('SORT');
$oFolderCollection->Optimized = $iCount !== \count($aMailFoldersHelper);
$aSortedByLenImapFolders = array();
foreach ($aMailFoldersHelper as /* @var $oMailFolder Folder */ $oMailFolder)
{
$aSortedByLenImapFolders[$oMailFolder->FullNameRaw()] = $oMailFolder;
}
unset($aMailFoldersHelper);
$aAddedFolders = array();
foreach ($aSortedByLenImapFolders as /* @var $oMailFolder Folder */ $oMailFolder)
{
$sDelimiter = $oMailFolder->Delimiter();
$aFolderExplode = \explode($sDelimiter, $oMailFolder->FullNameRaw());
if (1 < \count($aFolderExplode))
{ {
$aMailFoldersHelper[] = new Folder($oImapFolder, \array_pop($aFolderExplode);
(null === $aImapSubscribedFoldersHelper || \in_array($oImapFolder->FullNameRaw(), $aImapSubscribedFoldersHelper)) ||
$oImapFolder->IsInbox()
);
}
$iCount = \count($aMailFoldersHelper); $sNonExistenFolderFullNameRaw = '';
$aMailFoldersHelper = $this->folderListOptimization($aMailFoldersHelper, $iOptimizationLimit); foreach ($aFolderExplode as $sFolderExplodeItem)
{
$sNonExistenFolderFullNameRaw .= (0 < \strlen($sNonExistenFolderFullNameRaw))
? $sDelimiter.$sFolderExplodeItem : $sFolderExplodeItem;
if ($aMailFoldersHelper) if (!isset($aSortedByLenImapFolders[$sNonExistenFolderFullNameRaw]))
{ {
$oFolderCollection = new FolderCollection; try
$oFolderCollection->InitByUnsortedMailFolderArray($aMailFoldersHelper); {
$aAddedFolders[$sNonExistenFolderFullNameRaw] =
$oFolderCollection->Optimized = $iCount !== \count($aMailFoldersHelper); Folder::NewNonExistenInstance($sNonExistenFolderFullNameRaw, $sDelimiter);
}
catch (\Throwable $oExc)
{
unset($oExc);
}
}
}
} }
} }
if ($oFolderCollection) $aSortedByLenImapFolders = \array_merge($aSortedByLenImapFolders, $aAddedFolders);
unset($aAddedFolders);
\uasort($aSortedByLenImapFolders, function ($oFolderA, $oFolderB) {
return \strnatcmp($oFolderA->FullNameRaw(), $oFolderB->FullNameRaw());
});
foreach ($aSortedByLenImapFolders as $oMailFolder)
{ {
$oFolderCollection->SortByCallback(function ($oFolderA, $oFolderB) { $oFolderCollection->AddWithPositionSearch($oMailFolder);
$sA = \strtoupper($oFolderA->FullNameRaw()); unset($oMailFolder);
$sB = \strtoupper($oFolderB->FullNameRaw()); }
switch (true)
{
case 'INBOX' === $sA:
return -1;
case 'INBOX' === $sB:
return 1;
}
return \strnatcasecmp($oFolderA->FullName(), $oFolderB->FullName()); unset($aSortedByLenImapFolders);
});
$oNamespace = $this->oImapClient->GetNamespace(); $oFolderCollection->SortByCallback(function ($oFolderA, $oFolderB) {
if ($oNamespace) $sA = \strtoupper($oFolderA->FullNameRaw());
$sB = \strtoupper($oFolderB->FullNameRaw());
switch (true)
{ {
$oFolderCollection->SetNamespace($oNamespace->GetPersonalNamespace()); case 'INBOX' === $sA:
return -1;
case 'INBOX' === $sB:
return 1;
} }
$oFolderCollection->IsThreadsSupported = $this->IsThreadsSupported(); return \strnatcasecmp($oFolderA->FullName(), $oFolderB->FullName());
});
$oFolderCollection->IsSortSupported = $this->oImapClient->IsSupported('SORT'); $oNamespace = $this->oImapClient->GetNamespace();
if ($oNamespace)
{
$oFolderCollection->SetNamespace($oNamespace->GetPersonalNamespace());
} }
return $oFolderCollection; return $oFolderCollection;

View file

@ -1,9 +1,11 @@
<div class="e-item" data-bind="visible: visible, css: { 'i-am-inbox-wrapper': isInbox }"> <div class="e-item" data-bind="visible: visible, css: { 'i-am-inbox-wrapper': isInbox }">
<a class="e-link" data-bind="dropmessages: $data, <a data-bind="dropmessages: $data,
css: { 'i-am-inbox': isInbox, 'selected': selected() && !isSystemFolder(), 'selectable': canBeSelected, 'hidden' : hidden, 'print-count': hasUnreadMessages, 'unread-sub': hasSubscribedUnreadMessagesSubfolders, 'system': isSystemFolder, 'anim-action-class': actionBlink }"> css: { 'i-am-inbox': isInbox, 'selected': selected() && !isSystemFolder(), 'selectable': canBeSelected, 'hidden' : hidden, 'print-count': hasUnreadMessages, 'unread-sub': hasSubscribedUnreadMessagesSubfolders, 'system': isSystemFolder, 'anim-action-class': actionBlink }">
<span class="badge pull-right count" data-bind="text: printableUnreadCount"></span> <span class="badge pull-right count" data-bind="text: printableUnreadCount"></span>
<i data-bind="css: collapsedCss()"></i> <i data-bind="css: collapsedCss()"></i>
<span class="name" data-bind="text: name"></span> <span class="name" data-bind="text: name"></span>
</a> </a>
<!-- ko if: subFolders.length -->
<div class="b-sub-folders" data-bind="template: { name: 'MailFolderListItem', foreach: subFolders }, css: { 'collapsed': collapsed() }"></div> <div class="b-sub-folders" data-bind="template: { name: 'MailFolderListItem', foreach: subFolders }, css: { 'collapsed': collapsed() }"></div>
<!-- /ko -->
</div> </div>

View file

@ -1,5 +1,5 @@
<div class="e-item b-folder-system-item"> <div class="e-item b-folder-system-item">
<a class="e-link" data-bind="dropmessages: $data, css: { 'selected': selected, 'selectable': selectable, 'print-count': 0 < printableUnreadCount().length, 'anim-action-class': actionBlink, 'is-inbox': isInbox }"> <a data-bind="dropmessages: $data, css: { 'selected': selected, 'selectable': selectable, 'print-count': 0 < printableUnreadCount().length, 'anim-action-class': actionBlink, 'is-inbox': isInbox }">
<span class="badge pull-right count" data-bind="text: printableUnreadCount"></span> <span class="badge pull-right count" data-bind="text: printableUnreadCount"></span>
&nbsp; &nbsp;
<span class="name" data-bind="text: localName()"></span> <span class="name" data-bind="text: localName()"></span>