feat(snooze): Add snooze date label to snooze threads

- Adds a new InjectedComponentSet for the role 'ThreadList:Label'.
- Adds a new label to the thread list indicating the snooze date if the
  thread has been snoozed
- Coerces MailLabel to achieve this. This is a temporary hack, we should
  design a better view to display snooze date information
This commit is contained in:
Juan Tejada 2016-02-25 13:06:01 -08:00
parent f75eddb518
commit f22fdbae49
12 changed files with 107 additions and 18 deletions

View file

@ -80,7 +80,13 @@ c3 = new ListTabular.Column
labels.push c3LabelComponentCache[label.id] labels.push c3LabelComponentCache[label.id]
<span className="details"> <span className="details">
{labels} <InjectedComponentSet
inline
containerRequired={false}
children={labels}
matching={role: "ThreadList:Label"}
className="thread-injected-mail-labels"
exposedProps={thread: thread}/>
<span className="subject">{subject(thread.subject)}</span> <span className="subject">{subject(thread.subject)}</span>
<span className="snippet">{thread.snippet}</span> <span className="snippet">{thread.snippet}</span>
{attachment} {attachment}
@ -148,7 +154,13 @@ cNarrow = new ListTabular.Column
<div className="snippet-and-labels"> <div className="snippet-and-labels">
<div className="snippet">{thread.snippet}&nbsp;</div> <div className="snippet">{thread.snippet}&nbsp;</div>
<div style={flex: 1, flexShrink: 1}></div> <div style={flex: 1, flexShrink: 1}></div>
{labels} <InjectedComponentSet
inline
containerRequired={false}
children={labels}
matching={role: "ThreadList:Label"}
className="thread-injected-mail-labels-narrow"
exposedProps={thread: thread}/>
</div> </div>
</div> </div>

View file

@ -239,6 +239,9 @@
.thread-injected-icons { .thread-injected-icons {
vertical-align: top; vertical-align: top;
} }
.thread-injected-mail-labels {
margin-right: 6px;
}
.thread-icon { .thread-icon {
width:25px; width:25px;
height:24px; height:24px;

View file

@ -1,21 +1,24 @@
/** @babel */ /** @babel */
import {ComponentRegistry} from 'nylas-exports'; import {ComponentRegistry} from 'nylas-exports';
import {ToolbarSnooze, BulkThreadSnooze} from './toolbar-components'; import {ToolbarSnooze, BulkThreadSnooze} from './snooze-toolbar-components';
import QuickActionSnoozeButton from './quick-action-snooze-button' import SnoozeQuickActionButton from './snooze-quick-action-button'
import SnoozeMailLabel from './snooze-mail-label'
import SnoozeStore from './snooze-store' import SnoozeStore from './snooze-store'
export function activate() { export function activate() {
this.snoozeStore = new SnoozeStore() this.snoozeStore = new SnoozeStore()
ComponentRegistry.register(ToolbarSnooze, {role: 'message:Toolbar'}); ComponentRegistry.register(ToolbarSnooze, {role: 'message:Toolbar'});
ComponentRegistry.register(QuickActionSnoozeButton, {role: 'ThreadListQuickAction'}); ComponentRegistry.register(SnoozeQuickActionButton, {role: 'ThreadListQuickAction'});
ComponentRegistry.register(BulkThreadSnooze, {role: 'thread:BulkAction'}); ComponentRegistry.register(BulkThreadSnooze, {role: 'thread:BulkAction'});
ComponentRegistry.register(SnoozeMailLabel, {role: 'ThreadList:Label'});
} }
export function deactivate() { export function deactivate() {
ComponentRegistry.unregister(ToolbarSnooze); ComponentRegistry.unregister(ToolbarSnooze);
ComponentRegistry.unregister(QuickActionSnoozeButton); ComponentRegistry.unregister(SnoozeQuickActionButton);
ComponentRegistry.unregister(BulkThreadSnooze); ComponentRegistry.unregister(BulkThreadSnooze);
ComponentRegistry.unregister(SnoozeMailLabel);
this.snoozeStore.deactivate() this.snoozeStore.deactivate()
} }

View file

@ -5,4 +5,4 @@ export const PLUGIN_ID = plugin.appId[NylasEnv.config.get("env")];
export const PLUGIN_NAME = "Snooze Plugin" export const PLUGIN_NAME = "Snooze Plugin"
export const SNOOZE_CATEGORY_NAME = "N1-Snoozed" export const SNOOZE_CATEGORY_NAME = "N1-Snoozed"
export const DATE_FORMAT_LONG = 'ddd, MMM D, YYYY h:mmA' export const DATE_FORMAT_LONG = 'ddd, MMM D, YYYY h:mmA'
export const DATE_FORMAT_SHORT = 'MMM D h:mmA' export const DATE_FORMAT_SHORT = 'MMM D, h:mmA'

View file

@ -0,0 +1,44 @@
import _ from 'underscore';
import React, {Component, PropTypes} from 'react';
import {RetinaImg, MailLabel} from 'nylas-component-kit';
import {SNOOZE_CATEGORY_NAME, PLUGIN_ID} from './snooze-constants';
import {snoozeMessage} from './snooze-utils';
class SnoozeMailLabel extends Component {
static displayName = 'SnoozeMailLabel';
static propTypes = {
thread: PropTypes.object,
};
render() {
const {thread} = this.props;
if (_.findWhere(thread.categories, {displayName: SNOOZE_CATEGORY_NAME})) {
const metadata = thread.metadataForPluginId(PLUGIN_ID);
if (metadata) {
// TODO this is such a hack
const {snoozeDate} = metadata;
const message = snoozeMessage(snoozeDate).replace('Snoozed', '')
const content = (
<span className="snooze-mail-label">
<RetinaImg
name="icon-snoozed.png"
mode={RetinaImg.Mode.ContentIsMask} />
<span className="date-message">{message}</span>
</span>
)
const label = {
displayName: content,
isLockedCategory: ()=> true,
hue: ()=> 259,
}
return <MailLabel label={label} key={'snooze-message-' + thread.id} />;
}
return <span />
}
return <span />
}
}
export default SnoozeMailLabel;

View file

@ -1,7 +1,7 @@
/** @babel */ /** @babel */
import _ from 'underscore'; import _ from 'underscore';
import {Actions, NylasAPI, AccountStore} from 'nylas-exports'; import {Actions, NylasAPI, AccountStore} from 'nylas-exports';
import {moveThreadsToSnooze} from './snooze-category-helpers'; import {moveThreadsToSnooze, moveThreadsFromSnooze} from './snooze-utils';
import {PLUGIN_ID, PLUGIN_NAME} from './snooze-constants'; import {PLUGIN_ID, PLUGIN_NAME} from './snooze-constants';
import SnoozeActions from './snooze-actions'; import SnoozeActions from './snooze-actions';
@ -40,6 +40,7 @@ class SnoozeStore {
}) })
}) })
.catch((error)=> { .catch((error)=> {
moveThreadsFromSnooze(threads)
Actions.closePopover(); Actions.closePopover();
NylasEnv.reportError(error); NylasEnv.reportError(error);
NylasEnv.showErrorDialog(`Sorry, we were unable to save your snooze settings. ${error.message}`); NylasEnv.showErrorDialog(`Sorry, we were unable to save your snooze settings. ${error.message}`);

View file

@ -15,6 +15,25 @@ import {
} from 'nylas-exports'; } from 'nylas-exports';
import {SNOOZE_CATEGORY_NAME, DATE_FORMAT_SHORT} from './snooze-constants' import {SNOOZE_CATEGORY_NAME, DATE_FORMAT_SHORT} from './snooze-constants'
export function snoozeMessage(snoozeDate) {
let message = 'Snoozed'
if (snoozeDate) {
let dateFormat = DATE_FORMAT_SHORT
const date = moment(snoozeDate)
const now = moment()
const hourDifference = moment.duration(date.diff(now)).asHours()
if (hourDifference < 24) {
dateFormat = dateFormat.replace('MMM D, ', '');
}
if (date.minutes() === 0) {
dateFormat = dateFormat.replace(':mm', '');
}
message += ` until ${DateUtils.format(date, dateFormat)}`;
}
return message;
}
export function createSnoozeCategory(accountId, name = SNOOZE_CATEGORY_NAME) { export function createSnoozeCategory(accountId, name = SNOOZE_CATEGORY_NAME) {
const category = new Category({ const category = new Category({
@ -76,7 +95,7 @@ export function getSnoozeCategoriesByAccount(accounts = AccountStore.accounts())
export function groupProcessedThreadsByAccountId(categoriesByAccountId, threads) { export function groupProcessedThreadsByAccountId(categoriesByAccountId, threads) {
return DatabaseStore.modelify(Thread, _.pluck(threads, 'id')).then((updatedThreads)=> { return DatabaseStore.modelify(Thread, _.pluck(threads, 'clientId')).then((updatedThreads)=> {
const threadsByAccountId = {} const threadsByAccountId = {}
updatedThreads.forEach((thread)=> { updatedThreads.forEach((thread)=> {
const accId = thread.accountId const accId = thread.accountId
@ -95,15 +114,9 @@ export function groupProcessedThreadsByAccountId(categoriesByAccountId, threads)
} }
export function moveThreads(threads, categoriesByAccountId, {snooze, snoozeDate} = {}) { export function moveThreads(threads, categoriesByAccountId, {snooze, description} = {}) {
const inbox = CategoryStore.getInboxCategory const inbox = CategoryStore.getInboxCategory
const snoozeCat = (accId)=> categoriesByAccountId[accId] const snoozeCat = (accId)=> categoriesByAccountId[accId]
let description = 'Snoozed'
if (snoozeDate) {
const date = moment(snoozeDate)
description += ` until ${DateUtils.format(date, DATE_FORMAT_SHORT)}`
}
const tasks = TaskFactory.tasksForApplyingCategories({ const tasks = TaskFactory.tasksForApplyingCategories({
threads, threads,
categoriesToRemove: snooze ? inbox : snoozeCat, categoriesToRemove: snooze ? inbox : snoozeCat,
@ -125,7 +138,8 @@ export function moveThreads(threads, categoriesByAccountId, {snooze, snoozeDate}
export function moveThreadsToSnooze(threads, snoozeDate) { export function moveThreadsToSnooze(threads, snoozeDate) {
return getSnoozeCategoriesByAccount() return getSnoozeCategoriesByAccount()
.then((categoriesByAccountId)=> { .then((categoriesByAccountId)=> {
return moveThreads(threads, categoriesByAccountId, {snooze: true, snoozeDate}) const description = snoozeMessage(snoozeDate)
return moveThreads(threads, categoriesByAccountId, {snooze: true, description})
}) })
} }
@ -133,6 +147,7 @@ export function moveThreadsToSnooze(threads, snoozeDate) {
export function moveThreadsFromSnooze(threads) { export function moveThreadsFromSnooze(threads) {
return getSnoozeCategoriesByAccount() return getSnoozeCategoriesByAccount()
.then((categoriesByAccountId)=> { .then((categoriesByAccountId)=> {
return moveThreads(threads, categoriesByAccountId, {snooze: false}) const description = 'Unsnoozed';
return moveThreads(threads, categoriesByAccountId, {snooze: false, description})
}) })
} }

View file

@ -0,0 +1,11 @@
@snooze-color: #472B82;
.snooze-mail-label {
display: flex;
align-items: center;
img {
background-color: @snooze-color;
margin-right: 5px;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB