mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-21 15:56:10 +08:00
feat(theme-picker): Uninstall themes on click
Summary: Themes can now be uninstalled by clicking a button in the theme picker, instead of going to `~/.nylas/packages` to delete the directory. Test Plan: Tested locally. Reviewers: evan, bengotow Reviewed By: bengotow Differential Revision: https://phab.nylas.com/D2700
This commit is contained in:
parent
327eb43932
commit
f526bb2736
|
@ -1,18 +1,23 @@
|
|||
/** @babel */
|
||||
import React from 'react';
|
||||
import Actions from '../../../src/flux/actions'
|
||||
import Actions from '../../../src/flux/actions';
|
||||
|
||||
import ThemePicker from './theme-picker'
|
||||
import ThemePicker from './theme-picker';
|
||||
import ThemePickerStore from './theme-picker-store';
|
||||
|
||||
|
||||
export function activate() {
|
||||
this.disposable = NylasEnv.commands.add("body",
|
||||
"window:launch-theme-picker",
|
||||
() => Actions.openModal(children=<ThemePicker />,
|
||||
height=400,
|
||||
width=250));
|
||||
ThemePickerStore.activate();
|
||||
this.disposable = NylasEnv.commands.add("body", "window:launch-theme-picker", () => {
|
||||
Actions.openModal(
|
||||
children=<ThemePicker />,
|
||||
height=400,
|
||||
width=250,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function deactivate() {
|
||||
ThemePickerStore.deactivate();
|
||||
this.disposable.dispose();
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import fs from 'fs-plus';
|
||||
import path from 'path';
|
||||
|
||||
import {EventedIFrame} from 'nylas-component-kit';
|
||||
import {EventedIFrame, RetinaImg} from 'nylas-component-kit';
|
||||
import LessCompileCache from '../../../src/less-compile-cache'
|
||||
|
||||
|
||||
|
@ -10,6 +10,8 @@ class ThemeOption extends React.Component {
|
|||
static propTypes = {
|
||||
theme: React.PropTypes.object.isRequired,
|
||||
active: React.PropTypes.bool.isRequired,
|
||||
onSelect: React.PropTypes.func.isRequired,
|
||||
onUninstall: React.PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -65,7 +67,7 @@ class ThemeOption extends React.Component {
|
|||
}
|
||||
|
||||
_writeContent() {
|
||||
const domNode = React.findDOMNode(this);
|
||||
const domNode = React.findDOMNode(this.refs.iframe);
|
||||
const doc = domNode.contentDocument;
|
||||
if (!doc) return;
|
||||
|
||||
|
@ -93,8 +95,26 @@ class ThemeOption extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const uninstallButton = this.props.theme.path.indexOf("/n1/internal_packages") === -1 ? (
|
||||
<RetinaImg
|
||||
className="theme-uninstall-x"
|
||||
name="uninstall-x.png"
|
||||
mode={RetinaImg.Mode.ContentDark}
|
||||
style={{width: "14", height: "14"}}
|
||||
onMouseDown={this.props.onUninstall} />) : null;
|
||||
return (
|
||||
<EventedIFrame ref="iframe" className={`theme-preview-${this.props.theme.name}`} frameBorder="0" width="105px" height="65px" flex="1" style={{pointerEvents: "none"}} />
|
||||
<div>
|
||||
{uninstallButton}
|
||||
<div className="clickable-theme-option" onMouseDown={this.props.onSelect}>
|
||||
<EventedIFrame
|
||||
ref="iframe"
|
||||
className={`theme-preview-${this.props.theme.name}`}
|
||||
frameBorder="0"
|
||||
width="105px"
|
||||
height="65px"
|
||||
flex="1" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
12
internal_packages/theme-picker/lib/theme-picker-actions.js
Normal file
12
internal_packages/theme-picker/lib/theme-picker-actions.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
/** @babel */
|
||||
import Reflux from 'reflux';
|
||||
|
||||
ThemePickerActions = Reflux.createActions([
|
||||
"uninstallTheme",
|
||||
]);
|
||||
|
||||
for (key in ThemePickerActions) {
|
||||
ThemePickerActions[key].sync = true;
|
||||
}
|
||||
|
||||
export default ThemePickerActions;
|
32
internal_packages/theme-picker/lib/theme-picker-store.jsx
Normal file
32
internal_packages/theme-picker/lib/theme-picker-store.jsx
Normal file
|
@ -0,0 +1,32 @@
|
|||
import NylasStore from 'nylas-store';
|
||||
import {APMWrapper} from 'nylas-exports';
|
||||
|
||||
import ThemePickerActions from './theme-picker-actions';
|
||||
|
||||
|
||||
class ThemePickerStore extends NylasStore {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._apm = new APMWrapper();
|
||||
}
|
||||
|
||||
activate = ()=> {
|
||||
this.unlisten = ThemePickerActions.uninstallTheme.listen(this.uninstallTheme);
|
||||
}
|
||||
|
||||
uninstallTheme = (theme)=> {
|
||||
if (NylasEnv.packages.isPackageLoaded(theme.name)) {
|
||||
NylasEnv.packages.disablePackage(theme.name);
|
||||
NylasEnv.packages.unloadPackage(theme.name);
|
||||
}
|
||||
this._apm.uninstall(theme);
|
||||
}
|
||||
|
||||
deactivate = ()=> {
|
||||
this.unlisten();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new ThemePickerStore();
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import Actions from '../../../src/flux/actions'
|
||||
|
||||
import {Flexbox, RetinaImg} from 'nylas-component-kit';
|
||||
import ThemePickerActions from './theme-picker-actions';
|
||||
import ThemeOption from './theme-option';
|
||||
|
||||
|
||||
|
@ -10,12 +11,12 @@ class ThemePicker extends React.Component {
|
|||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._themeManager = NylasEnv.themes;
|
||||
this.themes = NylasEnv.themes;
|
||||
this.state = this._getState();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.disposable = this._themeManager.onDidChangeActiveThemes(() => {
|
||||
this.disposable = this.themes.onDidChangeActiveThemes(() => {
|
||||
this.setState(this._getState());
|
||||
});
|
||||
}
|
||||
|
@ -26,15 +27,14 @@ class ThemePicker extends React.Component {
|
|||
|
||||
_getState() {
|
||||
return {
|
||||
themes: this._themeManager.getLoadedThemes(),
|
||||
activeTheme: this._themeManager.getActiveTheme().name,
|
||||
themes: this.themes.getLoadedThemes(),
|
||||
activeTheme: this.themes.getActiveTheme().name,
|
||||
}
|
||||
}
|
||||
|
||||
_setActiveTheme(theme) {
|
||||
const prevActiveTheme = this.state.activeTheme;
|
||||
this.setState({activeTheme: theme});
|
||||
this._themeManager.setActiveTheme(theme);
|
||||
this.themes.setActiveTheme(theme);
|
||||
this._rewriteIFrame(prevActiveTheme, theme);
|
||||
}
|
||||
|
||||
|
@ -47,24 +47,25 @@ class ThemePicker extends React.Component {
|
|||
activeElement.className = "theme-option active-true";
|
||||
}
|
||||
|
||||
_onUninstallTheme(theme) {
|
||||
ThemePickerActions.uninstallTheme(theme);
|
||||
this.setState({themes: this.themes.getLoadedThemes()});
|
||||
}
|
||||
|
||||
_renderThemeOptions() {
|
||||
const themeOptions = this.state.themes.map((theme) =>
|
||||
<div
|
||||
className="clickable-theme-option"
|
||||
onMouseDown={() => this._setActiveTheme(theme.name)}
|
||||
style={{cursor: "pointer", width: "115px", margin: "2px"}}>
|
||||
<ThemeOption
|
||||
key={theme.name}
|
||||
theme={theme}
|
||||
active={this.state.activeTheme === theme.name} />
|
||||
</div>
|
||||
)
|
||||
return themeOptions;
|
||||
return this.state.themes.map((theme) =>
|
||||
<ThemeOption
|
||||
key={theme.name}
|
||||
theme={theme}
|
||||
active={this.state.activeTheme === theme.name}
|
||||
onSelect={() => this._setActiveTheme(theme.name)}
|
||||
onUninstall={() => this._onUninstallTheme(theme)} />
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={{textAlign: "center", cursor: "default"}}>
|
||||
<div className="theme-picker">
|
||||
<Flexbox direction="column">
|
||||
<RetinaImg
|
||||
style={{width: "14", height: "14", margin: "8px", WebkitFilter: "none"}}
|
||||
|
|
|
@ -7,19 +7,31 @@ 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 thirdPartyTheme = new ThemePackage(resourcePath + '/internal_packages/ui-light');
|
||||
thirdPartyTheme.name = 'third-party-theme'
|
||||
thirdPartyTheme.path = ''
|
||||
|
||||
describe('ThemePicker', ()=> {
|
||||
beforeEach(()=> {
|
||||
spyOn(ThemePicker.prototype, '_setActiveTheme').andCallThrough();
|
||||
spyOn(ThemePicker.prototype, '_rewriteIFrame');
|
||||
spyOn(NylasEnv.themes, 'getLoadedThemes').andReturn([light, dark]);
|
||||
spyOn(NylasEnv.themes, 'getLoadedThemes').andReturn([light, dark, thirdPartyTheme]);
|
||||
spyOn(NylasEnv.themes, 'getActiveTheme').andReturn(light);
|
||||
this.component = ReactTestUtils.renderIntoDocument(<ThemePicker />);
|
||||
});
|
||||
|
||||
it('changes the active theme when a theme is clicked', ()=> {
|
||||
spyOn(ThemePicker.prototype, '_setActiveTheme').andCallThrough();
|
||||
spyOn(ThemePicker.prototype, '_rewriteIFrame');
|
||||
const themeOption = React.findDOMNode(ReactTestUtils.scryRenderedDOMComponentsWithClass(this.component, 'clickable-theme-option')[1]);
|
||||
ReactTestUtils.Simulate.mouseDown(themeOption);
|
||||
expect(ThemePicker.prototype._setActiveTheme).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('uninstalls themes on click', ()=> {
|
||||
spyOn(ThemePicker.prototype, '_onUninstallTheme').andCallThrough();
|
||||
spyOn(ThemePicker.prototype, 'setState').andCallThrough();
|
||||
const uninstallButton = React.findDOMNode(ReactTestUtils.scryRenderedDOMComponentsWithClass(this.component, 'theme-uninstall-x')[0]);
|
||||
ReactTestUtils.Simulate.mouseDown(uninstallButton);
|
||||
expect(ThemePicker.prototype._onUninstallTheme).toHaveBeenCalled();
|
||||
expect(ThemePicker.prototype.setState).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
25
internal_packages/theme-picker/styles/theme-picker.less
Normal file
25
internal_packages/theme-picker/styles/theme-picker.less
Normal file
|
@ -0,0 +1,25 @@
|
|||
@import "ui-variables";
|
||||
|
||||
.theme-picker {
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
iframe {
|
||||
pointer-events: none;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
.theme-uninstall-x {
|
||||
float: right;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-bottom: -40px;
|
||||
margin-right: 10px;
|
||||
-webkit-filter: none;
|
||||
}
|
||||
.clickable-theme-option {
|
||||
cursor: pointer;
|
||||
width: 115px;
|
||||
margin: 2px;
|
||||
top: -10px;
|
||||
}
|
||||
}
|
BIN
static/images/theme-picker/uninstall-x@1x.png
Normal file
BIN
static/images/theme-picker/uninstall-x@1x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
BIN
static/images/theme-picker/uninstall-x@2x.png
Normal file
BIN
static/images/theme-picker/uninstall-x@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
Loading…
Reference in a new issue