mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-07 16:37:02 +03:00
Merge branch 'sort-messagelist'
This commit is contained in:
commit
ec03999f13
22 changed files with 286 additions and 63 deletions
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,22 @@ export const FolderType = {
|
||||||
User: 99
|
User: 99
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
export const FolderSortMode = {
|
||||||
|
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}
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import ko from 'ko';
|
import ko from 'ko';
|
||||||
|
|
||||||
import { FolderType } from 'Common/EnumsUser';
|
import { FolderType, FolderSortMode } 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(FolderSortMode) });
|
||||||
|
|
||||||
this.namespace = '';
|
this.namespace = '';
|
||||||
|
|
||||||
this.folderList = ko.observableArray();
|
this.folderList = ko.observableArray();
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,11 @@ html.rl-no-preview-pane {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#sort-list-dropdown-id {
|
||||||
|
padding-left: 6px;
|
||||||
|
padding-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.messageList {
|
.messageList {
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ export class MessageListMailBoxUserView extends AbstractViewRight {
|
||||||
this.messageListDisableAutoSelect = MessageUserStore.listDisableAutoSelect;
|
this.messageListDisableAutoSelect = MessageUserStore.listDisableAutoSelect;
|
||||||
|
|
||||||
this.folderList = FolderUserStore.folderList;
|
this.folderList = FolderUserStore.folderList;
|
||||||
|
this.sortSupported = FolderUserStore.sortSupported;
|
||||||
|
|
||||||
this.composeInEdit = AppUserStore.composeInEdit;
|
this.composeInEdit = AppUserStore.composeInEdit;
|
||||||
this.leftPanelDisabled = leftPanelDisabled;
|
this.leftPanelDisabled = leftPanelDisabled;
|
||||||
|
|
@ -106,6 +107,7 @@ export class MessageListMailBoxUserView extends AbstractViewRight {
|
||||||
this.addObservables({
|
this.addObservables({
|
||||||
moveDropdownTrigger: false,
|
moveDropdownTrigger: false,
|
||||||
moreDropdownTrigger: false,
|
moreDropdownTrigger: false,
|
||||||
|
sortDropdownTrigger: false,
|
||||||
|
|
||||||
dragOverArea: null,
|
dragOverArea: null,
|
||||||
dragOverBodyArea: null,
|
dragOverBodyArea: null,
|
||||||
|
|
@ -174,7 +176,20 @@ export class MessageListMailBoxUserView extends AbstractViewRight {
|
||||||
|
|
||||||
mobileCheckedStateHide: () => ThemeStore.isMobile() ? !MessageUserStore.listChecked().length : true,
|
mobileCheckedStateHide: () => ThemeStore.isMobile() ? !MessageUserStore.listChecked().length : true,
|
||||||
|
|
||||||
messageListFocused: () => Scope.MessageList === AppUserStore.focusedState()
|
messageListFocused: () => Scope.MessageList === AppUserStore.focusedState(),
|
||||||
|
|
||||||
|
sortText: () => {
|
||||||
|
let mode = FolderUserStore.sortMode();
|
||||||
|
if ('' === mode) {
|
||||||
|
return '📅⬇';
|
||||||
|
}
|
||||||
|
mode = mode.split(/\s+/);
|
||||||
|
return (mode.includes('SIZE') ? '✉'
|
||||||
|
: (mode.includes('FROM') ? '@'
|
||||||
|
: (mode.includes('SUBJECT') ? '𝐒'
|
||||||
|
: '📅')))
|
||||||
|
+ (mode.includes('REVERSE') ? '⬇' : '⬆');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.hasCheckedOrSelectedLines = MessageUserStore.hasCheckedOrSelected,
|
this.hasCheckedOrSelectedLines = MessageUserStore.hasCheckedOrSelected,
|
||||||
|
|
@ -246,6 +261,11 @@ export class MessageListMailBoxUserView extends AbstractViewRight {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeSort(self, event) {
|
||||||
|
FolderUserStore.sortMode(event.target.closest('li').dataset.sort);
|
||||||
|
this.reloadCommand();
|
||||||
|
}
|
||||||
|
|
||||||
clearCommand() {
|
clearCommand() {
|
||||||
if (Settings.capa(Capa.DangerousActions)) {
|
if (Settings.capa(Capa.DangerousActions)) {
|
||||||
showScreenPopup(FolderClearPopupView, [FolderUserStore.currentFolder()]);
|
showScreenPopup(FolderClearPopupView, [FolderUserStore.currentFolder()]);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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) ?
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,15 @@ en:
|
||||||
YESTERDAY_AT: "yesterday at %TIME%"
|
YESTERDAY_AT: "yesterday at %TIME%"
|
||||||
NEW_MESSAGE_NOTIFICATION: "You have %COUNT% new messages!"
|
NEW_MESSAGE_NOTIFICATION: "You have %COUNT% new messages!"
|
||||||
QUOTA_SIZE: "Using <strong>%SIZE% (%PROC%%)</strong> of your <strong>%LIMIT%</strong>"
|
QUOTA_SIZE: "Using <strong>%SIZE% (%PROC%%)</strong> of your <strong>%LIMIT%</strong>"
|
||||||
|
SORT: "Sort"
|
||||||
|
SORT_DATE_ASC: "Date ascending"
|
||||||
|
SORT_DATE_DESC: "Date descending"
|
||||||
|
SORT_SIZE_ASC: "Size ascending"
|
||||||
|
SORT_SIZE_DESC: "Size descending"
|
||||||
|
SORT_SUBJECT_ASC: "Subject A-Z"
|
||||||
|
SORT_SUBJECT_DESC: "Subject Z-A"
|
||||||
|
SORT_FROM_ASC: "From A-Z"
|
||||||
|
SORT_FROM_DESC: "From Z-A"
|
||||||
MESSAGE:
|
MESSAGE:
|
||||||
BUTTON_EDIT: "Edit"
|
BUTTON_EDIT: "Edit"
|
||||||
BUTTON_UNSUBSCRIBE: "Unsubscribe from this list"
|
BUTTON_UNSUBSCRIBE: "Unsubscribe from this list"
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,15 @@ de_DE:
|
||||||
YESTERDAY_AT: "Gestern um %TIME%"
|
YESTERDAY_AT: "Gestern um %TIME%"
|
||||||
NEW_MESSAGE_NOTIFICATION: "Sie haben %COUNT% neue Nachrichten!"
|
NEW_MESSAGE_NOTIFICATION: "Sie haben %COUNT% neue Nachrichten!"
|
||||||
QUOTA_SIZE: "Sie nutzen <strong>%SIZE% (%PROC%%)</strong> von <strong>%LIMIT%</strong>."
|
QUOTA_SIZE: "Sie nutzen <strong>%SIZE% (%PROC%%)</strong> von <strong>%LIMIT%</strong>."
|
||||||
|
SORT: "Sortieren"
|
||||||
|
SORT_DATE_ASC: "Datum aufsteigend"
|
||||||
|
SORT_DATE_DESC: "Datum absteigend"
|
||||||
|
SORT_SIZE_ASC: "Größe aufsteigend"
|
||||||
|
SORT_SIZE_DESC: "Größe absteigend"
|
||||||
|
SORT_SUBJECT_ASC: "Betreff A-Z"
|
||||||
|
SORT_SUBJECT_DESC: "Betreff Z-A"
|
||||||
|
SORT_FROM_ASC: "Von A-Z"
|
||||||
|
SORT_FROM_DESC: "Von Z-A"
|
||||||
MESSAGE:
|
MESSAGE:
|
||||||
BUTTON_EDIT: "Bearbeiten"
|
BUTTON_EDIT: "Bearbeiten"
|
||||||
BUTTON_UNSUBSCRIBE: "Diese Liste abbestellen"
|
BUTTON_UNSUBSCRIBE: "Diese Liste abbestellen"
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,15 @@ en_GB:
|
||||||
YESTERDAY_AT: "yesterday at %TIME%"
|
YESTERDAY_AT: "yesterday at %TIME%"
|
||||||
NEW_MESSAGE_NOTIFICATION: "You have %COUNT% new messages!"
|
NEW_MESSAGE_NOTIFICATION: "You have %COUNT% new messages!"
|
||||||
QUOTA_SIZE: "Using <strong>%SIZE% (%PROC%%)</strong> of your <strong>%LIMIT%</strong>"
|
QUOTA_SIZE: "Using <strong>%SIZE% (%PROC%%)</strong> of your <strong>%LIMIT%</strong>"
|
||||||
|
SORT: "Sort"
|
||||||
|
SORT_DATE_ASC: "Date ascending"
|
||||||
|
SORT_DATE_DESC: "Date descending"
|
||||||
|
SORT_SIZE_ASC: "Size ascending"
|
||||||
|
SORT_SIZE_DESC: "Size descending"
|
||||||
|
SORT_SUBJECT_ASC: "Subject A-Z"
|
||||||
|
SORT_SUBJECT_DESC: "Subject Z-A"
|
||||||
|
SORT_FROM_ASC: "From A-Z"
|
||||||
|
SORT_FROM_DESC: "From Z-A"
|
||||||
MESSAGE:
|
MESSAGE:
|
||||||
BUTTON_EDIT: "Edit"
|
BUTTON_EDIT: "Edit"
|
||||||
BUTTON_UNSUBSCRIBE: "Unsubscribe from this list"
|
BUTTON_UNSUBSCRIBE: "Unsubscribe from this list"
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,15 @@ en_US:
|
||||||
YESTERDAY_AT: "yesterday at %TIME%"
|
YESTERDAY_AT: "yesterday at %TIME%"
|
||||||
NEW_MESSAGE_NOTIFICATION: "You have %COUNT% new messages!"
|
NEW_MESSAGE_NOTIFICATION: "You have %COUNT% new messages!"
|
||||||
QUOTA_SIZE: "Using <strong>%SIZE% (%PROC%%)</strong> of your <strong>%LIMIT%</strong>"
|
QUOTA_SIZE: "Using <strong>%SIZE% (%PROC%%)</strong> of your <strong>%LIMIT%</strong>"
|
||||||
|
SORT: "Sort"
|
||||||
|
SORT_DATE_ASC: "Date ascending"
|
||||||
|
SORT_DATE_DESC: "Date descending"
|
||||||
|
SORT_SIZE_ASC: "Size ascending"
|
||||||
|
SORT_SIZE_DESC: "Size descending"
|
||||||
|
SORT_SUBJECT_ASC: "Subject A-Z"
|
||||||
|
SORT_SUBJECT_DESC: "Subject Z-A"
|
||||||
|
SORT_FROM_ASC: "From A-Z"
|
||||||
|
SORT_FROM_DESC: "From Z-A"
|
||||||
MESSAGE:
|
MESSAGE:
|
||||||
BUTTON_EDIT: "Edit"
|
BUTTON_EDIT: "Edit"
|
||||||
BUTTON_UNSUBSCRIBE: "Unsubscribe from this list"
|
BUTTON_UNSUBSCRIBE: "Unsubscribe from this list"
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,15 @@ es_ES:
|
||||||
YESTERDAY_AT: "ayer a las %TIME%"
|
YESTERDAY_AT: "ayer a las %TIME%"
|
||||||
NEW_MESSAGE_NOTIFICATION: "Tiene %COUNT% nuevos mensajes."
|
NEW_MESSAGE_NOTIFICATION: "Tiene %COUNT% nuevos mensajes."
|
||||||
QUOTA_SIZE: "Usando <strong>%SIZE% (%PROC%%)</strong> de sus <strong>%LIMIT%</strong>"
|
QUOTA_SIZE: "Usando <strong>%SIZE% (%PROC%%)</strong> de sus <strong>%LIMIT%</strong>"
|
||||||
|
SORT: "Ordenar"
|
||||||
|
SORT_DATE_ASC: "Fecha ascendente"
|
||||||
|
SORT_DATE_DESC: "Fecha descendente"
|
||||||
|
SORT_SIZE_ASC: "Tamaño ascendente"
|
||||||
|
SORT_SIZE_DESC: "Tamaño descendente"
|
||||||
|
SORT_SUBJECT_ASC: "Asunto A-Z"
|
||||||
|
SORT_SUBJECT_DESC: "Asunto Z-A"
|
||||||
|
SORT_FROM_ASC: "Desde A-Z"
|
||||||
|
SORT_FROM_DESC: "Desde Z-A"
|
||||||
MESSAGE:
|
MESSAGE:
|
||||||
BUTTON_EDIT: "Editar"
|
BUTTON_EDIT: "Editar"
|
||||||
BUTTON_UNSUBSCRIBE: "Eliminar suscripción de esta lista"
|
BUTTON_UNSUBSCRIBE: "Eliminar suscripción de esta lista"
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,15 @@ fr_FR:
|
||||||
YESTERDAY_AT: "hier à %TIME%"
|
YESTERDAY_AT: "hier à %TIME%"
|
||||||
NEW_MESSAGE_NOTIFICATION: "Vous avez %COUNT% nouveaux messages !"
|
NEW_MESSAGE_NOTIFICATION: "Vous avez %COUNT% nouveaux messages !"
|
||||||
QUOTA_SIZE: "Vous utilisez <strong>%SIZE% (%PROC%%)</strong> sur <strong>%LIMIT%</strong>"
|
QUOTA_SIZE: "Vous utilisez <strong>%SIZE% (%PROC%%)</strong> sur <strong>%LIMIT%</strong>"
|
||||||
|
SORT: "Trier"
|
||||||
|
SORT_DATE_ASC: "Date ascendante"
|
||||||
|
SORT_DATE_DESC: "Date décroissante"
|
||||||
|
SORT_SIZE_ASC: "Taille ascendante"
|
||||||
|
SORT_SIZE_DESC: "Taille décroissante"
|
||||||
|
SORT_SUBJECT_ASC: "Sujet A-Z"
|
||||||
|
SORT_SUBJECT_DESC: "Sujet Z-A"
|
||||||
|
SORT_FROM_ASC: "De A-Z"
|
||||||
|
SORT_FROM_DESC: "De Z-A"
|
||||||
MESSAGE:
|
MESSAGE:
|
||||||
BUTTON_EDIT: "Modifier"
|
BUTTON_EDIT: "Modifier"
|
||||||
BUTTON_UNSUBSCRIBE: "Se désinscrire de cette liste"
|
BUTTON_UNSUBSCRIBE: "Se désinscrire de cette liste"
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,15 @@ nl_NL:
|
||||||
YESTERDAY_AT: "gisteren om %TIME%"
|
YESTERDAY_AT: "gisteren om %TIME%"
|
||||||
NEW_MESSAGE_NOTIFICATION: "U heeft %COUNT% nieuwe berichten!"
|
NEW_MESSAGE_NOTIFICATION: "U heeft %COUNT% nieuwe berichten!"
|
||||||
QUOTA_SIZE: "U gebruikt <strong>%SIZE% (%PROC%%)</strong> van <strong>%LIMIT%</strong>"
|
QUOTA_SIZE: "U gebruikt <strong>%SIZE% (%PROC%%)</strong> van <strong>%LIMIT%</strong>"
|
||||||
|
SORT: "Sorteren"
|
||||||
|
SORT_DATE_ASC: "Datum oplopend"
|
||||||
|
SORT_DATE_DESC: "Datum aflopend"
|
||||||
|
SORT_SIZE_ASC: "Grootte oplopend"
|
||||||
|
SORT_SIZE_DESC: "Grootte aflopend"
|
||||||
|
SORT_SUBJECT_ASC: "Onderwerp A-Z"
|
||||||
|
SORT_SUBJECT_DESC: "Onderwerp Z-A"
|
||||||
|
SORT_FROM_ASC: "Van A-Z"
|
||||||
|
SORT_FROM_DESC: "Van Z-A"
|
||||||
MESSAGE:
|
MESSAGE:
|
||||||
BUTTON_EDIT: "Aanpassen"
|
BUTTON_EDIT: "Aanpassen"
|
||||||
BUTTON_UNSUBSCRIBE: "Abonnement op deze e-mail lijst opzeggen"
|
BUTTON_UNSUBSCRIBE: "Abonnement op deze e-mail lijst opzeggen"
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,15 @@ zh_CN:
|
||||||
YESTERDAY_AT: "昨天 %TIME%"
|
YESTERDAY_AT: "昨天 %TIME%"
|
||||||
NEW_MESSAGE_NOTIFICATION: "您有 %COUNT% 封新邮件!"
|
NEW_MESSAGE_NOTIFICATION: "您有 %COUNT% 封新邮件!"
|
||||||
QUOTA_SIZE: "使用了 <strong>%LIMIT%</strong> 存储空间中的<strong>%SIZE% (%PROC%%)</strong>"
|
QUOTA_SIZE: "使用了 <strong>%LIMIT%</strong> 存储空间中的<strong>%SIZE% (%PROC%%)</strong>"
|
||||||
|
SORT: "排序"
|
||||||
|
SORT_DATE_ASC: "日期升序"
|
||||||
|
SORT_DATE_DESC: "日期降序"
|
||||||
|
SORT_SIZE_ASC: "大小递增"
|
||||||
|
SORT_SIZE_DESC: "大小递减"
|
||||||
|
SORT_SUBJECT_ASC: "主题 A-Z"
|
||||||
|
SORT_SUBJECT_DESC: "主题 Z-A"
|
||||||
|
SORT_FROM_ASC: "发送自 A-Z"
|
||||||
|
SORT_FROM_DESC: "发送自 Z-A"
|
||||||
MESSAGE:
|
MESSAGE:
|
||||||
BUTTON_EDIT: "编辑"
|
BUTTON_EDIT: "编辑"
|
||||||
BUTTON_UNSUBSCRIBE: "从此列表中取消订阅"
|
BUTTON_UNSUBSCRIBE: "从此列表中取消订阅"
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,59 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="btn-group dropdown sortFolder" data-bind="visible: sortSupported() && mobileCheckedStateHide(), registerBootstrapDropdown: true, openDropdownTrigger: sortDropdownTrigger">
|
||||||
|
<a id="sort-list-dropdown-id" class="btn dropdown-toggle fontastic" href="#" tabindex="-1" data-toggle="dropdown" data-i18n="[title]MESSAGE_LIST/SORT" data-bind="text: sortText">⬇</a>
|
||||||
|
<ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="sort-list-dropdown-id">
|
||||||
|
<li class="e-item" role="presentation" data-sort="DATE" data-bind="click: changeSort">
|
||||||
|
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||||
|
<i class="fontastic">📅⬆</i>
|
||||||
|
<span data-i18n="MESSAGE_LIST/SORT_DATE_ASC"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="e-item" role="presentation" data-sort="" data-bind="click: changeSort">
|
||||||
|
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||||
|
<i class="fontastic">📅⬇</i>
|
||||||
|
<span data-i18n="MESSAGE_LIST/SORT_DATE_DESC"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="e-item dividerbar" role="presentation" data-sort="SIZE" data-bind="click: changeSort">
|
||||||
|
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||||
|
<i class="fontastic">✉⬆</i>
|
||||||
|
<span data-i18n="MESSAGE_LIST/SORT_SIZE_ASC"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="e-item" role="presentation" data-sort="REVERSE SIZE" data-bind="click: changeSort">
|
||||||
|
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||||
|
<i class="fontastic">✉⬇</i>
|
||||||
|
<span data-i18n="MESSAGE_LIST/SORT_SIZE_DESC"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="e-item dividerbar" role="presentation" data-sort="SUBJECT" data-bind="click: changeSort">
|
||||||
|
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||||
|
<i class="fontastic">𝐒⬆</i>
|
||||||
|
<span data-i18n="MESSAGE_LIST/SORT_SUBJECT_ASC"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="e-item" role="presentation" data-sort="REVERSE SUBJECT" data-bind="click: changeSort">
|
||||||
|
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||||
|
<i class="fontastic">𝐒⬇</i>
|
||||||
|
<span data-i18n="MESSAGE_LIST/SORT_SUBJECT_DESC"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="e-item dividerbar" role="presentation" data-sort="FROM" data-bind="click: changeSort">
|
||||||
|
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||||
|
<i class="fontastic">@⬆</i>
|
||||||
|
<span data-i18n="MESSAGE_LIST/SORT_FROM_ASC"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="e-item" role="presentation" data-sort="REVERSE FROM" data-bind="click: changeSort">
|
||||||
|
<a class="e-link menuitem" href="#" tabindex="-1">
|
||||||
|
<i class="fontastic">@⬇</i>
|
||||||
|
<span data-i18n="MESSAGE_LIST/SORT_FROM_DESC"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="b-message-list-wrapper">
|
<div class="b-message-list-wrapper">
|
||||||
|
|
|
||||||
BIN
vendors/fontastic/fonts/snappymail.woff
vendored
BIN
vendors/fontastic/fonts/snappymail.woff
vendored
Binary file not shown.
BIN
vendors/fontastic/fonts/snappymail.woff2
vendored
BIN
vendors/fontastic/fonts/snappymail.woff2
vendored
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue