mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-20 15:26:06 +08:00
Improve contacts window launch perf by lazy loading composer support, scanning less of fs for themes
This commit is contained in:
parent
07abd6cb71
commit
0c2b0eb03b
|
@ -4,9 +4,9 @@ module.exports = grunt => {
|
|||
src: ['internal_packages/**/*.less', 'dot-nylas/**/*.less', 'static/**/*.less'],
|
||||
options: {
|
||||
less: {
|
||||
paths: ['static', 'static/base/'],
|
||||
paths: ['static', 'static/style/'],
|
||||
},
|
||||
imports: ['static/base/*.less'],
|
||||
imports: ['static/style/*.less'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -105,7 +105,7 @@ export function activate() {
|
|||
const i = document.createElement('i');
|
||||
i.className = 'fa fa-list';
|
||||
i.style.position = 'absolute';
|
||||
i.style.top = '0';
|
||||
i.style.top = '-20px';
|
||||
document.body.appendChild(i);
|
||||
}, 1000);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import path from 'path';
|
|||
import { EventedIFrame } from 'mailspring-component-kit';
|
||||
import Package from '../../../src/package';
|
||||
import LessCompileCache from '../../../src/less-compile-cache';
|
||||
import _ from 'underscore';
|
||||
|
||||
interface ThemeOptionProps {
|
||||
theme: Package;
|
||||
|
@ -33,10 +34,10 @@ class ThemeOption extends React.Component<ThemeOptionProps> {
|
|||
}
|
||||
|
||||
_getImportPaths() {
|
||||
return [
|
||||
return _.uniq([
|
||||
this.props.theme.getStylesheetsPath(),
|
||||
AppEnv.themes.getBaseTheme().getStylesheetsPath(),
|
||||
];
|
||||
]);
|
||||
}
|
||||
|
||||
_loadStylesheet(stylesheetPath) {
|
||||
|
@ -58,7 +59,7 @@ class ThemeOption extends React.Component<ThemeOptionProps> {
|
|||
`${resourcePath}/internal_packages/theme-picker/preview-styles`,
|
||||
this.props.theme.getStylesheetsPath()
|
||||
);
|
||||
let varImports = `@import "../../../static/base/ui-variables";`;
|
||||
let varImports = `@import "base/ui-variables";`;
|
||||
if (fs.existsSync(`${this.props.theme.getStylesheetsPath()}/ui-variables.less`)) {
|
||||
varImports += `@import "${themeVarPath}/ui-variables";`;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
line-height: 23px;
|
||||
background: @input-bg;
|
||||
color: @text-color;
|
||||
margin-top: (38px - 23px) / 2;
|
||||
margin-top: 5px;
|
||||
padding-left: @padding-xs-horizontal;
|
||||
padding-right: @padding-xs-horizontal;
|
||||
border-radius: @border-radius-base;
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
@text-color-inverse: white;
|
||||
@text-color-inverse-subtle: fadeout(@text-color-inverse, 20%);
|
||||
@text-color-inverse-very-subtle: fadeout(@text-color-inverse, 50%);
|
||||
@text-color-heading: #FFF;
|
||||
@text-color-heading: #fff;
|
||||
|
||||
@text-color-link: @accent-primary;
|
||||
@text-color-link-hover: @accent-primary-dark;
|
||||
|
|
|
@ -83,12 +83,6 @@ body.platform-win32 {
|
|||
}
|
||||
}
|
||||
|
||||
.sheet-toolbar .btn-toolbar {
|
||||
height: 2em !important;
|
||||
line-height: 1 !important;
|
||||
margin-top: 6px !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dropdown
|
||||
*/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import '../../../static/base/ui-variables';
|
||||
@import 'base/ui-variables';
|
||||
@import 'variables';
|
||||
|
||||
#account-switcher .primary-item .name {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* all theme ui-variables files should inherit from the base one to ensure
|
||||
that all variables are defined. */
|
||||
@import 'base/ui-variables';
|
||||
@import 'ui-variables';
|
||||
|
||||
/* ui-light is a fake theme that just falls through to the app's base styles. */
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
import _ from 'underscore';
|
||||
import _str from 'underscore.string';
|
||||
import { jasmine } from './jasmine';
|
||||
|
||||
export default class TimeReporter extends jasmine.Reporter {
|
||||
|
@ -73,7 +72,7 @@ export default class TimeReporter extends jasmine.Reporter {
|
|||
if (index === 0) {
|
||||
return `${description}`;
|
||||
} else {
|
||||
return `${memo}\n${_str.repeat(' ', index)}${description}`;
|
||||
return `${memo}\n${' '.repeat(index)}${description}`;
|
||||
}
|
||||
};
|
||||
this.description = _.reduce(stack, reducer, '');
|
||||
|
|
|
@ -277,7 +277,7 @@ export default class WindowManager {
|
|||
width: 800,
|
||||
height: 500,
|
||||
toolbar: true,
|
||||
hidden: false,
|
||||
hidden: true,
|
||||
};
|
||||
|
||||
// The SPEC_WINDOW gets passed its own bootstrapScript
|
||||
|
|
|
@ -106,9 +106,7 @@ class EmptyListDataSource extends ListDataSource {
|
|||
itemsCurrentlyInViewMatching() {
|
||||
return [];
|
||||
}
|
||||
setRetainedRange() {
|
||||
return;
|
||||
}
|
||||
setRetainedRange() {}
|
||||
}
|
||||
|
||||
class DumbArrayDataSource<T extends Model> extends ListDataSource {
|
||||
|
@ -139,7 +137,5 @@ class DumbArrayDataSource<T extends Model> extends ListDataSource {
|
|||
itemsCurrentlyInViewMatching(matchFn: (item: T) => boolean) {
|
||||
return this._items.filter(matchFn);
|
||||
}
|
||||
setRetainedRange() {
|
||||
return;
|
||||
}
|
||||
setRetainedRange() {}
|
||||
}
|
||||
|
|
|
@ -189,7 +189,6 @@ export class ListTabular extends Component<ListTabularProps, ListTabularState> {
|
|||
componentDidMount() {
|
||||
window.addEventListener('resize', this.onWindowResize, true);
|
||||
this.setupDataSource(this.props.dataSource);
|
||||
this.updateRangeState();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
|
@ -214,7 +213,7 @@ export class ListTabular extends Component<ListTabularProps, ListTabularState> {
|
|||
prevProps.itemHeight !== this.props.itemHeight ||
|
||||
prevProps.dataSource !== this.props.dataSource
|
||||
) {
|
||||
this.updateRangeState();
|
||||
this.updateRangeStateIfChanged();
|
||||
}
|
||||
|
||||
if (!this._cleanupAnimationTimeout) {
|
||||
|
@ -232,7 +231,7 @@ export class ListTabular extends Component<ListTabularProps, ListTabularState> {
|
|||
|
||||
onWindowResize = () => {
|
||||
if (this._onWindowResize == null) {
|
||||
this._onWindowResize = _.debounce(this.updateRangeState, 50);
|
||||
this._onWindowResize = _.debounce(this.updateRangeStateIfChanged, 50);
|
||||
}
|
||||
this._onWindowResize();
|
||||
};
|
||||
|
@ -240,7 +239,7 @@ export class ListTabular extends Component<ListTabularProps, ListTabularState> {
|
|||
onScroll = () => {
|
||||
// If we've shifted enough pixels from our previous scrollTop to require
|
||||
// new rows to be rendered, update our state!
|
||||
this.updateRangeState();
|
||||
this.updateRangeStateIfChanged();
|
||||
};
|
||||
|
||||
onCleanupAnimatingItems = () => {
|
||||
|
@ -264,10 +263,11 @@ export class ListTabular extends Component<ListTabularProps, ListTabularState> {
|
|||
|
||||
setupDataSource(dataSource) {
|
||||
this._unlisten();
|
||||
this._unlisten = dataSource.listen(() => {
|
||||
this.setState(this.buildStateForRange());
|
||||
});
|
||||
this.setState(this.buildStateForRange({ start: -1, end: -1, dataSource }));
|
||||
this._unlisten = dataSource.listen(() => this.setState(this.buildStateForRange()));
|
||||
|
||||
const range = this.getRange();
|
||||
this.props.dataSource.setRetainedRange(range);
|
||||
this.setState(this.buildStateForRange({ ...range, dataSource }));
|
||||
}
|
||||
|
||||
getRowsToRender() {
|
||||
|
@ -307,7 +307,7 @@ export class ListTabular extends Component<ListTabularProps, ListTabularState> {
|
|||
this._scrollRegion.scrollTop += height * direction;
|
||||
}
|
||||
|
||||
updateRangeState() {
|
||||
getRange() {
|
||||
if (!this._scrollRegion) {
|
||||
return;
|
||||
}
|
||||
|
@ -323,23 +323,21 @@ export class ListTabular extends Component<ListTabularProps, ListTabularState> {
|
|||
// we have items to move to and then scroll to.
|
||||
rangeStart = Math.max(0, rangeStart - 2);
|
||||
rangeEnd = Math.min(rangeEnd + 2, this.state.count + 1);
|
||||
return { start: rangeStart, end: rangeEnd };
|
||||
}
|
||||
|
||||
updateRangeStateIfChanged() {
|
||||
const range = this.getRange();
|
||||
|
||||
// Final sanity check to prevent needless work
|
||||
const shouldNotUpdate =
|
||||
rangeEnd === this.state.renderedRangeEnd && rangeStart === this.state.renderedRangeStart;
|
||||
if (shouldNotUpdate) {
|
||||
return;
|
||||
if (
|
||||
range.end !== this.state.renderedRangeEnd ||
|
||||
range.start !== this.state.renderedRangeStart
|
||||
) {
|
||||
this.updateRangeStateFiring = true;
|
||||
this.props.dataSource.setRetainedRange(range);
|
||||
this.setState(this.buildStateForRange(range));
|
||||
}
|
||||
|
||||
this.updateRangeStateFiring = true;
|
||||
|
||||
this.props.dataSource.setRetainedRange({
|
||||
start: rangeStart,
|
||||
end: rangeEnd,
|
||||
});
|
||||
|
||||
const nextState = this.buildStateForRange({ start: rangeStart, end: rangeEnd });
|
||||
this.setState(nextState);
|
||||
}
|
||||
|
||||
buildStateForRange(args: { start?: number; end?: number; dataSource?: ListDataSource } = {}) {
|
||||
|
|
|
@ -49,8 +49,7 @@ class ContactStore extends MailspringStore {
|
|||
.order(Contact.attributes.refs.descending());
|
||||
|
||||
return (query.then(async _results => {
|
||||
// remove query results that were already found in ranked contacts
|
||||
let results = this._distinctByEmail(_results);
|
||||
let results = this._distinctByEmail(this._omitFindInMailDisabled(_results));
|
||||
for (const ext of extensions) {
|
||||
results = await ext.findAdditionalContacts(search, results);
|
||||
}
|
||||
|
@ -69,7 +68,7 @@ class ContactStore extends MailspringStore {
|
|||
.where(Contact.attributes.hidden.equal(false))
|
||||
.order(Contact.attributes.refs.descending())
|
||||
.then(async _results => {
|
||||
let results = this._distinctByEmail(_results);
|
||||
let results = this._distinctByEmail(this._omitFindInMailDisabled(_results));
|
||||
if (results.length > limit) {
|
||||
results.length = limit;
|
||||
}
|
||||
|
@ -145,6 +144,13 @@ class ContactStore extends MailspringStore {
|
|||
);
|
||||
}
|
||||
|
||||
_omitFindInMailDisabled(results: Contact[]) {
|
||||
// remove results that the user has asked not to see. (Cheaper to do this in JS
|
||||
// than construct a WHERE clause that makes SQLite's index selection non-obvious.)
|
||||
const findInMailDisabled = AppEnv.config.get('core.contacts.findInMailDisabled');
|
||||
return results.filter(r => !(r.source === 'mail' && findInMailDisabled.includes(r.accountId)));
|
||||
}
|
||||
|
||||
_distinctByEmail(contacts: Contact[]) {
|
||||
// remove query results that are duplicates, prefering ones that have names
|
||||
const uniq: { [email: string]: Contact } = {};
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import MailspringStore from 'mailspring-store';
|
||||
import { Editor } from 'slate';
|
||||
import { Editor, Value } from 'slate';
|
||||
|
||||
import { Conversion } from '../../components/composer-editor/composer-support';
|
||||
import RegExpUtils from '../../regexp-utils';
|
||||
import { localized } from '../../intl';
|
||||
|
||||
|
@ -20,7 +19,30 @@ import { SyncbackDraftTask } from '../tasks/syncback-draft-task';
|
|||
|
||||
export type MessageWithEditorState = Message & { bodyEditorState: any };
|
||||
|
||||
const { convertFromHTML, convertToHTML, convertToShapeWithoutContent } = Conversion;
|
||||
/*
|
||||
Note: This is a bit of a hack, but pulling in composer-support is what triggers slate,
|
||||
slate-react, etc. to be loaded in windows where the components are not actually in use.
|
||||
By lazily resolving this import, we can save ~400ms of window start-up time.
|
||||
*/
|
||||
let Conversion = null;
|
||||
function resolveConversion() {
|
||||
if (Conversion) return;
|
||||
Conversion = require('../../components/composer-editor/composer-support').Conversion;
|
||||
}
|
||||
|
||||
function convertFromHTML(html: string) {
|
||||
resolveConversion();
|
||||
return Conversion.convertFromHTML(html);
|
||||
}
|
||||
function convertToHTML(value: Value) {
|
||||
resolveConversion();
|
||||
return Conversion.convertToHTML(value);
|
||||
}
|
||||
function convertToShapeWithoutContent(value: Value) {
|
||||
resolveConversion();
|
||||
return Conversion.convertToShapeWithoutContent(value);
|
||||
}
|
||||
|
||||
const MetadataChangePrefix = 'metadata.';
|
||||
let DraftStore = null;
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Task } from './task';
|
||||
import { AttributeValues } from '../models/model';
|
||||
import Attributes from '../attributes';
|
||||
import ICAL from 'ical.js';
|
||||
import {
|
||||
localized,
|
||||
ICSParticipantStatus,
|
||||
|
@ -12,6 +11,8 @@ import {
|
|||
Actions,
|
||||
} from 'mailspring-exports';
|
||||
|
||||
let ICAL: typeof import('ical.js') = null;
|
||||
|
||||
export class EventRSVPTask extends Task {
|
||||
ics: string;
|
||||
icsRSVPStatus: ICSParticipantStatus;
|
||||
|
@ -56,6 +57,9 @@ export class EventRSVPTask extends Task {
|
|||
icsOriginalData: string;
|
||||
icsRSVPStatus: ICSParticipantStatus;
|
||||
}) {
|
||||
if (!ICAL) {
|
||||
ICAL = require('ical.js');
|
||||
}
|
||||
const jcalData = ICAL.parse(icsOriginalData);
|
||||
const comp = new ICAL.Component(jcalData);
|
||||
const event = new ICAL.Event(comp.getFirstSubcomponent('vevent'));
|
||||
|
|
|
@ -7,21 +7,25 @@ module.exports = exports = {};
|
|||
|
||||
// Because requiring files the first time they're used hurts performance, we
|
||||
// automatically load components slowly in the background using idle cycles.
|
||||
setTimeout(() => {
|
||||
const remaining = Object.keys(module.exports);
|
||||
const fn = deadline => {
|
||||
let key = null;
|
||||
let bogus = 0; // eslint-disable-line
|
||||
while ((key = remaining.pop())) {
|
||||
bogus += module.exports[key] ? 1 : 0;
|
||||
if (deadline.timeRemaining() <= 0) {
|
||||
window.requestIdleCallback(fn, { timeout: 5000 });
|
||||
return;
|
||||
if (AppEnv.isMainWindow()) {
|
||||
setTimeout(() => {
|
||||
const remaining = Object.keys(module.exports);
|
||||
const fn = deadline => {
|
||||
let key = null;
|
||||
let bogus = 0; // eslint-disable-line
|
||||
while ((key = remaining.pop())) {
|
||||
bogus += module.exports[key] ? 1 : 0;
|
||||
if (deadline.timeRemaining() <= 0) {
|
||||
window.requestIdleCallback(fn, { timeout: 5000 });
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
window.requestIdleCallback(fn, { timeout: 5000 });
|
||||
}, 500);
|
||||
};
|
||||
window.requestIdleCallback(fn, { timeout: 5000 });
|
||||
}, 500);
|
||||
} else {
|
||||
// just wait for them to be required
|
||||
}
|
||||
|
||||
const resolveExport = (requireValue, name) => {
|
||||
return requireValue.default || requireValue[name] || requireValue;
|
||||
|
|
|
@ -12,8 +12,8 @@ export default class LessCompileCache {
|
|||
|
||||
constructor({ configDirPath, resourcePath, importPaths = [] }) {
|
||||
this.lessSearchPaths = [
|
||||
path.join(resourcePath, 'static', 'base'),
|
||||
path.join(resourcePath, 'static'),
|
||||
path.join(resourcePath, 'static', 'style'),
|
||||
path.join(resourcePath, 'static', 'style', 'base'),
|
||||
];
|
||||
|
||||
this.cache = new LessCache({
|
||||
|
|
|
@ -72,8 +72,7 @@ export default class ThemeManager {
|
|||
reloadCoreStyles() {
|
||||
console.log('Reloading /static and /internal_packages to incorporate LESS changes');
|
||||
const reloadStylesIn = folder => {
|
||||
fs
|
||||
.listTreeSync(folder)
|
||||
fs.listTreeSync(folder)
|
||||
.filter(stylePath => stylePath.endsWith('.less'))
|
||||
.forEach(stylePath => {
|
||||
const styleEl = document.head.querySelector(`[source-path="${stylePath}"]`);
|
||||
|
@ -171,8 +170,8 @@ export default class ThemeManager {
|
|||
}
|
||||
|
||||
loadStaticStylesheets() {
|
||||
this.requireStylesheet('../static/index');
|
||||
this.requireStylesheet('../static/email-frame');
|
||||
this.requireStylesheet('../static/style/index');
|
||||
this.requireStylesheet('../static/style/email-frame');
|
||||
}
|
||||
|
||||
resolveStylesheetPath(stylesheetPath) {
|
||||
|
|
BIN
app/static/images/source-list/people@1x.png
Normal file
BIN
app/static/images/source-list/people@1x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 627 B |
BIN
app/static/images/source-list/people@2x.png
Normal file
BIN
app/static/images/source-list/people@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
app/static/images/source-list/person@1x.png
Normal file
BIN
app/static/images/source-list/person@1x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 611 B |
BIN
app/static/images/source-list/person@2x.png
Normal file
BIN
app/static/images/source-list/person@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -60,7 +60,7 @@ mailspring-workspace {
|
|||
}
|
||||
|
||||
.toolbar-window-controls {
|
||||
margin-top: 9px;
|
||||
margin-top: 7px;
|
||||
margin-left: @spacing-half /*rtl:ignore */;
|
||||
order: -1000 /*rtl: 1000*/;
|
||||
min-width: 72px;
|
||||
|
@ -164,33 +164,17 @@ body.is-blurred {
|
|||
color: @text-color-heading;
|
||||
}
|
||||
|
||||
.layout-mode-popout {
|
||||
.sheet-toolbar {
|
||||
background: @background-primary;
|
||||
height: 35px;
|
||||
min-height: 35px;
|
||||
max-height: 35px;
|
||||
|
||||
.btn-toolbar {
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
.toolbar-window-controls {
|
||||
margin-top: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
.sheet-toolbar {
|
||||
position: relative;
|
||||
-webkit-app-region: drag;
|
||||
border-bottom: 1px solid darken(@toolbar-background-color, 9%);
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
height: 34px;
|
||||
|
||||
// prevent flexbox from ever, ever resizing toolbars, no matter
|
||||
// how much it thinks other content is being squished
|
||||
min-height: 38px;
|
||||
max-height: 38px;
|
||||
min-height: 34px;
|
||||
max-height: 34px;
|
||||
|
||||
// cover up the vertical resizing separators, so the toolbar appears
|
||||
// to be one continuous bar.
|
||||
|
@ -214,7 +198,7 @@ body.is-blurred {
|
|||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
-webkit-app-region: drag;
|
||||
line-height: 36px;
|
||||
line-height: 34px;
|
||||
&:hover {
|
||||
cursor: default;
|
||||
}
|
||||
|
@ -249,7 +233,7 @@ body.is-blurred {
|
|||
}
|
||||
|
||||
.btn-toolbar {
|
||||
margin-top: 8px;
|
||||
margin-top: 5px;
|
||||
margin-left: @spacing-three-quarters;
|
||||
margin-right: 0;
|
||||
flex-shrink: 0;
|
||||
|
@ -266,7 +250,7 @@ body.is-blurred {
|
|||
}
|
||||
}
|
||||
.btn-toolbar:only-of-type {
|
||||
margin-right: @spacing-three-quarters;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.button-group {
|
Loading…
Reference in a new issue