mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-12-27 02:23:28 +08:00
fix(lint): Various linter issues
This commit is contained in:
parent
813669f7d4
commit
7533dc0ac6
78 changed files with 445 additions and 524 deletions
|
@ -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"
|
||||
|
|
|
@ -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?',
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -92,7 +92,7 @@ function InflateDraftClientId(ComposedComponent) {
|
|||
|
||||
render() {
|
||||
if (!this.state.draft) {
|
||||
return <span/>;
|
||||
return <span />;
|
||||
}
|
||||
return <ComposedComponent ref="composed" {...this.props} {...this.state} />;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ class ComposerWithWindowProps extends React.Component {
|
|||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._usub) {this._usub()}
|
||||
if (this._usub) { this._usub() }
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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})
|
||||
}
|
||||
|
|
|
@ -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" />);
|
||||
}
|
||||
|
|
|
@ -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: {},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>);
|
||||
});
|
||||
|
|
|
@ -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()};
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)}
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -103,7 +103,8 @@ class ThemeOption extends React.Component {
|
|||
frameBorder="0"
|
||||
width="115px"
|
||||
height="70px"
|
||||
flex="1" />
|
||||
flex="1"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,8 @@ function InjectsToolbarButtons(ToolbarComponent, {getObservable, extraRoles = []
|
|||
<InjectedComponentSet
|
||||
key="injected"
|
||||
matching={{roles}}
|
||||
exposedProps={exposedProps} />
|
||||
exposedProps={exposedProps}
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<ToolbarComponent
|
||||
|
|
|
@ -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}`],
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 />
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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}),
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint global-require: 0*/
|
||||
import {dialog} from 'electron';
|
||||
import {EventEmitter} from 'events';
|
||||
import uuid from 'node-uuid';
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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}
|
||||
>
|
||||
|
|
|
@ -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()}
|
||||
|
|
|
@ -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}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}}>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)}
|
||||
>‹</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)}
|
||||
>›</div>
|
||||
</div>
|
||||
|
|
|
@ -7,10 +7,6 @@ export default class MonthView extends React.Component {
|
|||
changeView: React.PropTypes.func,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
_onClick = () => {
|
||||
this.props.changeView("WeekView");
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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()}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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} />
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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("")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint global-require:0 */
|
||||
import Attributes from '../attributes'
|
||||
import ModelWithMetadata from './model-with-metadata'
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint global-require:0 */
|
||||
import _ from 'underscore'
|
||||
import url from 'url'
|
||||
import {Emitter} from 'event-kit'
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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
|
||||
});
|
||||
}
|
||||
|
|
|
@ -120,5 +120,6 @@ export default class ChangeFolderTask extends ChangeMailTask {
|
|||
if (model instanceof Message) {
|
||||
return {folder: model.folder ? model.folder.id : null};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint global-require:0 */
|
||||
import _ from 'underscore'
|
||||
import Task from './task'
|
||||
import NylasAPI from '../nylas-api'
|
||||
|
|
2
src/pro
2
src/pro
|
@ -1 +1 @@
|
|||
Subproject commit 074d6b99501b0c1b20dbd57a18982556cb1acde2
|
||||
Subproject commit 797179d156bfeaba611e755b8c8abdc4615df0c8
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue