mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-10-06 19:26:55 +08:00
feat(not-spam): Unmark items as junk / spam #2112
This commit is contained in:
parent
29c81221f2
commit
c87680878a
6 changed files with 372 additions and 246 deletions
|
@ -1,241 +0,0 @@
|
||||||
_ = require 'underscore'
|
|
||||||
React = require "react"
|
|
||||||
classNames = require 'classnames'
|
|
||||||
ThreadListStore = require './thread-list-store'
|
|
||||||
{RetinaImg} = require 'nylas-component-kit'
|
|
||||||
{Actions,
|
|
||||||
TaskFactory,
|
|
||||||
AccountStore,
|
|
||||||
CategoryStore,
|
|
||||||
FocusedContentStore,
|
|
||||||
FocusedPerspectiveStore} = require "nylas-exports"
|
|
||||||
|
|
||||||
class ArchiveButton extends React.Component
|
|
||||||
@displayName: 'ArchiveButton'
|
|
||||||
@containerRequired: false
|
|
||||||
|
|
||||||
@propTypes:
|
|
||||||
items: React.PropTypes.array.isRequired
|
|
||||||
|
|
||||||
render: ->
|
|
||||||
allowed = FocusedPerspectiveStore.current().canArchiveThreads(@props.items)
|
|
||||||
return <span /> unless allowed
|
|
||||||
|
|
||||||
<button
|
|
||||||
tabIndex={-1}
|
|
||||||
style={order:-107}
|
|
||||||
className="btn btn-toolbar"
|
|
||||||
title="Archive"
|
|
||||||
onClick={@_onArchive}>
|
|
||||||
<RetinaImg name="toolbar-archive.png" mode={RetinaImg.Mode.ContentIsMask} />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
_onArchive: (event) =>
|
|
||||||
tasks = TaskFactory.tasksForArchiving
|
|
||||||
threads: @props.items
|
|
||||||
Actions.queueTasks(tasks)
|
|
||||||
Actions.popSheet()
|
|
||||||
event.stopPropagation()
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class TrashButton extends React.Component
|
|
||||||
@displayName: 'TrashButton'
|
|
||||||
@containerRequired: false
|
|
||||||
|
|
||||||
@propTypes:
|
|
||||||
items: React.PropTypes.array.isRequired
|
|
||||||
|
|
||||||
render: ->
|
|
||||||
allowed = FocusedPerspectiveStore.current().canMoveThreadsTo(@props.items, 'trash')
|
|
||||||
return <span /> unless allowed
|
|
||||||
|
|
||||||
<button tabIndex={-1}
|
|
||||||
style={order:-106}
|
|
||||||
className="btn btn-toolbar"
|
|
||||||
title="Move to Trash"
|
|
||||||
onClick={@_onRemove}>
|
|
||||||
<RetinaImg name="toolbar-trash.png" mode={RetinaImg.Mode.ContentIsMask} />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
_onRemove: (event) =>
|
|
||||||
tasks = TaskFactory.tasksForMovingToTrash
|
|
||||||
threads: @props.items
|
|
||||||
Actions.queueTasks(tasks)
|
|
||||||
Actions.popSheet()
|
|
||||||
event.stopPropagation()
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class MarkAsSpamButton extends React.Component
|
|
||||||
@displayName: 'MarkAsSpamButton'
|
|
||||||
@containerRequired: false
|
|
||||||
|
|
||||||
@propTypes:
|
|
||||||
items: React.PropTypes.array.isRequired
|
|
||||||
|
|
||||||
render: ->
|
|
||||||
allowed = FocusedPerspectiveStore.current().canMoveThreadsTo(@props.items, 'spam')
|
|
||||||
return <span /> unless allowed
|
|
||||||
|
|
||||||
<button tabIndex={-1}
|
|
||||||
style={order:-105}
|
|
||||||
className="btn btn-toolbar"
|
|
||||||
title="Mark as Spam"
|
|
||||||
onClick={@_onClick}>
|
|
||||||
<RetinaImg name="toolbar-spam.png" mode={RetinaImg.Mode.ContentIsMask} />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
_onClick: (event) =>
|
|
||||||
tasks = TaskFactory.tasksForMarkingAsSpam
|
|
||||||
threads: @props.items
|
|
||||||
Actions.queueTasks(tasks)
|
|
||||||
Actions.popSheet()
|
|
||||||
event.stopPropagation()
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class ToggleStarredButton extends React.Component
|
|
||||||
@displayName: 'ToggleStarredButton'
|
|
||||||
@containerRequired: false
|
|
||||||
|
|
||||||
@propTypes:
|
|
||||||
items: React.PropTypes.array.isRequired
|
|
||||||
|
|
||||||
render: ->
|
|
||||||
postClickStarredState = _.every @props.items, (t) -> t.starred is false
|
|
||||||
title = "Unstar"
|
|
||||||
imageName = "toolbar-star-selected.png"
|
|
||||||
|
|
||||||
if postClickStarredState
|
|
||||||
title = "Star"
|
|
||||||
imageName = "toolbar-star.png"
|
|
||||||
|
|
||||||
<button tabIndex={-1}
|
|
||||||
style={order:-103}
|
|
||||||
className="btn btn-toolbar"
|
|
||||||
title={title}
|
|
||||||
onClick={@_onStar}>
|
|
||||||
<RetinaImg name={imageName} mode={RetinaImg.Mode.ContentIsMask} />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
_onStar: (event) =>
|
|
||||||
task = TaskFactory.taskForInvertingStarred(threads: @props.items)
|
|
||||||
Actions.queueTask(task)
|
|
||||||
event.stopPropagation()
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class ToggleUnreadButton extends React.Component
|
|
||||||
@displayName: 'ToggleUnreadButton'
|
|
||||||
@containerRequired: false
|
|
||||||
|
|
||||||
@propTypes:
|
|
||||||
items: React.PropTypes.array.isRequired
|
|
||||||
|
|
||||||
render: =>
|
|
||||||
postClickUnreadState = _.every @props.items, (t) -> _.isMatch(t, {unread: false})
|
|
||||||
fragment = if postClickUnreadState then "unread" else "read"
|
|
||||||
|
|
||||||
<button tabIndex={-1}
|
|
||||||
style={order:-104}
|
|
||||||
className="btn btn-toolbar"
|
|
||||||
title="Mark as #{fragment}"
|
|
||||||
onClick={@_onClick}>
|
|
||||||
<RetinaImg name="toolbar-markas#{fragment}.png"
|
|
||||||
mode={RetinaImg.Mode.ContentIsMask} />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
_onClick: (event) =>
|
|
||||||
task = TaskFactory.taskForInvertingUnread(threads: @props.items)
|
|
||||||
Actions.queueTask(task)
|
|
||||||
Actions.popSheet()
|
|
||||||
event.stopPropagation()
|
|
||||||
return
|
|
||||||
|
|
||||||
ThreadNavButtonMixin =
|
|
||||||
getInitialState: ->
|
|
||||||
@_getStateFromStores()
|
|
||||||
|
|
||||||
componentDidMount: ->
|
|
||||||
@_unsubscribe = ThreadListStore.listen @_onStoreChange
|
|
||||||
@_unsubscribe_focus = FocusedContentStore.listen @_onStoreChange
|
|
||||||
|
|
||||||
isFirstThread: ->
|
|
||||||
selectedId = FocusedContentStore.focusedId('thread')
|
|
||||||
ThreadListStore.dataSource().get(0)?.id is selectedId
|
|
||||||
|
|
||||||
isLastThread: ->
|
|
||||||
selectedId = FocusedContentStore.focusedId('thread')
|
|
||||||
|
|
||||||
lastIndex = ThreadListStore.dataSource().count() - 1
|
|
||||||
ThreadListStore.dataSource().get(lastIndex)?.id is selectedId
|
|
||||||
|
|
||||||
componentWillUnmount: ->
|
|
||||||
@_unsubscribe()
|
|
||||||
@_unsubscribe_focus()
|
|
||||||
|
|
||||||
_onStoreChange: ->
|
|
||||||
@setState @_getStateFromStores()
|
|
||||||
|
|
||||||
|
|
||||||
DownButton = React.createClass
|
|
||||||
displayName: 'DownButton'
|
|
||||||
mixins: [ThreadNavButtonMixin]
|
|
||||||
|
|
||||||
render: ->
|
|
||||||
<div className={@_classSet()} onClick={@_onClick} title="Next thread">
|
|
||||||
<RetinaImg name="toolbar-down-arrow.png" mode={RetinaImg.Mode.ContentIsMask} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
_classSet: ->
|
|
||||||
classNames
|
|
||||||
"btn-icon": true
|
|
||||||
"message-toolbar-arrow": true
|
|
||||||
"down": true
|
|
||||||
"disabled": @state.disabled
|
|
||||||
|
|
||||||
_onClick: ->
|
|
||||||
return if @state.disabled
|
|
||||||
NylasEnv.commands.dispatch('core:next-item')
|
|
||||||
return
|
|
||||||
|
|
||||||
_getStateFromStores: ->
|
|
||||||
disabled: @isLastThread()
|
|
||||||
|
|
||||||
UpButton = React.createClass
|
|
||||||
displayName: 'UpButton'
|
|
||||||
mixins: [ThreadNavButtonMixin]
|
|
||||||
|
|
||||||
render: ->
|
|
||||||
<div className={@_classSet()} onClick={@_onClick} title="Previous thread">
|
|
||||||
<RetinaImg name="toolbar-up-arrow.png" mode={RetinaImg.Mode.ContentIsMask} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
_classSet: ->
|
|
||||||
classNames
|
|
||||||
"btn-icon": true
|
|
||||||
"message-toolbar-arrow": true
|
|
||||||
"up": true
|
|
||||||
"disabled": @state.disabled
|
|
||||||
|
|
||||||
_onClick: ->
|
|
||||||
return if @state.disabled
|
|
||||||
NylasEnv.commands.dispatch('core:previous-item')
|
|
||||||
return
|
|
||||||
|
|
||||||
_getStateFromStores: ->
|
|
||||||
disabled: @isFirstThread()
|
|
||||||
|
|
||||||
UpButton.containerRequired = false
|
|
||||||
DownButton.containerRequired = false
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
UpButton,
|
|
||||||
DownButton,
|
|
||||||
TrashButton,
|
|
||||||
MarkAsSpamButton,
|
|
||||||
ArchiveButton,
|
|
||||||
ToggleStarredButton,
|
|
||||||
ToggleUnreadButton
|
|
||||||
}
|
|
317
internal_packages/thread-list/lib/thread-toolbar-buttons.jsx
Normal file
317
internal_packages/thread-list/lib/thread-toolbar-buttons.jsx
Normal file
|
@ -0,0 +1,317 @@
|
||||||
|
import React from "react";
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import ThreadListStore from './thread-list-store';
|
||||||
|
import {RetinaImg} from 'nylas-component-kit';
|
||||||
|
import {
|
||||||
|
Actions,
|
||||||
|
TaskFactory,
|
||||||
|
AccountStore,
|
||||||
|
CategoryStore,
|
||||||
|
FocusedContentStore,
|
||||||
|
FocusedPerspectiveStore,
|
||||||
|
} from "nylas-exports";
|
||||||
|
|
||||||
|
export class ArchiveButton extends React.Component {
|
||||||
|
static displayName = 'ArchiveButton';
|
||||||
|
static containerRequired = false;
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
items: React.PropTypes.array.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
_onArchive = (event) => {
|
||||||
|
const tasks = TaskFactory.tasksForArchiving({threads: this.props.items})
|
||||||
|
Actions.queueTasks(tasks);
|
||||||
|
Actions.popSheet();
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const allowed = FocusedPerspectiveStore.current().canArchiveThreads(this.props.items);
|
||||||
|
if (!allowed) {
|
||||||
|
return <span />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
tabIndex={-1}
|
||||||
|
style={{order: -107}}
|
||||||
|
className="btn btn-toolbar"
|
||||||
|
title="Archive"
|
||||||
|
onClick={this._onArchive}
|
||||||
|
>
|
||||||
|
<RetinaImg name="toolbar-archive.png" mode={RetinaImg.Mode.ContentIsMask} />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TrashButton extends React.Component {
|
||||||
|
static displayName = 'TrashButton'
|
||||||
|
static containerRequired = false;
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
items: React.PropTypes.array.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
_onRemove = (event) => {
|
||||||
|
const tasks = TaskFactory.tasksForMovingToTrash({threads: this.props.items});
|
||||||
|
Actions.queueTasks(tasks);
|
||||||
|
Actions.popSheet();
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const allowed = FocusedPerspectiveStore.current().canMoveThreadsTo(this.props.items, 'trash')
|
||||||
|
if (!allowed) {
|
||||||
|
return <span />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
tabIndex={-1}
|
||||||
|
style={{order: -106}}
|
||||||
|
className="btn btn-toolbar"
|
||||||
|
title="Move to Trash"
|
||||||
|
onClick={this._onRemove}
|
||||||
|
>
|
||||||
|
<RetinaImg name="toolbar-trash.png" mode={RetinaImg.Mode.ContentIsMask} />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MarkAsSpamButton extends React.Component {
|
||||||
|
static displayName = 'MarkAsSpamButton';
|
||||||
|
static containerRequired = false;
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
items: React.PropTypes.array.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
_allInSpam() {
|
||||||
|
return this.props.items.every(item => item.categories.map(c => c.name).includes('spam'));
|
||||||
|
}
|
||||||
|
|
||||||
|
_onNotSpam = (event) => {
|
||||||
|
const tasks = TaskFactory.tasksForApplyingCategories({
|
||||||
|
threads: this.props.items,
|
||||||
|
categoriesToAdd: (accountId) => {
|
||||||
|
const account = AccountStore.accountForId(accountId)
|
||||||
|
return account.usesFolders() ? [CategoryStore.getInboxCategory(accountId)] : [];
|
||||||
|
},
|
||||||
|
categoriesToRemove: (accountId) => {
|
||||||
|
return [CategoryStore.getSpamCategory(accountId)];
|
||||||
|
},
|
||||||
|
})
|
||||||
|
Actions.queueTasks(tasks);
|
||||||
|
Actions.popSheet();
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onMarkAsSpam = (event) => {
|
||||||
|
const tasks = TaskFactory.tasksForMarkingAsSpam({threads: this.props.items});
|
||||||
|
Actions.queueTasks(tasks);
|
||||||
|
Actions.popSheet();
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this._allInSpam()) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
tabIndex={-1}
|
||||||
|
style={{order: -105}}
|
||||||
|
className="btn btn-toolbar"
|
||||||
|
title="Not Spam"
|
||||||
|
onClick={this._onNotSpam}
|
||||||
|
>
|
||||||
|
<RetinaImg name="toolbar-not-spam.png" mode={RetinaImg.Mode.ContentIsMask} />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const allowed = FocusedPerspectiveStore.current().canMoveThreadsTo(this.props.items, 'spam');
|
||||||
|
if (!allowed) {
|
||||||
|
return <span />;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
tabIndex={-1}
|
||||||
|
style={{order: -105}}
|
||||||
|
className="btn btn-toolbar"
|
||||||
|
title="Mark as Spam"
|
||||||
|
onClick={this._onMarkAsSpam}
|
||||||
|
>
|
||||||
|
<RetinaImg name="toolbar-spam.png" mode={RetinaImg.Mode.ContentIsMask} />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ToggleStarredButton extends React.Component {
|
||||||
|
static displayName = 'ToggleStarredButton';
|
||||||
|
static containerRequired = false;
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
items: React.PropTypes.array.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
_onStar = (event) => {
|
||||||
|
const task = TaskFactory.taskForInvertingStarred({threads: this.props.items});
|
||||||
|
Actions.queueTask(task);
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const postClickStarredState = this.props.items.every((t) => t.starred === false);
|
||||||
|
const title = postClickStarredState ? "Star" : "Unstar";
|
||||||
|
const imageName = postClickStarredState ? "toolbar-star.png" : "toolbar-star-selected.png"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
tabIndex={-1}
|
||||||
|
style={{order: -103}}
|
||||||
|
className="btn btn-toolbar"
|
||||||
|
title={title}
|
||||||
|
onClick={this._onStar}
|
||||||
|
>
|
||||||
|
<RetinaImg name={imageName} mode={RetinaImg.Mode.ContentIsMask} />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ToggleUnreadButton extends React.Component {
|
||||||
|
static displayName = 'ToggleUnreadButton';
|
||||||
|
static containerRequired = false;
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
items: React.PropTypes.array.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
_onClick = (event) => {
|
||||||
|
const task = TaskFactory.taskForInvertingUnread({threads: this.props.items});
|
||||||
|
Actions.queueTask(task);
|
||||||
|
Actions.popSheet();
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const postClickUnreadState = this.props.items.every(t => t.unread === false);
|
||||||
|
const fragment = postClickUnreadState ? "unread" : "read";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
tabIndex={-1}
|
||||||
|
style={{order: -104}}
|
||||||
|
className="btn btn-toolbar"
|
||||||
|
title={`Mark as ${fragment}`}
|
||||||
|
onClick={this._onClick}
|
||||||
|
>
|
||||||
|
<RetinaImg
|
||||||
|
name={`toolbar-markas${fragment}.png`}
|
||||||
|
mode={RetinaImg.Mode.ContentIsMask}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ThreadArrowButton extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
getStateFromStores: React.PropTypes.func,
|
||||||
|
direction: React.PropTypes.string,
|
||||||
|
command: React.PropTypes.string,
|
||||||
|
title: React.PropTypes.string,
|
||||||
|
}
|
||||||
|
constructor() {
|
||||||
|
this.state = this.props.getStateFromStores();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this._unsubscribe = ThreadListStore.listen(this._onStoreChange);
|
||||||
|
this._unsubscribe_focus = FocusedContentStore.listen(this._onStoreChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this._unsubscribe();
|
||||||
|
this._unsubscribe_focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onClick() {
|
||||||
|
if (this.state.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NylasEnv.commands.dispatch(this.props.command);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onStoreChange() {
|
||||||
|
this.setState(this.props.getStateFromStores());
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {direction, title} = this.props;
|
||||||
|
const classes = classNames({
|
||||||
|
"btn-icon": true,
|
||||||
|
"message-toolbar-arrow": true,
|
||||||
|
"disabled": this.state.disabled,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${classes} ${direction}`} onClick={this._onClick} title={title}>
|
||||||
|
<RetinaImg name={`toolbar-${direction}-arrow.png`} mode={RetinaImg.Mode.ContentIsMask} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DownButton = () => {
|
||||||
|
const getStateFromStores = () => {
|
||||||
|
const selectedId = FocusedContentStore.focusedId('thread');
|
||||||
|
const lastIndex = ThreadListStore.dataSource().count() - 1
|
||||||
|
const lastItem = ThreadListStore.dataSource().get(lastIndex);
|
||||||
|
return {
|
||||||
|
disabled: (lastItem && lastItem.id === selectedId),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThreadArrowButton
|
||||||
|
getStateFromStores={getStateFromStores}
|
||||||
|
direction={"down"}
|
||||||
|
title={"Next thread"}
|
||||||
|
command={'core:next-item'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
DownButton.displayName = 'DownButton';
|
||||||
|
DownButton.containerRequired = false;
|
||||||
|
|
||||||
|
export const UpButton = () => {
|
||||||
|
const getStateFromStores = () => {
|
||||||
|
const selectedId = FocusedContentStore.focusedId('thread');
|
||||||
|
const item = ThreadListStore.dataSource().get(0)
|
||||||
|
return {
|
||||||
|
disabled: (item && item.id === selectedId),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThreadArrowButton
|
||||||
|
getStateFromStores={getStateFromStores}
|
||||||
|
direction={"up"}
|
||||||
|
title={"Previous thread"}
|
||||||
|
command={'core:previous-item'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
UpButton.displayName = 'UpButton';
|
||||||
|
UpButton.containerRequired = false;
|
|
@ -1,8 +1,16 @@
|
||||||
React = require "react"
|
React = require "react"
|
||||||
ReactDOM = require "react-dom"
|
ReactDOM = require "react-dom"
|
||||||
ReactTestUtils = require 'react-addons-test-utils'
|
ReactTestUtils = require 'react-addons-test-utils'
|
||||||
{Thread, FocusedContentStore, Actions, ChangeUnreadTask} = require "nylas-exports"
|
{
|
||||||
{ToggleStarredButton, ToggleUnreadButton} = require '../lib/thread-toolbar-buttons'
|
Thread,
|
||||||
|
FocusedContentStore,
|
||||||
|
Actions,
|
||||||
|
CategoryStore,
|
||||||
|
ChangeUnreadTask,
|
||||||
|
TaskFactory,
|
||||||
|
MailboxPerspective
|
||||||
|
} = require "nylas-exports"
|
||||||
|
{ToggleStarredButton, ToggleUnreadButton, MarkAsSpamButton} = require '../lib/thread-toolbar-buttons'
|
||||||
|
|
||||||
test_thread = (new Thread).fromJSON({
|
test_thread = (new Thread).fromJSON({
|
||||||
"id" : "thread_12345"
|
"id" : "thread_12345"
|
||||||
|
@ -63,3 +71,45 @@ describe "ThreadToolbarButtons", ->
|
||||||
ReactTestUtils.Simulate.click ReactDOM.findDOMNode(markUnreadBtn).childNodes[0]
|
ReactTestUtils.Simulate.click ReactDOM.findDOMNode(markUnreadBtn).childNodes[0]
|
||||||
|
|
||||||
expect(Actions.popSheet).toHaveBeenCalled()
|
expect(Actions.popSheet).toHaveBeenCalled()
|
||||||
|
|
||||||
|
describe "Marking as spam", ->
|
||||||
|
thread = null
|
||||||
|
markSpamButton = null
|
||||||
|
|
||||||
|
describe "when the thread is already in spam", ->
|
||||||
|
beforeEach ->
|
||||||
|
thread = new Thread({
|
||||||
|
id: "thread-id-lol-123",
|
||||||
|
accountId: TEST_ACCOUNT_ID,
|
||||||
|
categories: [{name: 'spam'}]
|
||||||
|
})
|
||||||
|
markSpamButton = ReactTestUtils.renderIntoDocument(
|
||||||
|
<MarkAsSpamButton items={[thread]} />
|
||||||
|
)
|
||||||
|
|
||||||
|
it "queues a task to remove spam", ->
|
||||||
|
spyOn(Actions, 'queueTasks')
|
||||||
|
spyOn(CategoryStore, 'getSpamCategory').andReturn(thread.categories[0])
|
||||||
|
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(markSpamButton))
|
||||||
|
{labelsToAdd, labelsToRemove} = Actions.queueTasks.mostRecentCall.args[0][0]
|
||||||
|
expect(labelsToAdd).toEqual([])
|
||||||
|
expect(labelsToRemove).toEqual([thread.categories[0]])
|
||||||
|
|
||||||
|
describe "when the thread can be moved to spam", ->
|
||||||
|
beforeEach ->
|
||||||
|
spyOn(Actions, 'queueTasks')
|
||||||
|
spyOn(MailboxPerspective.prototype, 'canMoveThreadsTo').andReturn(true)
|
||||||
|
thread = new Thread(id: "thread-id-lol-123", accountId: TEST_ACCOUNT_ID, categories: [])
|
||||||
|
markSpamButton = ReactTestUtils.renderIntoDocument(
|
||||||
|
<MarkAsSpamButton items={[thread]} />
|
||||||
|
)
|
||||||
|
|
||||||
|
it "queues a task to mark as spam", ->
|
||||||
|
spyOn(TaskFactory, 'tasksForMarkingAsSpam')
|
||||||
|
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(markSpamButton))
|
||||||
|
expect(TaskFactory.tasksForMarkingAsSpam).toHaveBeenCalledWith({threads: [thread]})
|
||||||
|
|
||||||
|
it "returns to the thread list", ->
|
||||||
|
spyOn(Actions, 'popSheet')
|
||||||
|
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(markSpamButton))
|
||||||
|
expect(Actions.popSheet).toHaveBeenCalled()
|
||||||
|
|
|
@ -11,7 +11,7 @@ import Category from '../models/category'
|
||||||
|
|
||||||
const TaskFactory = {
|
const TaskFactory = {
|
||||||
|
|
||||||
tasksForApplyingCategories({threads, categoriesToRemove = () => [], categoriesToAdd = () => [], taskDescription} = {}) {
|
tasksForApplyingCategories({threads, categoriesToRemove, categoriesToAdd, taskDescription}) {
|
||||||
const byAccount = {}
|
const byAccount = {}
|
||||||
const tasks = []
|
const tasks = []
|
||||||
|
|
||||||
|
@ -22,8 +22,8 @@ const TaskFactory = {
|
||||||
const {accountId} = thread
|
const {accountId} = thread
|
||||||
if (!byAccount[accountId]) {
|
if (!byAccount[accountId]) {
|
||||||
byAccount[accountId] = {
|
byAccount[accountId] = {
|
||||||
categoriesToRemove: categoriesToRemove(accountId),
|
categoriesToRemove: categoriesToRemove ? categoriesToRemove(accountId) : [],
|
||||||
categoriesToAdd: categoriesToAdd(accountId),
|
categoriesToAdd: categoriesToAdd ? categoriesToAdd(accountId) : [],
|
||||||
threadsToUpdate: [],
|
threadsToUpdate: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
static/images/toolbar/toolbar-not-spam@1x.png
Normal file
BIN
static/images/toolbar/toolbar-not-spam@1x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
BIN
static/images/toolbar/toolbar-not-spam@2x.png
Normal file
BIN
static/images/toolbar/toolbar-not-spam@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
Loading…
Add table
Reference in a new issue