mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-09-05 20:24:26 +08:00
fix(getting-started): New menu item, sample package for devs
This commit is contained in:
parent
72dd3171ad
commit
f13d0e04a6
11 changed files with 176 additions and 37 deletions
|
@ -58,9 +58,9 @@ Exports =
|
||||||
WorkspaceStore: require '../src/flux/stores/workspace-store'
|
WorkspaceStore: require '../src/flux/stores/workspace-store'
|
||||||
FocusedTagStore: require '../src/flux/stores/focused-tag-store'
|
FocusedTagStore: require '../src/flux/stores/focused-tag-store'
|
||||||
FocusedContentStore: require '../src/flux/stores/focused-content-store'
|
FocusedContentStore: require '../src/flux/stores/focused-content-store'
|
||||||
|
FocusedContactsStore: require '../src/flux/stores/focused-contacts-store'
|
||||||
FileUploadStore: require '../src/flux/stores/file-upload-store'
|
FileUploadStore: require '../src/flux/stores/file-upload-store'
|
||||||
FileDownloadStore: require '../src/flux/stores/file-download-store'
|
FileDownloadStore: require '../src/flux/stores/file-download-store'
|
||||||
FocusedContactsStore: require '../src/flux/stores/focused-contacts-store'
|
|
||||||
UnreadCountStore: require '../src/flux/stores/unread-count-store'
|
UnreadCountStore: require '../src/flux/stores/unread-count-store'
|
||||||
|
|
||||||
# Errors
|
# Errors
|
||||||
|
|
|
@ -25,6 +25,9 @@ SettingsPackagesStore = Reflux.createStore
|
||||||
@listenTo SettingsActions.refreshFeaturedPackages, @_refreshFeatured
|
@listenTo SettingsActions.refreshFeaturedPackages, @_refreshFeatured
|
||||||
@listenTo SettingsActions.refreshInstalledPackages, @_refreshInstalled
|
@listenTo SettingsActions.refreshInstalledPackages, @_refreshInstalled
|
||||||
|
|
||||||
|
atom.commands.add 'body',
|
||||||
|
'application:create-package': => @_onCreatePackage()
|
||||||
|
|
||||||
@listenTo SettingsActions.createPackage, @_onCreatePackage
|
@listenTo SettingsActions.createPackage, @_onCreatePackage
|
||||||
@listenTo SettingsActions.updatePackage, @_onUpdatePackage
|
@listenTo SettingsActions.updatePackage, @_onUpdatePackage
|
||||||
@listenTo SettingsActions.setGlobalSearchValue, @_onGlobalSearchChange
|
@listenTo SettingsActions.setGlobalSearchValue, @_onGlobalSearchChange
|
||||||
|
|
|
@ -54,13 +54,14 @@
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: 'Toggle Hacker Theme', command: 'application:toggle-theme' }
|
{ label: 'Toggle Hacker Theme', command: 'application:toggle-theme' }
|
||||||
{ type: 'separator' }
|
{ type: 'separator' }
|
||||||
{ label: 'Open In Dev Mode...', command: 'application:open-dev' }
|
{ label: 'Relaunch with Debug Flags...', command: 'application:open-dev' }
|
||||||
{ type: 'separator' }
|
{ type: 'separator' }
|
||||||
{ label: 'Open Detailed Logs', command: 'window:open-errorreporter-logs' }
|
{ label: 'Open Detailed Logs', command: 'window:open-errorreporter-logs' }
|
||||||
{ label: 'Ship Detailed Logs to Nylas', command: 'application:ship-logs' }
|
{ label: 'Ship Detailed Logs to Nylas', command: 'application:ship-logs' }
|
||||||
{ type: 'separator' }
|
{ type: 'separator' }
|
||||||
|
{ label: 'Create a Package...', command: 'application:create-package' }
|
||||||
|
{ label: 'Run Package Specs...', command: 'application:run-package-specs' }
|
||||||
{ label: 'Run Nylas Mail Specs', command: 'application:run-all-specs' }
|
{ label: 'Run Nylas Mail Specs', command: 'application:run-all-specs' }
|
||||||
{ label: 'Run Package Specs', command: 'application:run-package-specs' }
|
|
||||||
{ label: 'Toggle Developer Tools', command: 'window:toggle-dev-tools' }
|
{ label: 'Toggle Developer Tools', command: 'window:toggle-dev-tools' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,13 +37,14 @@
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: 'Toggle Hacker Theme', command: 'application:toggle-theme' }
|
{ label: 'Toggle Hacker Theme', command: 'application:toggle-theme' }
|
||||||
{ type: 'separator' }
|
{ type: 'separator' }
|
||||||
{ label: 'Open In &Dev Mode...', command: 'application:open-dev' }
|
{ label: 'Relaunch with &Debug Flags...', command: 'application:open-dev' }
|
||||||
{ type: 'separator' }
|
{ type: 'separator' }
|
||||||
{ label: 'Open Detailed Logs', command: 'window:open-errorreporter-logs' }
|
{ label: 'Open Detailed Logs', command: 'window:open-errorreporter-logs' }
|
||||||
{ label: 'Ship Detailed Logs to Nylas', command: 'application:ship-logs' }
|
{ label: 'Ship Detailed Logs to Nylas', command: 'application:ship-logs' }
|
||||||
{ type: 'separator' }
|
{ type: 'separator' }
|
||||||
|
{ label: 'Create a Package...', command: 'application:create-package' }
|
||||||
|
{ label: 'Run Package &Specs...', command: 'application:run-package-specs' }
|
||||||
{ label: 'Run &Nylas Mail Specs', command: 'application:run-all-specs' }
|
{ label: 'Run &Nylas Mail Specs', command: 'application:run-all-specs' }
|
||||||
{ label: 'Run Package &Specs', command: 'application:run-package-specs' }
|
|
||||||
{ label: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' }
|
{ label: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,13 +39,14 @@
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: 'Toggle Hacker Theme', command: 'application:toggle-theme' }
|
{ label: 'Toggle Hacker Theme', command: 'application:toggle-theme' }
|
||||||
{ type: 'separator' }
|
{ type: 'separator' }
|
||||||
{ label: 'Open In &Dev Mode...', command: 'application:open-dev' }
|
{ label: 'Relaunch with &Debug Flags...', command: 'application:open-dev' }
|
||||||
{ type: 'separator' }
|
{ type: 'separator' }
|
||||||
{ label: 'Open Detailed Logs', command: 'window:open-errorreporter-logs' }
|
{ label: 'Open Detailed Logs', command: 'window:open-errorreporter-logs' }
|
||||||
{ label: 'Ship Detailed Logs to Nylas', command: 'application:ship-logs' }
|
{ label: 'Ship Detailed Logs to Nylas', command: 'application:ship-logs' }
|
||||||
{ type: 'separator' }
|
{ type: 'separator' }
|
||||||
{ label: 'Run &Atom Specs', command: 'application:run-all-specs' }
|
{ label: 'Create a Package...', command: 'application:create-package' }
|
||||||
{ label: 'Run Package &Specs', command: 'application:run-package-specs' }
|
{ label: 'Run Package &Specs...', command: 'application:run-package-specs' }
|
||||||
|
{ label: 'Run &Nylas Mail Specs', command: 'application:run-all-specs' }
|
||||||
{ label: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' }
|
{ label: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
|
|
||||||
## My Package
|
## My Package
|
||||||
|
|
||||||
A sample package for Nylas Mail.
|
A sample package for Nylas Mail. It demonstrates how to add components to the composer's action bar and the message sidebar. Enjoy!
|
|
@ -1,30 +1,19 @@
|
||||||
{Utils, React, ComponentRegistry} = require 'nylas-exports'
|
{ComponentRegistry} = require 'nylas-exports'
|
||||||
{RetinaImg} = require 'nylas-component-kit'
|
|
||||||
|
|
||||||
class MyButton extends React.Component
|
MyComposerButton = require './my-composer-button'
|
||||||
|
MyMessageSidebar = require './my-message-sidebar'
|
||||||
# Adding a `displayName` makes debugging React easier
|
|
||||||
@displayName: 'MyButton'
|
|
||||||
|
|
||||||
render: =>
|
|
||||||
<div className="my-package">
|
|
||||||
<button className="btn btn-toolbar" onClick={@_onClick}>
|
|
||||||
Hello World
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
#
|
|
||||||
_onClick: =>
|
|
||||||
dialog = require('remote').require('dialog')
|
|
||||||
dialog.showErrorBox('Success!', 'Button was clicked.')
|
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
# Activate is called when the package is loaded. If your package previously
|
# Activate is called when the package is loaded. If your package previously
|
||||||
# saved state using `serialize` it is provided.
|
# saved state using `serialize` it is provided.
|
||||||
#
|
#
|
||||||
activate: (@state) ->
|
activate: (@state) ->
|
||||||
ComponentRegistry.register MyButton,
|
ComponentRegistry.register MyComposerButton,
|
||||||
role: 'Composer:ActionButton'
|
role: 'Composer:ActionButton'
|
||||||
|
|
||||||
|
ComponentRegistry.register MyMessageSidebar,
|
||||||
|
role: 'sidebar:focusedContactInfo'
|
||||||
|
|
||||||
# Serialize is called when your package is about to be unmounted.
|
# Serialize is called when your package is about to be unmounted.
|
||||||
# You can return a state object that will be passed back to your package
|
# You can return a state object that will be passed back to your package
|
||||||
# when it is re-activated.
|
# when it is re-activated.
|
||||||
|
@ -37,4 +26,5 @@ module.exports =
|
||||||
# subscribing to events, release them here.
|
# subscribing to events, release them here.
|
||||||
#
|
#
|
||||||
deactivate: ->
|
deactivate: ->
|
||||||
ComponentRegistry.unregister(MyButton)
|
ComponentRegistry.unregister(MyComposerButton)
|
||||||
|
ComponentRegistry.unregister(MyMessageSidebar)
|
||||||
|
|
43
static/package-template/lib/my-composer-button.cjsx
Normal file
43
static/package-template/lib/my-composer-button.cjsx
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
{Utils, DraftStore, React} = require 'nylas-exports'
|
||||||
|
{RetinaImg} = require 'nylas-component-kit'
|
||||||
|
|
||||||
|
class MyComposerButton extends React.Component
|
||||||
|
|
||||||
|
# Note: You should assign a new displayName to avoid naming
|
||||||
|
# conflicts when injecting your item
|
||||||
|
@displayName: 'MyComposerButton'
|
||||||
|
|
||||||
|
# When you register as a composer button, you receive a
|
||||||
|
# reference to the draft, and you can look it up to perform
|
||||||
|
# actions and retrieve data.
|
||||||
|
@propTypes:
|
||||||
|
draftLocalId: React.PropTypes.string.isRequired
|
||||||
|
|
||||||
|
render: =>
|
||||||
|
<div className="my-package">
|
||||||
|
<button className="btn btn-toolbar" onClick={ => @_onClick()} ref="button">
|
||||||
|
Hello World
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
_onClick: =>
|
||||||
|
# To retrieve information about the draft, we fetch the current editing
|
||||||
|
# session from the draft store. We can access attributes of the draft
|
||||||
|
# and add changes to the session which will be appear immediately.
|
||||||
|
DraftStore.sessionForLocalId(@props.draftLocalId).then (session) =>
|
||||||
|
newSubject = "#{session.draft().subject} - It Worked!"
|
||||||
|
|
||||||
|
dialog = @_getDialog()
|
||||||
|
dialog.showMessageBox
|
||||||
|
title: 'Here we go...'
|
||||||
|
detail: "Adjusting the subject line To `#{newSubject}`"
|
||||||
|
buttons: ['OK']
|
||||||
|
type: 'info'
|
||||||
|
|
||||||
|
session.changes.add({subject: newSubject})
|
||||||
|
|
||||||
|
_getDialog: =>
|
||||||
|
require('remote').require('dialog')
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = MyComposerButton
|
68
static/package-template/lib/my-message-sidebar.cjsx
Normal file
68
static/package-template/lib/my-message-sidebar.cjsx
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
{Utils,
|
||||||
|
React,
|
||||||
|
FocusedContactsStore} = require 'nylas-exports'
|
||||||
|
{RetinaImg} = require 'nylas-component-kit'
|
||||||
|
|
||||||
|
class MyMessageSidebar extends React.Component
|
||||||
|
@displayName: 'MyMessageSidebar'
|
||||||
|
|
||||||
|
# Providing container styles tells the app how to constrain
|
||||||
|
# the column your component is being rendered in. The min and
|
||||||
|
# max size of the column are chosen automatically based on
|
||||||
|
# these values.
|
||||||
|
@containerStyles:
|
||||||
|
order: 1
|
||||||
|
maxWidth: 300
|
||||||
|
minWidth: 200
|
||||||
|
flexShrink: 0
|
||||||
|
|
||||||
|
# This sidebar component listens to the FocusedContactStore,
|
||||||
|
# which gives us access to the Contact object of the currently
|
||||||
|
# selected person in the conversation. If you wanted to take
|
||||||
|
# the contact and fetch your own data, you'd want to create
|
||||||
|
# your own store, so the flow of data would be:
|
||||||
|
#
|
||||||
|
# FocusedContactStore => Your Store => Your Component
|
||||||
|
#
|
||||||
|
constructor: (@props) ->
|
||||||
|
@state = @_getStateFromStores()
|
||||||
|
|
||||||
|
componentDidMount: =>
|
||||||
|
@unsubscribe = FocusedContactsStore.listen(@_onChange)
|
||||||
|
|
||||||
|
componentWillUnmount: =>
|
||||||
|
@unsubscribe()
|
||||||
|
|
||||||
|
render: =>
|
||||||
|
if @state.contact
|
||||||
|
content = @_renderContent()
|
||||||
|
else
|
||||||
|
content = @_renderPlaceholder()
|
||||||
|
|
||||||
|
<div className="my-message-sidebar">
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
_renderContent: =>
|
||||||
|
# Want to include images or other static assets in your components?
|
||||||
|
# Reference them using the nylas:// URL scheme:
|
||||||
|
#
|
||||||
|
# <RetinaImg
|
||||||
|
# url="nylas://<<package.name>>/assets/checkmark_template@2x.png"
|
||||||
|
# mode={RetinaImg.Mode.ContentIsMask}/>
|
||||||
|
#
|
||||||
|
<div className="header">
|
||||||
|
<h1>Hi there {@state.contact.name}!</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
_renderPlaceholder: =>
|
||||||
|
<div> No Data Available </div>
|
||||||
|
|
||||||
|
_onChange: =>
|
||||||
|
@setState(@_getStateFromStores())
|
||||||
|
|
||||||
|
_getStateFromStores: =>
|
||||||
|
contact: FocusedContactsStore.focusedContact()
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = MyMessageSidebar
|
|
@ -1,12 +1,19 @@
|
||||||
describe "AccountSidebarStore", ->
|
{ComponentRegistry} = require 'nylas-exports'
|
||||||
xit "should update it's selected ID when the focusTag action fires", ->
|
{activate, deactivate} = require '../lib/main'
|
||||||
true
|
|
||||||
|
|
||||||
xit "should update when the DatabaseStore emits changes to tags", ->
|
MyMessageSidebar = require '../lib/my-message-sidebar'
|
||||||
true
|
MyComposerButton = require '../lib/my-composer-button'
|
||||||
|
|
||||||
xit "should update when the NamespaceStore emits", ->
|
describe "activate", ->
|
||||||
true
|
it "should register the composer button and sidebar", ->
|
||||||
|
spyOn(ComponentRegistry, 'register')
|
||||||
|
activate()
|
||||||
|
expect(ComponentRegistry.register).toHaveBeenCalledWith(MyComposerButton, {role: 'Composer:ActionButton'})
|
||||||
|
expect(ComponentRegistry.register).toHaveBeenCalledWith(MyMessageSidebar, {role: 'sidebar:focusedContactInfo'})
|
||||||
|
|
||||||
xit "should provide an array of sections to the sidebar view", ->
|
describe "deactivate", ->
|
||||||
true
|
it "should unregister the composer button and sidebar", ->
|
||||||
|
spyOn(ComponentRegistry, 'unregister')
|
||||||
|
deactivate()
|
||||||
|
expect(ComponentRegistry.unregister).toHaveBeenCalledWith(MyComposerButton)
|
||||||
|
expect(ComponentRegistry.unregister).toHaveBeenCalledWith(MyMessageSidebar)
|
||||||
|
|
25
static/package-template/spec/my-composer-button-spec.cjsx
Normal file
25
static/package-template/spec/my-composer-button-spec.cjsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{React} = require 'nylas-exports'
|
||||||
|
ReactTestUtils = React.addons.TestUtils
|
||||||
|
|
||||||
|
MyComposerButton = require '../lib/my-composer-button'
|
||||||
|
|
||||||
|
dialogStub =
|
||||||
|
showMessageBox: jasmine.createSpy('showMessageBox')
|
||||||
|
|
||||||
|
describe "MyComposerButton", ->
|
||||||
|
beforeEach ->
|
||||||
|
@component = ReactTestUtils.renderIntoDocument(
|
||||||
|
<MyComposerButton draftLocalId="test" />
|
||||||
|
)
|
||||||
|
|
||||||
|
it "should render into the page", ->
|
||||||
|
expect(@component).toBeDefined()
|
||||||
|
|
||||||
|
it "should have a displayName", ->
|
||||||
|
expect(MyComposerButton.displayName).toBe('MyComposerButton')
|
||||||
|
|
||||||
|
it "should show a dialog box when clicked", ->
|
||||||
|
spyOn(@component, '_onClick')
|
||||||
|
buttonNode = React.findDOMNode(@component.refs.button)
|
||||||
|
ReactTestUtils.Simulate.click(buttonNode)
|
||||||
|
expect(@component._onClick).toHaveBeenCalled()
|
Loading…
Add table
Reference in a new issue