mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-20 23:36:21 +08:00
feat(post-auth): Initial prefs + packages screens, welcome copy changes
Summary: Package names must match directory names Not going to use new Swithc component, but might as well be part of component kit Move APMWrapper into core so it can be used from anywhere Move manual package install coe to package-manager Gray out window titles when in the background Do not allow multiple onboarding windows at the same time Finalize styling f initial-prefs and initial-packages, make it work (only github package atm) Other nits Change the welcome copy: - Call it easy to extend vs easy to use - Remove the subtitle from the first screen which doesn't really fit - Make the second page emphasize that its created /for/ developers and easy to extend with Javascript. - Explain what the sync engine is rather than saying it's "faster and more extensible" (??) Test Plan: Run tests Reviewers: evan, dillon Reviewed By: evan Maniphest Tasks: T3346 Differential Revision: https://phab.nylas.com/D2079
This commit is contained in:
parent
e9879a5495
commit
76cb33b3f6
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "translate",
|
||||
"name": "N1-Composer-Translate",
|
||||
"version": "0.2.0",
|
||||
"main": "./lib/main",
|
||||
"description": "An example package for N1 that translates drafts into other languages using the Yandex API.",
|
||||
|
|
|
@ -33,7 +33,7 @@ class GithubProfile extends React.Component
|
|||
# Coffeescript at transpile-time. We're actually creating a nested tree of Javascript
|
||||
# objects here that *represent* the DOM we want.
|
||||
<div className="profile">
|
||||
<img className="logo" src="nylas://sidebar-github-profile/assets/github.png"/>
|
||||
<img className="logo" src="nylas://N1-Github-Contact-Card-Section/assets/github.png"/>
|
||||
<a href={@props.profile.html_url}>{@props.profile.login}</a>
|
||||
<div>{repoElements}</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "sidebar-github-profile",
|
||||
"name": "N1-Github-Contact-Card-Section",
|
||||
"version": "0.1.0",
|
||||
"main": "./lib/main",
|
||||
"description": "View github user data in the sidebar!",
|
||||
|
|
|
@ -80,7 +80,11 @@ class ViewOnGithubButton extends React.Component
|
|||
return null unless @state.link
|
||||
<button className="btn btn-toolbar"
|
||||
onClick={@_openLink}
|
||||
data-tooltip={"Visit Thread on GitHub"}><RetinaImg mode={RetinaImg.Mode.ContentIsMask} url="nylas://github/assets/github@2x.png" /></button>
|
||||
data-tooltip={"Visit Thread on GitHub"}>
|
||||
<RetinaImg
|
||||
mode={RetinaImg.Mode.ContentIsMask}
|
||||
url="nylas://N1-Message-View-on-Github/assets/github@2x.png" />
|
||||
</button>
|
||||
|
||||
|
||||
#### Super common N1 Component private methods ####
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "github",
|
||||
"name": "N1-Message-View-on-Github",
|
||||
"version": "0.1.0",
|
||||
"main": "./lib/main",
|
||||
"description": "Github integration in Nylas",
|
||||
|
|
|
@ -13,6 +13,7 @@ class NylasComponentKit
|
|||
@load "Menu", 'menu'
|
||||
@load "DropZone", 'drop-zone'
|
||||
@load "Spinner", 'spinner'
|
||||
@load "Switch", 'switch'
|
||||
@load "Popover", 'popover'
|
||||
@load "Flexbox", 'flexbox'
|
||||
@load "RetinaImg", 'retina-img'
|
||||
|
|
|
@ -144,6 +144,7 @@ class NylasExports
|
|||
@load "LaunchServices", 'launch-services'
|
||||
@load "BufferedProcess", 'buffered-process'
|
||||
@load "BufferedNodeProcess", 'buffered-node-process'
|
||||
@get "APMWrapper", -> require('../src/apm-wrapper')
|
||||
|
||||
# Testing
|
||||
@get "NylasTestUtils", -> require '../spec-nylas/test_utils'
|
||||
|
|
BIN
internal_packages/onboarding/assets/green_check@2x.png
Normal file
BIN
internal_packages/onboarding/assets/green_check@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
internal_packages/onboarding/assets/installing-spinner.gif
Normal file
BIN
internal_packages/onboarding/assets/installing-spinner.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
|
@ -10,7 +10,7 @@ Providers = [
|
|||
settings: []
|
||||
}, {
|
||||
name: 'exchange'
|
||||
displayName: 'Microsoft Exchange / Live'
|
||||
displayName: 'Microsoft Exchange'
|
||||
icon: 'ic-settings-account-eas.png'
|
||||
header_icon: 'setup-icon-provider-exchange.png'
|
||||
color: '#1ea2a3'
|
||||
|
@ -92,7 +92,7 @@ Providers = [
|
|||
}]
|
||||
}, {
|
||||
name: 'imap'
|
||||
displayName: 'Other / Manual setup'
|
||||
displayName: 'IMAP / SMTP Setup'
|
||||
icon: 'ic-settings-account-imap.png'
|
||||
header_icon: 'setup-icon-provider-imap.png'
|
||||
pages: ['Set up your email account','Configure incoming mail','Configure outgoing mail']
|
||||
|
|
|
@ -1,85 +1,81 @@
|
|||
React = require 'react'
|
||||
path = require 'path'
|
||||
{RetinaImg, ConfigPropContainer} = require 'nylas-component-kit'
|
||||
{EdgehillAPI} = require 'nylas-exports'
|
||||
OnboardingActions = require './onboarding-actions'
|
||||
|
||||
InitialPackages = [{
|
||||
'name': 'Templates',
|
||||
'label': 'Templates',
|
||||
'packageName': 'templates',
|
||||
'description': 'Templates let you fill an email with a pre-set body of text and a snumber of fields you can fill quickly to save time.'
|
||||
'icon': 'setup-icon-templates.png'
|
||||
}, {
|
||||
'name': 'Signatures',
|
||||
'label': 'Signatures',
|
||||
'packageName': 'signatures',
|
||||
'description': 'Select from and edit mutiple signatures that N1 will automatically append to your sent messages.'
|
||||
'icon': 'setup-icon-signatures.png'
|
||||
},{
|
||||
'name': 'Github',
|
||||
'label': 'Github',
|
||||
'packageName': 'N1-Github-Contact-Card-Section'
|
||||
'description': 'Adds Github quick actions to many emails, and allows you to see the Github profiles of the people you email.'
|
||||
'icon': 'setup-icon-github.png'
|
||||
}]
|
||||
|
||||
class SlideSwitch extends React.Component
|
||||
@propTypes:
|
||||
active: React.PropTypes.bool.isRequired
|
||||
|
||||
class InstallButton extends React.Component
|
||||
constructor: (@props) ->
|
||||
@state =
|
||||
installed: atom.packages.resolvePackagePath(@props.packageName)?
|
||||
installing: false
|
||||
|
||||
render: =>
|
||||
classnames = "slide-switch"
|
||||
if @props.active
|
||||
classnames += " active"
|
||||
classname = "btn btn-install"
|
||||
classname += " installing" if @state.installing
|
||||
classname += " installed" if @state.installed
|
||||
|
||||
<div className={classnames} onClick={@props.onChange}>
|
||||
<div className="handle"></div>
|
||||
</div>
|
||||
<div className={classname} onClick={@_onInstall}></div>
|
||||
|
||||
_onInstall: =>
|
||||
return false unless @props.packageName
|
||||
{resourcePath} = atom.getLoadSettings()
|
||||
packagePath = path.join(resourcePath, "examples", @props.packageName)
|
||||
@setState(installing: true)
|
||||
atom.packages.installPackageFromPath packagePath, (err) =>
|
||||
@setState({
|
||||
installing: false
|
||||
installed: atom.packages.resolvePackagePath(@props.packageName)?
|
||||
})
|
||||
|
||||
class InitialPackagesList extends React.Component
|
||||
@displayName: "InitialPackagesList"
|
||||
|
||||
render: =>
|
||||
<div>
|
||||
{InitialPackages.map (item) =>
|
||||
<div className="initial-package" key={item.name}>
|
||||
<RetinaImg name={item.icon} mode={RetinaImg.Mode.ContentPreserve} />
|
||||
<div className="name">{item.name}</div>
|
||||
<div className="description">{item.description}</div>
|
||||
<SlideSwitch active={@_isPackageEnabled(item.packageName)} onChange={ => @_togglePackageEnabled(item.packageName)}/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
_isPackageEnabled: (packageName) =>
|
||||
!atom.packages.isPackageDisabled(packageName)
|
||||
|
||||
_togglePackageEnabled: (packageName) =>
|
||||
if atom.packages.isPackageDisabled(packageName)
|
||||
atom.packages.enablePackage(packageName)
|
||||
else
|
||||
atom.packages.disablePackage(packageName)
|
||||
|
||||
componentWillUnmount: =>
|
||||
@listener?.dispose()
|
||||
|
||||
class InitialPackagesPage extends React.Component
|
||||
@displayName: "InitialPackagesPage"
|
||||
|
||||
render: =>
|
||||
<div className="page opaque" style={width:900, height:650}>
|
||||
<div className="quit" onClick={ -> OnboardingActions.closeWindow() }>
|
||||
<RetinaImg name="onboarding-close.png" mode={RetinaImg.Mode.ContentPreserve}/>
|
||||
</div>
|
||||
<div className="back" onClick={@_onPrevPage}>
|
||||
<RetinaImg name="onboarding-back.png" mode={RetinaImg.Mode.ContentPreserve}/>
|
||||
</div>
|
||||
<h1 style={paddingTop: 20}>Welcome to N1</h1>
|
||||
<h4 style={marginBottom: 50}>Explore packages</h4>
|
||||
<p>
|
||||
Packages lie at the heart of N1—you can enable community packages or build<br/>
|
||||
your own to create the perfect workflow. Want to enable a few packages now?
|
||||
|
||||
<h1 style={paddingTop: 60, marginBottom: 20}>Explore packages</h1>
|
||||
<p style={paddingBottom: 20}>
|
||||
Packages lie at the heart of N1 and give it it's powerful features.<br/>
|
||||
Want to enable a few example packages now? They'll be installed to <code>~/.nylas</code>
|
||||
</p>
|
||||
|
||||
<ConfigPropContainer>
|
||||
<InitialPackagesList />
|
||||
</ConfigPropContainer>
|
||||
<button className="btn btn-large" onClick={@_onGetStarted}>Start Using N1</button>
|
||||
<div>
|
||||
{InitialPackages.map (item) =>
|
||||
<div className="initial-package" key={item.label}>
|
||||
<RetinaImg name={item.icon} mode={RetinaImg.Mode.ContentPreserve} />
|
||||
<div className="install-container">
|
||||
<InstallButton packageName={item.packageName} />
|
||||
</div>
|
||||
<div className="name">{item.label}</div>
|
||||
<div className="description">{item.description}</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<button className="btn btn-large btn-emphasis" style={marginTop: 15} onClick={@_onGetStarted}>Start Using N1</button>
|
||||
</div>
|
||||
|
||||
_onPrevPage: =>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
React = require 'react'
|
||||
path = require 'path'
|
||||
fs = require 'fs'
|
||||
_ = require 'underscore'
|
||||
{RetinaImg, Flexbox, ConfigPropContainer} = require 'nylas-component-kit'
|
||||
{EdgehillAPI} = require 'nylas-exports'
|
||||
OnboardingActions = require './onboarding-actions'
|
||||
|
@ -34,10 +35,30 @@ class InitialPreferencesOptions extends React.Component
|
|||
fs.readdir templatesDir, (err, files) =>
|
||||
return unless files and files instanceof Array
|
||||
templates = files.filter (filename) =>
|
||||
path.extname(filename) is '.cson' or path.extname(filename) is '.json'
|
||||
path.extname(filename) is '.cson' or path.extname(filename) is '.json'
|
||||
templates = templates.map (filename) =>
|
||||
path.parse(filename).name
|
||||
@setState(templates: templates)
|
||||
@_setConfigDefaultsForAccount(templates)
|
||||
|
||||
_setConfigDefaultsForAccount: (templates) =>
|
||||
return unless @props.account
|
||||
|
||||
templateWithBasename = (name) =>
|
||||
_.find templates, (t) -> t.indexOf(name) is 0
|
||||
|
||||
if @props.account.provider is 'gmail'
|
||||
@props.config.set('core.workspace.mode', 'list')
|
||||
@props.config.set('core.keymapTemplate', templateWithBasename('Gmail'))
|
||||
else if @props.account.provider is 'eas'
|
||||
@props.config.set('core.workspace.mode', 'split')
|
||||
@props.config.set('core.keymapTemplate', templateWithBasename('Outlook'))
|
||||
else
|
||||
@props.config.set('core.workspace.mode', 'split')
|
||||
if process.platform is 'darwin'
|
||||
@props.config.set('core.keymapTemplate', templateWithBasename('Apple Mail'))
|
||||
else
|
||||
@props.config.set('core.keymapTemplate', templateWithBasename('Outlook'))
|
||||
|
||||
render: =>
|
||||
return false unless @props.config
|
||||
|
@ -60,8 +81,8 @@ class InitialPreferencesOptions extends React.Component
|
|||
<div key="divider" style={marginLeft:20, marginRight:20, borderLeft:'1px solid #ccc'}></div>
|
||||
<div style={flex:1}>
|
||||
<p>
|
||||
We see you're a Gmail user, so N1 is set up to use
|
||||
Gmail keyboard shortcuts. You can also pick another set:
|
||||
We've picked a set of keyboard shortcuts based on your email
|
||||
account and platform. You can also pick another set:
|
||||
</p>
|
||||
<select
|
||||
style={margin:0}
|
||||
|
@ -81,13 +102,10 @@ class InitialPreferencesPage extends React.Component
|
|||
|
||||
render: =>
|
||||
<div className="page opaque" style={width:900, height:620}>
|
||||
<div className="quit" onClick={ -> OnboardingActions.closeWindow() }>
|
||||
<RetinaImg name="onboarding-close.png" mode={RetinaImg.Mode.ContentPreserve}/>
|
||||
</div>
|
||||
<h1 style={paddingTop: 100}>Welcome to N1</h1>
|
||||
<h4 style={marginBottom: 70}>Let's set things up to your liking.</h4>
|
||||
<ConfigPropContainer>
|
||||
<InitialPreferencesOptions />
|
||||
<InitialPreferencesOptions account={@props.pageData.account} />
|
||||
</ConfigPropContainer>
|
||||
<button className="btn btn-large" style={marginBottom:60} onClick={@_onNextPage}>Looks Good!</button>
|
||||
</div>
|
||||
|
|
|
@ -45,17 +45,18 @@ class WelcomePage extends React.Component
|
|||
|
||||
_renderStep0: ->
|
||||
<div className={@_stepClass(0)} key="step-0">
|
||||
<p className="hero-text" style={marginTop: 25}>N1 is a new email app that is fast,<br/>friendly, and easy to use.</p>
|
||||
<RetinaImg className="logo" style={zoom: 0.35, marginTop: 13} url="nylas://onboarding/assets/nylas-pictograph@2x.png" mode={RetinaImg.Mode.ContentIsMask} />
|
||||
<RetinaImg className="logo" style={zoom: 0.20, marginTop: 60} url="nylas://onboarding/assets/nylas-pictograph@2x.png" mode={RetinaImg.Mode.ContentIsMask} />
|
||||
<p className="hero-text" style={marginTop: 30, fontSize: 44}>Say hello to N1.</p>
|
||||
<p className="sub-text" style={marginTop: 0, fontSize: 24}>The next-generation email platform.</p>
|
||||
<div style={fontSize:17, marginTop: 45}>Built with ❤︎ by Nylas</div>
|
||||
<RetinaImg className="icons" style={position: "absolute", left: -45, top: 130} url="nylas://onboarding/assets/shapes-left@2x.png" mode={RetinaImg.Mode.ContentIsMask} />
|
||||
<RetinaImg className="icons" style={position: "absolute", right: -40, top: 130} url="nylas://onboarding/assets/shapes-right@2x.png" mode={RetinaImg.Mode.ContentIsMask} />
|
||||
<p className="sub-text" style={marginTop: 17}>It is the foundation for a highly extensible email experience</p>
|
||||
{@_renderNavBubble(0)}
|
||||
</div>
|
||||
|
||||
_renderStep1: ->
|
||||
<div className={@_stepClass(1)} key="step-1">
|
||||
<p className="hero-text" style={marginTop: 40}>Under the hood, N1 is built for developers.</p>
|
||||
<p className="hero-text" style={marginTop: 40}>Developers welcome.</p>
|
||||
<div className="gear-outer-container"><div className="gear-container">
|
||||
{@_gears()}
|
||||
</div></div>
|
||||
|
@ -64,7 +65,7 @@ class WelcomePage extends React.Component
|
|||
<RetinaImg className="wrench" mode={RetinaImg.Mode.ContentPreserve}
|
||||
url="nylas://onboarding/assets/wrench@2x.png" />
|
||||
|
||||
<p className="sub-text">You can extend it using packages and build your own components.</p>
|
||||
<p className="sub-text">N1 is built with modern web technologies and easy to extend with JavaScript.</p>
|
||||
{@_renderNavBubble(1)}
|
||||
</div>
|
||||
|
||||
|
@ -78,21 +79,21 @@ class WelcomePage extends React.Component
|
|||
|
||||
_renderStep2: ->
|
||||
<div className={@_stepClass(2)} key="step-2">
|
||||
<p className="hero-text" style={marginTop: 40}>N1 is secured and enhanced by the Nylas Sync Engine</p>
|
||||
<p className="hero-text" style={marginTop: 40}>N1 is made possible by the Nylas Sync Engine</p>
|
||||
<div className="cell-wrap">
|
||||
<div className="cell" style={float: "left"}>
|
||||
<RetinaImg mode={RetinaImg.Mode.ContentPreserve}
|
||||
style={paddingTop: 4, paddingBottom: 4}
|
||||
url="nylas://onboarding/assets/cloud@2x.png" />
|
||||
<p>A modern API layer for<br/>email, contacts & calendar</p>
|
||||
<a onClick={=> @_open("https://github.com/nylas/sync-engine")}>more info</a>
|
||||
</div>
|
||||
<div className="cell" style={float: "right"}>
|
||||
<RetinaImg mode={RetinaImg.Mode.ContentPreserve}
|
||||
url="nylas://onboarding/assets/lock@2x.png" />
|
||||
<p>Secured using<br/>bank-grade encryption</p>
|
||||
<a onClick={=> @_open("https://nylas.com/security/")}>more info</a>
|
||||
</div>
|
||||
<div className="cell" style={float: "right"}>
|
||||
<RetinaImg mode={RetinaImg.Mode.ContentPreserve}
|
||||
style={paddingTop: 4, paddingBottom: 4}
|
||||
url="nylas://onboarding/assets/cloud@2x.png" />
|
||||
<p>Synced by Nylas to be<br/>faster and more extensible</p>
|
||||
<a onClick={=> @_open("https://github.com/nylas/sync-engine")}>more info</a>
|
||||
</div>
|
||||
</div>
|
||||
{@_renderNavBubble(2)}
|
||||
</div>
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
h1 {
|
||||
font-weight: 100;
|
||||
font-size: 40pt;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
|
@ -325,15 +326,15 @@
|
|||
text-align: left;
|
||||
border-top: 1px solid rgba(0,0,0,0.05);
|
||||
cursor: default;
|
||||
line-height: 70px;
|
||||
|
||||
img.icon {
|
||||
}
|
||||
.icon-container {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: inline-block;
|
||||
box-sizing: content-box;
|
||||
padding: 10px 25px 12px 25px;
|
||||
padding: 0 25px 0 25px;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
.provider:hover{
|
||||
|
@ -348,13 +349,14 @@
|
|||
margin-bottom:10px;
|
||||
width:550px;
|
||||
padding:15px;
|
||||
border:1px solid #eee;
|
||||
border:1px solid #e1e1e1;
|
||||
text-align:left;
|
||||
cursor: default;
|
||||
img {
|
||||
float:left;
|
||||
margin-right:20px;
|
||||
margin-top:30px;
|
||||
margin-bottom:30px;
|
||||
margin: 20px;
|
||||
margin-left: 0;
|
||||
margin-right: 30px;
|
||||
}
|
||||
.name {
|
||||
font-size:20px;
|
||||
|
@ -363,6 +365,45 @@
|
|||
font-size:14px;
|
||||
max-width:500px;
|
||||
}
|
||||
|
||||
.install-container {
|
||||
width: 90px;
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
.btn-install {
|
||||
display:inline-block;
|
||||
width: 70px;
|
||||
margin: 24px;
|
||||
margin-right: 7px;
|
||||
margin-left: 14px;
|
||||
height: 24px;
|
||||
color: @text-color;
|
||||
transition: width 150ms ease-in-out, color 150ms ease-in-out;
|
||||
}
|
||||
.btn-install:after {
|
||||
content: "Install";
|
||||
}
|
||||
.btn-install.installing {
|
||||
width: 32px;
|
||||
color: transparent;
|
||||
background: url('nylas://onboarding/assets/installing-spinner.gif') center no-repeat;
|
||||
background-size: 18px;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn-install.installed {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-top: 20px;
|
||||
color: transparent;
|
||||
background: url('nylas://onboarding/assets/green_check@2x.png') center no-repeat;
|
||||
background-size: 27px;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn-install.installed:after,
|
||||
.btn-install.installing:after {
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-page {
|
||||
|
@ -479,7 +520,7 @@
|
|||
.nylas-static-wash-bg;
|
||||
|
||||
.hero-text {
|
||||
font-size: 36px;
|
||||
font-size: 34px;
|
||||
line-height: 41px;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ path = require 'path'
|
|||
fs = require 'fs-plus'
|
||||
shell = require 'shell'
|
||||
SettingsActions = require './settings-actions'
|
||||
APMWrapper = require './apm-wrapper'
|
||||
{APMWrapper} = require 'nylas-exports'
|
||||
dialog = require('remote').require('dialog')
|
||||
|
||||
module.exports =
|
||||
|
@ -46,7 +46,7 @@ SettingsPackagesStore = Reflux.createStore
|
|||
@_apm.install pkg, (err) =>
|
||||
if err
|
||||
delete @_installing[pkg.name]
|
||||
@_displayError(err)
|
||||
@_displayMessage("Sorry, an error occurred", err.toString())
|
||||
else
|
||||
if atom.packages.isPackageDisabled(pkg.name)
|
||||
atom.packages.enablePackage(pkg.name)
|
||||
|
@ -57,7 +57,7 @@ SettingsPackagesStore = Reflux.createStore
|
|||
atom.packages.disablePackage(pkg.name)
|
||||
atom.packages.unloadPackage(pkg.name)
|
||||
@_apm.uninstall pkg, (err) =>
|
||||
@_displayError(err) if err
|
||||
@_displayMessage("Sorry, an error occurred", err.toString()) if err
|
||||
@_onPackagesChanged()
|
||||
|
||||
@listenTo SettingsActions.enablePackage, (pkg) ->
|
||||
|
@ -167,34 +167,13 @@ SettingsPackagesStore = Reflux.createStore
|
|||
properties: ['openDirectory']
|
||||
, (filenames) =>
|
||||
return if not filenames or filenames.length is 0
|
||||
packagesDir = path.join(atom.getConfigDirPath(), 'packages')
|
||||
fs.makeTreeSync(packagesDir)
|
||||
|
||||
packageSourceDir = filenames[0]
|
||||
packageName = path.basename(packageSourceDir)
|
||||
packageTargetDir = path.join(packagesDir, packageName)
|
||||
|
||||
if fs.existsSync(packageTargetDir)
|
||||
msg = "A package named '#{packageName}' is already installed in \
|
||||
~/.nylas/packages. Remove it before trying to install another \
|
||||
package of the same name."
|
||||
dialog.showErrorBox('Package already installed', msg)
|
||||
return
|
||||
|
||||
fs.copySync(packageSourceDir, packageTargetDir)
|
||||
|
||||
@_apm.installDependenciesInPackageDirectory packageTargetDir, (err) =>
|
||||
shell.showItemInFolder(packageTargetDir)
|
||||
if err
|
||||
dialog.showErrorBox('Package installation failed', err.toString())
|
||||
return
|
||||
else
|
||||
atom.packages.enablePackage(packageTargetDir)
|
||||
atom.packages.activatePackage(packageName)
|
||||
msg = "#{packageName} has been installed and enabled. No need to \
|
||||
restart! If you don't see the package loaded, check the \
|
||||
console for errors."
|
||||
dialog.showErrorBox('Package installed', msg)
|
||||
atom.packages.installPackageFromPath filenames[0], (err) =>
|
||||
return if err
|
||||
packageName = path.basename(filenames[0])
|
||||
msg = "#{packageName} has been installed and enabled. No need to \
|
||||
restart! If you don't see the package loaded, check the \
|
||||
console for errors."
|
||||
@_displayMessage("Package installed", msg)
|
||||
|
||||
_onCreatePackage: ->
|
||||
packagesDir = path.join(atom.getConfigDirPath(), 'packages')
|
||||
|
@ -210,15 +189,19 @@ SettingsPackagesStore = Reflux.createStore
|
|||
packageName = path.basename(packageDir)
|
||||
|
||||
if not packageDir.startsWith(packagesDir)
|
||||
return dialog.showErrorBox('Invalid package location', 'Sorry, you must \
|
||||
create packages in the dev packages folder.')
|
||||
return @_displayMessage('Invalid package location', 'Sorry, you must
|
||||
create packages in the packages folder.')
|
||||
|
||||
if atom.packages.resolvePackagePath(packageName)
|
||||
return dialog.showErrorBox('Invalid package name', 'Sorry, you must \
|
||||
return @_displayMessage('Invalid package name', 'Sorry, you must
|
||||
give your package a unqiue name.')
|
||||
|
||||
if packageName.indexOf(' ') isnt -1
|
||||
return @_displayMessage('Invalid package name', 'Sorry, package names
|
||||
cannot contain spaces.')
|
||||
|
||||
fs.mkdir packageDir, (err) =>
|
||||
return dialog.showErrorBox('Could not create package', err.toString()) if err
|
||||
return @_displayMessage('Could not create package', err.toString()) if err
|
||||
|
||||
{resourcePath} = atom.getLoadSettings()
|
||||
packageTemplatePath = path.join(resourcePath, 'static', 'package-template')
|
||||
|
@ -264,10 +247,9 @@ SettingsPackagesStore = Reflux.createStore
|
|||
|
||||
pkgs
|
||||
|
||||
_displayError: (err) ->
|
||||
console.error(err)
|
||||
_displayMessage: (title, message) ->
|
||||
chosen = dialog.showMessageBox
|
||||
type: 'warning'
|
||||
message: "Sorry, an error occurred."
|
||||
detail: err.toString()
|
||||
message: title
|
||||
detail: message
|
||||
buttons: ["OK"]
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
_ = require 'underscore'
|
||||
{BufferedProcess} = require 'nylas-exports'
|
||||
Q = require 'q'
|
||||
semver = require 'semver'
|
||||
|
||||
BufferedProcess = require './buffered-process'
|
||||
|
||||
module.exports =
|
||||
class APMWrapper
|
||||
|
|
@ -371,9 +371,9 @@ class Atom extends Model
|
|||
isReleasedVersion: ->
|
||||
not /\w{7}/.test(@getVersion()) # Check if the release is a 7-character SHA prefix
|
||||
|
||||
# Public: Get the directory path to Atom's configuration area.
|
||||
# Public: Get the directory path to N1's configuration area.
|
||||
#
|
||||
# Returns the absolute path to `~/.atom`.
|
||||
# Returns the absolute path to `~/.nylas`.
|
||||
getConfigDirPath: ->
|
||||
@constructor.getConfigDirPath()
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ class Application
|
|||
@windowManager.showMainWindow(loadingMessage)
|
||||
@windowManager.ensureWorkWindow()
|
||||
else
|
||||
@windowManager.newOnboardingWindow()
|
||||
@windowManager.ensureOnboardingWindow(welcome: true)
|
||||
# The onboarding window automatically shows when it's ready
|
||||
|
||||
_resetConfigAndRelaunch: =>
|
||||
|
@ -169,19 +169,13 @@ class Application
|
|||
@config.set('nylas', null)
|
||||
@config.set('edgehill', null)
|
||||
@setDatabasePhase('setup')
|
||||
@windowManager.newOnboardingWindow()
|
||||
@windowManager.ensureOnboardingWindow(welcome: true)
|
||||
|
||||
_deleteDatabase: (callback) ->
|
||||
@deleteFileWithRetry path.join(configDirPath,'edgehill.db'), callback
|
||||
@deleteFileWithRetry path.join(configDirPath,'edgehill.db-wal')
|
||||
@deleteFileWithRetry path.join(configDirPath,'edgehill.db-shm')
|
||||
|
||||
_accountSetupSuccessful: =>
|
||||
@windowManager.showMainWindow()
|
||||
@windowManager.ensureWorkWindow()
|
||||
@windowManager.mainWindow().waitForLoad =>
|
||||
@windowManager.onboardingWindow()?.close()
|
||||
|
||||
databasePhase: ->
|
||||
@_databasePhase
|
||||
|
||||
|
@ -247,7 +241,7 @@ class Application
|
|||
atomWindow ?= @windowManager.focusedWindow()
|
||||
atomWindow?.browserWindow.inspectElement(x, y)
|
||||
|
||||
@on 'application:add-account', => @windowManager.newOnboardingWindow()
|
||||
@on 'application:add-account', => @windowManager.ensureOnboardingWindow()
|
||||
@on 'application:new-message', => @windowManager.sendToMainWindow('new-message')
|
||||
@on 'application:send-feedback', => @windowManager.sendToMainWindow('send-feedback')
|
||||
@on 'application:open-preferences', => @windowManager.sendToMainWindow('open-preferences')
|
||||
|
@ -378,7 +372,9 @@ class Application
|
|||
clipboard.writeText(selectedText, 'selection')
|
||||
|
||||
ipc.on 'account-setup-successful', (event) =>
|
||||
@_accountSetupSuccessful()
|
||||
@windowManager.showMainWindow()
|
||||
@windowManager.ensureWorkWindow()
|
||||
@windowManager.onboardingWindow()?.close()
|
||||
|
||||
ipc.on 'run-in-window', (event, params) =>
|
||||
@_sourceWindows ?= {}
|
||||
|
|
|
@ -132,22 +132,26 @@ class WindowManager
|
|||
|
||||
# Returns a new onboarding window
|
||||
#
|
||||
newOnboardingWindow: ({welcome}={}) ->
|
||||
options =
|
||||
title: "Add an Account"
|
||||
toolbar: false
|
||||
resizable: false
|
||||
hidden: true # The `PageRouter` will center and show on load
|
||||
windowType: 'onboarding'
|
||||
windowProps:
|
||||
page: 'account-choose'
|
||||
uniqueId: 'onboarding'
|
||||
ensureOnboardingWindow: ({welcome}={}) ->
|
||||
existing = @onboardingWindow()
|
||||
if existing
|
||||
existing.focus()
|
||||
else
|
||||
options =
|
||||
title: "Add an Account"
|
||||
toolbar: false
|
||||
resizable: false
|
||||
hidden: true # The `PageRouter` will center and show on load
|
||||
windowType: 'onboarding'
|
||||
windowProps:
|
||||
page: 'account-choose'
|
||||
uniqueId: 'onboarding'
|
||||
|
||||
if welcome
|
||||
options.title = "Welcome to N1"
|
||||
options.page = 'welcome'
|
||||
if welcome
|
||||
options.title = "Welcome to N1"
|
||||
options.windowProps.page = 'welcome'
|
||||
|
||||
win = @newWindow(options)
|
||||
@newWindow(options)
|
||||
|
||||
# Makes a new window appear of a certain `windowType`.
|
||||
#
|
||||
|
|
26
src/components/switch.cjsx
Normal file
26
src/components/switch.cjsx
Normal file
|
@ -0,0 +1,26 @@
|
|||
React = require 'react'
|
||||
|
||||
# Public: A small React component which renders as a horizontal on/off switch.
|
||||
# Provide it with `onChange` and `checked` props just like a checkbox:
|
||||
#
|
||||
# ```
|
||||
# <Switch onChange={@_onToggleChecked} checked={@state.form.isChecked} />
|
||||
# ```
|
||||
#
|
||||
class Switch extends React.Component
|
||||
@propTypes:
|
||||
checked: React.PropTypes.bool.isRequired
|
||||
onChange: React.PropTypes.func.isRequired
|
||||
|
||||
constructor: (@props) ->
|
||||
|
||||
render: =>
|
||||
classnames = "slide-switch"
|
||||
if @props.checked
|
||||
classnames += " active"
|
||||
|
||||
<div className={classnames} onClick={@props.onChange}>
|
||||
<div className="handle"></div>
|
||||
</div>
|
||||
|
||||
module.exports = Switch
|
|
@ -11,6 +11,7 @@ ServiceHub = require 'service-hub'
|
|||
Package = require './package'
|
||||
ThemePackage = require './theme-package'
|
||||
DatabaseStore = require './flux/stores/database-store'
|
||||
APMWrapper = require './apm-wrapper'
|
||||
|
||||
# Extended: Package manager for coordinating the lifecycle of Atom packages.
|
||||
#
|
||||
|
@ -148,7 +149,6 @@ class PackageManager
|
|||
@apmPath = path.join(@resourcePath, 'apm', 'bin', commandName)
|
||||
if not fs.isFileSync(@apmPath)
|
||||
@apmPath = path.join(@resourcePath, 'apm', 'node_modules', 'atom-package-manager', 'bin', commandName)
|
||||
console.log(@apmPath)
|
||||
@apmPath
|
||||
|
||||
# Public: Get the paths being used to look for packages.
|
||||
|
@ -318,6 +318,49 @@ class PackageManager
|
|||
packages.push(metadata)
|
||||
packages
|
||||
|
||||
installPackageFromPath: (packageSourceDir, callback) ->
|
||||
dialog = require('remote').require('dialog')
|
||||
shell = require('shell')
|
||||
|
||||
packagesDir = path.join(atom.getConfigDirPath(), 'packages')
|
||||
packageName = path.basename(packageSourceDir)
|
||||
packageTargetDir = path.join(packagesDir, packageName)
|
||||
|
||||
fs.makeTree packagesDir, (err) =>
|
||||
return callback(err) if err
|
||||
|
||||
fs.exists packageTargetDir, (packageAlreadyExists) =>
|
||||
if packageAlreadyExists
|
||||
message = "A package named '#{packageName}' is already installed
|
||||
in ~/.nylas/packages."
|
||||
dialog.showMessageBox({
|
||||
type: 'warning'
|
||||
buttons: ['OK']
|
||||
title: 'Package already installed'
|
||||
detail: 'Remove it before trying to install another package of the same name.'
|
||||
message: message
|
||||
})
|
||||
callback(new Error(message))
|
||||
return
|
||||
|
||||
fs.copySync(packageSourceDir, packageTargetDir)
|
||||
|
||||
apm = new APMWrapper()
|
||||
apm.installDependenciesInPackageDirectory packageTargetDir, (err) =>
|
||||
shell.showItemInFolder(packageTargetDir)
|
||||
if err
|
||||
dialog.showMessageBox({
|
||||
type: 'warning'
|
||||
buttons: ['OK']
|
||||
title: 'Package installation failed'
|
||||
message: err.toString()
|
||||
})
|
||||
callback(err)
|
||||
else
|
||||
@enablePackage(packageTargetDir)
|
||||
@activatePackage(packageName)
|
||||
callback(null)
|
||||
|
||||
###
|
||||
Section: Private
|
||||
###
|
||||
|
|
1
src/react-remote/react-remote-parent.js
vendored
1
src/react-remote/react-remote-parent.js
vendored
|
@ -297,7 +297,6 @@ var openWindowForComponent = function(Component, options) {
|
|||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
console.log(size[0], size[1]);
|
||||
thinWindow.setContentSize(size[0], size[1]);
|
||||
}
|
||||
};
|
||||
|
|
29
static/components/switch.less
Normal file
29
static/components/switch.less
Normal file
|
@ -0,0 +1,29 @@
|
|||
@import "ui-variables";
|
||||
|
||||
.slide-switch {
|
||||
border-radius: 12px;
|
||||
box-shadow: inset 0 1px 1.5px rgba(0,0,0,0.3);
|
||||
background-color: @gray-light;
|
||||
position: relative;
|
||||
display:inline-block;
|
||||
height:21px;
|
||||
width:40px;
|
||||
|
||||
.handle {
|
||||
border-radius:14px;
|
||||
width:25px;
|
||||
height: 25px;
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: -3px;
|
||||
background-color: @white;
|
||||
box-shadow: 0 0.5px 3px rgba(0,0,0,0.4);
|
||||
transition: all 150ms cubic-bezier(0.22, 0.61, 0.36, 1);
|
||||
}
|
||||
&.active {
|
||||
background-color: @component-active-color;
|
||||
.handle {
|
||||
left: 18px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
@import "components/popover";
|
||||
@import "components/menu";
|
||||
@import "components/switch";
|
||||
@import "components/tokenizing-text-field";
|
||||
@import "components/extra";
|
||||
@import "components/list-tabular";
|
||||
|
|
|
@ -138,6 +138,11 @@ body.is-blurred {
|
|||
background: none;
|
||||
img { opacity:0.5; }
|
||||
}
|
||||
.item-container {
|
||||
.window-title {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue