mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-03-02 11:03:45 +08:00
feat(hidden-messages): Filter trash/spam messages. Fixes #1135
Summary: By default, the messages in a thread are now filtered to exclude ones moved to trash or spam. You can choose to view those messages by clicking the new bar in the message list. When you view your spam or trash, we only show the messages on those threads that have been marked as spam/trash. Test Plan: Run a couple new tests Reviewers: juan, evan Reviewed By: evan Differential Revision: https://phab.nylas.com/D2662
This commit is contained in:
parent
a950b40175
commit
f413386b80
16 changed files with 240 additions and 69 deletions
build/config
internal_packages
message-list
thread-list
thread-snooze/lib
spec
src
|
@ -17,6 +17,7 @@
|
|||
"object-curly-spacing": [0],
|
||||
"no-console": [0],
|
||||
"no-loop-func": [0],
|
||||
"no-constant-condition": [0],
|
||||
"new-cap": [2, {"capIsNew": false}],
|
||||
"no-shadow": [1],
|
||||
"quotes": [0],
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
MessageList = require "./message-list"
|
||||
MessageListHiddenMessagesToggle = require './message-list-hidden-messages-toggle'
|
||||
MessageToolbarItems = require "./message-toolbar-items"
|
||||
{ComponentRegistry,
|
||||
ExtensionRegistry,
|
||||
|
@ -46,6 +47,9 @@ module.exports =
|
|||
ComponentRegistry.register ThreadToggleUnreadButton,
|
||||
role: 'message:Toolbar'
|
||||
|
||||
ComponentRegistry.register MessageListHiddenMessagesToggle,
|
||||
role: 'MessageListHeaders'
|
||||
|
||||
ExtensionRegistry.MessageView.register AutolinkerExtension
|
||||
ExtensionRegistry.MessageView.register TrackingPixelsExtension
|
||||
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
import {
|
||||
React,
|
||||
Actions,
|
||||
MessageStore,
|
||||
FocusedPerspectiveStore,
|
||||
} from 'nylas-exports';
|
||||
|
||||
export default class MessageListHiddenMessagesToggle extends React.Component {
|
||||
|
||||
static displayName = 'MessageListHiddenMessagesToggle';
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
numberOfHiddenItems: MessageStore.numberOfHiddenItems(),
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._unlisten = MessageStore.listen(() => {
|
||||
this.setState({
|
||||
numberOfHiddenItems: MessageStore.numberOfHiddenItems(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._unlisten();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {numberOfHiddenItems} = this.state;
|
||||
if (numberOfHiddenItems === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const viewing = FocusedPerspectiveStore.current().categoriesSharedName();
|
||||
let message = null;
|
||||
|
||||
if (MessageStore.CategoryNamesHiddenByDefault.includes(viewing)) {
|
||||
if (numberOfHiddenItems > 1) {
|
||||
message = `There are ${numberOfHiddenItems} more messages in this thread that are not in spam or trash.`;
|
||||
} else {
|
||||
message = `There is one more message in this thread that is not in spam or trash.`;
|
||||
}
|
||||
} else {
|
||||
if (numberOfHiddenItems > 1) {
|
||||
message = `${numberOfHiddenItems} messages in this thread are hidden because it was moved to trash or spam.`;
|
||||
} else {
|
||||
message = `One message in this thread is hidden because it was moved to trash or spam.`;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="show-hidden-messages">
|
||||
{message}
|
||||
<a onClick={function toggle() { Actions.toggleHiddenMessages() }}>Show all messages</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MessageListHiddenMessagesToggle.containerRequired = false;
|
|
@ -17,7 +17,7 @@ MessageItemContainer = require './message-item-container'
|
|||
|
||||
{Spinner,
|
||||
RetinaImg,
|
||||
MailLabel,
|
||||
MailLabelSet,
|
||||
ScrollRegion,
|
||||
MailImportantIcon,
|
||||
InjectedComponent,
|
||||
|
@ -218,7 +218,7 @@ class MessageList extends React.Component
|
|||
<MailImportantIcon thread={@state.currentThread}/>
|
||||
<div style={flex: 1}>
|
||||
<span className="message-subject">{subject}</span>
|
||||
{@_renderLabels()}
|
||||
<MailLabelSet thread={@state.currentThread} includeCurrentCategories={true} />
|
||||
</div>
|
||||
{@_renderIcons()}
|
||||
</div>
|
||||
|
@ -243,14 +243,6 @@ class MessageList extends React.Component
|
|||
<RetinaImg name={"collapse.png"} title={"Collapse All"} mode={RetinaImg.Mode.ContentIsMask}/>
|
||||
</div>
|
||||
|
||||
_renderLabels: =>
|
||||
account = AccountStore.accountForId(@state.currentThread.accountId)
|
||||
return false unless account.usesLabels()
|
||||
labels = @state.currentThread.sortedCategories()
|
||||
labels = _.reject labels, (l) -> l.name is 'important'
|
||||
labels.map (label) =>
|
||||
<MailLabel label={label} key={label.id} onRemove={ => @_onRemoveLabel(label) }/>
|
||||
|
||||
_renderReplyArea: =>
|
||||
<div className="footer-reply-area-wrap" onClick={@_onClickReplyArea} key='reply-area'>
|
||||
<div className="footer-reply-area">
|
||||
|
@ -280,10 +272,6 @@ class MessageList extends React.Component
|
|||
node = React.findDOMNode(@)
|
||||
Actions.printThread(@state.currentThread, node.innerHTML)
|
||||
|
||||
_onRemoveLabel: (label) =>
|
||||
task = new ChangeLabelsTask(thread: @state.currentThread, labelsToRemove: [label])
|
||||
Actions.queueTask(task)
|
||||
|
||||
_onClickReplyArea: =>
|
||||
return unless @state.currentThread
|
||||
@_createReplyOrUpdateExistingDraft(@_replyType())
|
||||
|
|
|
@ -120,6 +120,17 @@ body.platform-win32 {
|
|||
padding: 0;
|
||||
order: 2;
|
||||
|
||||
.show-hidden-messages {
|
||||
background-color: darken(@background-secondary, 4%);
|
||||
border: 1px solid darken(@background-secondary, 8%);
|
||||
border-radius: @border-radius-base;
|
||||
color: @text-color-very-subtle;
|
||||
margin-bottom: @padding-large-vertical;
|
||||
cursor: default;
|
||||
padding: @padding-base-vertical @padding-base-horizontal;
|
||||
a { float: right; }
|
||||
}
|
||||
|
||||
.message-subject-wrap {
|
||||
width: calc(~"100% - 12px");
|
||||
max-width: @message-max-width;
|
||||
|
@ -155,6 +166,9 @@ body.platform-win32 {
|
|||
margin-left: @padding-small-horizontal;
|
||||
}
|
||||
}
|
||||
.thread-injected-mail-labels {
|
||||
vertical-align: top;
|
||||
}
|
||||
.message-list-headers {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
|
|
|
@ -4,14 +4,11 @@ classNames = require 'classnames'
|
|||
|
||||
{ListTabular,
|
||||
RetinaImg,
|
||||
MailLabel,
|
||||
MailLabelSet,
|
||||
MailImportantIcon,
|
||||
InjectedComponentSet} = require 'nylas-component-kit'
|
||||
|
||||
{Thread,
|
||||
AccountStore,
|
||||
CategoryStore,
|
||||
FocusedPerspectiveStore} = require 'nylas-exports'
|
||||
{Thread} = require 'nylas-exports'
|
||||
|
||||
{ThreadArchiveQuickAction,
|
||||
ThreadTrashQuickAction} = require './thread-list-quick-actions'
|
||||
|
@ -57,36 +54,16 @@ c2 = new ListTabular.Column
|
|||
else
|
||||
<ThreadListParticipants thread={thread} />
|
||||
|
||||
c3LabelComponentCache = {}
|
||||
|
||||
c3 = new ListTabular.Column
|
||||
name: "Message"
|
||||
flex: 4
|
||||
resolver: (thread) =>
|
||||
attachment = []
|
||||
labels = []
|
||||
|
||||
if thread.hasAttachments
|
||||
attachment = <div className="thread-icon thread-icon-attachment"></div>
|
||||
|
||||
if AccountStore.accountForId(thread.accountId).usesLabels()
|
||||
currentCategories = FocusedPerspectiveStore.current().categories() ? []
|
||||
ignored = [].concat(currentCategories, CategoryStore.hiddenCategories(thread.accountId))
|
||||
ignoredIds = _.pluck(ignored, 'id')
|
||||
|
||||
for label in (thread.sortedCategories())
|
||||
continue if label.id in ignoredIds
|
||||
c3LabelComponentCache[label.id] ?= <MailLabel label={label} key={label.id} />
|
||||
labels.push c3LabelComponentCache[label.id]
|
||||
|
||||
<span className="details">
|
||||
<InjectedComponentSet
|
||||
inline
|
||||
containersRequired={false}
|
||||
children={labels}
|
||||
matching={role: "ThreadList:Label"}
|
||||
className="thread-injected-mail-labels"
|
||||
exposedProps={thread: thread}/>
|
||||
<MailLabelSet thread={thread} />
|
||||
<span className="subject">{subject(thread.subject)}</span>
|
||||
<span className="snippet">{thread.snippet}</span>
|
||||
{attachment}
|
||||
|
@ -127,17 +104,6 @@ cNarrow = new ListTabular.Column
|
|||
if hasDraft
|
||||
pencil = <RetinaImg name="icon-draft-pencil.png" className="draft-icon" mode={RetinaImg.Mode.ContentPreserve} />
|
||||
|
||||
labels = []
|
||||
if AccountStore.accountForId(thread.accountId).usesLabels()
|
||||
currentCategories = FocusedPerspectiveStore.current().categories() ? []
|
||||
ignored = [].concat(currentCategories, CategoryStore.hiddenCategories(thread.accountId))
|
||||
ignoredIds = _.pluck(ignored, 'id')
|
||||
|
||||
for label in (thread.sortedCategories())
|
||||
continue if label.id in ignoredIds
|
||||
c3LabelComponentCache[label.id] ?= <MailLabel label={label} key={label.id} />
|
||||
labels.push c3LabelComponentCache[label.id]
|
||||
|
||||
<div>
|
||||
<div style={display: 'flex', alignItems: 'center'}>
|
||||
<ThreadListIcon thread={thread} />
|
||||
|
@ -154,13 +120,7 @@ cNarrow = new ListTabular.Column
|
|||
<div className="snippet-and-labels">
|
||||
<div className="snippet">{thread.snippet} </div>
|
||||
<div style={flex: 1, flexShrink: 1}></div>
|
||||
<InjectedComponentSet
|
||||
inline
|
||||
containerRequired={false}
|
||||
children={labels}
|
||||
matching={role: "ThreadList:Label"}
|
||||
className="thread-injected-mail-labels-narrow"
|
||||
exposedProps={thread: thread}/>
|
||||
<MailLabelSet thread={thread} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -446,6 +446,12 @@ body.platform-win32 {
|
|||
padding: 1px 8px;
|
||||
font-size: 0.8em;
|
||||
line-height: 17px;
|
||||
.inner {
|
||||
position: inherit;
|
||||
}
|
||||
.x {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.snippet {
|
||||
font-size: @font-size-small;
|
||||
|
|
|
@ -11,7 +11,7 @@ export function activate() {
|
|||
ComponentRegistry.register(ToolbarSnooze, {role: 'message:Toolbar'});
|
||||
ComponentRegistry.register(SnoozeQuickActionButton, {role: 'ThreadListQuickAction'});
|
||||
ComponentRegistry.register(BulkThreadSnooze, {role: 'thread:BulkAction'});
|
||||
ComponentRegistry.register(SnoozeMailLabel, {role: 'ThreadList:Label'});
|
||||
ComponentRegistry.register(SnoozeMailLabel, {role: 'Thread:MailLabel'});
|
||||
}
|
||||
|
||||
export function deactivate() {
|
||||
|
|
|
@ -37,7 +37,6 @@ describe 'MailboxPerspective', ->
|
|||
@perspective = MailboxPerspective.forCategories(@categories)
|
||||
|
||||
describe 'canReceiveThreads', ->
|
||||
|
||||
it 'returns true if the thread account ids are included in the current account ids', ->
|
||||
expect(@perspective.canReceiveThreads(['a2'])).toBe true
|
||||
|
||||
|
@ -52,6 +51,22 @@ describe 'MailboxPerspective', ->
|
|||
)
|
||||
expect(@perspective.canReceiveThreads(['a2'])).toBe false
|
||||
|
||||
describe 'categoriesSharedName', ->
|
||||
it "returns the name if all the categories on the perspective have the same name", ->
|
||||
expect(MailboxPerspective.forCategories([
|
||||
new Category(name: 'c1', accountId: 'a1')
|
||||
new Category(name: 'c1', accountId: 'a2')
|
||||
]).categoriesSharedName()).toEqual('c1')
|
||||
|
||||
it "returns null if there are no categories", ->
|
||||
expect(MailboxPerspective.forStarred(['a1', 'a2']).categoriesSharedName()).toEqual(null)
|
||||
|
||||
it "returns null if the categories have different names", ->
|
||||
expect(MailboxPerspective.forCategories([
|
||||
new Category(name: 'c1', accountId: 'a1')
|
||||
new Category(name: 'c2', accountId: 'a2')
|
||||
]).categoriesSharedName()).toEqual(null)
|
||||
|
||||
describe 'receiveThreads', ->
|
||||
# TODO
|
||||
|
||||
|
|
68
src/components/mail-label-set.jsx
Normal file
68
src/components/mail-label-set.jsx
Normal file
|
@ -0,0 +1,68 @@
|
|||
import React from 'react';
|
||||
import FocusedPerspectiveStore from '../flux/stores/focused-perspective-store';
|
||||
import CategoryStore from '../flux/stores/category-store';
|
||||
import MessageStore from '../flux/stores/message-store';
|
||||
import AccountStore from '../flux/stores/account-store';
|
||||
import {MailLabel} from './mail-label';
|
||||
import Actions from '../flux/actions';
|
||||
import ChangeLabelsTask from '../flux/tasks/change-labels-task';
|
||||
import InjectedComponentSet from './injected-component-set';
|
||||
|
||||
const LabelComponentCache = {};
|
||||
|
||||
export default class MailLabelSet extends React.Component {
|
||||
static displayName = 'MailLabelSet';
|
||||
|
||||
static propTypes = {
|
||||
thread: React.PropTypes.object.isRequired,
|
||||
includeCurrentCategories: React.PropTypes.boolean,
|
||||
};
|
||||
|
||||
_onRemoveLabel(label) {
|
||||
const task = new ChangeLabelsTask({
|
||||
thread: this.props.thread,
|
||||
labelsToRemove: [label],
|
||||
});
|
||||
Actions.queueTask(task);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {thread, includeCurrentCategories} = this.props;
|
||||
const labels = [];
|
||||
|
||||
if (AccountStore.accountForId(thread.accountId).usesLabels()) {
|
||||
const hidden = CategoryStore.hiddenCategories(thread.accountId);
|
||||
let current = FocusedPerspectiveStore.current().categories();
|
||||
|
||||
if (includeCurrentCategories || !current) {
|
||||
current = [];
|
||||
}
|
||||
|
||||
const ignoredIds = [].concat(hidden, current).map(l=> l.id);
|
||||
const ignoredNames = MessageStore.CategoryNamesHiddenByDefault;
|
||||
|
||||
for (const label of thread.sortedCategories()) {
|
||||
if (ignoredNames.includes(label.name) || ignoredIds.includes(label.id)) {
|
||||
continue;
|
||||
}
|
||||
if (LabelComponentCache[label.id] === undefined) {
|
||||
LabelComponentCache[label.id] = (
|
||||
<MailLabel
|
||||
label={label}
|
||||
key={label.id}
|
||||
onRemove={()=> this._onRemoveLabel(label)}/>
|
||||
);
|
||||
}
|
||||
labels.push(LabelComponentCache[label.id]);
|
||||
}
|
||||
}
|
||||
return (<InjectedComponentSet
|
||||
inline
|
||||
containersRequired={false}
|
||||
children={labels}
|
||||
matching={{role: "Thread:MailLabel"}}
|
||||
className="thread-injected-mail-labels"
|
||||
exposedProps={{thread: thread}}/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -263,6 +263,12 @@ class Actions
|
|||
###
|
||||
@toggleMessageIdExpanded: ActionScopeWindow
|
||||
|
||||
###
|
||||
Public: Toggle whether messages from trash and spam are shown in the current
|
||||
message view.
|
||||
###
|
||||
@toggleHiddenMessages: ActionScopeWindow
|
||||
|
||||
###
|
||||
Public: This action toggles wether to collapse or expand all messages in a
|
||||
thread depending on if there are currently collapsed messages.
|
||||
|
|
|
@ -148,11 +148,10 @@ class Message extends ModelWithMetadata
|
|||
modelKey: 'replyToMessageId'
|
||||
jsonKey: 'reply_to_message_id'
|
||||
|
||||
'folder': Attributes.Object
|
||||
modelKey: 'folder'
|
||||
'categories': Attributes.Collection
|
||||
modelKey: 'categories'
|
||||
itemClass: Category
|
||||
|
||||
|
||||
@naturalSortOrder: ->
|
||||
Message.attributes.date.ascending()
|
||||
|
||||
|
@ -174,6 +173,7 @@ class Message extends ModelWithMetadata
|
|||
@files ||= []
|
||||
@uploads ||= []
|
||||
@events ||= []
|
||||
@categories ||= []
|
||||
@
|
||||
|
||||
toJSON: (options) ->
|
||||
|
@ -192,7 +192,12 @@ class Message extends ModelWithMetadata
|
|||
if json.object?
|
||||
@draft = (json.object is 'draft')
|
||||
|
||||
for attr in ['to', 'from', 'cc', 'bcc', 'files']
|
||||
if json['folder']
|
||||
@categories = @constructor.attributes.categories.fromJSON([json['folder']])
|
||||
else if json['labels']
|
||||
@categories = @constructor.attributes.categories.fromJSON(json['labels'])
|
||||
|
||||
for attr in ['to', 'from', 'cc', 'bcc', 'files', 'categories']
|
||||
values = @[attr]
|
||||
continue unless values and values instanceof Array
|
||||
item.accountId = @accountId for item in values
|
||||
|
|
|
@ -16,7 +16,7 @@ DatabaseTransaction = require './database-transaction'
|
|||
|
||||
{ipcRenderer} = require 'electron'
|
||||
|
||||
DatabaseVersion = 18
|
||||
DatabaseVersion = 19
|
||||
DatabasePhase =
|
||||
Setup: 'setup'
|
||||
Ready: 'ready'
|
||||
|
|
|
@ -5,6 +5,7 @@ Thread = require "../models/thread"
|
|||
Utils = require '../models/utils'
|
||||
DatabaseStore = require "./database-store"
|
||||
AccountStore = require "./account-store"
|
||||
FocusedPerspectiveStore = require './focused-perspective-store'
|
||||
FocusedContentStore = require "./focused-content-store"
|
||||
ChangeUnreadTask = require '../tasks/change-unread-task'
|
||||
NylasAPI = require '../nylas-api'
|
||||
|
@ -13,6 +14,8 @@ ExtensionRegistry = require '../../extension-registry'
|
|||
async = require 'async'
|
||||
_ = require 'underscore'
|
||||
|
||||
CategoryNamesHiddenByDefault = ['spam', 'trash']
|
||||
|
||||
class MessageStore extends NylasStore
|
||||
|
||||
constructor: ->
|
||||
|
@ -22,7 +25,16 @@ class MessageStore extends NylasStore
|
|||
########### PUBLIC #####################################################
|
||||
|
||||
items: ->
|
||||
@_items
|
||||
return @_items if @_showingHiddenItems
|
||||
|
||||
viewing = FocusedPerspectiveStore.current().categoriesSharedName()
|
||||
viewingHidden = viewing in CategoryNamesHiddenByDefault
|
||||
|
||||
return @_items.filter (item) ->
|
||||
inHidden = _.any item.categories, (cat) -> cat.name in CategoryNamesHiddenByDefault
|
||||
return false if viewingHidden and not inHidden
|
||||
return false if not viewingHidden and inHidden
|
||||
return true
|
||||
|
||||
threadId: -> @_thread?.id
|
||||
|
||||
|
@ -36,6 +48,9 @@ class MessageStore extends NylasStore
|
|||
hasCollapsedItems: ->
|
||||
_.size(@_itemsExpanded) < @_items.length
|
||||
|
||||
numberOfHiddenItems: ->
|
||||
@_items.length - @items().length
|
||||
|
||||
itemClientIds: ->
|
||||
_.pluck(@_items, "clientId")
|
||||
|
||||
|
@ -79,6 +94,7 @@ class MessageStore extends NylasStore
|
|||
@_items = []
|
||||
@_itemsExpanded = {}
|
||||
@_itemsLoading = false
|
||||
@_showingHiddenItems = false
|
||||
@_thread = null
|
||||
@_inflight = {}
|
||||
|
||||
|
@ -88,6 +104,11 @@ class MessageStore extends NylasStore
|
|||
@listenTo FocusedContentStore, @_onFocusChanged
|
||||
@listenTo Actions.toggleMessageIdExpanded, @_onToggleMessageIdExpanded
|
||||
@listenTo Actions.toggleAllMessagesExpanded, @_onToggleAllMessagesExpanded
|
||||
@listenTo Actions.toggleHiddenMessages, @_onToggleHiddenMessages
|
||||
@listenTo FocusedPerspectiveStore, @_onPerspectiveChanged
|
||||
|
||||
_onPerspectiveChanged: =>
|
||||
@trigger()
|
||||
|
||||
_onDataChanged: (change) =>
|
||||
return unless @_thread
|
||||
|
@ -151,6 +172,7 @@ class MessageStore extends NylasStore
|
|||
@_thread = focused
|
||||
@_items = []
|
||||
@_itemsLoading = true
|
||||
@_showingHiddenItems = false
|
||||
@_itemsExpanded = {}
|
||||
@trigger()
|
||||
|
||||
|
@ -187,6 +209,13 @@ class MessageStore extends NylasStore
|
|||
@_items[...-1].forEach @_collapseItem
|
||||
@trigger()
|
||||
|
||||
_onToggleHiddenMessages: =>
|
||||
@_showingHiddenItems = !@_showingHiddenItems
|
||||
@_expandItemsToDefault()
|
||||
@_fetchExpandedBodies(@_items)
|
||||
@_fetchExpandedAttachments(@_items)
|
||||
@trigger()
|
||||
|
||||
_onToggleMessageIdExpanded: (id) =>
|
||||
item = _.findWhere(@_items, {id})
|
||||
return unless item
|
||||
|
@ -269,8 +298,9 @@ class MessageStore extends NylasStore
|
|||
|
||||
# Expand all unread messages, all drafts, and the last message
|
||||
_expandItemsToDefault: ->
|
||||
for item, idx in @_items
|
||||
if item.unread or item.draft or idx is @_items.length - 1
|
||||
visibleItems = @items()
|
||||
for item, idx in visibleItems
|
||||
if item.unread or item.draft or idx is visibleItems.length - 1
|
||||
@_itemsExpanded[item.id] = "default"
|
||||
|
||||
_fetchMessages: ->
|
||||
|
@ -327,4 +357,6 @@ store.unregisterExtension = deprecate(
|
|||
store,
|
||||
store.unregisterExtension
|
||||
)
|
||||
store.CategoryNamesHiddenByDefault = CategoryNamesHiddenByDefault
|
||||
|
||||
module.exports = store
|
||||
|
|
|
@ -44,6 +44,7 @@ class NylasComponentKit
|
|||
|
||||
@loadFrom "MailLabel", "mail-label"
|
||||
@loadFrom "LabelColorizer", "mail-label"
|
||||
@load "MailLabelSet", "mail-label-set"
|
||||
@load "MailImportantIcon", 'mail-important-icon'
|
||||
|
||||
@loadFrom "FormItem", "generated-form"
|
||||
|
|
|
@ -81,6 +81,13 @@ class MailboxPerspective
|
|||
categories: =>
|
||||
[]
|
||||
|
||||
categoriesSharedName: =>
|
||||
cats = @categories()
|
||||
return null unless cats and cats.length > 0
|
||||
name = cats[0].name
|
||||
return null unless _.every cats, (cat) -> cat.name is name
|
||||
return name
|
||||
|
||||
category: =>
|
||||
return null unless @categories().length is 1
|
||||
return @categories()[0]
|
||||
|
@ -282,7 +289,7 @@ class CategoryMailboxPerspective extends MailboxPerspective
|
|||
@_categories
|
||||
|
||||
isInbox: =>
|
||||
@_categories[0].name is 'inbox'
|
||||
@categoriesSharedName() is 'inbox'
|
||||
|
||||
canReceiveThreads: =>
|
||||
super and not _.any @_categories, (c) -> c.isLockedCategory()
|
||||
|
|
Loading…
Reference in a new issue