mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Resolve #1028
This commit is contained in:
parent
1f8fddd3f4
commit
26fb2cf9fb
6 changed files with 56 additions and 35 deletions
|
|
@ -113,14 +113,12 @@ export class MessageModel extends AbstractModel {
|
||||||
|
|
||||||
// rfc8621
|
// rfc8621
|
||||||
id: '',
|
id: '',
|
||||||
// threadId: '',
|
// threadId: ''
|
||||||
|
|
||||||
hasUnseenSubMessage: false,
|
|
||||||
hasFlaggedSubMessage: false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.attachments = ko.observableArray(new AttachmentCollectionModel);
|
this.attachments = ko.observableArray(new AttachmentCollectionModel);
|
||||||
this.threads = ko.observableArray();
|
this.threads = ko.observableArray();
|
||||||
|
this.threadUnseen = ko.observableArray();
|
||||||
this.unsubsribeLinks = ko.observableArray();
|
this.unsubsribeLinks = ko.observableArray();
|
||||||
this.flags = ko.observableArray();
|
this.flags = ko.observableArray();
|
||||||
|
|
||||||
|
|
@ -128,6 +126,7 @@ export class MessageModel extends AbstractModel {
|
||||||
attachmentIconClass: () =>
|
attachmentIconClass: () =>
|
||||||
this.encrypted() ? 'icon-lock' : FileInfo.getAttachmentsIconClass(this.attachments()),
|
this.encrypted() ? 'icon-lock' : FileInfo.getAttachmentsIconClass(this.attachments()),
|
||||||
threadsLen: () => rl.app.messageList.threadUid() ? 0 : this.threads().length,
|
threadsLen: () => rl.app.messageList.threadUid() ? 0 : this.threads().length,
|
||||||
|
threadUnseenLen: () => rl.app.messageList.threadUid() ? 0 : this.threadUnseen().length,
|
||||||
|
|
||||||
isUnseen: () => !this.flags().includes('\\seen'),
|
isUnseen: () => !this.flags().includes('\\seen'),
|
||||||
isFlagged: () => this.flags().includes('\\flagged'),
|
isFlagged: () => this.flags().includes('\\flagged'),
|
||||||
|
|
@ -238,9 +237,7 @@ export class MessageModel extends AbstractModel {
|
||||||
focused: this.focused(),
|
focused: this.focused(),
|
||||||
priorityHigh: this.priority() === 1,
|
priorityHigh: this.priority() === 1,
|
||||||
withAttachments: !!this.attachments().length,
|
withAttachments: !!this.attachments().length,
|
||||||
// hasChildrenMessage: 1 < this.threadsLen(),
|
// hasChildrenMessage: 1 < this.threadsLen()
|
||||||
hasUnseenSubMessage: this.hasUnseenSubMessage(),
|
|
||||||
hasFlaggedSubMessage: this.hasFlaggedSubMessage()
|
|
||||||
}, (key, value) => value && classes.push(key));
|
}, (key, value) => value && classes.push(key));
|
||||||
flags && this.flags().forEach(value => classes.push('msgflag-'+value));
|
flags && this.flags().forEach(value => classes.push('msgflag-'+value));
|
||||||
return classes.join(' ');
|
return classes.join(' ');
|
||||||
|
|
|
||||||
|
|
@ -289,16 +289,6 @@
|
||||||
margin-right: 0.25em;
|
margin-right: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.hasUnseenSubMessage {
|
|
||||||
background-color: rgba(255, 255, 64, 0.15);
|
|
||||||
border-left-color: lighten(orange, 30%);
|
|
||||||
border-right-color: lighten(orange, 30%) !important;
|
|
||||||
&.focused {
|
|
||||||
border-left-color: darken(orange, 10%);
|
|
||||||
border-right-color: darken(orange, 10%) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.unseen {
|
&.unseen {
|
||||||
background-color: rgba(255, 255, 64, 0.15);
|
background-color: rgba(255, 255, 64, 0.15);
|
||||||
border-left-color: orange;
|
border-left-color: orange;
|
||||||
|
|
@ -312,6 +302,9 @@
|
||||||
border-right-color: darken(orange, 10%) !important;
|
border-right-color: darken(orange, 10%) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
[data-unseen] {
|
||||||
|
background-color: rgba(255, 255, 64, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
&.checked {
|
&.checked {
|
||||||
border-left-color: lighten(#398CF2, 10%);
|
border-left-color: lighten(#398CF2, 10%);
|
||||||
|
|
|
||||||
|
|
@ -446,7 +446,8 @@ class MailClient
|
||||||
* @throws \MailSo\Net\Exceptions\*
|
* @throws \MailSo\Net\Exceptions\*
|
||||||
* @throws \MailSo\Imap\Exceptions\*
|
* @throws \MailSo\Imap\Exceptions\*
|
||||||
*/
|
*/
|
||||||
protected function MessageListByRequestIndexOrUids(MessageCollection $oMessageCollection, SequenceSet $oRange, array &$aAllThreads = []) : void
|
protected function MessageListByRequestIndexOrUids(MessageCollection $oMessageCollection, SequenceSet $oRange,
|
||||||
|
array &$aAllThreads = [], array &$aUnseenUIDs = []) : void
|
||||||
{
|
{
|
||||||
if (\count($oRange)) {
|
if (\count($oRange)) {
|
||||||
$aFetchIterator = $this->oImapClient->FetchIterate(array(
|
$aFetchIterator = $this->oImapClient->FetchIterate(array(
|
||||||
|
|
@ -472,6 +473,7 @@ class MailClient
|
||||||
foreach ($aAllThreads as $aMap) {
|
foreach ($aAllThreads as $aMap) {
|
||||||
if (\in_array($iUid, $aMap)) {
|
if (\in_array($iUid, $aMap)) {
|
||||||
$oMessage->SetThreads($aMap);
|
$oMessage->SetThreads($aMap);
|
||||||
|
$oMessage->SetThreadUnseen(\array_values(\array_intersect($aUnseenUIDs, $aMap)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -490,7 +492,7 @@ class MailClient
|
||||||
* @throws \MailSo\Imap\Exceptions\*
|
* @throws \MailSo\Imap\Exceptions\*
|
||||||
*/
|
*/
|
||||||
private function GetUids(MessageListParams $oParams, string $sSearch,
|
private function GetUids(MessageListParams $oParams, string $sSearch,
|
||||||
string $sFolderHash, bool $bUseSort = false) : array
|
FolderInformation $oInfo, bool $bUseSort = false) : array
|
||||||
{
|
{
|
||||||
$oCacher = $oParams->oCacher;
|
$oCacher = $oParams->oCacher;
|
||||||
$sFolderName = $oParams->sFolderName;
|
$sFolderName = $oParams->sFolderName;
|
||||||
|
|
@ -562,7 +564,7 @@ class MailClient
|
||||||
if (!empty($sSerialized)) {
|
if (!empty($sSerialized)) {
|
||||||
$aSerialized = \json_decode($sSerialized, true);
|
$aSerialized = \json_decode($sSerialized, true);
|
||||||
if (\is_array($aSerialized) && isset($aSerialized['FolderHash'], $aSerialized['Uids']) &&
|
if (\is_array($aSerialized) && isset($aSerialized['FolderHash'], $aSerialized['Uids']) &&
|
||||||
$sFolderHash === $aSerialized['FolderHash'] &&
|
$oInfo->etag === $aSerialized['FolderHash'] &&
|
||||||
\is_array($aSerialized['Uids'])
|
\is_array($aSerialized['Uids'])
|
||||||
) {
|
) {
|
||||||
$this->logWrite('Get Serialized '.($bReturnUid?'UIDS':'IDS').' from cache ('.$sSerializedLog.') [count:'.\count($aSerialized['Uids']).']');
|
$this->logWrite('Get Serialized '.($bReturnUid?'UIDS':'IDS').' from cache ('.$sSerializedLog.') [count:'.\count($aSerialized['Uids']).']');
|
||||||
|
|
@ -594,7 +596,7 @@ class MailClient
|
||||||
|
|
||||||
if ($bUseCacheAfterSearch) {
|
if ($bUseCacheAfterSearch) {
|
||||||
$oCacher->Set($sSerializedHash, \json_encode(array(
|
$oCacher->Set($sSerializedHash, \json_encode(array(
|
||||||
'FolderHash' => $sFolderHash,
|
'FolderHash' => $oInfo->etag,
|
||||||
'Uids' => $aResultUids
|
'Uids' => $aResultUids
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
|
@ -604,6 +606,23 @@ class MailClient
|
||||||
return $aResultUids;
|
return $aResultUids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function MessageListUnseen(MessageListParams $oParams, FolderInformation $oInfo) : array
|
||||||
|
{
|
||||||
|
$oUnseenParams = new MessageListParams;
|
||||||
|
$oUnseenParams->sFolderName = $oParams->sFolderName;
|
||||||
|
// $oUnseenParams->sSearch = $oParams->sSearch;
|
||||||
|
// $oUnseenParams->sSort = $oParams->sSort;
|
||||||
|
$oUnseenParams->oCacher = $oParams->oCacher;
|
||||||
|
$oUnseenParams->bUseSort = false; // $oParams->bUseSort
|
||||||
|
$oUnseenParams->bUseThreads = false; // $oParams->bUseThreads;
|
||||||
|
$oUnseenParams->bHideDeleted = $oParams->bHideDeleted;
|
||||||
|
// $oUnseenParams->iOffset = $oParams->iOffset;
|
||||||
|
// $oUnseenParams->iLimit = $oParams->iLimit;
|
||||||
|
// $oUnseenParams->iPrevUidNext = $oParams->iPrevUidNext;
|
||||||
|
// $oUnseenParams->iThreadUid = $oParams->iThreadUid;
|
||||||
|
return $this->GetUids($oUnseenParams, 'unseen', $oInfo);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs SORT/SEARCH when $sSearch is provided
|
* Runs SORT/SEARCH when $sSearch is provided
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
|
|
@ -646,6 +665,7 @@ class MailClient
|
||||||
if ($oInfo->MESSAGES) {
|
if ($oInfo->MESSAGES) {
|
||||||
$bUseSort = $oParams->bUseSort || $oParams->sSort;
|
$bUseSort = $oParams->bUseSort || $oParams->sSort;
|
||||||
$aAllThreads = [];
|
$aAllThreads = [];
|
||||||
|
$aUnseenUIDs = [];
|
||||||
$aUids = [];
|
$aUids = [];
|
||||||
|
|
||||||
$message_list_limit = $this->oImapClient->Settings->message_list_limit;
|
$message_list_limit = $this->oImapClient->Settings->message_list_limit;
|
||||||
|
|
@ -657,7 +677,7 @@ class MailClient
|
||||||
$this->logWrite('List optimization (count: '.$oInfo->MESSAGES.', limit:'.$message_list_limit.')');
|
$this->logWrite('List optimization (count: '.$oInfo->MESSAGES.', limit:'.$message_list_limit.')');
|
||||||
if (\strlen($sSearch)) {
|
if (\strlen($sSearch)) {
|
||||||
// Don't use SORT for speed
|
// Don't use SORT for speed
|
||||||
$aUids = $this->GetUids($oParams, $sSearch, $oInfo->etag/*, $bUseSort*/);
|
$aUids = $this->GetUids($oParams, $sSearch, $oInfo/*, $bUseSort*/);
|
||||||
} else {
|
} else {
|
||||||
$bUseSort = $this->oImapClient->hasCapability('SORT');
|
$bUseSort = $this->oImapClient->hasCapability('SORT');
|
||||||
if (2 > $oInfo->MESSAGES) {
|
if (2 > $oInfo->MESSAGES) {
|
||||||
|
|
@ -667,7 +687,7 @@ class MailClient
|
||||||
$end = \min($oInfo->MESSAGES, \max(1, $oInfo->MESSAGES - $oParams->iOffset + $oParams->iLimit));
|
$end = \min($oInfo->MESSAGES, \max(1, $oInfo->MESSAGES - $oParams->iOffset + $oParams->iLimit));
|
||||||
$start = \max(1, $end - ($oParams->iLimit * 3) + 1);
|
$start = \max(1, $end - ($oParams->iLimit * 3) + 1);
|
||||||
$oParams->oSequenceSet = new SequenceSet(\range($end, $start), false);
|
$oParams->oSequenceSet = new SequenceSet(\range($end, $start), false);
|
||||||
$aRequestIndexes = $this->GetUids($oParams, '', $oInfo->etag, $bUseSort);
|
$aRequestIndexes = $this->GetUids($oParams, '', $oInfo, $bUseSort);
|
||||||
// Attempt to get the correct $oParams->iLimit slice
|
// Attempt to get the correct $oParams->iLimit slice
|
||||||
$aRequestIndexes = \array_slice($aRequestIndexes, $oParams->iOffset ? $oParams->iLimit : 0, $oParams->iLimit);
|
$aRequestIndexes = \array_slice($aRequestIndexes, $oParams->iOffset ? $oParams->iLimit : 0, $oParams->iLimit);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -681,7 +701,7 @@ class MailClient
|
||||||
} else {
|
} else {
|
||||||
$aUids = ($bUseThreads && $oParams->iThreadUid)
|
$aUids = ($bUseThreads && $oParams->iThreadUid)
|
||||||
? [$oParams->iThreadUid]
|
? [$oParams->iThreadUid]
|
||||||
: $this->GetUids($oParams, '', $oInfo->etag, $bUseSort);
|
: $this->GetUids($oParams, '', $oInfo, $bUseSort);
|
||||||
|
|
||||||
if ($bUseThreads) {
|
if ($bUseThreads) {
|
||||||
$aAllThreads = $this->MessageListThreadsMap($oMessageCollection, $oParams->oCacher);
|
$aAllThreads = $this->MessageListThreadsMap($oMessageCollection, $oParams->oCacher);
|
||||||
|
|
@ -706,11 +726,13 @@ class MailClient
|
||||||
$threadedUids = \array_merge($threadedUids, $aMap);
|
$threadedUids = \array_merge($threadedUids, $aMap);
|
||||||
}
|
}
|
||||||
$aUids = \array_diff($aUids, $threadedUids);
|
$aUids = \array_diff($aUids, $threadedUids);
|
||||||
|
// Get all unseen
|
||||||
|
$aUnseenUIDs = $this->MessageListUnseen($oParams, $oInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($aUids && \strlen($sSearch)) {
|
if ($aUids && \strlen($sSearch)) {
|
||||||
$aSearchedUids = $this->GetUids($oParams, $sSearch, $oInfo->etag/*, $bUseSort*/);
|
$aSearchedUids = $this->GetUids($oParams, $sSearch, $oInfo/*, $bUseSort*/);
|
||||||
if ($bUseThreads && !$oParams->iThreadUid) {
|
if ($bUseThreads && !$oParams->iThreadUid) {
|
||||||
$matchingThreadUids = [];
|
$matchingThreadUids = [];
|
||||||
foreach ($aAllThreads as $aMap) {
|
foreach ($aAllThreads as $aMap) {
|
||||||
|
|
@ -732,7 +754,7 @@ class MailClient
|
||||||
if (\count($aUids)) {
|
if (\count($aUids)) {
|
||||||
$oMessageCollection->totalEmails = \count($aUids);
|
$oMessageCollection->totalEmails = \count($aUids);
|
||||||
$aUids = \array_slice($aUids, $oParams->iOffset, $oParams->iLimit);
|
$aUids = \array_slice($aUids, $oParams->iOffset, $oParams->iLimit);
|
||||||
$this->MessageListByRequestIndexOrUids($oMessageCollection, new SequenceSet($aUids), $aAllThreads);
|
$this->MessageListByRequestIndexOrUids($oMessageCollection, new SequenceSet($aUids), $aAllThreads, $aUnseenUIDs);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->logWrite('No messages in '.$oMessageCollection->FolderName);
|
$this->logWrite('No messages in '.$oMessageCollection->FolderName);
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,8 @@ class Message implements \JsonSerializable
|
||||||
// $aFlags = [],
|
// $aFlags = [],
|
||||||
$aFlagsLowerCase = [],
|
$aFlagsLowerCase = [],
|
||||||
$UnsubsribeLinks = [],
|
$UnsubsribeLinks = [],
|
||||||
$aThreads = [];
|
$aThreadUIDs = [],
|
||||||
|
$aThreadUnseenUIDs = [];
|
||||||
|
|
||||||
private ?array $DraftInfo = null;
|
private ?array $DraftInfo = null;
|
||||||
private ?array $pgpSigned = null;
|
private ?array $pgpSigned = null;
|
||||||
|
|
@ -117,9 +118,14 @@ class Message implements \JsonSerializable
|
||||||
$this->SpamScore = \intval(\max(0, \min(100, $value)));
|
$this->SpamScore = \intval(\max(0, \min(100, $value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetThreads(array $aThreads)
|
public function SetThreads(array $aThreadUIDs)
|
||||||
{
|
{
|
||||||
$this->aThreads = $aThreads;
|
$this->aThreadUIDs = $aThreadUIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SetThreadUnseen(array $aUnseenUIDs)
|
||||||
|
{
|
||||||
|
$this->aThreadUnseenUIDs = $aUnseenUIDs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function fromFetchResponse(string $sFolder, \MailSo\Imap\FetchResponse $oFetchResponse, ?\MailSo\Imap\BodyStructure $oBodyStructure = null) : self
|
public static function fromFetchResponse(string $sFolder, \MailSo\Imap\FetchResponse $oFetchResponse, ?\MailSo\Imap\BodyStructure $oBodyStructure = null) : self
|
||||||
|
|
@ -456,7 +462,7 @@ class Message implements \JsonSerializable
|
||||||
$this->sFolder,
|
$this->sFolder,
|
||||||
$this->Uid,
|
$this->Uid,
|
||||||
\implode(',', $this->getFlags()),
|
\implode(',', $this->getFlags()),
|
||||||
// \implode(',', $this->aThreads),
|
// \implode(',', $this->aThreadUIDs),
|
||||||
$sClientHash
|
$sClientHash
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
@ -576,8 +582,11 @@ class Message implements \JsonSerializable
|
||||||
$result['pgpEncrypted'] = $this->pgpEncrypted;
|
$result['pgpEncrypted'] = $this->pgpEncrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->aThreads) {
|
if ($this->aThreadUIDs) {
|
||||||
$result['threads'] = $this->aThreads;
|
$result['threads'] = $this->aThreadUIDs;
|
||||||
|
}
|
||||||
|
if ($this->aThreadUnseenUIDs) {
|
||||||
|
$result['threadUnseen'] = $this->aThreadUnseenUIDs;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ class MessageListParams
|
||||||
$bHideDeleted = true;
|
$bHideDeleted = true;
|
||||||
|
|
||||||
protected int
|
protected int
|
||||||
$iOffset,
|
$iOffset = 0,
|
||||||
$iLimit,
|
$iLimit = 0,
|
||||||
$iPrevUidNext = 0, // used to check for new messages
|
$iPrevUidNext = 0, // used to check for new messages
|
||||||
$iThreadUid = 0;
|
$iThreadUid = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@
|
||||||
<div class="flagParent fontastic"></div>
|
<div class="flagParent fontastic"></div>
|
||||||
<div class="senderParent actionHandle" data-bind="attr: {'title': senderClearEmailsString}, text: senderEmailsString"></div>
|
<div class="senderParent actionHandle" data-bind="attr: {'title': senderClearEmailsString}, text: senderEmailsString"></div>
|
||||||
<div class="subjectParent actionHandle" data-bind="text: subject"></div>
|
<div class="subjectParent actionHandle" data-bind="text: subject"></div>
|
||||||
<div class="threads-len" data-bind="visible: 1 < threadsLen(), text: threadsLen"></div>
|
<div class="threads-len" data-bind="visible: 1 < threadsLen(), text: threadsLen, attr: {'data-unseen': threadUnseenLen() || null}"></div>
|
||||||
<div class="attachmentParent actionHandle">
|
<div class="attachmentParent actionHandle">
|
||||||
<i data-bind="css: attachmentIconClass"></i>
|
<i data-bind="css: attachmentIconClass"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue