fix(lint): Various linter issues

This commit is contained in:
Ben Gotow 2016-05-06 16:06:16 -07:00
parent 813669f7d4
commit 7533dc0ac6
78 changed files with 445 additions and 524 deletions

View file

@ -34,7 +34,7 @@
"quote-props": ["error", "consistent-as-needed", { "keywords": true }],
"no-param-reassign": ["error", { "props": false }],
"semi": "off",
"import/no-unresolved": ["error", {"ignore": ["nylas-exports", "nylas-component-kit", "electron", "nylas-store", "react-dom/server", "nylas-observables"]}],
"import/no-unresolved": ["error", {"ignore": ["nylas-exports", "nylas-component-kit", "electron", "nylas-store", "react-dom/server", "nylas-observables", "windows-shortcuts"]}],
"react/no-multi-comp": "off",
"react/prop-types": ["error", {"ignore": ["children"]}],
"react/sort-comp": "error"

View file

@ -1,3 +1,5 @@
/* eslint global-require: 0*/
import {DraftStore, Actions, QuotedHTMLTransformer} from 'nylas-exports';
import NylasStore from 'nylas-store';
import path from 'path';
@ -105,8 +107,8 @@ class TemplateStore extends NylasStore {
if (draftClientId) {
DraftStore.sessionForClientId(draftClientId).then((session) => {
const draft = session.draft();
const draftName = name ? name : draft.subject.replace(TemplateStore.INVALID_TEMPLATE_NAME_REGEX, '');
let draftContents = contents ? contents : QuotedHTMLTransformer.removeQuotedHTML(draft.body);
const draftName = name || draft.subject.replace(TemplateStore.INVALID_TEMPLATE_NAME_REGEX, '');
let draftContents = contents || QuotedHTMLTransformer.removeQuotedHTML(draft.body);
const sigIndex = draftContents.indexOf('<signature>');
draftContents = sigIndex > -1 ? draftContents.slice(0, sigIndex) : draftContents;
@ -203,7 +205,7 @@ class TemplateStore extends NylasStore {
deleteTemplate(name, callback) {
const template = this._getTemplate(name);
if (!template) { return undefined; }
if (!template) { return; }
if (this._displayDialog(
'Delete this template?',

View file

@ -31,7 +31,7 @@ describe('TemplateStore', function templateStore() {
});
it('should create the templates folder if it does not exist', () => {
spyOn(fs, 'exists').andCallFake((path, callback) => callback(false) );
spyOn(fs, 'exists').andCallFake((path, callback) => callback(false));
TemplateStore._init(stubTemplatesDir);
expect(fs.mkdir).toHaveBeenCalled();
});
@ -39,7 +39,7 @@ describe('TemplateStore', function templateStore() {
it('should expose templates in the templates directory', () => {
let watchCallback;
spyOn(fs, 'exists').andCallFake((path, callback) => { callback(true); });
spyOn(fs, 'watch').andCallFake((path, callback) => watchCallback = callback);
spyOn(fs, 'watch').andCallFake((path, callback) => { watchCallback = callback });
spyOn(fs, 'readdir').andCallFake((path, callback) => { callback(null, Object.keys(stubTemplateFiles)); });
TemplateStore._init(stubTemplatesDir);
watchCallback();
@ -51,7 +51,7 @@ describe('TemplateStore', function templateStore() {
let watchFired = false;
spyOn(fs, 'exists').andCallFake((path, callback) => callback(true));
spyOn(fs, 'watch').andCallFake((path, callback) => watchCallback = callback);
spyOn(fs, 'watch').andCallFake((path, callback) => { watchCallback = callback });
spyOn(fs, 'readdir').andCallFake((path, callback) => {
if (watchFired) {
callback(null, Object.keys(stubTemplateFiles));
@ -71,7 +71,7 @@ describe('TemplateStore', function templateStore() {
xit('should insert the template with the given id into the draft with the given id', () => {
let watchCallback;
spyOn(fs, 'exists').andCallFake((path, callback) => { callback(true); });
spyOn(fs, 'watch').andCallFake((path, callback) => watchCallback = callback);
spyOn(fs, 'watch').andCallFake((path, callback) => { watchCallback = callback });
spyOn(fs, 'readdir').andCallFake((path, callback) => { callback(null, Object.keys(stubTemplateFiles)); });
TemplateStore._init(stubTemplatesDir);
watchCallback();
@ -151,7 +151,7 @@ describe('TemplateStore', function templateStore() {
runs(() => {
TemplateStore._onCreateTemplate({draftClientId: 'localid-b'});
});
waitsFor(() => TemplateStore.trigger.callCount > 0 );
waitsFor(() => TemplateStore.trigger.callCount > 0);
runs(() => {
expect(TemplateStore.items().length).toEqual(1);
});
@ -163,7 +163,7 @@ describe('TemplateStore', function templateStore() {
runs(() => {
TemplateStore._onCreateTemplate({draftClientId: 'localid-nosubject'});
});
waitsFor(() => TemplateStore._displayError.callCount > 0 );
waitsFor(() => TemplateStore._displayError.callCount > 0);
runs(() => {
expect(TemplateStore._displayError).toHaveBeenCalled();
});

View file

@ -92,7 +92,7 @@ function InflateDraftClientId(ComposedComponent) {
render() {
if (!this.state.draft) {
return <span/>;
return <span />;
}
return <ComposedComponent ref="composed" {...this.props} {...this.state} />;
}

View file

@ -35,7 +35,7 @@ class ComposerWithWindowProps extends React.Component {
}
componentWillUnmount() {
if (this._usub) {this._usub()}
if (this._usub) { this._usub() }
}
componentDidUpdate() {

View file

@ -7,6 +7,11 @@ const SPLIT_KEY = "---splitvalue---"
export default class SidebarParticipantPicker extends React.Component {
static displayName = 'SidebarParticipantPicker';
static containerStyles = {
order: 0,
flexShrink: 0,
};
constructor(props) {
super(props);
this.state = this._getStateFromStores();
@ -22,11 +27,6 @@ export default class SidebarParticipantPicker extends React.Component {
this._usub();
}
static containerStyles = {
order: 0,
flexShrink: 0,
};
_getStateFromStores() {
return {
sortedContacts: FocusedContactsStore.sortedContacts(),

View file

@ -1,3 +1,4 @@
/* eslint global-require: 0 */
import {AccountStore, Account, Actions, React} from 'nylas-exports'
import {RetinaImg} from 'nylas-component-kit'
@ -39,12 +40,15 @@ export default class AccountErrorHeader extends React.Component {
renderErrorHeader(message, buttonName, actionCallback) {
return (
<div className="account-error-header notifications-sticky">
<div className={"notifications-sticky-item notification-error has-default-action"}
onClick={actionCallback}>
<RetinaImg
className="icon"
name="icon-alert-onred.png"
mode={RetinaImg.Mode.ContentPreserve} />
<div
className={"notifications-sticky-item notification-error has-default-action"}
onClick={actionCallback}
>
<RetinaImg
className="icon"
name="icon-alert-onred.png"
mode={RetinaImg.Mode.ContentPreserve}
/>
<div className="message">
{message}
</div>
@ -62,32 +66,32 @@ export default class AccountErrorHeader extends React.Component {
switch (account.syncState) {
case Account.SYNC_STATE_AUTH_FAILED:
return this.renderErrorHeader(
`Nylas N1 can no longer authenticate with ${account.emailAddress}. Click here to reconnect.`,
"Reconnect",
()=>this._reconnect(account));
case Account.SYNC_STATE_AUTH_FAILED:
return this.renderErrorHeader(
`Nylas N1 can no longer authenticate with ${account.emailAddress}. Click here to reconnect.`,
"Reconnect",
() => this._reconnect(account));
case Account.SYNC_STATE_STOPPED:
return this.renderErrorHeader(
`The cloud sync for ${account.emailAddress} has been disabled. You will
not be able to send or receive mail. Please contact Nylas support.`,
"Contact support",
()=>this._contactSupport());
case Account.SYNC_STATE_STOPPED:
return this.renderErrorHeader(
`The cloud sync for ${account.emailAddress} has been disabled. You will
not be able to send or receive mail. Please contact Nylas support.`,
"Contact support",
() => this._contactSupport());
default:
return this.renderErrorHeader(
`Nylas encountered an error while syncing mail for ${account.emailAddress} - we're
looking into it. Contact Nylas support for details.`,
"Contact support",
()=>this._contactSupport());
default:
return this.renderErrorHeader(
`Nylas encountered an error while syncing mail for ${account.emailAddress} - we're
looking into it. Contact Nylas support for details.`,
"Contact support",
() => this._contactSupport());
}
}
if (errorAccounts.length > 1) {
return this.renderErrorHeader("Several of your accounts are having issues. " +
"You will not be able to send or receive mail. Click here to manage your accounts.",
"Open preferences",
()=>this._openPreferences());
() => this._openPreferences());
}
return false;
}

View file

@ -88,7 +88,7 @@ export default class ConnectionStatusHeader extends React.Component {
const {connected, nextRetryText} = this.state;
if (connected) {
return (<span/>);
return (<span />);
}
const apiDomain = NylasAPI.APIRoot.split('//').pop();
@ -99,7 +99,8 @@ export default class ConnectionStatusHeader extends React.Component {
<RetinaImg
className="icon"
name="icon-alert-onred.png"
mode={RetinaImg.Mode.ContentPreserve} />
mode={RetinaImg.Mode.ContentPreserve}
/>
<div className="message">
Nylas N1 isn't able to reach {apiDomain}. Retrying {nextRetryText}.
</div>

View file

@ -13,6 +13,10 @@ export default class SidebarParticipantProfile extends React.Component {
contactThreads: React.PropTypes.array,
}
static containerStyles = {
order: 0,
}
constructor(props) {
super(props);
@ -42,16 +46,12 @@ export default class SidebarParticipantProfile extends React.Component {
this.usub()
}
static containerStyles = {
order: 0,
}
_renderProfilePhoto() {
if (this.state.profilePhotoUrl) {
return (
<div className="profile-photo-wrap">
<div className="profile-photo">
<img src={this.state.profilePhotoUrl}/>
<img alt="Profile" src={this.state.profilePhotoUrl} />
</div>
</div>
)
@ -66,8 +66,11 @@ export default class SidebarParticipantProfile extends React.Component {
return (
<div className="profile-photo-wrap">
<div className="profile-photo">
<div className="default-profile-image"
style={{backgroundColor: bgColor}}>{abv}
<div
className="default-profile-image"
style={{backgroundColor: bgColor}}
>
{abv}
</div>
</div>
</div>
@ -92,11 +95,13 @@ export default class SidebarParticipantProfile extends React.Component {
_renderSocialProfiles() {
if (!this.state.socialProfiles) { return false }
const profiles = _.map(this.state.socialProfiles, (profile, type) => {
const linkFn = () => {shell.openExternal(profile.url)}
const linkFn = () => { shell.openExternal(profile.url) }
return (
<a className="social-profile-item" onClick={linkFn} key={type} title={profile.url}>
<RetinaImg url={`nylas://participant-profile/assets/${type}-sidebar-icon@2x.png`}
mode={RetinaImg.Mode.ContentPreserve} />
<RetinaImg
url={`nylas://participant-profile/assets/${type}-sidebar-icon@2x.png`}
mode={RetinaImg.Mode.ContentPreserve}
/>
</a>
)
});
@ -135,9 +140,11 @@ export default class SidebarParticipantProfile extends React.Component {
if (!this.state.location) { return false; }
return (
<p className="location">
<RetinaImg url={`nylas://participant-profile/assets/location-icon@2x.png`}
mode={RetinaImg.Mode.ContentPreserve}
style={{float: "left"}} />
<RetinaImg
url={`nylas://participant-profile/assets/location-icon@2x.png`}
mode={RetinaImg.Mode.ContentPreserve}
style={{"float": "left"}}
/>
<span className="selectable" style={{display: "block", marginLeft: 20}}>{this.state.location}</span>
</p>
)

View file

@ -9,16 +9,16 @@ export default class RelatedThreads extends React.Component {
contactThreads: React.PropTypes.array,
}
static containerStyles = {
order: 99,
}
constructor(props) {
super(props)
this.state = {expanded: false}
this.DEFAULT_NUM = 3
}
static containerStyles = {
order: 99,
}
_onClick(thread) {
Actions.setFocus({collection: 'thread', item: thread})
}

View file

@ -9,11 +9,7 @@ class Package extends React.Component {
static displayName = 'Package';
static propTypes = {
package: React.PropTypes.object.isRequired,
}
constructor() {
super();
'package': React.PropTypes.object.isRequired,
}
_onDisablePackage = () => {
@ -47,7 +43,7 @@ class Package extends React.Component {
let uninstallButton = null;
if (this.props.package.icon) {
icon = (<img src={`nylas://${this.props.package.name}/${this.props.package.icon}`} style={{width: 27, alignContent: "center", objectFit: "scale-down"}} />);
icon = (<img src={`nylas://${this.props.package.name}/${this.props.package.icon}`} role="presentation" style={{width: 27, alignContent: "center", objectFit: "scale-down"}} />);
} else if (this.props.package.theme) {
icon = (<RetinaImg name="theme-icon-default.png" mode="ContentPreserve" />);
}

View file

@ -277,8 +277,10 @@ const PackagesStore = Reflux.createStore({
}
fs.mkdir(packageDir, (err) => {
if (err) return this._displayMessage('Could not create plugin', err.toString());
if (err) {
this._displayMessage('Could not create plugin', err.toString());
return;
}
const {resourcePath} = NylasEnv.getLoadSettings();
const packageTemplatePath = path.join(resourcePath, 'static', 'package-template');
const packageJSON = {
@ -293,8 +295,8 @@ const PackagesStore = Reflux.createStore({
nylas: `>=${NylasEnv.getVersion().split('-')[0]}`,
},
windowTypes: {
default: true,
composer: true,
'default': true,
'composer': true,
},
description: "Enter a description of your package!",
dependencies: {},

View file

@ -19,10 +19,8 @@ const Actions = Reflux.createActions([
'refreshInstalledPackages',
]);
for (const key in Actions) {
if (Actions.hasOwnProperty(key)) {
Actions[key].sync = true;
}
for (const key of Object.keys(Actions)) {
Actions[key].sync = true;
}
export default Actions;

View file

@ -11,7 +11,14 @@ class PluginsTabs extends React.Component {
static displayName = 'PluginsTabs';
static propTypes = {
'onChange': React.PropTypes.Func,
onChange: React.PropTypes.Func,
};
static containerRequired = false;
static containerStyles = {
minWidth: 200,
maxWidth: 290,
};
constructor() {
@ -28,13 +35,6 @@ class PluginsTabs extends React.Component {
this._unsubscribers.forEach(unsubscribe => unsubscribe());
}
static containerRequired = false;
static containerStyles = {
minWidth: 200,
maxWidth: 290,
};
_getStateFromStores() {
return {
tabIndex: TabsStore.tabIndex(),
@ -48,8 +48,8 @@ class PluginsTabs extends React.Component {
_renderItems() {
return Tabs.map(({name, key, icon}, idx) => {
const classes = classNames({
'tab': true,
'active': idx === this.state.tabIndex,
tab: true,
active: idx === this.state.tabIndex,
});
return (<li key={key} className={classes} onClick={() => PluginsActions.selectTabIndex(idx)}>{name}</li>);
});

View file

@ -8,6 +8,11 @@ class PluginsView extends React.Component {
static displayName = 'PluginsView';
static containerStyles = {
minWidth: 500,
maxWidth: 99999,
}
constructor() {
super();
this.state = this._getStateFromStores();
@ -22,11 +27,6 @@ class PluginsView extends React.Component {
this._unsubscribers.forEach(unsubscribe => unsubscribe());
}
static containerStyles = {
minWidth: 500,
maxWidth: 99999,
}
_getStateFromStores() {
return {tabIndex: TabsStore.tabIndex()};
}

View file

@ -67,16 +67,19 @@ class TabExplore extends React.Component {
type="text"
className="search"
value={this.state.search}
onChange={this._onSearchChange }
placeholder="Search Packages and Themes"/>
onChange={this._onSearchChange}
placeholder="Search Packages and Themes"
/>
<PackageSet
title={`${collectionPrefix} Themes`}
emptyText={emptyText || "There are no featured themes yet."}
packages={collection.themes} />
packages={collection.themes}
/>
<PackageSet
title={`${collectionPrefix} Packages`}
emptyText={emptyText || "There are no featured packages yet."}
packages={collection.packages} />
packages={collection.packages}
/>
</div>
</div>
);

View file

@ -69,7 +69,7 @@ class TabInstalled extends React.Component {
devEmpty = (<span>
{`You don't have any packages installed in ~/.nylas/dev/packages. `}
These plugins are only loaded when you run the app with debug flags
enabled (via the Developer menu).<br/><br/>Learn more about building
enabled (via the Developer menu).<br /><br />Learn more about building
plugins with <a href="https://nylas.com/N1/docs">our docs</a>.
</span>);
devCTA = (<div className="btn btn-small" onClick={this._onCreatePackage}>Create New Plugin...</div>);
@ -85,19 +85,23 @@ class TabInstalled extends React.Component {
className="search"
value={this.state.search}
onChange={this._onSearchChange}
placeholder="Search Installed Plugins" />
placeholder="Search Installed Plugins"
/>
</Flexbox>
<PackageSet
packages={this.state.packages.user}
title="Installed plugins"
emptyText={searchEmpty || <span>{`You don't have any plugins installed in ~/.nylas/packages.`}</span>} />
emptyText={searchEmpty || <span>{`You don't have any plugins installed in ~/.nylas/packages.`}</span>}
/>
<PackageSet
title="Built-in plugins"
packages={this.state.packages.example} />
packages={this.state.packages.example}
/>
<PackageSet
title="Development plugins"
packages={devPackages}
emptyText={searchEmpty || devEmpty} />
emptyText={searchEmpty || devEmpty}
/>
<div className="new-package">
{devCTA}
</div>

View file

@ -1,3 +1,4 @@
/* eslint global-require: 0 */
import _ from 'underscore';
import React, {Component, PropTypes} from 'react';
import {EditableList, NewsletterSignup} from 'nylas-component-kit';
@ -55,7 +56,7 @@ class PreferencesAccountDetails extends Component {
this.props.onAccountUpdated(this.props.account, this.state.account);
};
_setState = (updates, callback = ()=>{}) => {
_setState = (updates, callback = () => {}) => {
const updated = _.extend({}, this.state.account, updates);
this.setState({account: updated}, callback);
};
@ -131,6 +132,7 @@ class PreferencesAccountDetails extends Component {
</div>
);
}
return null;
}
@ -140,28 +142,30 @@ class PreferencesAccountDetails extends Component {
<a className="action" onClick={buttonAction}>{buttonText}</a>
</div>)
}
_renderSyncErrorDetails() {
const {account} = this.state;
if (account.hasSyncStateError()) {
switch (account.syncState) {
case Account.SYNC_STATE_AUTH_FAILED:
return this._renderErrorDetail(
`Nylas N1 can no longer authenticate with ${account.emailAddress}. The password or
authentication may have changed.`,
"Reconnect",
()=>this._reconnect());
case Account.SYNC_STATE_STOPPED:
return this._renderErrorDetail(
`The cloud sync for ${account.emailAddress} has been disabled. Please contact Nylas support.`,
"Contact support",
()=>this._contactSupport());
default:
return this._renderErrorDetail(
`Nylas encountered an error while syncing mail for ${account.emailAddress}. Contact Nylas support for details.`,
"Contact support",
()=>this._contactSupport());
case Account.SYNC_STATE_AUTH_FAILED:
return this._renderErrorDetail(
`Nylas N1 can no longer authenticate with ${account.emailAddress}. The password or
authentication may have changed.`,
"Reconnect",
() => this._reconnect());
case Account.SYNC_STATE_STOPPED:
return this._renderErrorDetail(
`The cloud sync for ${account.emailAddress} has been disabled. Please contact Nylas support.`,
"Contact support",
() => this._contactSupport());
default:
return this._renderErrorDetail(
`Nylas encountered an error while syncing mail for ${account.emailAddress}. Contact Nylas support for details.`,
"Contact support",
() => this._contactSupport());
}
}
return null;
}
render() {
@ -178,7 +182,8 @@ class PreferencesAccountDetails extends Component {
type="text"
value={account.label}
onBlur={this._saveChanges}
onChange={this._onAccountLabelUpdated} />
onChange={this._onAccountLabelUpdated}
/>
<h3>Aliases</h3>
@ -193,7 +198,8 @@ class PreferencesAccountDetails extends Component {
createInputProps={{placeholder: aliasPlaceholder}}
onItemCreated={this._onAccountAliasCreated}
onItemEdited={this._onAccountAliasUpdated}
onDeleteItem={this._onAccountAliasRemoved} />
onDeleteItem={this._onAccountAliasRemoved}
/>
{this._renderDefaultAliasSelector(account)}

View file

@ -38,7 +38,12 @@ class DefaultMailClientItem extends React.Component {
if (process.platform === "win32") return false;
return (
<div className="item">
<input type="checkbox" id="default-client" checked={this.state.defaultClient} onChange={this.toggleDefaultMailClient}/>
<input
type="checkbox"
id="default-client"
checked={this.state.defaultClient}
onChange={this.toggleDefaultMailClient}
/>
<label htmlFor="default-client">Use Nylas as default mail client</label>
</div>
);
@ -92,7 +97,12 @@ class LaunchSystemStartItem extends React.Component {
if (!this.state.available) return false;
return (
<div className="item">
<input type="checkbox" id="launch-on-start" checked={this.state.launchOnStart} onChange={this._toggleLaunchOnStart}/>
<input
type="checkbox"
id="launch-on-start"
checked={this.state.launchOnStart}
onChange={this._toggleLaunchOnStart}
/>
<label htmlFor="launch-on-start">Launch on system start</label>
</div>
);

View file

@ -103,7 +103,8 @@ class ThemeOption extends React.Component {
frameBorder="0"
width="115px"
height="70px"
flex="1" />
flex="1"
/>
</div>
);
}

View file

@ -36,7 +36,8 @@ function InjectsToolbarButtons(ToolbarComponent, {getObservable, extraRoles = []
<InjectedComponentSet
key="injected"
matching={{roles}}
exposedProps={exposedProps} />
exposedProps={exposedProps}
/>
)
return (
<ToolbarComponent

View file

@ -1,5 +1,5 @@
import Rx from 'rx-lite'
import React, {Component, PropTypes} from 'react'
import React, {PropTypes} from 'react'
import ReactCSSTransitionGroup from 'react-addons-css-transition-group'
import {FocusedContentStore} from 'nylas-exports'
import ThreadListStore from './thread-list-store'
@ -22,30 +22,26 @@ function getObservable() {
)
}
class MessageListToolbar extends Component {
static displayName = 'MessageListToolbar';
const MessageListToolbar = ({items, injectedButtons}) => {
const shouldRender = items.length > 0
static propTypes = {
items: PropTypes.array,
injectedButtons: PropTypes.element,
};
render() {
const {items, injectedButtons} = this.props
const shouldRender = items.length > 0
return (
<ReactCSSTransitionGroup
className="message-toolbar-items"
transitionLeaveTimeout={125}
transitionEnterTimeout={125}
transitionName="opacity-125ms">
{shouldRender ? injectedButtons : undefined}
</ReactCSSTransitionGroup>
)
}
return (
<ReactCSSTransitionGroup
className="message-toolbar-items"
transitionLeaveTimeout={125}
transitionEnterTimeout={125}
transitionName="opacity-125ms"
>
{shouldRender ? injectedButtons : undefined}
</ReactCSSTransitionGroup>
)
}
MessageListToolbar.propTypes = {
items: PropTypes.array,
injectedButtons: PropTypes.element,
};
const toolbarProps = {
getObservable,
extraRoles: [`MessageList:${ToolbarRole}`],

View file

@ -25,12 +25,12 @@ class SelectedItemsStack extends Component {
selectionCount: PropTypes.number,
};
static containerRequired = false;
onClearSelection = () => {
ThreadListStore.dataSource().selection.clear()
};
static containerRequired = false;
render() {
const {selectionCount} = this.props
if (selectionCount <= 1) {
@ -56,7 +56,7 @@ class SelectedItemsStack extends Component {
transform,
zIndex: 5 - idx,
}
return <div key={`card-${idx}`} style={style} className="card"/>
return <div key={`card-${idx}`} style={style} className="card" />
})}
</div>
<div className="count-info">

View file

@ -1,3 +1,4 @@
/* eslint global-require: 0*/
import _ from 'underscore'
import {
Thread,
@ -58,8 +59,11 @@ export default class ThreadListContextMenu {
}
replyAllItem() {
if (this.threadIds.length !== 1) { return null }
DatabaseStore.findBy(Message, {threadId: this.threadIds[0]})
if (this.threadIds.length !== 1) {
return null;
}
return DatabaseStore.findBy(Message, {threadId: this.threadIds[0]})
.order(Message.attributes.date.descending())
.limit(1)
.then((message) => {
@ -76,7 +80,7 @@ export default class ThreadListContextMenu {
},
}
}
return null
return null;
})
}

View file

@ -27,7 +27,8 @@ class SnoozeButton extends Component {
Actions.openPopover(
<SnoozePopover
threads={this.props.threads}
closePopover={Actions.closePopover} />,
closePopover={Actions.closePopover}
/>,
{originRect: buttonRect, direction: this.props.direction}
)
};
@ -47,12 +48,14 @@ class SnoozeButton extends Component {
<button
title="Snooze"
tabIndex={-1}
className={"snooze-button " + this.props.className}
onClick={this.onClick}>
className={`snooze-button ${this.props.className}`}
onClick={this.onClick}
>
{this.props.renderImage ?
<RetinaImg
name="toolbar-snooze.png"
mode={RetinaImg.Mode.ContentIsMask} /> :
mode={RetinaImg.Mode.ContentIsMask}
/> :
void 0
}
</button>
@ -68,6 +71,8 @@ export class QuickActionSnooze extends Component {
thread: PropTypes.object,
};
static containerRequired = false;
getBoundingClientRect = () => {
// Grab the parent node because of the zoom applied to this button. If we
// took this element directly, we'd have to divide everything by 2
@ -78,8 +83,6 @@ export class QuickActionSnooze extends Component {
return {height, width, top, bottom, right, left: left + 5}
};
static containerRequired = false;
render() {
if (!FocusedPerspectiveStore.current().isInbox()) {
return <span />;
@ -90,7 +93,8 @@ export class QuickActionSnooze extends Component {
renderImage={false}
threads={[this.props.thread]}
className="btn action action-snooze"
getBoundingClientRect={this.getBoundingClientRect} />
getBoundingClientRect={this.getBoundingClientRect}
/>
);
}
}
@ -110,7 +114,7 @@ export class ToolbarSnooze extends Component {
return <span />;
}
return (
<SnoozeButton threads={this.props.items}/>
<SnoozeButton threads={this.props.items} />
);
}
}

View file

@ -37,7 +37,8 @@ class SnoozeMailLabel extends Component {
<span className="snooze-mail-label">
<RetinaImg
name="icon-snoozed.png"
mode={RetinaImg.Mode.ContentIsMask} />
mode={RetinaImg.Mode.ContentIsMask}
/>
<span className="date-message">{message}</span>
</span>
)
@ -46,7 +47,7 @@ class SnoozeMailLabel extends Component {
isLockedCategory: () => true,
hue: () => 259,
}
return <MailLabel label={label} key={'snooze-message-' + thread.id} />;
return <MailLabel label={label} key={`snooze-message-${thread.id}`} />;
}
return <span />
}

View file

@ -88,10 +88,12 @@ class SnoozePopover extends Component {
<div
key={itemLabel}
className="snooze-item"
onClick={this.onSnooze.bind(this, date, itemLabel)}>
onClick={this.onSnooze.bind(this, date, itemLabel)}
>
<RetinaImg
url={iconPath}
mode={RetinaImg.Mode.ContentIsMask} />
mode={RetinaImg.Mode.ContentIsMask}
/>
{itemLabel}
</div>
)
@ -115,7 +117,8 @@ class SnoozePopover extends Component {
<DateInput
className="snooze-input"
dateFormat={DATE_FORMAT_LONG}
onSubmitDate={this.onSelectCustomDate} />
onSubmitDate={this.onSelectCustomDate}
/>
</div>
);
}

View file

@ -159,12 +159,12 @@ describe('Snooze Utils', function snoozeUtils() {
beforeEach(() => {
this.description = 'Snoozin';
this.snoozeCatsByAccount = {
'123': new Category({accountId: 123, displayName: this.name, serverId: 'sr-1'}),
'321': new Category({accountId: 321, displayName: this.name, serverId: 'sr-2'}),
123: new Category({accountId: 123, displayName: this.name, serverId: 'sr-1'}),
321: new Category({accountId: 321, displayName: this.name, serverId: 'sr-2'}),
}
this.inboxCatsByAccount = {
'123': new Category({accountId: 123, name: 'inbox', serverId: 'sr-3'}),
'321': new Category({accountId: 321, name: 'inbox', serverId: 'sr-4'}),
123: new Category({accountId: 123, name: 'inbox', serverId: 'sr-3'}),
321: new Category({accountId: 321, name: 'inbox', serverId: 'sr-4'}),
}
this.threads = [
new Thread({accountId: 123}),

View file

@ -76,8 +76,8 @@ describe('ComposerExtensionAdapter', function composerExtensionAdapter() {
it('adapts correctly when signature is (editor, ...)', () => {
const methodSpy = jasmine.createSpy('methodSpy');
const extension = {
method(editor, ev, other) {
methodSpy(editor, ev, other);
method(e, ev, other) {
methodSpy(e, ev, other);
},
};
adapter.adaptContenteditableMethod(extension, 'method');
@ -112,8 +112,8 @@ describe('ComposerExtensionAdapter', function composerExtensionAdapter() {
it('adapts correctly when using mutations instead of an event', () => {
const methodSpy = jasmine.createSpy('methodSpy');
const extension = {
method(editor, mutations) {
methodSpy(editor, mutations);
method(e, mutations) {
methodSpy(e, mutations);
},
};
adapter.adaptContenteditableMethod(extension, 'method');

View file

@ -14,12 +14,12 @@ describe('MailboxPerspective', function mailboxPerspective() {
beforeEach(() => {
this.accountIds = ['a1', 'a2']
this.accounts = {
'a1': {
a1: {
id: 'a1',
defaultFinishedCategory: () => ({displayName: 'archive'}),
categoryIcon: () => null,
},
'a2': {
a2: {
id: 'a2',
defaultFinishedCategory: () => ({displayName: 'trash2'}),
categoryIcon: () => null,
@ -115,17 +115,17 @@ describe('MailboxPerspective', function mailboxPerspective() {
describe('tasksForRemovingItems', () => {
beforeEach(() => {
this.categories = {
'a1': {
'archive': new Category({name: 'archive', displayName: 'archive', accountId: 'a1'}),
'inbox': new Category({name: 'inbox', displayName: 'inbox1', accountId: 'a1'}),
'trash': new Category({name: 'trash', displayName: 'trash1', accountId: 'a1'}),
'category': new Category({name: null, displayName: 'folder1', accountId: 'a1'}),
a1: {
archive: new Category({name: 'archive', displayName: 'archive', accountId: 'a1'}),
inbox: new Category({name: 'inbox', displayName: 'inbox1', accountId: 'a1'}),
trash: new Category({name: 'trash', displayName: 'trash1', accountId: 'a1'}),
category: new Category({name: null, displayName: 'folder1', accountId: 'a1'}),
},
'a2': {
'archive': new Category({name: 'all', displayName: 'all', accountId: 'a2'}),
'inbox': new Category({name: 'inbox', displayName: 'inbox2', accountId: 'a2'}),
'trash': new Category({name: 'trash', displayName: 'trash2', accountId: 'a2'}),
'category': new Category({name: null, displayName: 'label2', accountId: 'a2'}),
a2: {
archive: new Category({name: 'all', displayName: 'all', accountId: 'a2'}),
inbox: new Category({name: 'inbox', displayName: 'inbox2', accountId: 'a2'}),
trash: new Category({name: 'trash', displayName: 'trash2', accountId: 'a2'}),
category: new Category({name: null, displayName: 'label2', accountId: 'a2'}),
},
}
this.threads = [

View file

@ -266,7 +266,7 @@ describe('DraftFactory', function draftFactory() {
waitsForPromise(() => {
return DraftFactory.createDraftForReply({thread: fakeThread, message: fakeMessage1, type: 'reply-all'}).then((draft) => {
const ccEmails = draft.cc.map(cc => cc.email);
expect(ccEmails.sort()).toEqual([ 'ben@nylas.com', 'evan@nylas.com', 'mg@nylas.com']);
expect(ccEmails.sort()).toEqual(['ben@nylas.com', 'evan@nylas.com', 'mg@nylas.com']);
});
});
});
@ -576,7 +576,7 @@ describe('DraftFactory', function draftFactory() {
it("works for lowercase", () => {
waitsForPromise(() => {
return DraftFactory.createDraftForMailto('mailto:asdf@asdf.com?subject=' + this.expected).then((draft) => {
return DraftFactory.createDraftForMailto(`mailto:asdf@asdf.com?subject=${this.expected}`).then((draft) => {
expect(draft.subject).toBe(this.expected);
});
});
@ -584,7 +584,7 @@ describe('DraftFactory', function draftFactory() {
it("works for title case", () => {
waitsForPromise(() => {
return DraftFactory.createDraftForMailto('mailto:asdf@asdf.com?Subject=' + this.expected).then((draft) => {
return DraftFactory.createDraftForMailto(`mailto:asdf@asdf.com?Subject=${this.expected}`).then((draft) => {
expect(draft.subject).toBe(this.expected);
});
});
@ -592,7 +592,7 @@ describe('DraftFactory', function draftFactory() {
it("works for uppercase", () => {
waitsForPromise(() => {
return DraftFactory.createDraftForMailto('mailto:asdf@asdf.com?SUBJECT=' + this.expected).then((draft) => {
return DraftFactory.createDraftForMailto(`mailto:asdf@asdf.com?SUBJECT=${this.expected}`).then((draft) => {
expect(draft.subject).toBe(this.expected);
});
});

View file

@ -1,3 +1,4 @@
/* eslint global-require: 0*/
import {dialog} from 'electron';
import {EventEmitter} from 'events';
import uuid from 'node-uuid';

View file

@ -34,7 +34,7 @@ export default class WindowManager {
});
const score = (win) =>
win.loadSettings().mainWindow ? 1000 : win.browserWindow.id;
(win.loadSettings().mainWindow ? 1000 : win.browserWindow.id);
return values.sort((a, b) => score(b) - score(a));
}

View file

@ -69,7 +69,8 @@ class DateInput extends Component {
type="text"
placeholder="Or, 'next Monday at 2PM'"
onKeyDown={this.onInputKeyDown}
onChange={this.onInputChange}/>
onChange={this.onInputChange}
/>
{dateInterpretation}
</div>
)

View file

@ -76,13 +76,15 @@ export default class DatePicker extends React.Component {
render() {
const className = classnames({
'day-text': true,
focused: this.state.focused,
'focused': this.state.focused,
})
const dayTxt = moment(this.props.value).format(DateUtils.DATE_FORMAT_llll_NO_TIME)
return (
<div tabIndex={0} className="date-picker"
<div
tabIndex={0}
className="date-picker"
onKeyDown={this._onKeyDown} onFocus={this._onFocus}
onBlur={this._onBlur}
>

View file

@ -359,7 +359,8 @@ class EditableList extends Component {
defaultValue={itemContent}
onBlur={_.partial(onInputBlur, _, item, idx)}
onFocus={onInputFocus}
onKeyDown={_.partial(onInputKeyDown, _, item, idx)} />
onKeyDown={_.partial(onInputKeyDown, _, item, idx)}
/>
);
};
@ -378,7 +379,7 @@ class EditableList extends Component {
return (
<div className="create-item-input" key="create-item-input">
<input {...props}/>
<input {...props} />
</div>
);
};
@ -411,14 +412,16 @@ class EditableList extends Component {
draggable
onDragStart={this._onItemDragStart}
onClick={_.partial(onClick, _, item, idx)}
onDoubleClick={_.partial(onEdit, _, item, idx)}>
onDoubleClick={_.partial(onEdit, _, item, idx)}
>
{itemContent}
<RetinaImg
className="edit-icon"
name="edit-icon.png"
title="Edit Item"
mode={RetinaImg.Mode.ContentIsMask}
onClick={_.partial(onEdit, _, item, idx)} />
onClick={_.partial(onEdit, _, item, idx)}
/>
</div>
);
};
@ -447,7 +450,7 @@ class EditableList extends Component {
};
render() {
let items = this.props.items.map( (item, idx) => this._renderItem(item, idx));
let items = this.props.items.map((item, idx) => this._renderItem(item, idx));
if (this.state.creatingItem === true) {
items = items.concat(this._renderCreateInput());
}
@ -460,13 +463,15 @@ class EditableList extends Component {
<KeyCommandsRegion
tabIndex="1"
localHandlers={this._listKeymapHandlers()}
className={`nylas-editable-list ${this.props.className}`}>
className={`nylas-editable-list ${this.props.className}`}
>
<ScrollRegion
className="items-wrapper"
ref="itemsWrapper"
onDragOver={this._onDragOver}
onDragLeave={this._onDragLeave}
onDrop={this._onDrop}>
onDrop={this._onDrop}
>
{items}
</ScrollRegion>
{this._renderButtons()}

View file

@ -52,7 +52,8 @@ export default class MailLabelSet extends React.Component {
<MailLabel
label={label}
key={label.id}
onRemove={() => this._onRemoveLabel(label)} />
onRemove={() => this._onRemoveLabel(label)}
/>
)
} else {
if (LabelComponentCache[label.id] === undefined) {
@ -71,7 +72,8 @@ export default class MailLabelSet extends React.Component {
children={labels}
matching={{role: "Thread:MailLabel"}}
className="thread-injected-mail-labels"
exposedProps={{thread: thread}}/>
exposedProps={{thread: thread}}
/>
);
}
}

View file

@ -86,7 +86,8 @@ class Modal extends React.Component {
style={containerStyle}
className="modal-container"
onKeyDown={this._onKeyDown}
onBlur={this._onBlur}>
onBlur={this._onBlur}
>
<div className="modal" style={modalStyle}>
{children}
</div>

View file

@ -52,7 +52,8 @@ class MultiselectToolbar extends Component {
<button
style={{order: 100}}
className="btn btn-toolbar"
onClick={onClearSelection}>
onClick={onClearSelection}
>
Clear Selection
</button>
</div>
@ -68,7 +69,8 @@ class MultiselectToolbar extends Component {
transitionName="selection-bar-absolute"
component="div"
transitionLeaveTimeout={200}
transitionEnterTimeout={200}>
transitionEnterTimeout={200}
>
{selectionCount > 0 ? this.renderToolbar() : undefined}
</ReactCSSTransitionGroup>
)

View file

@ -91,7 +91,8 @@ export default class CalendarEventContainer extends React.Component {
render() {
return (
<div className="calendar-mouse-handler"
<div
className="calendar-mouse-handler"
onMouseUp={this._onCalendarMouseUp}
onMouseDown={this._onCalendarMouseDown}
onMouseMove={this._onCalendarMouseMove}

View file

@ -82,7 +82,8 @@ export default class CalendarEvent extends React.Component {
render() {
return (
<div className={`calendar-event ${this.props.direction}`}
<div
className={`calendar-event ${this.props.direction}`}
style={this._styles()}
>
<span className="default-header" style={{order: 0}}>

View file

@ -24,11 +24,13 @@ export default class HeaderControls extends React.Component {
_renderNextAction() {
if (!this.props.nextAction) { return false; }
return (
<button className="btn btn-icon next"
<button
className="btn btn-icon next"
ref="onNextAction"
onClick={this.props.nextAction}
>
<RetinaImg name="ic-calendar-right-arrow.png"
<RetinaImg
name="ic-calendar-right-arrow.png"
mode={RetinaImg.Mode.ContentIsMask}
/>
</button>
@ -38,11 +40,13 @@ export default class HeaderControls extends React.Component {
_renderPrevAction() {
if (!this.props.prevAction) { return false; }
return (
<button className="btn btn-icon prev"
<button
className="btn btn-icon prev"
ref="onPreviousAction"
onClick={this.props.prevAction}
>
<RetinaImg name="ic-calendar-left-arrow.png"
<RetinaImg
name="ic-calendar-left-arrow.png"
mode={RetinaImg.Mode.ContentIsMask}
/>
</button>

View file

@ -79,13 +79,14 @@ export default class MiniMonthView extends React.Component {
dayIter.weekday(weekday); // Locale aware!
const dayStr = dayIter.format("D");
const className = classnames({
day: true,
today: this._isSameDay(dayIter, this.today),
"day": true,
"today": this._isSameDay(dayIter, this.today),
"cur-day": this._isSameDay(dayIter, valDay),
"cur-month": dayIter.month() === curMonth,
})
dayEls.push(
<div className={className} key={`${week}-${weekday}`}
<div
className={className} key={`${week}-${weekday}`}
data-timestamp={dayIter.valueOf()}
>{dayStr}</div>
)
@ -101,11 +102,13 @@ export default class MiniMonthView extends React.Component {
return (
<div className="mini-month-view">
<div className="header">
<div className="btn btn-icon"
<div
className="btn btn-icon"
onClick={_.partial(this._changeMonth, -1)}
>&lsaquo;</div>
<span className="title">{this._shownMonthMoment().format("MMMM YYYY")}</span>
<div className="btn btn-icon"
<div
className="btn btn-icon"
onClick={_.partial(this._changeMonth, 1)}
>&rsaquo;</div>
</div>

View file

@ -7,10 +7,6 @@ export default class MonthView extends React.Component {
changeView: React.PropTypes.func,
}
constructor(props) {
super(props);
}
_onClick = () => {
this.props.changeView("WeekView");
}

View file

@ -70,6 +70,10 @@ export default class NylasCalendar extends React.Component {
footerComponents: {day: false, week: false, month: false, year: false},
}
static containerStyles = {
height: "100%",
}
constructor(props) {
super(props);
this.state = {
@ -78,10 +82,6 @@ export default class NylasCalendar extends React.Component {
};
}
static containerStyles = {
height: "100%",
}
_now() {
return moment()
}

View file

@ -29,7 +29,9 @@ export default class WeekViewAllDayEvents extends React.Component {
render() {
const eventComponents = this.props.allDayEvents.map((e) => {
return (
<CalendarEvent event={e} order={this.props.allDayOverlap[e.id].order}
<CalendarEvent
event={e}
order={this.props.allDayOverlap[e.id].order}
key={e.id}
scopeStart={this.props.start}
scopeEnd={this.props.end}

View file

@ -28,11 +28,14 @@ export default class WeekViewEventColumn extends React.Component {
_eventComponents() {
return this.props.events.map((e) => {
return (
<CalendarEvent event={e} order={this.props.eventOverlap[e.id].order}
<CalendarEvent
event={e}
order={this.props.eventOverlap[e.id].order}
key={e.id}
scopeEnd={this.props.dayEnd}
scopeStart={this.props.day.unix()}
concurrentEvents={this.props.eventOverlap[e.id].concurrentEvents}/>
concurrentEvents={this.props.eventOverlap[e.id].concurrentEvents}
/>
);
});
}
@ -44,8 +47,12 @@ export default class WeekViewEventColumn extends React.Component {
});
const end = moment(this.props.day).add(1, 'day').subtract(1, 'millisecond').valueOf()
return (
<div className={className} key={this.props.day.valueOf()}
data-start={this.props.day.valueOf()} data-end={end}>
<div
className={className}
key={this.props.day.valueOf()}
data-start={this.props.day.valueOf()}
data-end={end}
>
{this._eventComponents()}
</div>
)

View file

@ -98,7 +98,7 @@ export default class WeekView extends React.Component {
this._sub = this.props.dataSource.buildObservable({
startTime: startMoment.unix(),
endTime: endMoment.unix(),
}).subscribe((state) => {this.setState(state)})
}).subscribe((state) => { this.setState(state) })
this.setState({startMoment, endMoment})
}
@ -150,7 +150,9 @@ export default class WeekView extends React.Component {
const dayUnix = day.unix();
const events = eventsByDay[dayUnix]
return (
<WeekViewEventColumn day={day} dayEnd={dayUnix + DAY_DUR - 1}
<WeekViewEventColumn
day={day}
dayEnd={dayUnix + DAY_DUR - 1}
key={day.valueOf()}
events={events}
eventOverlap={this._eventOverlap(events)}
@ -252,7 +254,10 @@ export default class WeekView extends React.Component {
_headerComponents() {
const left = (
<button key="today" className="btn" ref="todayBtn"
<button
key="today"
className="btn"
ref="todayBtn"
onClick={this._onClickToday}
style={{position: 'absolute', left: 10}}
>
@ -434,7 +439,8 @@ export default class WeekView extends React.Component {
>
<TopBanner bannerComponents={this.props.bannerComponents} />
<HeaderControls title={this._currentWeekText()}
<HeaderControls
title={this._currentWeekText()}
ref="headerControls"
headerComponents={this._headerComponents()}
nextAction={this._onClickNextWeek}
@ -442,7 +448,8 @@ export default class WeekView extends React.Component {
/>
<div className="calendar-legend">
<div className="date-label-legend"
<div
className="date-label-legend"
style={{height: this._allDayEventHeight(allDayOverlap) + 75 + 1}}
>
<span className="legend-text">All Day</span>
@ -454,7 +461,9 @@ export default class WeekView extends React.Component {
</div>
</div>
<div className="calendar-area-wrap" ref="calendarAreaWrap"
<div
className="calendar-area-wrap"
ref="calendarAreaWrap"
onScroll={this._onScrollCalWrap}
>
<div className="week-header" style={{width: `${this._bufferRatio() * 100}%`}}>
@ -472,13 +481,16 @@ export default class WeekView extends React.Component {
allDayOverlap={allDayOverlap}
/>
</div>
<div className="event-grid-wrap" ref="eventGridWrap"
<div
className="event-grid-wrap"
ref="eventGridWrap"
onScroll={this._onGridScroll}
style={{width: `${this._bufferRatio() * 100}%`}}
>
<div className="event-grid" style={{height: this._gridHeight()}}>
{days.map(_.partial(this._renderEventColumn, eventsByDay))}
<EventGridBackground height={this._gridHeight()}
<EventGridBackground
height={this._gridHeight()}
intervalHeight={this.state.intervalHeight}
numColumns={BUFFER_DAYS * 2 + DAYS_IN_VIEW}
ref="eventGridBg"

View file

@ -1,3 +1,5 @@
/* eslint global-require:0 */
import _ from 'underscore';
import classnames from 'classnames';
import React, {Component, PropTypes} from 'react';
@ -133,6 +135,7 @@ class OutlineViewItem extends Component {
}).isRequired,
};
static CounterStyles = CounterStyles;
constructor(props) {
super(props);
@ -167,9 +170,6 @@ class OutlineViewItem extends Component {
}
}
static CounterStyles = CounterStyles;
// Helpers
_runCallback = (method, ...args) => {
@ -296,7 +296,8 @@ class OutlineViewItem extends Component {
<RetinaImg
name={item.iconName}
fallback={'folder.png'}
mode={RetinaImg.Mode.ContentIsMask} />
mode={RetinaImg.Mode.ContentIsMask}
/>
</div>
);
}
@ -314,7 +315,8 @@ class OutlineViewItem extends Component {
defaultValue={item.name}
onBlur={this._onInputBlur}
onFocus={this._onInputFocus}
onKeyDown={this._onInputKeyDown} />
onKeyDown={this._onInputKeyDown}
/>
);
}
return <div className="name">{item.name}</div>;
@ -322,9 +324,9 @@ class OutlineViewItem extends Component {
_renderItem(item = this.props.item, state = this.state) {
const containerClass = classnames({
'item': true,
'selected': item.selected,
'editing': state.editing,
item: true,
selected: item.selected,
editing: state.editing,
[item.className]: item.className,
});
@ -336,7 +338,8 @@ class OutlineViewItem extends Component {
onClick={this._onClick}
onDoubleClick={this._onEdit}
shouldAcceptDrop={this._shouldAcceptDrop}
onDragStateChange={this._onDragStateChange} >
onDragStateChange={this._onDragStateChange}
>
{this._renderCount()}
{this._renderIcon()}
{this._renderItemContent()}
@ -369,7 +372,8 @@ class OutlineViewItem extends Component {
<DisclosureTriangle
collapsed={item.collapsed}
visible={item.children.length > 0}
onCollapseToggled={this._onCollapseToggled} />
onCollapseToggled={this._onCollapseToggled}
/>
{this._renderItem()}
</span>
{this._renderChildren()}

View file

@ -120,16 +120,17 @@ class OutlineView extends Component {
}
_renderCreateButton() {
const title = this.props.title;
return (
<span
className="add-item-button"
onMouseDown={this._onCreateButtonMouseDown}
onMouseUp={this._onCreateButtonClicked.bind(this, title)}>
onMouseUp={this._onCreateButtonClicked}
>
<RetinaImg
url="nylas://account-sidebar/assets/icon-sidebar-addcategory@2x.png"
style={{height: 14, width: 14}}
mode={RetinaImg.Mode.ContentIsMask} />
mode={RetinaImg.Mode.ContentIsMask}
/>
</span>
);
}
@ -139,9 +140,10 @@ class OutlineView extends Component {
return (
<DropZone
className="heading"
onDrop={() => true }
onDrop={() => true}
onDragStateChange={this._onDragStateChange}
shouldAcceptDrop={() => true}>
shouldAcceptDrop={() => true}
>
<span className="text">
{this.props.title}
</span>
@ -149,7 +151,8 @@ class OutlineView extends Component {
{collapsible ?
<span
className="collapse-button"
onClick={this._onCollapseToggled}>
onClick={this._onCollapseToggled}
>
{collapseLabel}
</span>
: void 0

View file

@ -1,35 +1,32 @@
import React from 'react';
import _ from 'underscore';
import {remote} from 'electron';
import {remote, clipboard} from 'electron';
import {Utils, Contact, ContactStore} from 'nylas-exports';
import {TokenizingTextField, Menu, InjectedComponent, InjectedComponentSet} from 'nylas-component-kit';
class TokenRenderer extends React.Component {
static propTypes = {
token: React.PropTypes.object,
const TokenRenderer = (props) => {
const {email, name} = props.token
let chipText = email;
if (name && (name.length > 0) && (name !== email)) {
chipText = name;
}
return (
<div className="participant">
<InjectedComponentSet
matching={{role: "Composer:RecipientChip"}}
exposedProps={{contact: props.token}}
direction="column"
inline
/>
<span className="participant-primary">{chipText}</span>
</div>
);
};
render() {
const {email, name} = this.props.token
let chipText = email;
if (name && (name.length > 0) && (name !== email)) {
chipText = name;
}
return (
<div className="participant">
<InjectedComponentSet
matching={{role: "Composer:RecipientChip"}}
exposedProps={{contact: this.props.token}}
direction="column"
inline
/>
<span className="participant-primary">{chipText}</span>
</div>
);
}
}
TokenRenderer.propTypes = {
token: React.PropTypes.object,
};
export default class ParticipantsTextField extends React.Component {
static displayName = 'ParticipantsTextField';
@ -173,7 +170,7 @@ export default class ParticipantsTextField extends React.Component {
const menu = new MenuClass();
menu.append(new MenuItem({
label: `Copy ${participant.email}`,
click: () => require('electron').clipboard.writeText(participant.email),
click: () => clipboard.writeText(participant.email),
}))
menu.append(new MenuItem({
type: 'separator',

View file

@ -95,12 +95,12 @@ class RetinaImg extends React.Component {
resourcePath: React.PropTypes.string,
}
static Mode = Mode;
shouldComponentUpdate = (nextProps) => {
return !(_.isEqual(this.props, nextProps));
}
static Mode = Mode;
_pathFor = (name) => {
if (!name || !_.isString(name)) return null;
let pathName = name;
@ -141,18 +141,16 @@ class RetinaImg extends React.Component {
className += " content-light";
}
for (const key in style) {
if (style.hasOwnProperty(key)) {
const val = style[key].toString();
if (StylesImpactedByZoom.indexOf(key) !== -1 && val.indexOf("%") === -1) {
style[key] = val.replace('px', '') / style.zoom;
}
for (const key of Object.keys(style)) {
const val = style[key].toString();
if (StylesImpactedByZoom.indexOf(key) !== -1 && val.indexOf("%") === -1) {
style[key] = val.replace('px', '') / style.zoom;
}
}
const otherProps = Utils.fastOmit(this.props, Object.keys(this.constructor.propTypes));
return (
<img className={className} src={path} style={style} {...otherProps} />
<img alt={this.props.name} className={className} src={path} style={style} {...otherProps} />
);
}

View file

@ -24,7 +24,7 @@ export default class ScrollbarTicks extends React.Component {
percent = percentData;
} else {
percent = percentData.percent;
className = " " + percentData.className
className = ` ${percentData.className}`;
}
return `<div class="t${className}" style="top: ${percent * 100}%"></div>`
}).join("")

View file

@ -56,7 +56,7 @@ export class SelectableCell extends Component {
const {className} = this.props
const classes = classnames({
[className]: true,
'selected': this.isSelected(this.props),
selected: this.isSelected(this.props),
})
return (
<TableCell
@ -105,7 +105,7 @@ export class SelectableRow extends Component {
const {className} = this.props
const classes = classnames({
[className]: true,
'selected': this.isSelected(this.props),
selected: this.isSelected(this.props),
})
return (
<TableRow

View file

@ -8,30 +8,22 @@ import React from 'react';
```
*/
class Switch extends React.Component {
static propTypes = {
checked: React.PropTypes.bool,
onChange: React.PropTypes.func.isRequired,
}
constructor() {
super();
}
render() {
let classnames = "slide-switch";
if (this.props.checked) {
classnames += " active";
}
return (
<div className={classnames} onClick={this.props.onChange}>
<div className="handle"></div>
</div>
);
const Switch = (props) => {
let classnames = "slide-switch";
if (props.checked) {
classnames += " active";
}
return (
<div className={classnames} onClick={props.onChange}>
<div className="handle"></div>
</div>
);
}
Switch.propTypes = {
checked: React.PropTypes.bool,
onChange: React.PropTypes.func.isRequired,
};
export default Switch;

View file

@ -25,6 +25,12 @@ export function TableCell(props) {
)
}
TableCell.propTypes = {
isHeader: PropTypes.bool,
className: PropTypes.string,
children: PropTypes.children,
};
export class TableRow extends Component {
static propTypes = {

View file

@ -17,8 +17,8 @@ const Hours = {
const Days = {
// The value for next monday and next weekend varies depending if the current
// day is saturday or sunday. See http://momentjs.com/docs/#/get-set/day/
NextMonday: day => day === 0 ? 1 : 8,
ThisWeekend: day => day === 6 ? 13 : 6,
NextMonday: day => (day === 0 ? 1 : 8),
ThisWeekend: day => (day === 6 ? 13 : 6),
}
function oclock(momentDate) {

View file

@ -13,7 +13,7 @@ const DOMWalkers = {
if (!node) { return; }
if (node.childNodes.length > 0) {
for (let i = node.childNodes.length - 1; i >= 0; i--) {
yield *this.walkBackwards(node.childNodes[i]);
yield* this.walkBackwards(node.childNodes[i]);
}
}
yield node;

View file

@ -1,8 +1,6 @@
import _ from 'underscore';
import {Listener, Publisher} from './flux/modules/reflux-coffee';
import {includeModule} from './flux/coffee-helpers';
import composerExtAdapter from './extensions/composer-extension-adapter';
import messageViewExtAdapter from './extensions/message-view-extension-adapter';
export class Registry {

View file

@ -1,140 +0,0 @@
import _ from 'underscore';
import DOMUtils from '../dom-utils';
import {deprecate} from '../deprecate-utils';
import {getFunctionArgs} from './extension-utils';
export function isUsingOutdatedContenteditableApi(func) {
// Might not always be true, but it is our best guess
const firstArg = getFunctionArgs(func)[0];
if (func.length > 1) return true; // Not using a named arguments hash
return (
firstArg.includes('ev') ||
firstArg.includes('node') ||
firstArg.includes('Node')
);
}
export function isUsingOutdatedComposerApi(func) {
const firstArg = getFunctionArgs(func)[0];
return (
firstArg.includes('dr') ||
firstArg.includes('sess') ||
firstArg.includes('prox')
);
}
export function adaptComposerMethod(extension, method) {
const original = extension[method];
if (!original || !isUsingOutdatedComposerApi(original)) return;
extension[method] = (argsObj) => {
return original(argsObj.draft);
};
}
export function adaptContenteditableMethod(extension, method, original = extension[method]) {
// Check if it is using old API
if (!original || !isUsingOutdatedContenteditableApi(original)) return;
let deprecatedArgs = '';
extension[method] = (argsObj) => {
const {editor, event, mutations} = argsObj;
const eventOrMutations = event || mutations || {};
const extraArgs = Object.keys(_.omit(argsObj, ['editor', 'event', 'mutations'])).map(
key => argsObj[key]
);
// This is our best guess at the function signature that is being used
const firstArg = getFunctionArgs(original)[0];
if (firstArg.includes('editor')) {
deprecatedArgs = '(editor, ...)';
return original(editor, eventOrMutations, ...extraArgs);
} else if (firstArg.includes('ev')) {
deprecatedArgs = '(event, editableNode, selection, ...)';
return original(eventOrMutations, editor.rootNode, editor.currentSelection(), ...extraArgs);
}
deprecatedArgs = '(editableNode, selection, ...)';
return original(editor.rootNode, editor.currentSelection(), eventOrMutations, ...extraArgs);
};
extension[method] = deprecate(
`ComposerExtension.${method}${deprecatedArgs}`,
`ComposerExtension.${method}(args = {editor, ...})`,
extension,
extension[method]
);
}
export function adaptOnInput(extension) {
if (extension.onContentChanged != null) return;
adaptContenteditableMethod(extension, 'onContentChanged', extension.onInput);
}
export function adaptOnTabDown(extension) {
if (!extension.onTabDown) return;
const origOnKeyDown = extension.onKeyDown;
extension.onKeyDown = ({editor, event}) => {
if (event.key === 'Tab') {
const range = DOMUtils.getRangeInScope(editor.rootNode);
extension.onTabDown(editor.rootNode, range, event);
} else {
// At this point, onKeyDown should have already been adapted
if (origOnKeyDown != null) origOnKeyDown(editor, event);
}
};
extension.onKeyDown = deprecate(
'DraftStoreExtension.onTabDown',
'ComposerExtension.onKeyDown',
extension,
extension.onKeyDown
);
}
export function adaptOnMouseUp(extension) {
if (!extension.onMouseUp) return;
const origOnClick = extension.onClick;
extension.onClick = ({editor, event}) => {
const range = DOMUtils.getRangeInScope(editor.rootNode);
extension.onMouseUp(editor.rootNode, range, event);
// At this point, onClick should have already been adapted
if (origOnClick != null) origOnClick(editor, event);
};
extension.onClick = deprecate(
'DraftStoreExtension.onMouseUp',
'ComposerExtension.onClick',
extension,
extension.onClick
);
}
export default function adaptExtension(extension) {
const contenteditableMethods = [
'onContentChanged',
'onBlur',
'onFocus',
'onClick',
'onKeyDown',
'onShowContextMenu',
];
contenteditableMethods.forEach(
method => adaptContenteditableMethod(extension, method)
);
// Special contenteditable cases
adaptOnInput(extension);
adaptOnTabDown(extension);
adaptOnMouseUp(extension);
const composerMethods = [
'warningsForSending',
'prepareNewDraft',
];
composerMethods.forEach(
method => adaptComposerMethod(extension, method)
);
return extension;
}

View file

@ -1,21 +0,0 @@
import {getFunctionArgs} from './extension-utils';
export function isUsingOutdatedAPI(func) {
// Might not always be true, but it is our best guess
const firstArg = getFunctionArgs(func)[0];
return (
firstArg.includes('mes') ||
firstArg.includes('msg') ||
firstArg.includes('body') ||
firstArg.includes('draft')
);
}
export default function adaptExtension(extension) {
const original = extension.formatMessageBody;
if (!original || !isUsingOutdatedAPI(original)) return extension;
extension.formatMessageBody = ({message}) => {
return original(message);
};
return extension;
}

View file

@ -1,3 +1,4 @@
/* eslint global-require:0 */
import Attributes from '../attributes'
import ModelWithMetadata from './model-with-metadata'

View file

@ -1,3 +1,4 @@
/* eslint global-require:0 */
import _ from 'underscore'
import url from 'url'
import {Emitter} from 'event-kit'

View file

@ -1,3 +1,4 @@
/* eslint global-require:0 */
import NylasStore from 'nylas-store';
import FocusedPerspectiveStore from './focused-perspective-store';
import ThreadCountsStore from './thread-counts-store';

View file

@ -31,7 +31,7 @@ class RecentlyReadStore extends NylasStore {
tasks.filter(task =>
task instanceof ChangeUnreadTask
).forEach(({threads}) => {
const threadIds = threads.map(t => t.id ? t.id : t);
const threadIds = threads.map(t => (t.id ? t.id : t));
this.ids = this.ids.concat(threadIds);
changed = true;
});
@ -39,7 +39,7 @@ class RecentlyReadStore extends NylasStore {
tasks.filter(task =>
task instanceof ChangeLabelsTask || task instanceof ChangeFolderTask
).forEach(({threads}) => {
const threadIds = threads.map(t => t.id ? t.id : t);
const threadIds = threads.map(t => (t.id ? t.id : t));
this.ids = this.ids.filter(id => !threadIds.includes(id));
changed = true;
});

View file

@ -146,7 +146,7 @@ class SearchableComponentStore extends NylasStore {
// We save the position relative to the top of the scrollAncestor
// instead of the current getBoudingClientRect (which is dependent
// on the current scroll position)
this.matches.map((match) => {
this.matches.forEach((match) => {
match.top -= scrollTop
});
}

View file

@ -120,5 +120,6 @@ export default class ChangeFolderTask extends ChangeMailTask {
if (model instanceof Message) {
return {folder: model.folder ? model.folder.id : null};
}
return null;
}
}

View file

@ -20,7 +20,8 @@ const mapLimit = (input, numberInParallel, fn) => {
let outputError = null;
if (input.length === 0) {
return resolve([]);
resolve([]);
return;
}
const startNext = () => {
@ -299,7 +300,7 @@ export default class ChangeMailTask extends Task {
// Never give the undo task the Model objects - make it look them up!
// This ensures that they never revert other fields
const toIds = (arr) => _.map(arr, v => _.isString(v) ? v : v.id);
const toIds = (arr) => _.map(arr, v => (_.isString(v) ? v : v.id));
task.threads = toIds(this.threads);
task.messages = (this.threads.length > 0) ? [] : toIds(this.messages);
return task;
@ -307,7 +308,7 @@ export default class ChangeMailTask extends Task {
objectIds() {
return [].concat(this.threads, this.messages).map((v) =>
_.isString(v) ? v : v.id
(_.isString(v) ? v : v.id)
);
}

View file

@ -7,9 +7,6 @@ import NylasAPI from '../nylas-api';
import BaseDraftTask from './base-draft-task';
export default class DestroyDraftTask extends BaseDraftTask {
constructor(draftClientId) {
super(draftClientId);
}
shouldDequeueOtherTask(other) {
return (other instanceof BaseDraftTask && other.draftClientId === this.draftClientId);

View file

@ -1,3 +1,4 @@
/* eslint global-require:0 */
import _ from 'underscore'
import Task from './task'
import NylasAPI from '../nylas-api'

@ -1 +1 @@
Subproject commit 074d6b99501b0c1b20dbd57a18982556cb1acde2
Subproject commit 797179d156bfeaba611e755b8c8abdc4615df0c8

View file

@ -9,7 +9,7 @@ export default class RealDOMParser extends UnifiedDOMParser {
}
if (node && !pruneFn(node) && node.childNodes.length > 0) {
for (let i = 0; i < node.childNodes.length; i++) {
yield *this._pruningDOMWalker({node: node.childNodes[i], pruneFn, filterFn});
yield* this._pruningDOMWalker({node: node.childNodes[i], pruneFn, filterFn});
}
}
return;

View file

@ -1,20 +1,21 @@
import React from 'react'
export default class SearchMatch extends React.Component {
static displayName = "SearchMatch";
static propTypes = {
regionId: React.PropTypes.string,
className: React.PropTypes.string,
renderIndex: React.PropTypes.number,
}
render() {
return (
<span data-region-id={this.props.regionId}
data-render-index={this.props.renderIndex}
className={`search-match ${this.props.className}`}>{this.props.children}</span>
)
}
const SearchMatch = (props) => {
return (
<span
data-region-id={props.regionId}
data-render-index={props.renderIndex}
className={`search-match ${props.className}`}
>
{props.children}
</span>
)
}
SearchMatch.propTypes = {
regionId: React.PropTypes.string,
className: React.PropTypes.string,
renderIndex: React.PropTypes.number,
};
export default SearchMatch;

View file

@ -8,7 +8,7 @@ class SearchableComponent {
componentDidMount(superMethod, ...args) {
if (superMethod) superMethod.apply(this, args);
this.__regionId = Utils.generateTempId();
this._searchableListener = SearchableComponentStore.listen(() => {this._onSearchableComponentStoreChange()})
this._searchableListener = SearchableComponentStore.listen(() => { this._onSearchableComponentStoreChange() })
SearchableComponentStore.registerSearchRegion(this.__regionId, ReactDOM.findDOMNode(this))
}
@ -53,8 +53,9 @@ class SearchableComponent {
const matchNodeMap = parser.getElementsWithNewMatchNodes(normalizedDOM, searchTerm, this.state.__searchIndex);
return parser.highlightSearch(normalizedDOM, matchNodeMap)
}
return vDOM
return vDOM;
}
return null;
}
}

View file

@ -26,12 +26,10 @@ export default class SerializableRegistry {
getAllConstructors() {
const constructors = []
for (const name in this._constructorFactories) {
if (this._constructorFactories.hasOwnProperty(name)) {
constructors.push(this.get(name))
}
for (const name of Object.keys(this._constructorFactories)) {
constructors.push(this.get(name))
}
return constructors
return constructors;
}
isInRegistry(name) {

View file

@ -10,14 +10,12 @@ class StoreRegistry extends SerializableRegistry {
* takes considerable time to process.
*/
activateAllStores() {
for (const name in this._constructorFactories) {
if (this._constructorFactories.hasOwnProperty(name)) {
// All we need to do is hit `require` on the store. This will
// construct the object an initialize the require cache. The
// stores are now available in nylas-exports or from the node
// require cache.
this.get(name)
}
for (const name of Object.keys(this._constructorFactories)) {
// All we need to do is hit `require` on the store. This will
// construct the object an initialize the require cache. The
// stores are now available in nylas-exports or from the node
// require cache.
this.get(name)
}
}
}

View file

@ -63,10 +63,10 @@ class SystemStartServiceDarwin extends SystemStartServiceBase {
_launchdPlist() {
return {
"Label": "com.nylas.n1",
"Program": this._launcherPath(),
"ProgramArguments": ["--background"],
"RunAtLoad": true,
Label: "com.nylas.n1",
Program: this._launcherPath(),
ProgramArguments: ["--background"],
RunAtLoad: true,
}
}
}

View file

@ -12,14 +12,14 @@ const VirtualDOMUtils = {
yield {element: children, parentNode: element, childOffset: 0}
} else if (children.length > 0) {
for (let i = 0; i < children.length; i++) {
yield *this.walk({element: children[i], parentNode: element, childOffset: i, pruneFn})
yield* this.walk({element: children[i], parentNode: element, childOffset: i, pruneFn})
}
} else {
yield *this.walk({element: children, parentNode: element, childOffset: 0, pruneFn})
yield* this.walk({element: children, parentNode: element, childOffset: 0, pruneFn})
}
} else if (_.isArray(element)) {
for (let i = 0; i < element.length; i++) {
yield *this.walk({element: element[i], parentNode: element, childOffset: i})
yield* this.walk({element: element[i], parentNode: element, childOffset: i})
}
}
return