Mailspring/internal_packages/thread-list/lib/thread-toolbar-buttons.jsx
Evan Morikawa a20b979208 feat(analytics): add analytics for change mail tasks
Summary:
Adds the following new events:

- Threads Moved to Folder
  - isArchive
  - source
  - folderType
  - folderDisplayName
  - numThreads
  - numMessages
  - description
  - isUndo

- Threads Changed Labels
  - isArchive
  - source
  - labelTypesToAdd
  - labelTypesToRemove
  - labelDisplayNamesToAdd
  - labelDisplayNamesToRemove
  - numThreads
  - numMessages
  - description
  - isUndo

- Threads Starred
  - source
  - numThreads
  - description
  - isUndo

- Threads Unstarred
  - source
  - numThreads
  - description
  - isUndo

- Threads Marked as Read
  - source
  - numThreads
  - description
  - isUndo

- Threads Marked as Unread
  - source
  - numThreads
  - description
  - isUndo

Each new action has a "source" property that's one of the following:
- Category Picker: New Category
- Category Picker: Existing Category
- Toolbar Button: Message List
- Toolbar Button: Thread List
- Send and Archive
- Context Menu: Thread List
- Thread List Icon
- Quick Actions: Thread List
- Swipe
- Keyboard Shortcut
- Dragged Out of List
- Snooze Move
- Important Icon
- Label Remove Icon
- Thread Selected
- Mail Rules
- Dragged Into List

Test Plan: manual

Reviewers: juan

Reviewed By: juan

Differential Revision: https://phab.nylas.com/D3760
2017-01-24 12:22:33 -05:00

323 lines
8.2 KiB
JavaScript

import React from "react";
import classNames from 'classnames';
import {RetinaImg} from 'nylas-component-kit';
import {
Actions,
TaskFactory,
AccountStore,
CategoryStore,
FocusedContentStore,
FocusedPerspectiveStore,
} from "nylas-exports";
import ThreadListStore from './thread-list-store';
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, source: "Toolbar Button: Thread List"})
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, source: "Toolbar Button: Thread List"});
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({
source: "Toolbar Button: Thread List",
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, source: "Toolbar Button: Thread List"});
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, source: "Toolbar Button: Thread List"});
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, source: "Toolbar Button: Thread List"});
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(props) {
super(props);
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;