mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-09-07 05:04:58 +08:00
fix(lint): Various linter fixes
This commit is contained in:
parent
7533dc0ac6
commit
2ff92946b7
23 changed files with 184 additions and 305 deletions
|
@ -5,7 +5,7 @@ const EmojiActions = Reflux.createActions([
|
|||
"useEmoji",
|
||||
]);
|
||||
|
||||
for (const key in EmojiActions) {
|
||||
for (const key of Object.keys(EmojiActions)) {
|
||||
EmojiActions[key].sync = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint global-require: 0 */
|
||||
import {PreferencesUIStore, ComponentRegistry, ExtensionRegistry} from 'nylas-exports';
|
||||
import TemplatePicker from './template-picker';
|
||||
import TemplateStatusBar from './template-status-bar';
|
||||
|
|
|
@ -10,12 +10,12 @@ const Fields = {
|
|||
Fields.ParticipantFields = [Fields.To, Fields.Cc, Fields.Bcc];
|
||||
|
||||
Fields.Order = {
|
||||
"textFieldTo": 1,
|
||||
"textFieldCc": 2,
|
||||
"textFieldBcc": 3,
|
||||
"fromField": -1, // Not selectable
|
||||
"textFieldSubject": 5,
|
||||
"contentBody": 6,
|
||||
textFieldTo: 1,
|
||||
textFieldCc: 2,
|
||||
textFieldBcc: 3,
|
||||
fromField: -1, // Not selectable
|
||||
textFieldSubject: 5,
|
||||
contentBody: 6,
|
||||
};
|
||||
|
||||
export default Fields
|
||||
|
|
|
@ -13,9 +13,9 @@ describe('AutoloadImagesExtension', function autoloadImagesExtension() {
|
|||
const name = filename.replace('-in.html', '');
|
||||
|
||||
scenarios.push({
|
||||
name: name,
|
||||
in: fs.readFileSync(path.join(fixtures, filename)).toString(),
|
||||
out: fs.readFileSync(path.join(fixtures, `${name}-out.html`)).toString(),
|
||||
'name': name,
|
||||
'in': fs.readFileSync(path.join(fixtures, filename)).toString(),
|
||||
'out': fs.readFileSync(path.join(fixtures, `${name}-out.html`)).toString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -59,7 +59,7 @@ export function autolink(doc, {async} = {}) {
|
|||
['mailto:', RegExpUtils.emailRegex(), {
|
||||
// Technically, gmail.com/bengotow@gmail.com is an email address. After
|
||||
// matching, manully exclude any email that follows the .*[/?].*@ pattern.
|
||||
exclude: [/\..*[\/|\?].*\@/],
|
||||
exclude: [/\..*[\/|\?].*@/],
|
||||
}],
|
||||
['tel:', RegExpUtils.phoneRegex()],
|
||||
['', RegExpUtils.urlRegex({matchEntireString: false})],
|
||||
|
@ -85,7 +85,7 @@ export function autolink(doc, {async} = {}) {
|
|||
// Traverse the new DOM tree and make sure everything with an href has a title.
|
||||
const aTagWalker = document.createTreeWalker(doc.body, NodeFilter.SHOW_ELEMENT, {
|
||||
acceptNode: (node) =>
|
||||
node.href ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
|
||||
(node.href ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP)
|
||||
,
|
||||
});
|
||||
while (aTagWalker.nextNode()) {
|
||||
|
|
|
@ -16,10 +16,6 @@ class PreferencesTabItem extends React.Component {
|
|||
tabItem: React.PropTypes.instanceOf(PreferencesUIStore.TabItem).isRequired,
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
_onClick = () => {
|
||||
Actions.switchPreferencesTab(this.props.tabItem.tabId);
|
||||
}
|
||||
|
@ -33,18 +29,21 @@ class PreferencesTabItem extends React.Component {
|
|||
const {tabId, displayName} = this.props.tabItem;
|
||||
|
||||
const classes = classNames({
|
||||
"item": true,
|
||||
"active": tabId === this.props.selection.get('tabId'),
|
||||
item: true,
|
||||
active: tabId === this.props.selection.get('tabId'),
|
||||
});
|
||||
|
||||
let path = `icon-preferences-${displayName.toLowerCase().replace(" ", "-")}.png`
|
||||
if (!fs.existsSync(Utils.imageNamed(path))) {
|
||||
path = "icon-preferences-general.png";
|
||||
}
|
||||
const icon = (<RetinaImg
|
||||
className="tab-icon"
|
||||
name={path}
|
||||
mode={RetinaImg.Mode.ContentPreserve} />);
|
||||
const icon = (
|
||||
<RetinaImg
|
||||
className="tab-icon"
|
||||
name={path}
|
||||
mode={RetinaImg.Mode.ContentPreserve}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={classes} onClick={this._onClick}>
|
||||
|
@ -66,16 +65,13 @@ class PreferencesTabsBar extends React.Component {
|
|||
selection: React.PropTypes.instanceOf(Immutable.Map).isRequired,
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
renderTabs() {
|
||||
return this.props.tabs.map((tabItem) =>
|
||||
<PreferencesTabItem
|
||||
key={tabItem.tabId}
|
||||
tabItem={tabItem}
|
||||
selection={this.props.selection} />
|
||||
selection={this.props.selection}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,12 @@ class ConfigSchemaItem extends React.Component {
|
|||
} else if (this.props.configSchema.type === 'boolean') {
|
||||
return (
|
||||
<div className="item">
|
||||
<input id={this.props.keyPath} type="checkbox" onChange={this._onChangeChecked} checked={ this.props.config.get(this.props.keyPath) }/>
|
||||
<input
|
||||
id={this.props.keyPath}
|
||||
type="checkbox"
|
||||
onChange={this._onChangeChecked}
|
||||
checked={this.props.config.get(this.props.keyPath)}
|
||||
/>
|
||||
<label htmlFor={this.props.keyPath}>{this.props.configSchema.title}</label>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -15,11 +15,17 @@ class PreferencesAccountList extends Component {
|
|||
|
||||
_renderAccountStateIcon(account) {
|
||||
if (account.syncState !== "running") {
|
||||
return (<div className="sync-error-icon"><RetinaImg
|
||||
className="sync-error-icon"
|
||||
name="ic-settings-account-error.png"
|
||||
mode={RetinaImg.Mode.ContentIsMask} /></div>)
|
||||
return (
|
||||
<div className="sync-error-icon">
|
||||
<RetinaImg
|
||||
className="sync-error-icon"
|
||||
name="ic-settings-account-error.png"
|
||||
mode={RetinaImg.Mode.ContentIsMask}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_renderAccount = (account) => {
|
||||
|
@ -29,14 +35,16 @@ class PreferencesAccountList extends Component {
|
|||
|
||||
return (
|
||||
<div
|
||||
className={classnames({account: true, "sync-error": syncError})}
|
||||
key={account.id} >
|
||||
className={classnames({"account": true, "sync-error": syncError})}
|
||||
key={account.id}
|
||||
>
|
||||
<Flexbox direction="row" style={{alignItems: 'middle'}}>
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<RetinaImg
|
||||
name={syncError ? "ic-settings-account-error.png" : `ic-settings-account-${account.provider}.png`}
|
||||
fallback="ic-settings-account-imap.png"
|
||||
mode={RetinaImg.Mode.ContentPreserve} />
|
||||
mode={RetinaImg.Mode.ContentPreserve}
|
||||
/>
|
||||
</div>
|
||||
<div style={{flex: 1, marginLeft: 10}}>
|
||||
<div className="account-name">
|
||||
|
@ -62,7 +70,8 @@ class PreferencesAccountList extends Component {
|
|||
onReorderItem={this.props.onReorderAccount}
|
||||
onCreateItem={this.props.onAddAccount}
|
||||
onSelectItem={this.props.onSelectAccount}
|
||||
onDeleteItem={this.props.onRemoveAccount} />
|
||||
onDeleteItem={this.props.onRemoveAccount}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -64,10 +64,12 @@ class PreferencesAccounts extends React.Component {
|
|||
onAddAccount={this._onAddAccount}
|
||||
onReorderAccount={this._onReorderAccount}
|
||||
onSelectAccount={this._onSelectAccount}
|
||||
onRemoveAccount={this._onRemoveAccount} />
|
||||
onRemoveAccount={this._onRemoveAccount}
|
||||
/>
|
||||
<PreferencesAccountDetails
|
||||
account={this.state.selected}
|
||||
onAccountUpdated={this._onAccountUpdated} />
|
||||
onAccountUpdated={this._onAccountUpdated}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -4,49 +4,47 @@ import ConfigSchemaItem from './config-schema-item';
|
|||
import WorkspaceSection from './workspace-section';
|
||||
import SendingSection from './sending-section';
|
||||
|
||||
class PreferencesGeneral extends React.Component {
|
||||
static displayName = 'PreferencesGeneral';
|
||||
const PreferencesGeneral = (props) => {
|
||||
return (
|
||||
<div className="container-general" style={{maxWidth: 600}}>
|
||||
|
||||
static propTypes = {
|
||||
config: React.PropTypes.object,
|
||||
configSchema: React.PropTypes.object,
|
||||
}
|
||||
<WorkspaceSection config={props.config} configSchema={props.configSchema} />
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="container-general" style={{maxWidth: 600}}>
|
||||
|
||||
<WorkspaceSection config={this.props.config} configSchema={this.props.configSchema} />
|
||||
|
||||
<ConfigSchemaItem
|
||||
configSchema={this.props.configSchema.properties.notifications}
|
||||
keyName="Notifications"
|
||||
keyPath="core.notifications"
|
||||
config={this.props.config} />
|
||||
|
||||
<div className="platform-note platform-linux-only">
|
||||
N1 desktop notifications on Linux require Zenity. You may need to install
|
||||
it with your package manager (i.e., <code>sudo apt-get install zenity</code>).
|
||||
</div>
|
||||
|
||||
<ConfigSchemaItem
|
||||
configSchema={this.props.configSchema.properties.reading}
|
||||
keyName="Reading"
|
||||
keyPath="core.reading"
|
||||
config={this.props.config} />
|
||||
|
||||
<SendingSection config={this.props.config} configSchema={this.props.configSchema} />
|
||||
|
||||
<ConfigSchemaItem
|
||||
configSchema={this.props.configSchema.properties.attachments}
|
||||
keyName="Attachments"
|
||||
keyPath="core.attachments"
|
||||
config={this.props.config} />
|
||||
<ConfigSchemaItem
|
||||
configSchema={props.configSchema.properties.notifications}
|
||||
keyName="Notifications"
|
||||
keyPath="core.notifications"
|
||||
config={props.config}
|
||||
/>
|
||||
|
||||
<div className="platform-note platform-linux-only">
|
||||
N1 desktop notifications on Linux require Zenity. You may need to install
|
||||
it with your package manager (i.e., <code>sudo apt-get install zenity</code>).
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
<ConfigSchemaItem
|
||||
configSchema={props.configSchema.properties.reading}
|
||||
keyName="Reading"
|
||||
keyPath="core.reading"
|
||||
config={props.config}
|
||||
/>
|
||||
|
||||
<SendingSection config={props.config} configSchema={props.configSchema} />
|
||||
|
||||
<ConfigSchemaItem
|
||||
configSchema={props.configSchema.properties.attachments}
|
||||
keyName="Attachments"
|
||||
keyPath="core.attachments"
|
||||
config={props.config}
|
||||
/>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
PreferencesGeneral.propTypes = {
|
||||
config: React.PropTypes.object,
|
||||
configSchema: React.PropTypes.object,
|
||||
};
|
||||
|
||||
export default PreferencesGeneral;
|
||||
|
|
|
@ -111,7 +111,8 @@ class PreferencesKeymaps extends React.Component {
|
|||
style={{margin: 0}}
|
||||
tabIndex={-1}
|
||||
value={this.props.config.get('core.keymapTemplate')}
|
||||
onChange={(event) => this.props.config.set('core.keymapTemplate', event.target.value)}>
|
||||
onChange={(event) => this.props.config.set('core.keymapTemplate', event.target.value)}
|
||||
>
|
||||
{this.state.templates.map((template) => {
|
||||
return <option key={template} value={template}>{template}</option>
|
||||
})}
|
||||
|
|
|
@ -141,7 +141,8 @@ class PreferencesMailRules extends React.Component {
|
|||
<select
|
||||
value={this.state.currentAccount.accountId}
|
||||
onChange={this._onSelectAccount}
|
||||
style={{margin: 0}} >
|
||||
style={{margin: 0}}
|
||||
>
|
||||
{options}
|
||||
</select>
|
||||
);
|
||||
|
@ -154,7 +155,8 @@ class PreferencesMailRules extends React.Component {
|
|||
<RetinaImg
|
||||
className="icon-mail-rules"
|
||||
name="rules-big.png"
|
||||
mode={RetinaImg.Mode.ContentDark} />
|
||||
mode={RetinaImg.Mode.ContentDark}
|
||||
/>
|
||||
<h2>No rules</h2>
|
||||
<button className="btn btn-small" onMouseDown={this._onAddRule}>
|
||||
Create a new rule
|
||||
|
@ -174,7 +176,8 @@ class PreferencesMailRules extends React.Component {
|
|||
onDeleteItem={this._onDeleteRule}
|
||||
onItemEdited={this._onRuleNameEdited}
|
||||
selected={this.state.selectedRule}
|
||||
onSelectItem={this._onSelectRule} />
|
||||
onSelectItem={this._onSelectRule}
|
||||
/>
|
||||
{this._renderDetail()}
|
||||
</Flexbox>
|
||||
);
|
||||
|
@ -204,14 +207,16 @@ class PreferencesMailRules extends React.Component {
|
|||
<ScenarioEditor
|
||||
instances={rule.conditions}
|
||||
templates={this.state.conditionTemplates}
|
||||
onChange={ (conditions) => Actions.updateMailRule(rule.id, {conditions}) }
|
||||
className="well well-matchers"/>
|
||||
onChange={(conditions) => Actions.updateMailRule(rule.id, {conditions})}
|
||||
className="well well-matchers"
|
||||
/>
|
||||
<span>Perform the following actions:</span>
|
||||
<ScenarioEditor
|
||||
instances={rule.actions}
|
||||
templates={this.state.actionTemplates}
|
||||
onChange={ (actions) => Actions.updateMailRule(rule.id, {actions}) }
|
||||
className="well well-actions"/>
|
||||
onChange={(actions) => Actions.updateMailRule(rule.id, {actions})}
|
||||
className="well well-actions"
|
||||
/>
|
||||
</div>
|
||||
</ScrollRegion>
|
||||
);
|
||||
|
@ -251,7 +256,9 @@ class PreferencesMailRules extends React.Component {
|
|||
{` — ${Number(task.numberOfImpactedItems()).toLocaleString()} processed...`}
|
||||
</div>
|
||||
<div style={{flex: 1}}></div>
|
||||
<button className="btn btn-sm" onClick={() => Actions.dequeueTask(task.id) }>Cancel</button>
|
||||
<button className="btn btn-sm" onClick={() => Actions.dequeueTask(task.id)}>
|
||||
Cancel
|
||||
</button>
|
||||
</Flexbox>
|
||||
);
|
||||
})}
|
||||
|
@ -277,7 +284,7 @@ class PreferencesMailRules extends React.Component {
|
|||
|
||||
<Flexbox style={{marginTop: 40, maxWidth: 600}}>
|
||||
<div>
|
||||
<button disabled={processDisabled} className="btn" style={{float: 'right'}} onClick={this._onReprocessRules}>
|
||||
<button disabled={processDisabled} className="btn" style={{'float': 'right'}} onClick={this._onReprocessRules}>
|
||||
Process entire inbox
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -16,19 +16,19 @@ class SendingSection extends React.Component {
|
|||
_getExtendedSchema(configSchema) {
|
||||
const accounts = AccountStore.accounts();
|
||||
|
||||
let values = accounts.map((acc) => {return acc.id});
|
||||
let labels = accounts.map((acc) => {return acc.me().toString()});
|
||||
let values = accounts.map(acc => acc.id);
|
||||
let labels = accounts.map(acc => acc.me().toString());
|
||||
|
||||
values = ['selected-mailbox'].concat(values);
|
||||
labels = ['Account of selected mailbox'].concat(labels);
|
||||
|
||||
_.extend(configSchema.properties.sending.properties, {
|
||||
defaultAccountIdForSend: {
|
||||
type: 'string',
|
||||
title: 'Send new messages from',
|
||||
default: 'selected-mailbox',
|
||||
enum: values,
|
||||
enumLabels: labels,
|
||||
'type': 'string',
|
||||
'title': 'Send new messages from',
|
||||
'default': 'selected-mailbox',
|
||||
'enum': values,
|
||||
'enumLabels': labels,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -43,7 +43,8 @@ class SendingSection extends React.Component {
|
|||
config={this.props.config}
|
||||
configSchema={sendingSchema}
|
||||
keyName="Sending"
|
||||
keyPath="core.sending" />
|
||||
keyPath="core.sending"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -111,49 +111,47 @@ class LaunchSystemStartItem extends React.Component {
|
|||
}
|
||||
|
||||
|
||||
class WorkspaceSection extends React.Component {
|
||||
const WorkspaceSection = (props) => {
|
||||
return (
|
||||
<section>
|
||||
<DefaultMailClientItem />
|
||||
|
||||
static displayName = 'WorkspaceSection';
|
||||
<LaunchSystemStartItem />
|
||||
|
||||
static propTypes = {
|
||||
config: React.PropTypes.object,
|
||||
configSchema: React.PropTypes.object,
|
||||
}
|
||||
<ConfigSchemaItem
|
||||
configSchema={props.configSchema.properties.workspace.properties.systemTray}
|
||||
keyPath="core.workspace.systemTray"
|
||||
config={props.config}
|
||||
/>
|
||||
|
||||
render() {
|
||||
return (
|
||||
<section>
|
||||
<DefaultMailClientItem />
|
||||
<ConfigSchemaItem
|
||||
configSchema={props.configSchema.properties.workspace.properties.showImportant}
|
||||
keyPath="core.workspace.showImportant"
|
||||
config={props.config}
|
||||
/>
|
||||
|
||||
<LaunchSystemStartItem />
|
||||
<ConfigSchemaItem
|
||||
configSchema={props.configSchema.properties.workspace.properties.showUnreadForAllCategories}
|
||||
keyPath="core.workspace.showUnreadForAllCategories"
|
||||
config={props.config}
|
||||
/>
|
||||
|
||||
<ConfigSchemaItem
|
||||
configSchema={this.props.configSchema.properties.workspace.properties.systemTray}
|
||||
keyPath="core.workspace.systemTray"
|
||||
config={this.props.config} />
|
||||
<ConfigSchemaItem
|
||||
configSchema={props.configSchema.properties.workspace.properties.interfaceZoom}
|
||||
keyPath="core.workspace.interfaceZoom"
|
||||
config={props.config}
|
||||
/>
|
||||
|
||||
<ConfigSchemaItem
|
||||
configSchema={this.props.configSchema.properties.workspace.properties.showImportant}
|
||||
keyPath="core.workspace.showImportant"
|
||||
config={this.props.config} />
|
||||
|
||||
<ConfigSchemaItem
|
||||
configSchema={this.props.configSchema.properties.workspace.properties.showUnreadForAllCategories}
|
||||
keyPath="core.workspace.showUnreadForAllCategories"
|
||||
config={this.props.config} />
|
||||
|
||||
<ConfigSchemaItem
|
||||
configSchema={this.props.configSchema.properties.workspace.properties.interfaceZoom}
|
||||
keyPath="core.workspace.interfaceZoom"
|
||||
config={this.props.config} />
|
||||
|
||||
<div className="platform-note platform-linux-only">
|
||||
N1 launch on system start only works in XDG-compliant desktop environments.
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
<div className="platform-note platform-linux-only">
|
||||
N1 launch on system start only works in XDG-compliant desktop environments.
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
WorkspaceSection.propTypes = {
|
||||
config: React.PropTypes.object,
|
||||
configSchema: React.PropTypes.object,
|
||||
}
|
||||
|
||||
export default WorkspaceSection;
|
||||
|
|
|
@ -57,7 +57,8 @@ class ThemePicker extends React.Component {
|
|||
key={theme.name}
|
||||
theme={theme}
|
||||
active={this.state.activeTheme === theme.name}
|
||||
onSelect={() => this._setActiveTheme(theme.name)} />
|
||||
onSelect={() => this._setActiveTheme(theme.name)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -70,21 +71,24 @@ class ThemePicker extends React.Component {
|
|||
style={{width: "14", WebkitFilter: "none"}}
|
||||
name="picker-close.png"
|
||||
mode={RetinaImg.Mode.ContentDark}
|
||||
onMouseDown={() => Actions.closeModal()} />
|
||||
onMouseDown={() => Actions.closeModal()}
|
||||
/>
|
||||
<h4 style={{color: "#434648"}}>Themes</h4>
|
||||
<div style={{color: "rgba(35, 31, 32, 0.5)", fontSize: "12px"}}>Click any theme to apply:</div>
|
||||
<ScrollRegion style={{margin: "10px 5px 0 5px", height: "290px"}}>
|
||||
<Flexbox
|
||||
direction="row"
|
||||
height="auto"
|
||||
style={{alignItems: "flex-start", flexWrap: "wrap"}}>
|
||||
style={{alignItems: "flex-start", flexWrap: "wrap"}}
|
||||
>
|
||||
{this._renderThemeOptions()}
|
||||
</Flexbox>
|
||||
</ScrollRegion>
|
||||
<div className="create-theme">
|
||||
<a
|
||||
href="https://github.com/nylas/N1-theme-starter"
|
||||
style={{color: "#3187e1"}}>
|
||||
style={{color: "#3187e1"}}
|
||||
>
|
||||
Create a Theme
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -6,8 +6,8 @@ import ThemePackage from '../../../src/theme-package';
|
|||
import ThemePicker from '../lib/theme-picker';
|
||||
|
||||
const {resourcePath} = NylasEnv.getLoadSettings();
|
||||
const light = new ThemePackage(resourcePath + '/internal_packages/ui-light');
|
||||
const dark = new ThemePackage(resourcePath + '/internal_packages/ui-dark');
|
||||
const light = new ThemePackage(`${resourcePath}/internal_packages/ui-light`);
|
||||
const dark = new ThemePackage(`${resourcePath}/internal_packages/ui-dark`);
|
||||
|
||||
describe('ThemePicker', function themePicker() {
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -29,7 +29,7 @@ class SearchIndexStore {
|
|||
this.accountIds = _.pluck(AccountStore.accounts(), 'id')
|
||||
this.initializeIndex()
|
||||
.then(() => {
|
||||
console.log('Thread Search: Index built successfully in ' + ((Date.now() - date) / 1000) + 's')
|
||||
console.log(`Thread Search: Index built successfully in ${((Date.now() - date) / 1000)}s`)
|
||||
this.unsubscribers = [
|
||||
AccountStore.listen(::this.onAccountsChanged),
|
||||
DatabaseStore.listen(::this.onDataChanged),
|
||||
|
@ -50,7 +50,7 @@ class SearchIndexStore {
|
|||
initializeIndex() {
|
||||
return DatabaseStore.searchIndexSize(Thread)
|
||||
.then((size) => {
|
||||
console.log('Thread Search: Current index size is ' + (size || 0) + ' threads')
|
||||
console.log(`Thread Search: Current index size is ${(size || 0)} threads`)
|
||||
if (!size || size >= MAX_INDEX_SIZE || size === 0) {
|
||||
return this.clearIndex().thenReturn(this.accountIds)
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ class SearchIndexStore {
|
|||
return;
|
||||
}
|
||||
const date = Date.now()
|
||||
console.log('Thread Search: Updating thread search index for accounts: ' + latestIds)
|
||||
console.log(`Thread Search: Updating thread search index for accounts ${latestIds}`)
|
||||
|
||||
const newIds = _.difference(latestIds, this.accountIds)
|
||||
const removedIds = _.difference(this.accountIds, latestIds)
|
||||
|
@ -102,7 +102,7 @@ class SearchIndexStore {
|
|||
this.accountIds = latestIds
|
||||
Promise.all(promises)
|
||||
.then(() => {
|
||||
console.log('Thread Search: Index updated successfully in ' + ((Date.now() - date) / 1000) + 's')
|
||||
console.log(`Thread Search: Index updated successfully in ${((Date.now() - date) / 1000)}s`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -212,9 +212,7 @@ class SearchIndexStore {
|
|||
{body: QuotedHTMLTransformer.removeQuotedHTML(body)}
|
||||
))
|
||||
.map(({body, snippet}) => (
|
||||
snippet ?
|
||||
snippet :
|
||||
Utils.extractTextFromHtml(body, {maxLength: MESSAGE_BODY_LENGTH}).replace(/(\s)+/g, ' ')
|
||||
snippet || Utils.extractTextFromHtml(body, {maxLength: MESSAGE_BODY_LENGTH}).replace(/(\s)+/g, ' ')
|
||||
))
|
||||
.join(' ')
|
||||
)
|
||||
|
|
|
@ -88,7 +88,7 @@ class SnoozePopover extends Component {
|
|||
<div
|
||||
key={itemLabel}
|
||||
className="snooze-item"
|
||||
onClick={this.onSnooze.bind(this, date, itemLabel)}
|
||||
onClick={() => this.onSnooze(date, itemLabel)}
|
||||
>
|
||||
<RetinaImg
|
||||
url={iconPath}
|
||||
|
|
|
@ -46,7 +46,7 @@ const SnoozeUtils = {
|
|||
const task = new SyncbackCategoryTask({category})
|
||||
|
||||
Actions.queueTask(task)
|
||||
return TaskQueueStatusStore.waitForPerformRemote(task).then(()=>{
|
||||
return TaskQueueStatusStore.waitForPerformRemote(task).then(() => {
|
||||
return DatabaseStore.findBy(Category, {clientId: category.clientId})
|
||||
.then((updatedCat) => {
|
||||
if (updatedCat && updatedCat.isSavedRemotely()) {
|
||||
|
|
|
@ -17,12 +17,12 @@ describe('SnoozeStore', function snoozeStore() {
|
|||
this.accounts = [{id: 123}, {id: 321}]
|
||||
|
||||
this.snoozeCatsByAccount = {
|
||||
'123': new Category({accountId: 123, displayName: this.name, serverId: 'sn-1'}),
|
||||
'321': new Category({accountId: 321, displayName: this.name, serverId: 'sn-2'}),
|
||||
123: new Category({accountId: 123, displayName: this.name, serverId: 'sn-1'}),
|
||||
321: new Category({accountId: 321, displayName: this.name, serverId: 'sn-2'}),
|
||||
}
|
||||
this.inboxCatsByAccount = {
|
||||
'123': new Category({accountId: 123, name: 'inbox', serverId: 'in-1'}),
|
||||
'321': new Category({accountId: 321, name: 'inbox', serverId: 'in-2'}),
|
||||
123: new Category({accountId: 123, name: 'inbox', serverId: 'in-1'}),
|
||||
321: new Category({accountId: 321, name: 'inbox', serverId: 'in-2'}),
|
||||
}
|
||||
this.threads = [
|
||||
new Thread({accountId: 123, serverId: 's-1'}),
|
||||
|
@ -30,12 +30,12 @@ describe('SnoozeStore', function snoozeStore() {
|
|||
new Thread({accountId: 321, serverId: 's-3'}),
|
||||
]
|
||||
this.updatedThreadsByAccountId = {
|
||||
'123': {
|
||||
123: {
|
||||
threads: [this.threads[0], this.threads[1]],
|
||||
snoozeCategoryId: 'sn-1',
|
||||
returnCategoryId: 'in-1',
|
||||
},
|
||||
'321': {
|
||||
321: {
|
||||
threads: [this.threads[2]],
|
||||
snoozeCategoryId: 'sn-2',
|
||||
returnCategoryId: 'in-2',
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
import * as adapter from '../../src/extensions/composer-extension-adapter';
|
||||
import {DOMUtils} from 'nylas-exports';
|
||||
|
||||
const selection = 'selection';
|
||||
const node = 'node';
|
||||
const event = 'event';
|
||||
const extra = 'extra';
|
||||
const editor = {
|
||||
rootNode: node,
|
||||
currentSelection() {
|
||||
return selection;
|
||||
},
|
||||
};
|
||||
|
||||
describe('ComposerExtensionAdapter', function composerExtensionAdapter() {
|
||||
describe('adaptOnInput', () => {
|
||||
it('adapts correctly if onContentChanged already defined', () => {
|
||||
const onInputSpy = jasmine.createSpy('onInput');
|
||||
const extension = {
|
||||
onContentChanged() {},
|
||||
onInput(ev, editableNode, sel) {
|
||||
onInputSpy(ev, editableNode, sel);
|
||||
},
|
||||
};
|
||||
adapter.adaptOnInput(extension);
|
||||
extension.onContentChanged({editor, mutations: []});
|
||||
expect(onInputSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('adapts correctly when signature is (event, ...)', () => {
|
||||
const onInputSpy = jasmine.createSpy('onInput');
|
||||
const extension = {
|
||||
onInput(ev, editableNode, sel) {
|
||||
onInputSpy(ev, editableNode, sel);
|
||||
},
|
||||
};
|
||||
adapter.adaptOnInput(extension);
|
||||
expect(extension.onContentChanged).toBeDefined();
|
||||
extension.onContentChanged({editor, mutations: []});
|
||||
expect(onInputSpy).toHaveBeenCalledWith([], node, selection);
|
||||
});
|
||||
|
||||
it('adapts correctly when signature is (editableNode, selection, ...)', () => {
|
||||
const onInputSpy = jasmine.createSpy('onInput');
|
||||
const extension = {
|
||||
onInput(editableNode, sel, ev) {
|
||||
onInputSpy(editableNode, sel, ev);
|
||||
},
|
||||
};
|
||||
adapter.adaptOnInput(extension);
|
||||
expect(extension.onContentChanged).toBeDefined();
|
||||
extension.onContentChanged({editor, mutations: []});
|
||||
expect(onInputSpy).toHaveBeenCalledWith(node, selection, []);
|
||||
});
|
||||
});
|
||||
|
||||
describe('adaptOnTabDown', () => {
|
||||
it('adapts onTabDown correctly', () => {
|
||||
const onTabDownSpy = jasmine.createSpy('onTabDownSpy');
|
||||
const mockEvent = {key: 'Tab'};
|
||||
const range = 'range';
|
||||
spyOn(DOMUtils, 'getRangeInScope').andReturn(range);
|
||||
const extension = {
|
||||
onTabDown(editableNode, rn, ev) {
|
||||
onTabDownSpy(editableNode, rn, ev);
|
||||
},
|
||||
};
|
||||
adapter.adaptOnTabDown(extension, 'method');
|
||||
expect(extension.onKeyDown).toBeDefined();
|
||||
extension.onKeyDown({editor, event: mockEvent});
|
||||
expect(onTabDownSpy).toHaveBeenCalledWith(node, range, mockEvent);
|
||||
});
|
||||
});
|
||||
|
||||
describe('adaptContenteditableMethod', () => {
|
||||
it('adapts correctly when signature is (editor, ...)', () => {
|
||||
const methodSpy = jasmine.createSpy('methodSpy');
|
||||
const extension = {
|
||||
method(e, ev, other) {
|
||||
methodSpy(e, ev, other);
|
||||
},
|
||||
};
|
||||
adapter.adaptContenteditableMethod(extension, 'method');
|
||||
extension.method({editor, event, extra});
|
||||
expect(methodSpy).toHaveBeenCalledWith(editor, event, extra);
|
||||
});
|
||||
|
||||
it('adapts correctly when signature is (event, ...)', () => {
|
||||
const methodSpy = jasmine.createSpy('methodSpy');
|
||||
const extension = {
|
||||
method(ev, editableNode, sel, other) {
|
||||
methodSpy(ev, editableNode, sel, other);
|
||||
},
|
||||
};
|
||||
adapter.adaptContenteditableMethod(extension, 'method');
|
||||
extension.method({editor, event, extra});
|
||||
expect(methodSpy).toHaveBeenCalledWith(event, node, selection, extra);
|
||||
});
|
||||
|
||||
it('adapts correctly when signature is (editableNode, selection, ...)', () => {
|
||||
const methodSpy = jasmine.createSpy('methodSpy');
|
||||
const extension = {
|
||||
method(editableNode, sel, ev, other) {
|
||||
methodSpy(editableNode, sel, ev, other);
|
||||
},
|
||||
};
|
||||
adapter.adaptContenteditableMethod(extension, 'method');
|
||||
extension.method({editor, event, extra});
|
||||
expect(methodSpy).toHaveBeenCalledWith(node, selection, event, extra);
|
||||
});
|
||||
|
||||
it('adapts correctly when using mutations instead of an event', () => {
|
||||
const methodSpy = jasmine.createSpy('methodSpy');
|
||||
const extension = {
|
||||
method(e, mutations) {
|
||||
methodSpy(e, mutations);
|
||||
},
|
||||
};
|
||||
adapter.adaptContenteditableMethod(extension, 'method');
|
||||
extension.method({editor, mutations: []});
|
||||
expect(methodSpy).toHaveBeenCalledWith(editor, []);
|
||||
});
|
||||
});
|
||||
|
||||
describe('adaptComposerMethod', () => {
|
||||
it('adapts correctly for other composer extension methods', () => {
|
||||
const methodSpy = jasmine.createSpy('methodSpy');
|
||||
const draft = 'draft';
|
||||
const extension = {
|
||||
warningsForSending(dr) {
|
||||
methodSpy(dr);
|
||||
return 'result';
|
||||
},
|
||||
};
|
||||
adapter.adaptComposerMethod(extension, 'warningsForSending');
|
||||
const res = extension.warningsForSending({draft});
|
||||
expect(res).toEqual('result');
|
||||
expect(methodSpy).toHaveBeenCalledWith(draft);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -40,31 +40,31 @@ import ModelWithMetadata from './model-with-metadata'
|
|||
class Thread extends ModelWithMetadata {
|
||||
|
||||
static attributes = _.extend({}, ModelWithMetadata.attributes, {
|
||||
'snippet': Attributes.String({
|
||||
snippet: Attributes.String({
|
||||
modelKey: 'snippet',
|
||||
}),
|
||||
|
||||
'subject': Attributes.String({
|
||||
subject: Attributes.String({
|
||||
queryable: true,
|
||||
modelKey: 'subject',
|
||||
}),
|
||||
|
||||
'unread': Attributes.Boolean({
|
||||
unread: Attributes.Boolean({
|
||||
queryable: true,
|
||||
modelKey: 'unread',
|
||||
}),
|
||||
|
||||
'starred': Attributes.Boolean({
|
||||
starred: Attributes.Boolean({
|
||||
queryable: true,
|
||||
modelKey: 'starred',
|
||||
}),
|
||||
|
||||
'version': Attributes.Number({
|
||||
version: Attributes.Number({
|
||||
queryable: true,
|
||||
modelKey: 'version',
|
||||
}),
|
||||
|
||||
'categories': Attributes.Collection({
|
||||
categories: Attributes.Collection({
|
||||
queryable: true,
|
||||
modelKey: 'categories',
|
||||
joinOnField: 'id',
|
||||
|
@ -72,11 +72,11 @@ class Thread extends ModelWithMetadata {
|
|||
itemClass: Category,
|
||||
}),
|
||||
|
||||
'categoriesType': Attributes.String({
|
||||
categoriesType: Attributes.String({
|
||||
modelKey: 'categoriesType',
|
||||
}),
|
||||
|
||||
'participants': Attributes.Collection({
|
||||
participants: Attributes.Collection({
|
||||
queryable: true,
|
||||
modelKey: 'participants',
|
||||
joinOnField: 'email',
|
||||
|
@ -84,23 +84,23 @@ class Thread extends ModelWithMetadata {
|
|||
itemClass: Contact,
|
||||
}),
|
||||
|
||||
'hasAttachments': Attributes.Boolean({
|
||||
hasAttachments: Attributes.Boolean({
|
||||
modelKey: 'has_attachments',
|
||||
}),
|
||||
|
||||
'lastMessageReceivedTimestamp': Attributes.DateTime({
|
||||
lastMessageReceivedTimestamp: Attributes.DateTime({
|
||||
queryable: true,
|
||||
modelKey: 'lastMessageReceivedTimestamp',
|
||||
jsonKey: 'last_message_received_timestamp',
|
||||
}),
|
||||
|
||||
'lastMessageSentTimestamp': Attributes.DateTime({
|
||||
lastMessageSentTimestamp: Attributes.DateTime({
|
||||
queryable: true,
|
||||
modelKey: 'lastMessageSentTimestamp',
|
||||
jsonKey: 'last_message_sent_timestamp',
|
||||
}),
|
||||
|
||||
'inAllMail': Attributes.Boolean({
|
||||
inAllMail: Attributes.Boolean({
|
||||
queryable: true,
|
||||
modelKey: 'inAllMail',
|
||||
jsonKey: 'in_all_mail',
|
||||
|
|
2
src/pro
2
src/pro
|
@ -1 +1 @@
|
|||
Subproject commit 797179d156bfeaba611e755b8c8abdc4615df0c8
|
||||
Subproject commit d2ba2ad629341375009b6270c3906c5933e1a255
|
Loading…
Add table
Reference in a new issue