mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-20 15:26:06 +08:00
Upgrade prettier to 1.10, fix linter issues
This commit is contained in:
parent
d2de477023
commit
22357dff60
|
@ -42,8 +42,6 @@ let thirdPartyClasses = {
|
|||
};
|
||||
|
||||
module.exports = function(grunt) {
|
||||
let { cp, mkdir, rm } = grunt.config('taskHelpers');
|
||||
|
||||
let relativePathForClass = classname => classname + '.html';
|
||||
|
||||
let outputPathFor = function(relativePath) {
|
||||
|
@ -51,14 +49,8 @@ module.exports = function(grunt) {
|
|||
return path.join(classDocsOutputDir, relativePath);
|
||||
};
|
||||
|
||||
var processFields = function(json, fields, tasks) {
|
||||
var processFields = function(json, fields = [], tasks = []) {
|
||||
let val;
|
||||
if (fields == null) {
|
||||
fields = [];
|
||||
}
|
||||
if (tasks == null) {
|
||||
tasks = [];
|
||||
}
|
||||
if (json instanceof Array) {
|
||||
return (() => {
|
||||
let result = [];
|
||||
|
@ -90,7 +82,7 @@ module.exports = function(grunt) {
|
|||
};
|
||||
|
||||
return grunt.registerTask('docs-render', 'Builds html from the API docs', function() {
|
||||
let documentation, filename, html, match, meta, name, result, section, val;
|
||||
let documentation, match, name, result, section, val;
|
||||
let classDocsOutputDir = grunt.config.get('classDocsOutputDir');
|
||||
|
||||
// Parse API reference Markdown
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
|
||||
const React = require("react");
|
||||
const AccountSidebar = require("./components/account-sidebar");
|
||||
const {ComponentRegistry, WorkspaceStore} = require("mailspring-exports");
|
||||
const AccountSidebar = require('./components/account-sidebar');
|
||||
const { ComponentRegistry, WorkspaceStore } = require('mailspring-exports');
|
||||
|
||||
module.exports = {
|
||||
item: null, // The DOM item the main React component renders into
|
||||
|
||||
activate(state) {
|
||||
this.state = state;
|
||||
ComponentRegistry.register(AccountSidebar,
|
||||
{location: WorkspaceStore.Location.RootSidebar});
|
||||
ComponentRegistry.register(AccountSidebar, { location: WorkspaceStore.Location.RootSidebar });
|
||||
},
|
||||
|
||||
deactivate(state) {
|
||||
this.state = state;
|
||||
ComponentRegistry.unregister(AccountSidebar);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -5,10 +5,7 @@
|
|||
*/
|
||||
const Reflux = require('reflux');
|
||||
|
||||
const Actions = [
|
||||
'focusAccounts',
|
||||
'setKeyCollapsed',
|
||||
];
|
||||
const Actions = ['focusAccounts', 'setKeyCollapsed'];
|
||||
|
||||
for (let idx of Array.from(Actions)) {
|
||||
Actions[idx] = Reflux.createAction(Actions[idx]);
|
||||
|
|
|
@ -96,15 +96,14 @@ const onEditItem = function(item, value) {
|
|||
};
|
||||
|
||||
class SidebarItem {
|
||||
static forPerspective(id, perspective, opts) {
|
||||
let counterStyle, left;
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
static forPerspective(id, perspective, opts = {}) {
|
||||
let counterStyle;
|
||||
if (perspective.isInbox()) {
|
||||
counterStyle = OutlineViewItem.CounterStyles.Alt;
|
||||
}
|
||||
|
||||
const collapsed = isItemCollapsed(id);
|
||||
|
||||
return Object.assign(
|
||||
{
|
||||
id,
|
||||
|
@ -115,7 +114,7 @@ class SidebarItem {
|
|||
children: [],
|
||||
perspective,
|
||||
selected: isItemSelected(perspective),
|
||||
collapsed: (left = isItemCollapsed(id)) != null ? left : true,
|
||||
collapsed: collapsed != null ? collapsed : true,
|
||||
counterStyle,
|
||||
onDelete: opts.deletable ? onDeleteItem : undefined,
|
||||
onEdited: opts.editable ? onEditItem : undefined,
|
||||
|
@ -160,13 +159,7 @@ class SidebarItem {
|
|||
);
|
||||
}
|
||||
|
||||
static forCategories(categories, opts) {
|
||||
if (categories == null) {
|
||||
categories = [];
|
||||
}
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
static forCategories(categories = [], opts = {}) {
|
||||
const id = idForCategories(categories);
|
||||
const contextMenuLabel = _str.capitalize(
|
||||
categories[0] != null ? categories[0].displayType() : undefined
|
||||
|
@ -183,10 +176,7 @@ class SidebarItem {
|
|||
return this.forPerspective(id, perspective, opts);
|
||||
}
|
||||
|
||||
static forStarred(accountIds, opts) {
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
static forStarred(accountIds, opts = {}) {
|
||||
const perspective = MailboxPerspective.forStarred(accountIds);
|
||||
let id = 'Starred';
|
||||
if (opts.name) {
|
||||
|
@ -195,10 +185,7 @@ class SidebarItem {
|
|||
return this.forPerspective(id, perspective, opts);
|
||||
}
|
||||
|
||||
static forUnread(accountIds, opts) {
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
static forUnread(accountIds, opts = {}) {
|
||||
let categories = accountIds.map(accId => {
|
||||
return CategoryStore.getCategoryByRole(accId, 'inbox');
|
||||
});
|
||||
|
@ -218,10 +205,7 @@ class SidebarItem {
|
|||
return this.forPerspective(id, perspective, opts);
|
||||
}
|
||||
|
||||
static forDrafts(accountIds, opts) {
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
static forDrafts(accountIds, opts = {}) {
|
||||
const perspective = MailboxPerspective.forDrafts(accountIds);
|
||||
const id = `Drafts-${opts.name}`;
|
||||
return this.forPerspective(id, perspective, opts);
|
||||
|
|
|
@ -173,12 +173,8 @@ class SidebarSection {
|
|||
};
|
||||
}
|
||||
|
||||
static forUserCategories(account, param) {
|
||||
static forUserCategories(account, { title, collapsible } = {}) {
|
||||
let onCollapseToggled;
|
||||
if (param == null) {
|
||||
param = {};
|
||||
}
|
||||
let { title, collapsible } = param;
|
||||
if (!account) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -152,9 +152,9 @@ class ActivityEventStore extends MailspringStore {
|
|||
threadId: message.threadId,
|
||||
data: {
|
||||
title: 'New open',
|
||||
subtitle: `${recipient
|
||||
? recipient.displayName()
|
||||
: 'Someone'} just opened ${message.subject}`,
|
||||
subtitle: `${recipient ? recipient.displayName() : 'Someone'} just opened ${
|
||||
message.subject
|
||||
}`,
|
||||
canReply: false,
|
||||
tag: 'message-open',
|
||||
onActivate: () => {
|
||||
|
@ -192,9 +192,9 @@ class ActivityEventStore extends MailspringStore {
|
|||
threadId: message.threadId,
|
||||
data: {
|
||||
title: 'New click',
|
||||
subtitle: `${recipient
|
||||
? recipient.displayName()
|
||||
: 'Someone'} just clicked ${link.url}.`,
|
||||
subtitle: `${recipient ? recipient.displayName() : 'Someone'} just clicked ${
|
||||
link.url
|
||||
}.`,
|
||||
canReply: false,
|
||||
tag: 'link-open',
|
||||
onActivate: () => {
|
||||
|
|
|
@ -12,10 +12,7 @@ const ActivityListEmptyState = function ActivityListEmptyState() {
|
|||
<div className="text">
|
||||
Enable read receipts{' '}
|
||||
<RetinaImg name="icon-activity-mailopen.png" mode={RetinaImg.Mode.ContentDark} /> or link
|
||||
tracking <RetinaImg
|
||||
name="icon-activity-linkopen.png"
|
||||
mode={RetinaImg.Mode.ContentDark}
|
||||
/>{' '}
|
||||
tracking <RetinaImg name="icon-activity-linkopen.png" mode={RetinaImg.Mode.ContentDark} />{' '}
|
||||
to see notifications here.
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,10 +4,7 @@ const LabelPicker = require('./label-picker');
|
|||
const { ComponentRegistry } = require('mailspring-exports');
|
||||
|
||||
module.exports = {
|
||||
activate(state) {
|
||||
if (state == null) {
|
||||
state = {};
|
||||
}
|
||||
activate(state = {}) {
|
||||
this.state = state;
|
||||
ComponentRegistry.register(MovePicker, { role: 'ThreadActionsToolbarButton' });
|
||||
ComponentRegistry.register(LabelPicker, { role: 'ThreadActionsToolbarButton' });
|
||||
|
|
|
@ -32,7 +32,9 @@ export function applySignature(body, signature) {
|
|||
if (signature) {
|
||||
const contentBefore = newBody.slice(0, insertionPoint);
|
||||
const contentAfter = newBody.slice(insertionPoint);
|
||||
return `${contentBefore}${additionalWhitespace}<signature id="${signature.id}">${signature.body}</signature>${contentAfter}`;
|
||||
return `${contentBefore}${additionalWhitespace}<signature id="${signature.id}">${
|
||||
signature.body
|
||||
}</signature>${contentAfter}`;
|
||||
} else {
|
||||
return newBody;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,9 @@ describe('SignatureComposerExtension', function signatureComposerExtension() {
|
|||
|
||||
SignatureComposerExtension.prepareNewDraft({ draft: a });
|
||||
expect(a.body).toEqual(
|
||||
`This is a test! <br/><signature id="1">${TEST_SIGNATURE.body}</signature><div class="gmail_quote">Hello world</div>`
|
||||
`This is a test! <br/><signature id="1">${
|
||||
TEST_SIGNATURE.body
|
||||
}</signature><div class="gmail_quote">Hello world</div>`
|
||||
);
|
||||
SignatureComposerExtension.prepareNewDraft({ draft: b });
|
||||
expect(b.body).toEqual(
|
||||
|
@ -50,7 +52,9 @@ describe('SignatureComposerExtension', function signatureComposerExtension() {
|
|||
{
|
||||
name: 'With blockquote',
|
||||
body: `This is a test! <signature id="x"><div>SIG</div></signature><div class="gmail_quote">Hello world</div>`,
|
||||
expected: `This is a test! <signature id="1">${TEST_SIGNATURE.body}</signature><div class="gmail_quote">Hello world</div>`,
|
||||
expected: `This is a test! <signature id="1">${
|
||||
TEST_SIGNATURE.body
|
||||
}</signature><div class="gmail_quote">Hello world</div>`,
|
||||
},
|
||||
{
|
||||
name: 'Populated signature div',
|
||||
|
@ -70,7 +74,9 @@ describe('SignatureComposerExtension', function signatureComposerExtension() {
|
|||
];
|
||||
|
||||
scenarios.forEach(scenario => {
|
||||
it(`should replace the signature if a signature is already present (${scenario.name})`, () => {
|
||||
it(`should replace the signature if a signature is already present (${
|
||||
scenario.name
|
||||
})`, () => {
|
||||
const message = new Message({
|
||||
draft: true,
|
||||
from: ['one@nylas.com'],
|
||||
|
|
|
@ -72,8 +72,9 @@ class TemplateStore extends MailspringStore {
|
|||
if (err) {
|
||||
AppEnv.showErrorDialog({
|
||||
title: 'Cannot scan templates directory',
|
||||
message: `Mailspring was unable to read the contents of your templates directory (${this
|
||||
._templatesDir}). You may want to delete this folder or ensure filesystem permissions are set correctly.`,
|
||||
message: `Mailspring was unable to read the contents of your templates directory (${
|
||||
this._templatesDir
|
||||
}). You may want to delete this folder or ensure filesystem permissions are set correctly.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import _ from 'underscore';
|
||||
import React from 'react';
|
||||
import { Utils } from 'mailspring-exports';
|
||||
import { InjectedComponentSet, ListTabular } from 'mailspring-component-kit';
|
||||
|
|
|
@ -24,7 +24,9 @@ export default class LinkTrackingButton extends React.Component {
|
|||
) {
|
||||
return `Link tracking does not work offline. Please re-enable when you come back online.`;
|
||||
}
|
||||
return `Unfortunately, link tracking servers are currently not available. Please try again later. Error: ${error.message}`;
|
||||
return `Unfortunately, link tracking servers are currently not available. Please try again later. Error: ${
|
||||
error.message
|
||||
}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -52,7 +52,9 @@ export default class LinkTrackingComposerExtension extends ComposerExtension {
|
|||
return;
|
||||
}
|
||||
const encoded = encodeURIComponent(url);
|
||||
const redirectUrl = `${PLUGIN_URL}/link/${draft.headerMessageId}/${links.length}?redirect=${encoded}`;
|
||||
const redirectUrl = `${PLUGIN_URL}/link/${draft.headerMessageId}/${
|
||||
links.length
|
||||
}?redirect=${encoded}`;
|
||||
|
||||
links.push({
|
||||
url,
|
||||
|
|
|
@ -34,9 +34,9 @@ export default class LinkTrackingMessageExtension extends MessageViewExtension {
|
|||
dotNode.style =
|
||||
'margin-bottom: 0.75em; margin-left: 1px; margin-right: 1px; vertical-align: text-bottom; width: 6px;';
|
||||
if (links[nodeHref].click_count > 0) {
|
||||
dotNode.title = `${links[nodeHref].click_count} click${links[nodeHref].click_count === 1
|
||||
? ''
|
||||
: 's'} (${originalHref})`;
|
||||
dotNode.title = `${links[nodeHref].click_count} click${
|
||||
links[nodeHref].click_count === 1 ? '' : 's'
|
||||
} (${originalHref})`;
|
||||
dotNode.src = 'mailspring://link-tracking/assets/ic-tracking-visited@2x.png';
|
||||
dotNode.style =
|
||||
'margin-bottom: 0.75em; margin-left: 1px; margin-right: 1px; vertical-align: text-bottom; width: 6px; cursor: pointer;';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const React = require('react');
|
||||
const ReactDOM = require('react-dom');
|
||||
const ReactTestUtils = require('react-dom/test-utils');
|
||||
const { Contact, Message } = require('mailspring-exports');
|
||||
const { Message } = require('mailspring-exports');
|
||||
const MessageParticipants = require('../lib/message-participants').default;
|
||||
|
||||
const user_1 = {
|
||||
|
@ -25,10 +25,6 @@ const user_5 = {
|
|||
email: 'user5@nylas.com',
|
||||
};
|
||||
|
||||
const many_users = __range__(0, 100, true).map(
|
||||
i => new Contact({ name: `User ${i}`, email: `${i}@app.com` })
|
||||
);
|
||||
|
||||
const test_message = new Message().fromJSON({
|
||||
id: '111',
|
||||
from: [user_1],
|
||||
|
@ -136,13 +132,3 @@ describe('MessageParticipants', function() {
|
|||
// )
|
||||
// # this should be false because we don't count bccs
|
||||
// expect(p2._isToEveryone()).toBe false
|
||||
|
||||
function __range__(left, right, inclusive) {
|
||||
let range = [];
|
||||
let ascending = left < right;
|
||||
let end = !inclusive ? right : ascending ? right + 1 : right - 1;
|
||||
for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {
|
||||
range.push(i);
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
|
|
@ -48,9 +48,9 @@ export default class UpdateNotification extends React.Component {
|
|||
return (
|
||||
<Notification
|
||||
priority="4"
|
||||
title={`An update to Mailspring is available ${version
|
||||
? `(${version.replace('Mailspring', '').trim()})`
|
||||
: ''}`}
|
||||
title={`An update to Mailspring is available ${
|
||||
version ? `(${version.replace('Mailspring', '').trim()})` : ''
|
||||
}`}
|
||||
subtitle="View changelog"
|
||||
subtitleAction={this._onViewChangelog}
|
||||
icon="volstead-upgrade.png"
|
||||
|
|
|
@ -33,29 +33,29 @@ export default class SyncActivity extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
let accountComponents = Object.entries(
|
||||
this.props.syncState
|
||||
).map(([accountId, accountSyncState]) => {
|
||||
const account = AccountStore.accountForId(accountId);
|
||||
if (!account) {
|
||||
return false;
|
||||
let accountComponents = Object.entries(this.props.syncState).map(
|
||||
([accountId, accountSyncState]) => {
|
||||
const account = AccountStore.accountForId(accountId);
|
||||
if (!account) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let folderComponents = Object.entries(accountSyncState).map(([folderPath, folderState]) => {
|
||||
return this.renderFolderProgress(folderPath, folderState);
|
||||
});
|
||||
|
||||
if (folderComponents.length === 0) {
|
||||
folderComponents = <div>Gathering folders...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="account" key={accountId}>
|
||||
<h2>{account.emailAddress}</h2>
|
||||
{folderComponents}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let folderComponents = Object.entries(accountSyncState).map(([folderPath, folderState]) => {
|
||||
return this.renderFolderProgress(folderPath, folderState);
|
||||
});
|
||||
|
||||
if (folderComponents.length === 0) {
|
||||
folderComponents = <div>Gathering folders...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="account" key={accountId}>
|
||||
<h2>{account.emailAddress}</h2>
|
||||
{folderComponents}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
);
|
||||
|
||||
if (accountComponents.length === 0) {
|
||||
accountComponents = (
|
||||
|
|
|
@ -115,9 +115,9 @@ export default class TutorialPage extends React.Component {
|
|||
<div
|
||||
key={step.id}
|
||||
id={step.id}
|
||||
className={`overlay ${seen.includes(step) ? 'seen' : ''} ${current === step
|
||||
? 'expanded'
|
||||
: ''}`}
|
||||
className={`overlay ${seen.includes(step) ? 'seen' : ''} ${
|
||||
current === step ? 'expanded' : ''
|
||||
}`}
|
||||
style={{ left: `${step.xDot}%`, top: `${step.yDot}%` }}
|
||||
onMouseOver={this._onMouseOverOverlay}
|
||||
>
|
||||
|
|
|
@ -24,7 +24,9 @@ export default class OpenTrackingButton extends React.Component {
|
|||
) {
|
||||
return `Open tracking does not work offline. Please re-enable when you come back online.`;
|
||||
}
|
||||
return `Unfortunately, open tracking is currently not available. Please try again later. Error: ${error.message}`;
|
||||
return `Unfortunately, open tracking is currently not available. Please try again later. Error: ${
|
||||
error.message
|
||||
}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -70,14 +70,13 @@ class ConfigSchemaItem extends React.Component {
|
|||
<div className="item">
|
||||
<label htmlFor={this.props.keyPath}>{this.props.configSchema.title}:</label>
|
||||
<select onChange={this._onChangeValue} value={this.props.config.get(this.props.keyPath)}>
|
||||
{_.zip(
|
||||
this.props.configSchema.enum,
|
||||
this.props.configSchema.enumLabels
|
||||
).map(([value, label]) => (
|
||||
<option key={value} value={value}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
{_.zip(this.props.configSchema.enum, this.props.configSchema.enumLabels).map(
|
||||
([value, label]) => (
|
||||
<option key={value} value={value}>
|
||||
{label}
|
||||
</option>
|
||||
)
|
||||
)}
|
||||
</select>
|
||||
{note}
|
||||
</div>
|
||||
|
|
|
@ -196,9 +196,7 @@ class PreferencesIdentity extends React.Component {
|
|||
<div className="row padded">
|
||||
<div>
|
||||
Thank you for using{' '}
|
||||
<strong
|
||||
style={{ textTransform: 'capitalize' }}
|
||||
>{`Mailspring ${planDisplayName}`}</strong>{' '}
|
||||
<strong style={{ textTransform: 'capitalize' }}>{`Mailspring ${planDisplayName}`}</strong>{' '}
|
||||
and supporting independent software. Get the most out of your subscription: visit the{' '}
|
||||
<a href="https://foundry376.zendesk.com/hc/en-us/sections/115000521592-Getting-Started">
|
||||
Help Center
|
||||
|
|
|
@ -46,7 +46,8 @@ class DefaultMailClientItem extends React.Component {
|
|||
style={{ marginBottom: 12 }}
|
||||
className="btn btn-small"
|
||||
onClick={() =>
|
||||
shell.openExternal('https://foundry376.zendesk.com/hc/en-us/articles/115002281851')}
|
||||
shell.openExternal('https://foundry376.zendesk.com/hc/en-us/articles/115002281851')
|
||||
}
|
||||
>
|
||||
Use Mailspring as default mail client
|
||||
</div>
|
||||
|
|
|
@ -40,7 +40,9 @@ function assertMetadataShape(value) {
|
|||
}
|
||||
if (t.lastReplyTimestamp && !(t.lastReplyTimestamp < Date.now() / 100)) {
|
||||
throw new Error(
|
||||
`"lastReplyTimestamp" should always be a unix timestamp in seconds. Received ${t.lastReplyTimestamp}`
|
||||
`"lastReplyTimestamp" should always be a unix timestamp in seconds. Received ${
|
||||
t.lastReplyTimestamp
|
||||
}`
|
||||
);
|
||||
}
|
||||
delete t.expiration;
|
||||
|
|
|
@ -6,18 +6,16 @@ import ThreadListStore from './thread-list-store';
|
|||
import InjectsToolbarButtons, { ToolbarRole } from './injects-toolbar-buttons';
|
||||
|
||||
function getObservable() {
|
||||
return Rx.Observable
|
||||
.combineLatest(
|
||||
Rx.Observable.fromStore(FocusedContentStore),
|
||||
ThreadListStore.selectionObservable(),
|
||||
(store, items) => ({ focusedThread: store.focused('thread'), items })
|
||||
)
|
||||
.map(({ focusedThread, items }) => {
|
||||
if (focusedThread) {
|
||||
return [focusedThread];
|
||||
}
|
||||
return items;
|
||||
});
|
||||
return Rx.Observable.combineLatest(
|
||||
Rx.Observable.fromStore(FocusedContentStore),
|
||||
ThreadListStore.selectionObservable(),
|
||||
(store, items) => ({ focusedThread: store.focused('thread'), items })
|
||||
).map(({ focusedThread, items }) => {
|
||||
if (focusedThread) {
|
||||
return [focusedThread];
|
||||
}
|
||||
return items;
|
||||
});
|
||||
}
|
||||
|
||||
const MessageListToolbar = ({ items, injectedButtons }) => {
|
||||
|
|
|
@ -50,7 +50,7 @@ class ThreadListStore extends MailspringStore {
|
|||
this.createListDataSource();
|
||||
};
|
||||
|
||||
_onDataChanged = param => {
|
||||
_onDataChanged = ({ previous, next } = {}) => {
|
||||
// This code keeps the focus and keyboard cursor in sync with the thread list.
|
||||
// When the thread list changes, it looks to see if the focused thread is gone,
|
||||
// or no longer matches the query criteria and advances the focus to the next
|
||||
|
@ -58,11 +58,6 @@ class ThreadListStore extends MailspringStore {
|
|||
|
||||
// This means that removing a thread from view in any way causes selection
|
||||
// to advance to the adjacent thread. Nice and declarative.
|
||||
|
||||
if (param == null) {
|
||||
param = {};
|
||||
}
|
||||
const { previous, next } = param;
|
||||
if (previous && next) {
|
||||
const focused = FocusedContentStore.focused('thread');
|
||||
const keyboard = FocusedContentStore.keyboardCursor('thread');
|
||||
|
|
|
@ -160,7 +160,6 @@ class ThreadList extends React.Component {
|
|||
}
|
||||
|
||||
_threadPropsProvider(item) {
|
||||
let left;
|
||||
let classes = classnames({
|
||||
unread: item.unread,
|
||||
});
|
||||
|
@ -206,8 +205,7 @@ class ThreadList extends React.Component {
|
|||
callback(true);
|
||||
};
|
||||
|
||||
const disabledPackages =
|
||||
(left = AppEnv.config.get('core.disabledPackages')) != null ? left : [];
|
||||
const disabledPackages = AppEnv.config.get('core.disabledPackages') || [];
|
||||
if (disabledPackages.includes('thread-snooze')) {
|
||||
return props;
|
||||
}
|
||||
|
@ -235,14 +233,13 @@ class ThreadList extends React.Component {
|
|||
}
|
||||
|
||||
_targetItemsForMouseEvent(event) {
|
||||
let needle;
|
||||
const itemThreadId = this.refs.list.itemIdAtPoint(event.clientX, event.clientY);
|
||||
if (!itemThreadId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const dataSource = ThreadListStore.dataSource();
|
||||
if (((needle = itemThreadId), dataSource.selection.ids().includes(needle))) {
|
||||
if (itemThreadId && dataSource.selection.ids().includes(itemThreadId)) {
|
||||
return {
|
||||
threadIds: dataSource.selection.ids(),
|
||||
accountIds: _.uniq(_.pluck(dataSource.selection.items(), 'accountId')),
|
||||
|
@ -327,9 +324,7 @@ class ThreadList extends React.Component {
|
|||
};
|
||||
|
||||
_onSnoozeItem = () => {
|
||||
let left;
|
||||
const disabledPackages =
|
||||
(left = AppEnv.config.get('core.disabledPackages')) != null ? left : [];
|
||||
const disabledPackages = AppEnv.config.get('core.disabledPackages') || [];
|
||||
if (disabledPackages.includes('thread-snooze')) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -73,7 +73,8 @@ class ThreadSearchBar extends Component {
|
|||
suggestions={suggestions}
|
||||
isSearching={isSearching}
|
||||
suggestionKey={suggestion =>
|
||||
suggestion.label || (suggestion.contact || {}).id || (suggestion.thread || {}).id}
|
||||
suggestion.label || (suggestion.contact || {}).id || (suggestion.thread || {}).id
|
||||
}
|
||||
suggestionRenderer={this._renderSuggestion}
|
||||
onSelectSuggestion={this._onSelectSuggestion}
|
||||
onSubmitSearchQuery={this._onSubmitSearchQuery}
|
||||
|
|
|
@ -186,37 +186,36 @@ export class Notifier {
|
|||
|
||||
// TODO: Use xGMLabels + folder on message to identify which ones
|
||||
// are in the inbox to avoid needing threads here.
|
||||
return DatabaseStore.findAll(
|
||||
Thread,
|
||||
Thread.attributes.id.in(Object.keys(threadIds))
|
||||
).then(threadsArray => {
|
||||
const threads = {};
|
||||
for (const t of threadsArray) {
|
||||
threads[t.id] = t;
|
||||
}
|
||||
|
||||
// Filter new messages to just the ones in the inbox
|
||||
const newMessagesInInbox = newMessages.filter(({ threadId }) => {
|
||||
return threads[threadId] && threads[threadId].categories.find(c => c.role === 'inbox');
|
||||
});
|
||||
|
||||
if (newMessagesInInbox.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const msg of newMessagesInInbox) {
|
||||
this.unnotifiedQueue.push({ message: msg, thread: threads[msg.threadId] });
|
||||
}
|
||||
if (!this.hasScheduledNotify) {
|
||||
if (AppEnv.config.get('core.notifications.sounds')) {
|
||||
this._playNewMailSound =
|
||||
this._playNewMailSound ||
|
||||
_.debounce(() => SoundRegistry.playSound('new-mail'), 5000, true);
|
||||
this._playNewMailSound();
|
||||
return DatabaseStore.findAll(Thread, Thread.attributes.id.in(Object.keys(threadIds))).then(
|
||||
threadsArray => {
|
||||
const threads = {};
|
||||
for (const t of threadsArray) {
|
||||
threads[t.id] = t;
|
||||
}
|
||||
|
||||
// Filter new messages to just the ones in the inbox
|
||||
const newMessagesInInbox = newMessages.filter(({ threadId }) => {
|
||||
return threads[threadId] && threads[threadId].categories.find(c => c.role === 'inbox');
|
||||
});
|
||||
|
||||
if (newMessagesInInbox.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const msg of newMessagesInInbox) {
|
||||
this.unnotifiedQueue.push({ message: msg, thread: threads[msg.threadId] });
|
||||
}
|
||||
if (!this.hasScheduledNotify) {
|
||||
if (AppEnv.config.get('core.notifications.sounds')) {
|
||||
this._playNewMailSound =
|
||||
this._playNewMailSound ||
|
||||
_.debounce(() => SoundRegistry.playSound('new-mail'), 5000, true);
|
||||
this._playNewMailSound();
|
||||
}
|
||||
this._notifyMessages();
|
||||
}
|
||||
this._notifyMessages();
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,7 @@ describe('QuotedHTMLTransformer', function() {
|
|||
return fs.readFileSync(emailPath, 'utf8');
|
||||
};
|
||||
|
||||
const removeQuotedHTML = function(fname, opts) {
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
const removeQuotedHTML = function(fname, opts = {}) {
|
||||
return QuotedHTMLTransformer.removeQuotedHTML(readFile(fname), opts);
|
||||
};
|
||||
|
||||
|
|
|
@ -15,10 +15,7 @@ const _ = require('underscore');
|
|||
// forward.
|
||||
class TimeOverride {
|
||||
static initClass() {
|
||||
this.advanceClock = delta => {
|
||||
if (delta == null) {
|
||||
delta = 1;
|
||||
}
|
||||
this.advanceClock = (delta = 1) => {
|
||||
this.now += delta;
|
||||
const callbacks = [];
|
||||
|
||||
|
|
|
@ -64,9 +64,9 @@ module.exports = class ApplicationMenu {
|
|||
}
|
||||
|
||||
const focusHandler = () => {
|
||||
let template;
|
||||
this.lastFocusedWindow = window;
|
||||
if ((template = this.windowTemplates.get(window))) {
|
||||
const template = this.windowTemplates.get(window);
|
||||
if (template) {
|
||||
this.setActiveTemplate(template);
|
||||
}
|
||||
};
|
||||
|
@ -135,8 +135,8 @@ module.exports = class ApplicationMenu {
|
|||
|
||||
// Replaces VERSION with the current version.
|
||||
extendTemplateWithVersion(template) {
|
||||
let item;
|
||||
if ((item = this.flattenMenuTemplate(template).find(({ label }) => label === 'VERSION'))) {
|
||||
const item = this.flattenMenuTemplate(template).find(({ label }) => label === 'VERSION');
|
||||
if (item) {
|
||||
item.label = `Version ${this.version}`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,9 @@ export default class AutoUpdateManager extends EventEmitter {
|
|||
host = `updates-staging.getmailspring.com`;
|
||||
}
|
||||
|
||||
this.feedURL = `https://${host}/check/${params.platform}/${params.arch}/${params.version}/${params.id}/${params.channel}`;
|
||||
this.feedURL = `https://${host}/check/${params.platform}/${params.arch}/${params.version}/${
|
||||
params.id
|
||||
}/${params.channel}`;
|
||||
if (autoUpdater) {
|
||||
autoUpdater.setFeedURL(this.feedURL);
|
||||
}
|
||||
|
|
|
@ -55,11 +55,13 @@ export default class ConfigPersistenceManager {
|
|||
let detail = error.message;
|
||||
|
||||
if (error instanceof SyntaxError) {
|
||||
detail += `\n\nThe file ${this
|
||||
.configFilePath} has incorrect JSON formatting or is empty. Fix the formatting to resolve this error, or reset your settings to continue using N1.`;
|
||||
detail += `\n\nThe file ${
|
||||
this.configFilePath
|
||||
} has incorrect JSON formatting or is empty. Fix the formatting to resolve this error, or reset your settings to continue using N1.`;
|
||||
} else {
|
||||
detail += `\n\nWe were unable to read the file ${this
|
||||
.configFilePath}. Make sure you have permissions to access this file, and check that the file is not open or being edited and try again.`;
|
||||
detail += `\n\nWe were unable to read the file ${
|
||||
this.configFilePath
|
||||
}. Make sure you have permissions to access this file, and check that the file is not open or being edited and try again.`;
|
||||
}
|
||||
|
||||
const clickedIndex = dialog.showMessageBox({
|
||||
|
@ -123,8 +125,9 @@ export default class ConfigPersistenceManager {
|
|||
const clickedIndex = dialog.showMessageBox({
|
||||
type: 'error',
|
||||
message: `Failed to save "${path.basename(this.configFilePath)}"`,
|
||||
detail: `\n\nWe were unable to save the file ${this
|
||||
.configFilePath}. Make sure you have permissions to access this file, and check that the file is not open or being edited and try again.`,
|
||||
detail: `\n\nWe were unable to save the file ${
|
||||
this.configFilePath
|
||||
}. Make sure you have permissions to access this file, and check that the file is not open or being edited and try again.`,
|
||||
buttons: ['Okay', 'Try again'],
|
||||
});
|
||||
return ['ignore', 'retry'][clickedIndex];
|
||||
|
|
|
@ -10,23 +10,20 @@ let idNum = 0;
|
|||
module.exports = class MailspringWindow extends EventEmitter {
|
||||
static includeShellLoadTime = true;
|
||||
|
||||
constructor(settings) {
|
||||
constructor(settings = {}) {
|
||||
super();
|
||||
|
||||
let frame, height, pathToOpen, resizable, title, toolbar, width;
|
||||
let frame, height, pathToOpen, resizable, title, width;
|
||||
this.browserWindow = null;
|
||||
this.loaded = null;
|
||||
this.isSpec = null;
|
||||
|
||||
if (settings == null) {
|
||||
settings = {};
|
||||
}
|
||||
({
|
||||
frame,
|
||||
title,
|
||||
width,
|
||||
height,
|
||||
toolbar,
|
||||
// toolbar, present but passed through to client-side
|
||||
resizable,
|
||||
pathToOpen,
|
||||
isSpec: this.isSpec,
|
||||
|
|
|
@ -93,7 +93,9 @@ export default class WindowManager {
|
|||
|
||||
if (this._windows[win.windowKey]) {
|
||||
throw new Error(
|
||||
`WindowManager: Attempting to register a new window for an existing windowKey (${win.windowKey}). Use 'get()' to retrieve the existing window instead.`
|
||||
`WindowManager: Attempting to register a new window for an existing windowKey (${
|
||||
win.windowKey
|
||||
}). Use 'get()' to retrieve the existing window instead.`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ function ImageNode(props) {
|
|||
onRemoveAttachment={() =>
|
||||
editor.change(change => {
|
||||
change.removeNodeByKey(node.key);
|
||||
})}
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,10 +9,8 @@ const {
|
|||
ReactDOM,
|
||||
PropTypes,
|
||||
Utils,
|
||||
RegExpUtils,
|
||||
IdentityStore,
|
||||
MailspringAPIRequest,
|
||||
SearchableComponentMaker,
|
||||
SearchableComponentStore,
|
||||
} = require('mailspring-exports');
|
||||
const IFrameSearcher = require('../searchable-components/iframe-searcher').default;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
const _ = require('underscore');
|
||||
const SwipeContainer = require('./swipe-container').default;
|
||||
const { React, PropTypes, Utils } = require('mailspring-exports');
|
||||
|
||||
|
|
|
@ -24,10 +24,7 @@ class MailImportantIcon extends React.Component {
|
|||
this.state = this.getState();
|
||||
}
|
||||
|
||||
getState = props => {
|
||||
if (props == null) {
|
||||
({ props } = this);
|
||||
}
|
||||
getState = (props = this.props) => {
|
||||
let category = null;
|
||||
let visible = false;
|
||||
|
||||
|
|
|
@ -145,12 +145,8 @@ class MultiselectActionBar extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
_getStateFromStores(props) {
|
||||
let left;
|
||||
if (props == null) {
|
||||
({ props } = this);
|
||||
}
|
||||
return { items: (left = props.dataSource.selection.items()) != null ? left : [] };
|
||||
_getStateFromStores(props = this.props) {
|
||||
return { items: props.dataSource.selection.items() || [] };
|
||||
}
|
||||
|
||||
_onChange = () => {
|
||||
|
|
|
@ -67,10 +67,7 @@ module.exports = class MultiselectListInteractionHandler {
|
|||
this.props.dataSource.selection.toggle(this.props.dataSource.getById(id));
|
||||
};
|
||||
|
||||
onShift = (delta, options) => {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
onShift = (delta, options = {}) => {
|
||||
const { id, action } = this._keyboardContext();
|
||||
|
||||
const current = this.props.dataSource.getById(id);
|
||||
|
|
|
@ -57,10 +57,9 @@ class MultiselectList extends React.Component {
|
|||
prevProps.focusedId !== this.props.focusedId ||
|
||||
prevProps.keyboardCursorId !== this.props.keyboardCursorId
|
||||
) {
|
||||
let item = ReactDOM.findDOMNode(this).querySelector('.focused');
|
||||
if (item == null) {
|
||||
item = ReactDOM.findDOMNode(this).querySelector('.keyboard-cursor');
|
||||
}
|
||||
const item =
|
||||
ReactDOM.findDOMNode(this).querySelector('.focused') ||
|
||||
ReactDOM.findDOMNode(this).querySelector('.keyboard-cursor');
|
||||
if (!(item instanceof Node)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -64,13 +64,12 @@ module.exports = class MultiselectSplitInteractionHandler {
|
|||
};
|
||||
|
||||
onShift = (delta, options) => {
|
||||
let action, id, selection;
|
||||
if (options.select) {
|
||||
this._turnFocusIntoSelection();
|
||||
}
|
||||
|
||||
let action, id;
|
||||
if (this.props.dataSource.selection.count() > 0) {
|
||||
({ selection } = this.props.dataSource);
|
||||
const keyboardId = this.props.keyboardCursorId;
|
||||
id = keyboardId != null ? keyboardId : this.props.dataSource.selection.top().id;
|
||||
action = this.onSetCursorPosition;
|
||||
|
|
|
@ -93,10 +93,7 @@ class Scrollbar extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
recomputeDimensions = options => {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
recomputeDimensions = (options = {}) => {
|
||||
if (this.props.getScrollRegion != null) {
|
||||
this.props.getScrollRegion()._recomputeDimensions(options);
|
||||
}
|
||||
|
@ -356,10 +353,7 @@ class ScrollRegion extends React.Component {
|
|||
|
||||
// Public: Scroll to the DOM Node provided.
|
||||
//
|
||||
scrollTo = (node, param) => {
|
||||
if (param == null) {
|
||||
param = {};
|
||||
}
|
||||
scrollTo = (node, param = {}) => {
|
||||
const { position, settle, done } = param;
|
||||
if (node instanceof React.Component) {
|
||||
node = ReactDOM.findDOMNode(node);
|
||||
|
@ -376,10 +370,7 @@ class ScrollRegion extends React.Component {
|
|||
// a ClientRect or similar object with top, left, width, height relative to the
|
||||
// window, not the scroll region. This is designed to make it easy to use with
|
||||
// node.getBoundingClientRect()
|
||||
scrollToRect(rect, param) {
|
||||
if (param == null) {
|
||||
param = {};
|
||||
}
|
||||
scrollToRect(rect, param = {}) {
|
||||
const { position, settle, done } = param;
|
||||
if (rect instanceof Node) {
|
||||
throw new Error('ScrollRegion.scrollToRect: requires a rect. Maybe you meant scrollTo?');
|
||||
|
@ -473,17 +464,8 @@ class ScrollRegion extends React.Component {
|
|||
scrollIfSettled();
|
||||
};
|
||||
|
||||
recomputeDimensions(options) {
|
||||
let left;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
const scrollbar =
|
||||
(left =
|
||||
typeof this.props.getScrollbar === 'function' ? this.props.getScrollbar() : undefined) !=
|
||||
null
|
||||
? left
|
||||
: this.refs.scrollbar;
|
||||
recomputeDimensions(options = {}) {
|
||||
const scrollbar = this.props.getScrollbar ? this.props.getScrollbar() : this.refs.scrollbar;
|
||||
if (scrollbar) {
|
||||
scrollbar._recomputeDimensions(options);
|
||||
}
|
||||
|
@ -509,8 +491,6 @@ class ScrollRegion extends React.Component {
|
|||
if (useCachedValues) {
|
||||
totalHeight =
|
||||
this.state.totalHeight != null ? this.state.totalHeight : contentNode.scrollHeight;
|
||||
const trackHeight =
|
||||
this.state.trackHeight != null ? this.state.trackHeight : contentNode.scrollHeight;
|
||||
viewportHeight =
|
||||
this.state.viewportHeight != null ? this.state.viewportHeight : contentNode.clientHeight;
|
||||
} else {
|
||||
|
@ -528,13 +508,7 @@ class ScrollRegion extends React.Component {
|
|||
};
|
||||
|
||||
_setSharedState(state) {
|
||||
let left;
|
||||
const scrollbar =
|
||||
(left =
|
||||
typeof this.props.getScrollbar === 'function' ? this.props.getScrollbar() : undefined) !=
|
||||
null
|
||||
? left
|
||||
: this.refs.scrollbar;
|
||||
const scrollbar = this.props.getScrollbar ? this.props.getScrollbar() : this.refs.scrollbar;
|
||||
if (scrollbar) {
|
||||
scrollbar.setStateFromScrollRegion(state);
|
||||
}
|
||||
|
|
|
@ -522,8 +522,7 @@ class Config {
|
|||
*/
|
||||
|
||||
pushAtKeyPath(keyPath, value) {
|
||||
let left;
|
||||
const arrayValue = (left = this.get(keyPath)) != null ? left : [];
|
||||
const arrayValue = this.get(keyPath) || [];
|
||||
if (!(arrayValue instanceof Array)) {
|
||||
throw new Error(
|
||||
`Config.pushAtKeyPath is intended for array values. Value ${JSON.stringify(
|
||||
|
@ -537,8 +536,7 @@ class Config {
|
|||
}
|
||||
|
||||
unshiftAtKeyPath(keyPath, value) {
|
||||
let left;
|
||||
const arrayValue = (left = this.get(keyPath)) != null ? left : [];
|
||||
const arrayValue = this.get(keyPath) || [];
|
||||
if (!(arrayValue instanceof Array)) {
|
||||
throw new Error(
|
||||
`Config.unshiftAtKeyPath is intended for array values. Value ${JSON.stringify(
|
||||
|
@ -552,8 +550,7 @@ class Config {
|
|||
}
|
||||
|
||||
removeAtKeyPath(keyPath, value) {
|
||||
let left;
|
||||
const arrayValue = (left = this.get(keyPath)) != null ? left : [];
|
||||
const arrayValue = this.get(keyPath) || [];
|
||||
if (!(arrayValue instanceof Array)) {
|
||||
throw new Error(
|
||||
`Config.removeAtKeyPath is intended for array values. Value ${JSON.stringify(
|
||||
|
@ -703,8 +700,8 @@ class Config {
|
|||
}
|
||||
|
||||
makeValueConformToSchema(keyPath, value) {
|
||||
let schema;
|
||||
if ((schema = this.getSchema(keyPath))) {
|
||||
let schema = this.getSchema(keyPath);
|
||||
if (schema) {
|
||||
value = this.constructor.executeSchemaEnforcers(keyPath, value, schema);
|
||||
}
|
||||
return value;
|
||||
|
@ -759,7 +756,7 @@ class Config {
|
|||
Config.addSchemaEnforcers({
|
||||
integer: {
|
||||
coerce(keyPath, value, schema) {
|
||||
value = parseInt(value);
|
||||
value = parseInt(value, 10);
|
||||
if (isNaN(value) || !isFinite(value)) {
|
||||
throw new Error(
|
||||
`Validation failed at ${keyPath}, ${JSON.stringify(value)} cannot be coerced into an int`
|
||||
|
|
|
@ -129,8 +129,9 @@ class Mac {
|
|||
}
|
||||
|
||||
getLaunchServicesPlistPath(callback) {
|
||||
const secure = `${process.env
|
||||
.HOME}/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist`;
|
||||
const secure = `${
|
||||
process.env.HOME
|
||||
}/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist`;
|
||||
const insecure = `${process.env.HOME}/Library/Preferences/com.apple.LaunchServices.plist`;
|
||||
|
||||
fs.exists(secure, exists => (exists ? callback(secure) : callback(insecure)));
|
||||
|
|
|
@ -88,7 +88,7 @@ var DOMUtils = {
|
|||
|
||||
commonAncestor(nodes, parentFilter) {
|
||||
if (nodes == null) {
|
||||
nodes = [];
|
||||
return null;
|
||||
}
|
||||
if (nodes.length === 0) {
|
||||
return null;
|
||||
|
@ -104,6 +104,7 @@ var DOMUtils = {
|
|||
const getParents = function(node) {
|
||||
const parentNodes = [node];
|
||||
let depth = 0;
|
||||
// eslint-disable-next-line
|
||||
while ((node = node.parentNode)) {
|
||||
if (parentFilter) {
|
||||
if (parentFilter(node)) {
|
||||
|
@ -185,6 +186,7 @@ var DOMUtils = {
|
|||
replacedNodes.unshift(lastChild);
|
||||
parent.replaceChild(lastChild, node);
|
||||
|
||||
// eslint-disable-next-line
|
||||
while ((child = _.last(node.childNodes))) {
|
||||
replacedNodes.unshift(child);
|
||||
parent.insertBefore(child, lastChild);
|
||||
|
|
|
@ -47,8 +47,9 @@ export default class Attribute {
|
|||
}
|
||||
if (val.length === 0) {
|
||||
console.warn(
|
||||
`Attribute::in (${this
|
||||
.modelKey}) called with an empty set. You should avoid this useless query!`
|
||||
`Attribute::in (${
|
||||
this.modelKey
|
||||
}) called with an empty set. You should avoid this useless query!`
|
||||
);
|
||||
}
|
||||
if (val.length === 1) {
|
||||
|
|
|
@ -84,8 +84,9 @@ export async function makeRequest(options) {
|
|||
}
|
||||
if (!resp.ok) {
|
||||
error.statusCode = resp.status;
|
||||
error.message = `${options.method ||
|
||||
'GET'} ${options.url} returned ${resp.status} ${resp.statusText}`;
|
||||
error.message = `${options.method || 'GET'} ${options.url} returned ${resp.status} ${
|
||||
resp.statusText
|
||||
}`;
|
||||
throw error;
|
||||
}
|
||||
return resp.json();
|
||||
|
|
|
@ -217,7 +217,9 @@ export default class MailsyncBridge {
|
|||
|
||||
AppEnv.showErrorDialog({
|
||||
title: `Cleanup Started`,
|
||||
message: `Mailspring is clearing it's cache for ${account.emailAddress}. Depending on the size of the mailbox, this may take a few seconds or a few minutes. An alert will appear when cleanup is complete.`,
|
||||
message: `Mailspring is clearing it's cache for ${
|
||||
account.emailAddress
|
||||
}. Depending on the size of the mailbox, this may take a few seconds or a few minutes. An alert will appear when cleanup is complete.`,
|
||||
});
|
||||
|
||||
try {
|
||||
|
|
|
@ -14,10 +14,7 @@ const DatabaseObjectRegistry = require('../../registries/database-object-registr
|
|||
let imageData = null;
|
||||
|
||||
module.exports = Utils = {
|
||||
waitFor(latch, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
waitFor(latch, options = {}) {
|
||||
const timeout = options.timeout || 400;
|
||||
const expire = Date.now() + timeout;
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
@ -41,10 +38,7 @@ module.exports = Utils = {
|
|||
return files.find(f => !f.contentId || f.size > 12 * 1024);
|
||||
},
|
||||
|
||||
extractTextFromHtml(html, param) {
|
||||
if (param == null) {
|
||||
param = {};
|
||||
}
|
||||
extractTextFromHtml(html, param = {}) {
|
||||
const { maxLength } = param;
|
||||
if ((html != null ? html : '').trim().length === 0) {
|
||||
return '';
|
||||
|
@ -97,10 +91,7 @@ module.exports = Utils = {
|
|||
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
|
||||
},
|
||||
|
||||
range(left, right, inclusive) {
|
||||
if (inclusive == null) {
|
||||
inclusive = true;
|
||||
}
|
||||
range(left, right, inclusive = true) {
|
||||
let range = [];
|
||||
let ascending = left < right;
|
||||
let end = !inclusive ? right : ascending ? right + 1 : right - 1;
|
||||
|
@ -115,24 +106,15 @@ module.exports = Utils = {
|
|||
//
|
||||
// See regex explanation and test here:
|
||||
// https://regex101.com/r/zG7aW4/2
|
||||
wordSearchRegExp(str) {
|
||||
if (str == null) {
|
||||
str = '';
|
||||
}
|
||||
wordSearchRegExp(str = '') {
|
||||
return new RegExp(`((?:^|\\W|$)${Utils.escapeRegExp(str.trim())})`, 'ig');
|
||||
},
|
||||
|
||||
// Takes an optional customizer. The customizer is passed the key and the
|
||||
// new cloned value for that key. The customizer is expected to either
|
||||
// modify the value and return it or simply be the identity function.
|
||||
deepClone(object, customizer, stackSeen, stackRefs) {
|
||||
deepClone(object, customizer, stackSeen = [], stackRefs = []) {
|
||||
let newObject;
|
||||
if (stackSeen == null) {
|
||||
stackSeen = [];
|
||||
}
|
||||
if (stackRefs == null) {
|
||||
stackRefs = [];
|
||||
}
|
||||
if (!_.isObject(object)) {
|
||||
return object;
|
||||
}
|
||||
|
@ -172,10 +154,7 @@ module.exports = Utils = {
|
|||
return newObject;
|
||||
},
|
||||
|
||||
toSet(arr) {
|
||||
if (arr == null) {
|
||||
arr = [];
|
||||
}
|
||||
toSet(arr = []) {
|
||||
const set = {};
|
||||
for (let item of arr) {
|
||||
set[item] = true;
|
||||
|
@ -185,19 +164,9 @@ module.exports = Utils = {
|
|||
|
||||
// Given a File object or uploadData of an uploading file object,
|
||||
// determine if it looks like an image and is in the size range for previews
|
||||
shouldDisplayAsImage(file) {
|
||||
let left, left1, left2;
|
||||
if (file == null) {
|
||||
file = {};
|
||||
}
|
||||
const name =
|
||||
(left =
|
||||
(left1 = file.filename != null ? file.filename : file.fileName) != null
|
||||
? left1
|
||||
: file.name) != null
|
||||
? left
|
||||
: '';
|
||||
const size = (left2 = file.size != null ? file.size : file.fileSize) != null ? left2 : 0;
|
||||
shouldDisplayAsImage(file = {}) {
|
||||
const name = file.filename || file.fileName || file.name;
|
||||
const size = file.size || file.fileSize || 0;
|
||||
const ext = path.extname(name).toLowerCase();
|
||||
const extensions = ['.jpg', '.bmp', '.gif', '.png', '.jpeg'];
|
||||
|
||||
|
@ -263,9 +232,8 @@ module.exports = Utils = {
|
|||
}
|
||||
|
||||
if (!imageData) {
|
||||
let left, left1;
|
||||
imageData = (left = AppEnv.fileListCache().imageData) != null ? left : '{}';
|
||||
Utils.images = (left1 = JSON.parse(imageData)) != null ? left1 : {};
|
||||
imageData = AppEnv.fileListCache().imageData || '{}';
|
||||
Utils.images = JSON.parse(imageData) || {};
|
||||
}
|
||||
|
||||
if (!Utils.images || !Utils.images[resourcePath]) {
|
||||
|
@ -341,10 +309,7 @@ module.exports = Utils = {
|
|||
if (args.length < 2) {
|
||||
return false;
|
||||
}
|
||||
const domains = args.map(function(email) {
|
||||
if (email == null) {
|
||||
email = '';
|
||||
}
|
||||
const domains = args.map((email = '') => {
|
||||
return _.last(
|
||||
email
|
||||
.toLowerCase()
|
||||
|
@ -356,10 +321,7 @@ module.exports = Utils = {
|
|||
return _.every(domains, domain => domain.length > 0 && toMatch === domain);
|
||||
},
|
||||
|
||||
emailHasCommonDomain(email) {
|
||||
if (email == null) {
|
||||
email = '';
|
||||
}
|
||||
emailHasCommonDomain(email = '') {
|
||||
const domain = _.last(
|
||||
email
|
||||
.toLowerCase()
|
||||
|
@ -399,10 +361,7 @@ module.exports = Utils = {
|
|||
return !(r2.left > r1.right || r2.right < r1.left || r2.top > r1.bottom || r2.bottom < r1.top);
|
||||
},
|
||||
|
||||
isEqualReact(a, b, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
isEqualReact(a, b, options = {}) {
|
||||
options.functionsAreEqual = true;
|
||||
options.ignoreKeys = (options.ignoreKeys != null ? options.ignoreKeys : []).push('id');
|
||||
return Utils.isEqual(a, b, options);
|
||||
|
@ -413,10 +372,7 @@ module.exports = Utils = {
|
|||
// - functionsAreEqual: if true then all functions are equal
|
||||
// - keysToIgnore: an array of object keys to ignore checks on
|
||||
// - logWhenFalse: logs when isEqual returns false
|
||||
isEqual(a, b, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
isEqual(a, b, options = {}) {
|
||||
const value = Utils._isEqual(a, b, [], [], options);
|
||||
if (options.logWhenFalse) {
|
||||
if (value === false) {
|
||||
|
@ -428,13 +384,10 @@ module.exports = Utils = {
|
|||
return value;
|
||||
},
|
||||
|
||||
_isEqual(a, b, aStack, bStack, options) {
|
||||
_isEqual(a, b, aStack, bStack, options = {}) {
|
||||
// Identical objects are equal. `0 is -0`, but they aren't identical.
|
||||
// See the [Harmony `egal`
|
||||
// proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
if (a === b) {
|
||||
return a !== 0 || 1 / a === 1 / b;
|
||||
}
|
||||
|
|
|
@ -150,7 +150,9 @@ class FeatureUsageStore extends MailspringStore {
|
|||
} else if (featureData.period === 'yearly') {
|
||||
time = 'a year';
|
||||
}
|
||||
rechargeText = `You can ${lexicon.usagePhrase} ${featureData.quota} emails ${time} with Mailspring Basic. Upgrade to Pro today!`;
|
||||
rechargeText = `You can ${lexicon.usagePhrase} ${
|
||||
featureData.quota
|
||||
} emails ${time} with Mailspring Basic. Upgrade to Pro today!`;
|
||||
}
|
||||
return { headerText, rechargeText };
|
||||
}
|
||||
|
|
|
@ -141,31 +141,30 @@ class MessageBodyProcessor {
|
|||
// Sanitizing <script> tags, etc. isn't necessary because we use CORS rules
|
||||
// to prevent their execution and sandbox content in the iFrame, but we still
|
||||
// want to remove contenteditable attributes and other strange things.
|
||||
return SanitizeTransformer.run(
|
||||
message.body,
|
||||
SanitizeTransformer.Preset.UnsafeOnly
|
||||
).then(sanitized => {
|
||||
let body = sanitized;
|
||||
for (const extension of MessageStore.extensions()) {
|
||||
if (!extension.formatMessageBody) {
|
||||
continue;
|
||||
}
|
||||
return SanitizeTransformer.run(message.body, SanitizeTransformer.Preset.UnsafeOnly).then(
|
||||
sanitized => {
|
||||
let body = sanitized;
|
||||
for (const extension of MessageStore.extensions()) {
|
||||
if (!extension.formatMessageBody) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Give each extension the message object to process the body, but don't
|
||||
// allow them to modify anything but the body for the time being.
|
||||
const previousBody = body;
|
||||
try {
|
||||
const virtual = message.clone();
|
||||
virtual.body = body;
|
||||
extension.formatMessageBody({ message: virtual });
|
||||
body = virtual.body;
|
||||
} catch (err) {
|
||||
AppEnv.reportError(err);
|
||||
body = previousBody;
|
||||
// Give each extension the message object to process the body, but don't
|
||||
// allow them to modify anything but the body for the time being.
|
||||
const previousBody = body;
|
||||
try {
|
||||
const virtual = message.clone();
|
||||
virtual.body = body;
|
||||
extension.formatMessageBody({ message: virtual });
|
||||
body = virtual.body;
|
||||
} catch (err) {
|
||||
AppEnv.reportError(err);
|
||||
body = previousBody;
|
||||
}
|
||||
}
|
||||
return body;
|
||||
}
|
||||
return body;
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
_addToCache(key, body) {
|
||||
|
|
|
@ -237,15 +237,9 @@ class WorkspaceStore extends MailspringStore {
|
|||
// *`columns` An {Object} with keys for each layout mode the Sheet
|
||||
// supports. For each key, provide an array of column names.
|
||||
//
|
||||
defineSheet(id, options, columns) {
|
||||
defineSheet(id, options = {}, columns = {}) {
|
||||
// Make sure all the locations have definitions so that packages
|
||||
// can register things into these locations and their toolbars.
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
if (columns == null) {
|
||||
columns = {};
|
||||
}
|
||||
for (let layout in columns) {
|
||||
const cols = columns[layout];
|
||||
for (let idx = 0; idx < cols.length; idx++) {
|
||||
|
|
|
@ -140,14 +140,14 @@ export default class SendDraftTask extends Task {
|
|||
const [smtpError, emails] = debuginfo.split(':::');
|
||||
errorMessage =
|
||||
"We were unable to deliver this message to some recipients. Click 'See Details' for more information.";
|
||||
errorDetail = `We encountered an SMTP Gateway error that prevented this message from being delivered to all recipients. The message was only sent successfully to these recipients:\n${emails}\n\nError: ${LocalizedErrorStrings[
|
||||
smtpError
|
||||
]}`;
|
||||
errorDetail = `We encountered an SMTP Gateway error that prevented this message from being delivered to all recipients. The message was only sent successfully to these recipients:\n${emails}\n\nError: ${
|
||||
LocalizedErrorStrings[smtpError]
|
||||
}`;
|
||||
} else if (key === 'send-failed') {
|
||||
errorMessage = `We were unable to deliver this message. ${LocalizedErrorStrings[debuginfo]}`;
|
||||
errorDetail = `We encountered an SMTP error that prevented this message from being delivered:\n\n${LocalizedErrorStrings[
|
||||
debuginfo
|
||||
]}`;
|
||||
errorDetail = `We encountered an SMTP error that prevented this message from being delivered:\n\n${
|
||||
LocalizedErrorStrings[debuginfo]
|
||||
}`;
|
||||
} else {
|
||||
errorMessage = 'We were unable to deliver this message.';
|
||||
errorDetail = `An unknown error occurred: ${JSON.stringify({ key, debuginfo })}`;
|
||||
|
|
|
@ -51,13 +51,13 @@ const CategoryObservables = {
|
|||
},
|
||||
|
||||
standard(account) {
|
||||
const observable = Rx.Observable
|
||||
.fromConfig('core.workspace.showImportant')
|
||||
.flatMapLatest(showImportant => {
|
||||
const observable = Rx.Observable.fromConfig('core.workspace.showImportant').flatMapLatest(
|
||||
showImportant => {
|
||||
return CategoryObservables.forAccount(account)
|
||||
.sort()
|
||||
.categoryFilter(cat => cat.isStandardCategory(showImportant));
|
||||
});
|
||||
}
|
||||
);
|
||||
Object.assign(observable, CategoryOperators);
|
||||
return observable;
|
||||
},
|
||||
|
|
|
@ -78,6 +78,7 @@ export default class MailspringStore {
|
|||
stopListeningToAll() {
|
||||
let remaining = undefined;
|
||||
const subs = this.subscriptions || [];
|
||||
// eslint-disable-next-line
|
||||
while ((remaining = subs.length)) {
|
||||
subs[0].stop();
|
||||
if (subs.length !== remaining - 1) {
|
||||
|
|
|
@ -85,8 +85,7 @@ export function ActionTemplatesForAccount(account) {
|
|||
|
||||
const templates = [].concat(ActionTemplates);
|
||||
|
||||
const CategoryNamesObservable = MailspringObservables.Categories
|
||||
.forAccount(account)
|
||||
const CategoryNamesObservable = MailspringObservables.Categories.forAccount(account)
|
||||
.sort()
|
||||
.map(cats => cats.filter(cat => !cat.isLockedCategory()))
|
||||
.map(cats =>
|
||||
|
|
|
@ -94,7 +94,9 @@ export default class PackageManager {
|
|||
if (!pkg.json.engines.mailspring) {
|
||||
// don't use AppEnv.reportError, I don't want to know about these.
|
||||
console.error(
|
||||
`This plugin or theme ${pkg.name} does not list "mailspring" in it's package.json's "engines" field. Ask the developer to test the plugin with Mailspring and add it, or follow the instructions here: http://support.getmailspring.com/hc/en-us/articles/115001918391`
|
||||
`This plugin or theme ${
|
||||
pkg.name
|
||||
} does not list "mailspring" in it's package.json's "engines" field. Ask the developer to test the plugin with Mailspring and add it, or follow the instructions here: http://support.getmailspring.com/hc/en-us/articles/115001918391`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -59,11 +59,7 @@ const RegExpUtils = {
|
|||
},
|
||||
|
||||
// Test cases: https://regex101.com/r/pD7iS5/4
|
||||
urlRegex(param) {
|
||||
if (param == null) {
|
||||
param = {};
|
||||
}
|
||||
const { matchStartOfString, matchTailOfString } = param;
|
||||
urlRegex({ matchStartOfString, matchTailOfString } = {}) {
|
||||
const commonTlds = [
|
||||
'com',
|
||||
'org',
|
||||
|
@ -207,11 +203,7 @@ const RegExpUtils = {
|
|||
return /["|']https:\/\/link\.getmailspring\.com\/link\/.*?\?.*?redirect=([^&"']*).*?["|']/g;
|
||||
},
|
||||
|
||||
punctuation(param) {
|
||||
if (param == null) {
|
||||
param = {};
|
||||
}
|
||||
let { exclude } = param;
|
||||
punctuation({ exclude } = {}) {
|
||||
if (exclude == null) {
|
||||
exclude = [];
|
||||
}
|
||||
|
|
|
@ -224,8 +224,9 @@ class StructuredSearchQueryVisitor extends SearchQueryExpressionVisitor {
|
|||
// in sqlite3, you use '' to escape a '. Weird right?
|
||||
const escaped = node.rawQuery.replace(/'/g, "''");
|
||||
|
||||
this._result = `(\`${this
|
||||
._className}\`.\`id\` IN (SELECT \`content_id\` FROM \`${searchTable}\` WHERE \`${searchTable}\` MATCH '${escaped}' LIMIT 1000))`;
|
||||
this._result = `(\`${
|
||||
this._className
|
||||
}\`.\`id\` IN (SELECT \`content_id\` FROM \`${searchTable}\` WHERE \`${searchTable}\` MATCH '${escaped}' LIMIT 1000))`;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,12 +27,12 @@
|
|||
"electron-packager": "8.7.x",
|
||||
"electron-winstaller": "2.x.x",
|
||||
"eslint": "^4.7.2",
|
||||
"eslint-config-prettier": "^2.6.0",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
"eslint-config-react-app": "^2.0.0",
|
||||
"eslint-plugin-flowtype": "^2.36.0",
|
||||
"eslint-plugin-import": "^2.7.0",
|
||||
"eslint-plugin-jsx-a11y": "^5.1.1",
|
||||
"eslint-plugin-prettier": "^2.3.1",
|
||||
"eslint-plugin-prettier": "^2.5.0",
|
||||
"eslint-plugin-react": "^7.4.0",
|
||||
"eslint_d": "4.2.0",
|
||||
"fs-extra": "2.x.x",
|
||||
|
@ -55,8 +55,7 @@
|
|||
"load-grunt-parent-tasks": "0.1.1",
|
||||
"meta-marked": "^0.4.2",
|
||||
"mkdirp": "^0.5.1",
|
||||
"pm2": "2.4.0",
|
||||
"prettier": "^1.7.0",
|
||||
"prettier": "^1.10.0",
|
||||
"request": "2.x.x",
|
||||
"rimraf": "^2.6.1",
|
||||
"targz": "^1.0.1",
|
||||
|
|
Loading…
Reference in a new issue