mirror of
https://github.com/the-djmaze/snappymail.git
synced 2025-03-04 18:53:42 +08:00
Improved FolderCollection handling and try to solve https://github.com/the-djmaze/snappymail/issues/112#issuecomment-904193275
This commit is contained in:
parent
2def2fc118
commit
327d6c32ca
9 changed files with 115 additions and 112 deletions
|
@ -25,7 +25,7 @@ const
|
|||
INBOX: 1,
|
||||
SENT: 2,
|
||||
DRAFTS: 3,
|
||||
JUNK: 4,
|
||||
SPAM: 4, // JUNK
|
||||
TRASH: 5,
|
||||
// IMPORTANT: 10,
|
||||
// FLAGGED: 11,
|
||||
|
@ -39,6 +39,8 @@ normalizeFolder = sFolderFullNameRaw => ('' === sFolderFullNameRaw
|
|||
? sFolderFullNameRaw
|
||||
: '';
|
||||
|
||||
let SystemFolders = {};
|
||||
|
||||
export class FolderCollectionModel extends AbstractCollectionModel
|
||||
{
|
||||
/*
|
||||
|
@ -59,7 +61,11 @@ export class FolderCollectionModel extends AbstractCollectionModel
|
|||
*/
|
||||
static reviveFromJson(object) {
|
||||
const expandedFolders = Local.get(ClientSideKeyName.ExpandedFolders);
|
||||
return super.reviveFromJson(object, (oFolder, self) => {
|
||||
if (object && object.SystemFolders) {
|
||||
SystemFolders = object.SystemFolders;
|
||||
}
|
||||
|
||||
return super.reviveFromJson(object, oFolder => {
|
||||
let oCacheFolder = Cache.getFolderFromCacheList(oFolder.FullNameRaw);
|
||||
/*
|
||||
if (oCacheFolder) {
|
||||
|
@ -68,7 +74,7 @@ export class FolderCollectionModel extends AbstractCollectionModel
|
|||
}
|
||||
*/
|
||||
if (!oCacheFolder && (oCacheFolder = FolderModel.reviveFromJson(oFolder))) {
|
||||
if (oFolder.FullNameRaw == self.SystemFolders[ServerFolderType.INBOX]) {
|
||||
if (oFolder.FullNameRaw == SystemFolders[ServerFolderType.INBOX]) {
|
||||
oCacheFolder.type(FolderType.Inbox);
|
||||
Cache.setFolderInboxName(oFolder.FullNameRaw);
|
||||
}
|
||||
|
@ -76,6 +82,25 @@ export class FolderCollectionModel extends AbstractCollectionModel
|
|||
}
|
||||
|
||||
if (oCacheFolder) {
|
||||
switch (oFolder.FullNameRaw)
|
||||
{
|
||||
case (SettingsGet('SentFolder') || SystemFolders[ServerFolderType.SENT]):
|
||||
oCacheFolder.type(FolderType.Sent);
|
||||
break;
|
||||
case (SettingsGet('DraftFolder') || SystemFolders[ServerFolderType.DRAFTS]):
|
||||
oCacheFolder.type(FolderType.Drafts);
|
||||
break;
|
||||
case (SettingsGet('SpamFolder') || SystemFolders[ServerFolderType.SPAM]):
|
||||
oCacheFolder.type(FolderType.Spam);
|
||||
break;
|
||||
case (SettingsGet('TrashFolder') || SystemFolders[ServerFolderType.TRASH]):
|
||||
oCacheFolder.type(FolderType.Trash);
|
||||
break;
|
||||
case (SettingsGet('ArchiveFolder') || SystemFolders[ServerFolderType.ARCHIVE]):
|
||||
oCacheFolder.type(FolderType.Archive);
|
||||
break;
|
||||
}
|
||||
|
||||
oCacheFolder.collapsed(!expandedFolders
|
||||
|| !isArray(expandedFolders)
|
||||
|| !expandedFolders.includes(oCacheFolder.fullNameHash));
|
||||
|
@ -104,6 +129,23 @@ export class FolderCollectionModel extends AbstractCollectionModel
|
|||
FolderUserStore.displaySpecSetting(0 >= cnt
|
||||
|| Math.max(10, Math.min(100, pInt(Settings.app('folderSpecLimit')))) < cnt);
|
||||
|
||||
if (SystemFolders &&
|
||||
!('' +
|
||||
SettingsGet('SentFolder') +
|
||||
SettingsGet('DraftFolder') +
|
||||
SettingsGet('SpamFolder') +
|
||||
SettingsGet('TrashFolder') +
|
||||
SettingsGet('ArchiveFolder'))
|
||||
) {
|
||||
FolderUserStore.saveSystemFolders({
|
||||
SentFolder: SystemFolders[ServerFolderType.SENT] || null,
|
||||
DraftFolder: SystemFolders[ServerFolderType.DRAFTS] || null,
|
||||
SpamFolder: SystemFolders[ServerFolderType.SPAM] || null,
|
||||
TrashFolder: SystemFolders[ServerFolderType.TRASH] || null,
|
||||
ArchiveFolder: SystemFolders[ServerFolderType.ARCHIVE] || null
|
||||
});
|
||||
}
|
||||
|
||||
FolderUserStore.folderList(this);
|
||||
|
||||
if (undefined !== this.Namespace) {
|
||||
|
@ -115,41 +157,13 @@ export class FolderCollectionModel extends AbstractCollectionModel
|
|||
FolderUserStore.folderListOptimized(!!this.Optimized);
|
||||
FolderUserStore.sortSupported(!!this.IsSortSupported);
|
||||
|
||||
let update = false;
|
||||
|
||||
if (
|
||||
this.SystemFolders &&
|
||||
!('' +
|
||||
SettingsGet('SentFolder') +
|
||||
SettingsGet('DraftFolder') +
|
||||
SettingsGet('SpamFolder') +
|
||||
SettingsGet('TrashFolder') +
|
||||
SettingsGet('ArchiveFolder'))
|
||||
) {
|
||||
Settings.set('SentFolder', this.SystemFolders[ServerFolderType.SENT] || null);
|
||||
Settings.set('DraftFolder', this.SystemFolders[ServerFolderType.DRAFTS] || null);
|
||||
Settings.set('SpamFolder', this.SystemFolders[ServerFolderType.JUNK] || null);
|
||||
Settings.set('TrashFolder', this.SystemFolders[ServerFolderType.TRASH] || null);
|
||||
Settings.set('ArchiveFolder', this.SystemFolders[ServerFolderType.ARCHIVE] || null);
|
||||
|
||||
update = true;
|
||||
}
|
||||
|
||||
FolderUserStore.sentFolder(normalizeFolder(SettingsGet('SentFolder')));
|
||||
FolderUserStore.draftFolder(normalizeFolder(SettingsGet('DraftFolder')));
|
||||
FolderUserStore.spamFolder(normalizeFolder(SettingsGet('SpamFolder')));
|
||||
FolderUserStore.trashFolder(normalizeFolder(SettingsGet('TrashFolder')));
|
||||
FolderUserStore.archiveFolder(normalizeFolder(SettingsGet('ArchiveFolder')));
|
||||
|
||||
if (update) {
|
||||
rl.app.Remote.saveSystemFolders(()=>0, {
|
||||
SentFolder: FolderUserStore.sentFolder(),
|
||||
DraftFolder: FolderUserStore.draftFolder(),
|
||||
SpamFolder: FolderUserStore.spamFolder(),
|
||||
TrashFolder: FolderUserStore.trashFolder(),
|
||||
ArchiveFolder: FolderUserStore.archiveFolder()
|
||||
});
|
||||
}
|
||||
// FolderUserStore.folderList.valueHasMutated();
|
||||
|
||||
Local.set(ClientSideKeyName.FoldersLashHash, this.FoldersHash);
|
||||
}
|
||||
|
@ -254,26 +268,22 @@ export class FolderModel extends AbstractModel {
|
|||
|
||||
hasSubscribedSubfolders:
|
||||
() =>
|
||||
!!folder.subFolders.find(
|
||||
!!folder.subFolders().find(
|
||||
oFolder => (oFolder.subscribed() || oFolder.hasSubscribedSubfolders()) && !oFolder.isSystemFolder()
|
||||
),
|
||||
|
||||
canBeEdited: () => FolderType.User === folder.type() && folder.exists/* && folder.selectable*/,
|
||||
|
||||
visible: () => {
|
||||
const isSubscribed = folder.subscribed(),
|
||||
isSubFolders = folder.hasSubscribedSubfolders();
|
||||
|
||||
return isSubscribed || (isSubFolders && (!folder.exists/* || !folder.selectable*/));
|
||||
const hasSubFolders = folder.hasSubscribedSubfolders();
|
||||
return folder.subscribed() || hasSubFolders;
|
||||
},
|
||||
|
||||
isSystemFolder: () => FolderType.User !== folder.type(),
|
||||
|
||||
hidden: () => {
|
||||
const isSystem = folder.isSystemFolder(),
|
||||
isSubFolders = folder.hasSubscribedSubfolders();
|
||||
|
||||
return (isSystem && !isSubFolders) || (!folder.selectable && !isSubFolders);
|
||||
let hasSubFolders = folder.hasSubscribedSubfolders();
|
||||
return (folder.isSystemFolder() || !folder.selectable) && !hasSubFolders;
|
||||
},
|
||||
|
||||
printableUnreadCount: () => {
|
||||
|
@ -298,7 +308,7 @@ export class FolderModel extends AbstractModel {
|
|||
return null;
|
||||
},
|
||||
|
||||
canBeDeleted: () => !folder.isSystemFolder() && !folder.subFolders.length,
|
||||
canBeDeleted: () => !folder.isSystemFolder() && !folder.subFolders().length,
|
||||
|
||||
canBeSubscribed: () => !folder.isSystemFolder()
|
||||
&& SettingsUserStore.hideUnsubscribed()
|
||||
|
@ -329,16 +339,16 @@ export class FolderModel extends AbstractModel {
|
|||
},
|
||||
|
||||
collapsed: {
|
||||
read: () => !folder.hidden() && folder.collapsedPrivate(),
|
||||
write: (value) => {
|
||||
folder.collapsedPrivate(value);
|
||||
}
|
||||
read: () => folder.isInbox() && FolderUserStore.singleRootFolder()
|
||||
? false
|
||||
: !folder.hidden() && folder.collapsedPrivate(),
|
||||
write: value => folder.collapsedPrivate(value)
|
||||
},
|
||||
|
||||
hasUnreadMessages: () => 0 < folder.messageCountUnread() && folder.printableUnreadCount(),
|
||||
|
||||
hasSubscribedUnreadMessagesSubfolders: () =>
|
||||
!!folder.subFolders.find(
|
||||
!!folder.subFolders().find(
|
||||
folder => folder.hasUnreadMessages() || folder.hasSubscribedUnreadMessagesSubfolders()
|
||||
)
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ import { FolderType, FolderSortMode } from 'Common/EnumsUser';
|
|||
import { UNUSED_OPTION_VALUE } from 'Common/Consts';
|
||||
import { addObservablesTo, addSubscribablesTo } from 'Common/Utils';
|
||||
import { getFolderInboxName, getFolderFromCacheList } from 'Common/Cache';
|
||||
import { SettingsGet } from 'Common/Globals';
|
||||
import { Settings, SettingsGet } from 'Common/Globals';
|
||||
|
||||
export const FolderUserStore = new class {
|
||||
constructor() {
|
||||
|
@ -55,9 +55,17 @@ export const FolderUserStore = new class {
|
|||
() => !this.draftFolder() || UNUSED_OPTION_VALUE === this.draftFolder()
|
||||
);
|
||||
|
||||
this.foldersListWithSingleInboxRootFolder = ko.computed(
|
||||
() => !this.folderList.find(folder => folder && !folder.isSystemFolder() && folder.visible())
|
||||
);
|
||||
// foldersListWithSingleInboxRootFolder
|
||||
/** returns true when there are no non-system folders in the root of the folders tree */
|
||||
this.singleRootFolder = ko.computed(() => {
|
||||
let multiple = false;
|
||||
this.folderList.forEach(folder => {
|
||||
let subscribed = folder.subscribed(),
|
||||
hasSub = folder.hasSubscribedSubfolders();
|
||||
multiple |= (!folder.isSystemFolder() || (hasSub && !folder.isInbox())) && (subscribed || hasSub)
|
||||
});
|
||||
return !multiple;
|
||||
});
|
||||
|
||||
this.currentFolderFullNameRaw = ko.computed(() => (this.currentFolder() ? this.currentFolder().fullNameRaw : ''));
|
||||
|
||||
|
@ -156,4 +164,16 @@ export const FolderUserStore = new class {
|
|||
|
||||
return result.filter((value, index, self) => self.indexOf(value) == index);
|
||||
}
|
||||
|
||||
saveSystemFolders(folders) {
|
||||
folders = folders || {
|
||||
SentFolder: FolderUserStore.sentFolder(),
|
||||
DraftFolder: FolderUserStore.draftFolder(),
|
||||
SpamFolder: FolderUserStore.spamFolder(),
|
||||
TrashFolder: FolderUserStore.trashFolder(),
|
||||
ArchiveFolder: FolderUserStore.archiveFolder()
|
||||
};
|
||||
Object.entries(folders).forEach(([k,v])=>Settings.set(k,v));
|
||||
rl.app.Remote.saveSystemFolders(()=>0, folders);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
.b-folders {
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.move-action-content-wrapper {
|
||||
z-index: -1;
|
||||
position: fixed;
|
||||
|
@ -60,8 +64,8 @@
|
|||
border-bottom: 1px solid #999;
|
||||
}
|
||||
|
||||
.e-item {
|
||||
|
||||
li {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
|
@ -143,7 +147,7 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.b-sub-folders.collapsed {
|
||||
ul.collapsed {
|
||||
max-height: 0;
|
||||
height: 0;
|
||||
display: none;
|
||||
|
@ -182,45 +186,39 @@
|
|||
}
|
||||
}
|
||||
|
||||
.b-folder-system-item {
|
||||
.b-folders-system {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.b-sub-folders .e-item a {
|
||||
li li a {
|
||||
padding-left: @subPadding * 1 + @folderItemPadding;
|
||||
}
|
||||
.b-sub-folders .b-sub-folders .e-item a {
|
||||
li li li a {
|
||||
padding-left: @subPadding * 2 + @folderItemPadding;
|
||||
}
|
||||
.b-sub-folders .b-sub-folders .b-sub-folders .e-item a {
|
||||
li li li li a {
|
||||
padding-left: @subPadding * 3 + @folderItemPadding;
|
||||
}
|
||||
.b-sub-folders .b-sub-folders .b-sub-folders .b-sub-folders .e-item a {
|
||||
li li li li li a {
|
||||
padding-left: @subPadding * 4 + @folderItemPadding;
|
||||
}
|
||||
|
||||
/**/
|
||||
&.single-root-inbox a.i-am-inbox {
|
||||
&.single-root-inbox .b-folders-user > li > a {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
&.single-root-inbox .i-am-inbox-wrapper > .b-sub-folders {
|
||||
max-height: none !important;
|
||||
height: inherit !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
&.single-root-inbox .i-am-inbox-wrapper {
|
||||
.b-sub-folders .e-item a {
|
||||
&.single-root-inbox {
|
||||
li li a {
|
||||
padding-left: @folderItemPadding;
|
||||
}
|
||||
.b-sub-folders .b-sub-folders .e-item a {
|
||||
li li li a {
|
||||
padding-left: @subPadding * 1 + @folderItemPadding;
|
||||
}
|
||||
.b-sub-folders .b-sub-folders .b-sub-folders .e-item a {
|
||||
li li li li a {
|
||||
padding-left: @subPadding * 2 + @folderItemPadding;
|
||||
}
|
||||
.b-sub-folders .b-sub-folders .b-sub-folders .b-sub-folders .e-item a {
|
||||
li li li li li a {
|
||||
padding-left: @subPadding * 3 + @folderItemPadding;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,12 @@ import ko from 'ko';
|
|||
|
||||
import { SetSystemFoldersNotification } from 'Common/EnumsUser';
|
||||
import { UNUSED_OPTION_VALUE } from 'Common/Consts';
|
||||
import { Settings } from 'Common/Globals';
|
||||
import { defaultOptionsAfterRender, addSubscribablesTo } from 'Common/Utils';
|
||||
import { folderListOptionsBuilder } from 'Common/UtilsUser';
|
||||
import { initOnStartOrLangChange, i18n } from 'Common/Translator';
|
||||
|
||||
import { FolderUserStore } from 'Stores/User/Folder';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
class FolderSystemPopupView extends AbstractViewPopup {
|
||||
|
@ -43,35 +40,14 @@ class FolderSystemPopupView extends AbstractViewPopup {
|
|||
this.trashFolder = FolderUserStore.trashFolder;
|
||||
this.archiveFolder = FolderUserStore.archiveFolder;
|
||||
|
||||
const settingsSet = Settings.set,
|
||||
fSetSystemFolders = () => {
|
||||
settingsSet('SentFolder', FolderUserStore.sentFolder());
|
||||
settingsSet('DraftFolder', FolderUserStore.draftFolder());
|
||||
settingsSet('SpamFolder', FolderUserStore.spamFolder());
|
||||
settingsSet('TrashFolder', FolderUserStore.trashFolder());
|
||||
settingsSet('ArchiveFolder', FolderUserStore.archiveFolder());
|
||||
},
|
||||
fSaveSystemFolders = (()=>{
|
||||
fSetSystemFolders();
|
||||
Remote.saveSystemFolders(()=>0, {
|
||||
SentFolder: FolderUserStore.sentFolder(),
|
||||
DraftFolder: FolderUserStore.draftFolder(),
|
||||
SpamFolder: FolderUserStore.spamFolder(),
|
||||
TrashFolder: FolderUserStore.trashFolder(),
|
||||
ArchiveFolder: FolderUserStore.archiveFolder()
|
||||
});
|
||||
}).debounce(1000),
|
||||
fCallback = () => {
|
||||
fSetSystemFolders();
|
||||
fSaveSystemFolders();
|
||||
};
|
||||
const fSaveSystemFolders = (()=>FolderUserStore.saveSystemFolders()).debounce(1000);
|
||||
|
||||
addSubscribablesTo(FolderUserStore, {
|
||||
sentFolder: fCallback,
|
||||
draftFolder: fCallback,
|
||||
spamFolder: fCallback,
|
||||
trashFolder: fCallback,
|
||||
archiveFolder: fCallback
|
||||
sentFolder: fSaveSystemFolders,
|
||||
draftFolder: fSaveSystemFolders,
|
||||
spamFolder: fSaveSystemFolders,
|
||||
trashFolder: fSaveSystemFolders,
|
||||
archiveFolder: fSaveSystemFolders
|
||||
});
|
||||
|
||||
this.defaultOptionsAfterRender = defaultOptionsAfterRender;
|
||||
|
|
|
@ -32,7 +32,7 @@ export class FolderListMailBoxUserView extends AbstractViewLeft {
|
|||
|
||||
this.moveAction = moveAction;
|
||||
|
||||
this.foldersListWithSingleInboxRootFolder = FolderUserStore.foldersListWithSingleInboxRootFolder;
|
||||
this.foldersListWithSingleInboxRootFolder = FolderUserStore.singleRootFolder;
|
||||
|
||||
this.leftPanelDisabled = leftPanelDisabled;
|
||||
|
||||
|
|
|
@ -157,8 +157,7 @@ class FolderCollection extends \MailSo\Base\Collection
|
|||
'IsSortSupported' => $this->IsSortSupported,
|
||||
'Optimized' => $this->Optimized,
|
||||
'CountRec' => $this->CountRec(),
|
||||
'SystemFolders' => isset($this->SystemFolders) && \is_array($this->SystemFolders) ?
|
||||
$this->SystemFolders : array()
|
||||
'SystemFolders' => empty($this->SystemFolders) ? null : $this->SystemFolders
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
<a class="btn buttonContacts fontastic" data-bind="visible: allowContacts, click: contactsClick" data-i18n="[title]GLOBAL/CONTACTS">📇</a>
|
||||
</div>
|
||||
<div class="b-content">
|
||||
<div class="b-folders-system" data-bind="template: { name: 'MailFolderListSystemItem', foreach: folderListSystem }"></div>
|
||||
<ul class="b-folders-system" data-bind="template: { name: 'MailFolderListSystemItem', foreach: folderListSystem }"></ul>
|
||||
<hr/>
|
||||
<div class="b-folders-user" data-bind="template: { name: 'MailFolderListItem', foreach: folderList }"></div>
|
||||
<ul class="b-folders-user" data-bind="template: { name: 'MailFolderListItem', foreach: folderList }"></ul>
|
||||
<div class="move-action-content-wrapper" data-bind="visible: moveAction"></div>
|
||||
</div>
|
||||
<div class="b-content show-on-panel-disabled" data-bind="click: function () { leftPanelDisabled(false); }"></div>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<div class="e-item" data-bind="visible: visible, css: { 'i-am-inbox-wrapper': isInbox }">
|
||||
<li data-bind="visible: visible">
|
||||
<a data-bind="dropmessages: $data,
|
||||
css: { 'i-am-inbox': isInbox, 'selected': selected() && !isSystemFolder(), 'selectable': canBeSelected, 'hidden' : hidden, 'unread-sub': hasSubscribedUnreadMessagesSubfolders, 'system': isSystemFolder, 'anim-action-class': actionBlink },
|
||||
css: { 'selected': selected() && !isSystemFolder(), 'selectable': canBeSelected, 'hidden' : hidden, 'unread-sub': hasSubscribedUnreadMessagesSubfolders, 'system': isSystemFolder, 'anim-action-class': actionBlink },
|
||||
attr: { 'data-unread': printableUnreadCount }">
|
||||
<i data-bind="css: collapsedCss()"></i>
|
||||
<!-- ko text: name --><!-- /ko -->
|
||||
</a>
|
||||
<!-- ko if: subFolders.length -->
|
||||
<div class="b-sub-folders" data-bind="template: { name: 'MailFolderListItem', foreach: subFolders }, css: { 'collapsed': collapsed() }"></div>
|
||||
<ul data-bind="template: { name: 'MailFolderListItem', foreach: subFolders }, css: { 'collapsed': collapsed() }"></ul>
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="e-item b-folder-system-item">
|
||||
<li>
|
||||
<a data-bind="dropmessages: $data,
|
||||
css: { 'selected': selected, 'selectable': selectable, 'anim-action-class': actionBlink },
|
||||
attr: { 'data-unread': printableUnreadCount }">
|
||||
|
@ -7,4 +7,4 @@
|
|||
<span class="inbox-star-icon fontastic"></span>
|
||||
<!-- /ko -->
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
|
|
Loading…
Reference in a new issue