djmaze 2021-03-26 15:07:14 +01:00
parent fd5940f5fc
commit 883bf6b026
9 changed files with 135 additions and 62 deletions

View file

@ -187,16 +187,18 @@ class AppUser extends AbstractApp {
} }
MessageUserStore.listLoading(false); MessageUserStore.listLoading(false);
}, },
FolderUserStore.currentFolderFullNameRaw(), {
iOffset, Folder: FolderUserStore.currentFolderFullNameRaw(),
SettingsUserStore.messagesPerPage(), Offset: iOffset,
MessageUserStore.listSearch(), Limit: SettingsUserStore.messagesPerPage(),
MessageUserStore.listThreadUid() Search: MessageUserStore.listSearch(),
UidNext: MessageUserStore.listThreadUid()
}
); );
} }
recacheInboxMessageList() { recacheInboxMessageList() {
Remote.messageList(()=>{}, getFolderInboxName(), 0, SettingsUserStore.messagesPerPage(), '', '', true); Remote.messageList(null, {Folder: getFolderInboxName()}, true);
} }
/** /**

View file

@ -14,6 +14,22 @@ export const FolderType = {
User: 99 User: 99
}; };
/**
* @enum {string}
*/
export const FolderSortType = {
DateDesc: '', // default 'REVERSE DATE'
DateAsc: 'DATE',
FromDesc: 'REVERSE FROM',
FromAsc: 'FROM',
SizeDesc: 'REVERSE SIZE',
SizeAsc: 'SIZE',
SubjectDesc: 'REVERSE SUBJECT',
SubjectAsc: 'SUBJECT'
// ToDesc: 'REVERSE TO',
// ToAsc: 'TO',
};
/** /**
* @enum {string} * @enum {string}
*/ */

View file

@ -113,6 +113,7 @@ export class FolderCollectionModel extends AbstractCollectionModel
AppUserStore.threadsAllowed(!!(Settings.app('useImapThread') && this.IsThreadsSupported)); AppUserStore.threadsAllowed(!!(Settings.app('useImapThread') && this.IsThreadsSupported));
FolderUserStore.folderListOptimized(!!this.Optimized); FolderUserStore.folderListOptimized(!!this.Optimized);
FolderUserStore.sortSupported(!!this.IsSortSupported);
let update = false; let update = false;

View file

@ -297,54 +297,44 @@ class RemoteUserFetch extends AbstractFetchRemote {
/** /**
* @param {Function} fCallback * @param {Function} fCallback
* @param {string} sFolderFullNameRaw * @param {object} params
* @param {number=} iOffset = 0
* @param {number=} iLimit = 20
* @param {string=} sSearch = ''
* @param {string=} sThreadUid = ''
* @param {boolean=} bSilent = false * @param {boolean=} bSilent = false
*/ */
messageList(fCallback, sFolderFullNameRaw, iOffset = 0, iLimit = 20, sSearch = '', sThreadUid = '', bSilent = false) { messageList(fCallback, params, bSilent = false) {
sFolderFullNameRaw = pString(sFolderFullNameRaw); const
sFolderFullNameRaw = pString(params.Folder),
const folderHash = getFolderHash(sFolderFullNameRaw), folderHash = getFolderHash(sFolderFullNameRaw),
useThreads = AppUserStore.threadsAllowed() && SettingsUserStore.useThreads(), useThreads = AppUserStore.threadsAllowed() && SettingsUserStore.useThreads() ? 1 : 0,
inboxUidNext = getFolderInboxName() === sFolderFullNameRaw ? getFolderUidNext(sFolderFullNameRaw) : ''; inboxUidNext = getFolderInboxName() === sFolderFullNameRaw ? getFolderUidNext(sFolderFullNameRaw) : '';
let params = {}, sGetAdd = ''; params.Folder = sFolderFullNameRaw;
params.ThreadUid = useThreads ? params.ThreadUid : '';
params = Object.assign({
Folder: '',
Offset: 0,
Limit: SettingsUserStore.messagesPerPage(),
Search: '',
UidNext: inboxUidNext,
UseThreads: useThreads,
ThreadUid: '',
Sort: FolderUserStore.sortMode()
}, params);
if (folderHash && (!sSearch || !sSearch.includes('is:'))) { let sGetAdd = '';
if (folderHash && (!params.Search || !params.Search.includes('is:'))) {
sGetAdd = 'MessageList/' + sGetAdd = 'MessageList/' +
SUB_QUERY_PREFIX + SUB_QUERY_PREFIX +
'/' + '/' +
urlsafeArray([ urlsafeArray([SettingsGet('ProjectHash'),folderHash].concat(Object.values(params)));
sFolderFullNameRaw, params = {};
iOffset,
iLimit,
sSearch,
SettingsGet('ProjectHash'),
folderHash,
inboxUidNext,
useThreads ? 1 : 0,
useThreads ? sThreadUid : ''
]);
} else {
params = {
Folder: sFolderFullNameRaw,
Offset: iOffset,
Limit: iLimit,
Search: sSearch,
UidNext: inboxUidNext,
UseThreads: useThreads ? 1 : 0,
ThreadUid: useThreads ? sThreadUid : ''
};
} }
this.defaultRequest( this.defaultRequest(
fCallback, fCallback,
'MessageList', 'MessageList',
params, params,
sSearch ? 300000 : 30000, 30000,
sGetAdd, sGetAdd,
bSilent ? [] : ['MessageList'] bSilent ? [] : ['MessageList']
); );
@ -562,7 +552,7 @@ class RemoteUserFetch extends AbstractFetchRemote {
* @param {Object} oData * @param {Object} oData
*/ */
sendMessage(fCallback, oData) { sendMessage(fCallback, oData) {
this.defaultRequest(fCallback, 'SendMessage', oData, 300000); this.defaultRequest(fCallback, 'SendMessage', oData, 30000);
} }
/** /**

View file

@ -1,6 +1,6 @@
import ko from 'ko'; import ko from 'ko';
import { FolderType } from 'Common/EnumsUser'; import { FolderType, FolderSortType } from 'Common/EnumsUser';
import { UNUSED_OPTION_VALUE } from 'Common/Consts'; import { UNUSED_OPTION_VALUE } from 'Common/Consts';
import { addObservablesTo, addSubscribablesTo } from 'Common/Utils'; import { addObservablesTo, addSubscribablesTo } from 'Common/Utils';
import { folderListOptionsBuilder } from 'Common/UtilsUser'; import { folderListOptionsBuilder } from 'Common/UtilsUser';
@ -19,6 +19,12 @@ export const FolderUserStore = new class {
*/ */
displaySpecSetting: false, displaySpecSetting: false,
/**
* If the IMAP server supports SORT
*/
sortSupported: false,
// sortMode: '',
sentFolder: '', sentFolder: '',
draftFolder: '', draftFolder: '',
spamFolder: '', spamFolder: '',
@ -36,6 +42,8 @@ export const FolderUserStore = new class {
foldersInboxUnreadCount: 0 foldersInboxUnreadCount: 0
}); });
this.sortMode = ko.observable('').extend({ limitedList: Object.values(FolderSortType) });
this.namespace = ''; this.namespace = '';
this.folderList = ko.observableArray(); this.folderList = ko.observableArray();

View file

@ -293,6 +293,13 @@ class ImapClient extends \MailSo\Net\NetClient
} }
/** /**
* Test support for things like:
* IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY
* THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND
* URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED
* I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH
* LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY STATUS=SIZE LITERAL+ NOTIFY SPECIAL-USE
*
* @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Net\Exceptions\Exception
* @throws \MailSo\Imap\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception
*/ */
@ -596,6 +603,7 @@ class ImapClient extends \MailSo\Net\NetClient
} }
/** /**
* See https://tools.ietf.org/html/rfc5256
* @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Base\Exceptions\InvalidArgumentException
* @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Net\Exceptions\Exception
* @throws \MailSo\Imap\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception

View file

@ -32,6 +32,11 @@ class FolderCollection extends \MailSo\Base\Collection
*/ */
public $IsThreadsSupported; public $IsThreadsSupported;
/**
* @var bool
*/
public $IsSortSupported;
/** /**
* @var bool * @var bool
*/ */
@ -50,6 +55,7 @@ class FolderCollection extends \MailSo\Base\Collection
$this->FoldersHash = ''; $this->FoldersHash = '';
$this->SystemFolders = array(); $this->SystemFolders = array();
$this->IsThreadsSupported = false; $this->IsThreadsSupported = false;
$this->IsSortSupported = false;
$this->Optimized = false; $this->Optimized = false;
} }
@ -239,6 +245,7 @@ class FolderCollection extends \MailSo\Base\Collection
'Namespace' => $this->GetNamespace(), 'Namespace' => $this->GetNamespace(),
'FoldersHash' => $this->FoldersHash ?: '', 'FoldersHash' => $this->FoldersHash ?: '',
'IsThreadsSupported' => $this->IsThreadsSupported, 'IsThreadsSupported' => $this->IsThreadsSupported,
'IsSortSupported' => $this->IsSortSupported,
'Optimized' => $this->Optimized, 'Optimized' => $this->Optimized,
'CountRec' => $this->CountRec(), 'CountRec' => $this->CountRec(),
'SystemFolders' => isset($this->SystemFolders) && \is_array($this->SystemFolders) ? 'SystemFolders' => isset($this->SystemFolders) && \is_array($this->SystemFolders) ?

View file

@ -1473,8 +1473,47 @@ class MailClient
* @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Net\Exceptions\Exception
* @throws \MailSo\Imap\Exceptions\Exception * @throws \MailSo\Imap\Exceptions\Exception
*/ */
public function GetUids(?\MailSo\Cache\CacheClient $oCacher, string $sSearch, string $sFilter, string $sFolderName, string $sFolderHash, bool $bUseSortIfSupported = false) : array public function GetUids(?\MailSo\Cache\CacheClient $oCacher, string $sSearch,
string $sFilter, string $sFolderName, string $sFolderHash,
bool $bUseSortIfSupported = false, string $sSort = '') : array
{ {
/* TODO: Validate $sSort
ARRIVAL
Internal date and time of the message. This differs from the
ON criteria in SEARCH, which uses just the internal date.
CC
[IMAP] addr-mailbox of the first "cc" address.
DATE
Sent date and time, as described in section 2.2.
FROM
[IMAP] addr-mailbox of the first "From" address.
REVERSE
Followed by another sort criterion, has the effect of that
criterion but in reverse (descending) order.
Note: REVERSE only reverses a single criterion, and does not
affect the implicit "sequence number" sort criterion if all
other criteria are identical. Consequently, a sort of
REVERSE SUBJECT is not the same as a reverse ordering of a
SUBJECT sort. This can be avoided by use of additional
criteria, e.g., SUBJECT DATE vs. REVERSE SUBJECT REVERSE
DATE. In general, however, it's better (and faster, if the
client has a "reverse current ordering" command) to reverse
the results in the client instead of issuing a new SORT.
SIZE
Size of the message in octets.
SUBJECT
Base subject text.
TO
[IMAP] addr-mailbox of the first "To" address.
*/
$aResultUids = false; $aResultUids = false;
$bUidsFromCacher = false; $bUidsFromCacher = false;
$bUseCacheAfterSearch = true; $bUseCacheAfterSearch = true;
@ -1482,18 +1521,13 @@ class MailClient
$sSerializedHash = ''; $sSerializedHash = '';
$sSerializedLog = ''; $sSerializedLog = '';
$bUseSortIfSupported = $bUseSortIfSupported ? !!$this->oImapClient->IsSupported('SORT') : false; $bUseSortIfSupported = $bUseSortIfSupported && !\strlen($sSearch) && $this->oImapClient->IsSupported('SORT');
if (0 < \strlen($sSearch))
{
$bUseSortIfSupported = false;
}
$sSearchCriterias = $this->getImapSearchCriterias($sSearch, $sFilter, 0, $bUseCacheAfterSearch); $sSearchCriterias = $this->getImapSearchCriterias($sSearch, $sFilter, 0, $bUseCacheAfterSearch);
if ($bUseCacheAfterSearch && $oCacher && $oCacher->IsInited()) if ($bUseCacheAfterSearch && $oCacher && $oCacher->IsInited())
{ {
$sSerializedHash = 'GetUids/'. $sSerializedHash = 'GetUids/'.
($bUseSortIfSupported ? 'S': 'N').'/'. ($bUseSortIfSupported ? 'S' . $sSort : 'N').'/'.
$this->GenerateImapClientHash().'/'. $this->GenerateImapClientHash().'/'.
$sFolderName.'/'.$sSearchCriterias; $sFolderName.'/'.$sSearchCriterias;
@ -1522,7 +1556,7 @@ class MailClient
if (!\is_array($aResultUids)) if (!\is_array($aResultUids))
{ {
$aResultUids = $bUseSortIfSupported ? $aResultUids = $bUseSortIfSupported ?
$this->oImapClient->MessageSimpleSort(array('REVERSE DATE'), $sSearchCriterias, true) : $this->oImapClient->MessageSimpleSort(array($sSort ?: 'REVERSE DATE'), $sSearchCriterias, true) :
$this->oImapClient->MessageSimpleSearch($sSearchCriterias, true, \MailSo\Base\Utils::IsAscii($sSearchCriterias) ? '' : 'UTF-8') $this->oImapClient->MessageSimpleSearch($sSearchCriterias, true, \MailSo\Base\Utils::IsAscii($sSearchCriterias) ? '' : 'UTF-8')
; ;
@ -1551,7 +1585,7 @@ class MailClient
public function MessageList(string $sFolderName, int $iOffset = 0, int $iLimit = 10, public function MessageList(string $sFolderName, int $iOffset = 0, int $iLimit = 10,
string $sSearch = '', string $sPrevUidNext = '', ?\MailSo\Cache\CacheClient $oCacher = null, string $sSearch = '', string $sPrevUidNext = '', ?\MailSo\Cache\CacheClient $oCacher = null,
bool $bUseSortIfSupported = false, bool $bUseThreadSortIfSupported = false, bool $bUseSortIfSupported = false, bool $bUseThreadSortIfSupported = false,
string $sThreadUid = '', string $sFilter = '') : MessageCollection string $sThreadUid = '', string $sFilter = '', string $sSort = '') : MessageCollection
{ {
$sFilter = \trim($sFilter); $sFilter = \trim($sFilter);
$sSearch = \trim($sSearch); $sSearch = \trim($sSearch);
@ -1584,7 +1618,7 @@ class MailClient
$sUidNext = '0'; $sUidNext = '0';
$sHighestModSeq = ''; $sHighestModSeq = '';
$bUseSortIfSupported = $bUseSortIfSupported ? $this->oImapClient->IsSupported('SORT') : false; $bUseSortIfSupported = $bUseSortIfSupported && $this->oImapClient->IsSupported('SORT');
$bUseThreadSortIfSupported = $bUseThreadSortIfSupported ? $bUseThreadSortIfSupported = $bUseThreadSortIfSupported ?
($this->oImapClient->IsSupported('THREAD=REFS') || $this->oImapClient->IsSupported('THREAD=REFERENCES') || $this->oImapClient->IsSupported('THREAD=ORDEREDSUBJECT')) : false; ($this->oImapClient->IsSupported('THREAD=REFS') || $this->oImapClient->IsSupported('THREAD=REFERENCES') || $this->oImapClient->IsSupported('THREAD=ORDEREDSUBJECT')) : false;
@ -1630,7 +1664,7 @@ class MailClient
if (0 < $iMessageRealCount && !$bMessageListOptimization) if (0 < $iMessageRealCount && !$bMessageListOptimization)
{ {
$mAllSortedUids = $this->GetUids($oCacher, '', $sFilter, $mAllSortedUids = $this->GetUids($oCacher, '', $sFilter,
$oMessageCollection->FolderName, $oMessageCollection->FolderHash, $bUseSortIfSupported); $oMessageCollection->FolderName, $oMessageCollection->FolderHash, $bUseSortIfSupported, $sSort);
$mAllThreads = $bUseThreadSortIfSupported ? $this->MessageListThreadsMap( $mAllThreads = $bUseThreadSortIfSupported ? $this->MessageListThreadsMap(
$oMessageCollection->FolderName, $oMessageCollection->FolderHash, $mAllSortedUids, $oCacher) : null; $oMessageCollection->FolderName, $oMessageCollection->FolderHash, $mAllSortedUids, $oCacher) : null;
@ -2010,6 +2044,8 @@ class MailClient
} }
$oFolderCollection->IsThreadsSupported = $this->IsThreadsSupported(); $oFolderCollection->IsThreadsSupported = $this->IsThreadsSupported();
$oFolderCollection->IsSortSupported = $this->oImapClient->IsSupported('SORT');
} }
return $oFolderCollection; return $oFolderCollection;

View file

@ -24,16 +24,17 @@ trait Messages
$sUidNext = ''; $sUidNext = '';
$bUseThreads = false; $bUseThreads = false;
$sThreadUid = ''; $sThreadUid = '';
$sSort = '';
$sRawKey = $this->GetActionParam('RawKey', ''); $sRawKey = $this->GetActionParam('RawKey', '');
$aValues = $this->getDecodedClientRawKeyValue($sRawKey, 9); $aValues = $this->getDecodedClientRawKeyValue($sRawKey, 10);
if ($aValues && 7 < \count($aValues)) if ($aValues && 7 < \count($aValues))
{ {
$sFolder =(string) $aValues[0]; $sFolder = (string) $aValues[2];
$iOffset = (int) $aValues[1]; $iOffset = (int) $aValues[3];
$iLimit = (int) $aValues[2]; $iLimit = (int) $aValues[4];
$sSearch = (string) $aValues[3]; $sSearch = (string) $aValues[5];
$sUidNext = (string) $aValues[6]; $sUidNext = (string) $aValues[6];
$bUseThreads = (bool) $aValues[7]; $bUseThreads = (bool) $aValues[7];
@ -42,6 +43,8 @@ trait Messages
$sThreadUid = isset($aValues[8]) ? (string) $aValues[8] : ''; $sThreadUid = isset($aValues[8]) ? (string) $aValues[8] : '';
} }
$sSort = isset($aValues[9]) ? (string) $aValues[9] : '';
$this->verifyCacheByKey($sRawKey); $this->verifyCacheByKey($sRawKey);
} }
else else
@ -50,8 +53,9 @@ trait Messages
$iOffset = (int) $this->GetActionParam('Offset', 0); $iOffset = (int) $this->GetActionParam('Offset', 0);
$iLimit = (int) $this->GetActionParam('Limit', 10); $iLimit = (int) $this->GetActionParam('Limit', 10);
$sSearch = $this->GetActionParam('Search', ''); $sSearch = $this->GetActionParam('Search', '');
$sSort = $this->GetActionParam('Sort', '');
$sUidNext = $this->GetActionParam('UidNext', ''); $sUidNext = $this->GetActionParam('UidNext', '');
$bUseThreads = '1' === (string) $this->GetActionParam('UseThreads', '0'); $bUseThreads = !empty($this->GetActionParam('UseThreads', '0'));
if ($bUseThreads) if ($bUseThreads)
{ {
@ -76,10 +80,11 @@ trait Messages
$oMessageList = $this->MailClient()->MessageList( $oMessageList = $this->MailClient()->MessageList(
$sFolder, $iOffset, $iLimit, $sSearch, $sUidNext, $sFolder, $iOffset, $iLimit, $sSearch, $sUidNext,
$this->cacherForUids(), $this->cacherForUids(),
!!$this->Config()->Get('labs', 'use_imap_sort', false), !!$this->Config()->Get('labs', 'use_imap_sort', true),
$bUseThreads, $bUseThreads,
$sThreadUid, $sThreadUid,
'' '',
$sSort
); );
} }
catch (\Throwable $oException) catch (\Throwable $oException)