From f13d0e04a689828584d2f332ff8b428e660c3b2d Mon Sep 17 00:00:00 2001 From: Ben Gotow Date: Mon, 6 Jul 2015 14:23:51 -0400 Subject: [PATCH] fix(getting-started): New menu item, sample package for devs --- exports/nylas-exports.coffee | 2 +- .../lib/settings-packages-store.coffee | 3 + menus/darwin.cson | 5 +- menus/linux.cson | 5 +- menus/win32.cson | 7 +- static/package-template/README.md | 2 +- static/package-template/lib/main.cjsx | 28 +++----- .../lib/my-composer-button.cjsx | 43 ++++++++++++ .../lib/my-message-sidebar.cjsx | 68 +++++++++++++++++++ static/package-template/spec/main-spec.coffee | 25 ++++--- .../spec/my-composer-button-spec.cjsx | 25 +++++++ 11 files changed, 176 insertions(+), 37 deletions(-) create mode 100644 static/package-template/lib/my-composer-button.cjsx create mode 100644 static/package-template/lib/my-message-sidebar.cjsx create mode 100644 static/package-template/spec/my-composer-button-spec.cjsx diff --git a/exports/nylas-exports.coffee b/exports/nylas-exports.coffee index 4d063b8be..97ef50c66 100644 --- a/exports/nylas-exports.coffee +++ b/exports/nylas-exports.coffee @@ -58,9 +58,9 @@ Exports = WorkspaceStore: require '../src/flux/stores/workspace-store' FocusedTagStore: require '../src/flux/stores/focused-tag-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' FileDownloadStore: require '../src/flux/stores/file-download-store' - FocusedContactsStore: require '../src/flux/stores/focused-contacts-store' UnreadCountStore: require '../src/flux/stores/unread-count-store' # Errors diff --git a/internal_packages/settings/lib/settings-packages-store.coffee b/internal_packages/settings/lib/settings-packages-store.coffee index dec213b4f..986120695 100644 --- a/internal_packages/settings/lib/settings-packages-store.coffee +++ b/internal_packages/settings/lib/settings-packages-store.coffee @@ -25,6 +25,9 @@ SettingsPackagesStore = Reflux.createStore @listenTo SettingsActions.refreshFeaturedPackages, @_refreshFeatured @listenTo SettingsActions.refreshInstalledPackages, @_refreshInstalled + atom.commands.add 'body', + 'application:create-package': => @_onCreatePackage() + @listenTo SettingsActions.createPackage, @_onCreatePackage @listenTo SettingsActions.updatePackage, @_onUpdatePackage @listenTo SettingsActions.setGlobalSearchValue, @_onGlobalSearchChange diff --git a/menus/darwin.cson b/menus/darwin.cson index a7cbd6d06..e406d89f7 100644 --- a/menus/darwin.cson +++ b/menus/darwin.cson @@ -54,13 +54,14 @@ submenu: [ { label: 'Toggle Hacker Theme', command: 'application:toggle-theme' } { type: 'separator' } - { label: 'Open In Dev Mode...', command: 'application:open-dev' } + { label: 'Relaunch with Debug Flags...', command: 'application:open-dev' } { type: 'separator' } { label: 'Open Detailed Logs', command: 'window:open-errorreporter-logs' } { label: 'Ship Detailed Logs to Nylas', command: 'application:ship-logs' } { 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 Package Specs', command: 'application:run-package-specs' } { label: 'Toggle Developer Tools', command: 'window:toggle-dev-tools' } ] } diff --git a/menus/linux.cson b/menus/linux.cson index ce6cf61f4..531b38616 100644 --- a/menus/linux.cson +++ b/menus/linux.cson @@ -37,13 +37,14 @@ submenu: [ { label: 'Toggle Hacker Theme', command: 'application:toggle-theme' } { type: 'separator' } - { label: 'Open In &Dev Mode...', command: 'application:open-dev' } + { label: 'Relaunch with &Debug Flags...', command: 'application:open-dev' } { type: 'separator' } { label: 'Open Detailed Logs', command: 'window:open-errorreporter-logs' } { label: 'Ship Detailed Logs to Nylas', command: 'application:ship-logs' } { 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 Package &Specs', command: 'application:run-package-specs' } { label: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' } ] } diff --git a/menus/win32.cson b/menus/win32.cson index debc3ddb6..417a00473 100644 --- a/menus/win32.cson +++ b/menus/win32.cson @@ -39,13 +39,14 @@ submenu: [ { label: 'Toggle Hacker Theme', command: 'application:toggle-theme' } { type: 'separator' } - { label: 'Open In &Dev Mode...', command: 'application:open-dev' } + { label: 'Relaunch with &Debug Flags...', command: 'application:open-dev' } { type: 'separator' } { label: 'Open Detailed Logs', command: 'window:open-errorreporter-logs' } { label: 'Ship Detailed Logs to Nylas', command: 'application:ship-logs' } { type: 'separator' } - { label: 'Run &Atom Specs', command: 'application:run-all-specs' } - { label: 'Run Package &Specs', command: 'application:run-package-specs' } + { 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: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' } ] } diff --git a/static/package-template/README.md b/static/package-template/README.md index edf460b46..33efc5383 100644 --- a/static/package-template/README.md +++ b/static/package-template/README.md @@ -1,4 +1,4 @@ ## 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! \ No newline at end of file diff --git a/static/package-template/lib/main.cjsx b/static/package-template/lib/main.cjsx index 5d2fdf957..28753af07 100644 --- a/static/package-template/lib/main.cjsx +++ b/static/package-template/lib/main.cjsx @@ -1,30 +1,19 @@ -{Utils, React, ComponentRegistry} = require 'nylas-exports' -{RetinaImg} = require 'nylas-component-kit' +{ComponentRegistry} = require 'nylas-exports' -class MyButton extends React.Component - - # Adding a `displayName` makes debugging React easier - @displayName: 'MyButton' - - render: => -
- -
- # - _onClick: => - dialog = require('remote').require('dialog') - dialog.showErrorBox('Success!', 'Button was clicked.') +MyComposerButton = require './my-composer-button' +MyMessageSidebar = require './my-message-sidebar' module.exports = # Activate is called when the package is loaded. If your package previously # saved state using `serialize` it is provided. # activate: (@state) -> - ComponentRegistry.register MyButton, + ComponentRegistry.register MyComposerButton, role: 'Composer:ActionButton' + ComponentRegistry.register MyMessageSidebar, + role: 'sidebar:focusedContactInfo' + # 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 # when it is re-activated. @@ -37,4 +26,5 @@ module.exports = # subscribing to events, release them here. # deactivate: -> - ComponentRegistry.unregister(MyButton) + ComponentRegistry.unregister(MyComposerButton) + ComponentRegistry.unregister(MyMessageSidebar) diff --git a/static/package-template/lib/my-composer-button.cjsx b/static/package-template/lib/my-composer-button.cjsx new file mode 100644 index 000000000..573748cf6 --- /dev/null +++ b/static/package-template/lib/my-composer-button.cjsx @@ -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: => +
+ +
+ + _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 \ No newline at end of file diff --git a/static/package-template/lib/my-message-sidebar.cjsx b/static/package-template/lib/my-message-sidebar.cjsx new file mode 100644 index 000000000..2178a61b5 --- /dev/null +++ b/static/package-template/lib/my-message-sidebar.cjsx @@ -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() + +
+ {content} +
+ + _renderContent: => + # Want to include images or other static assets in your components? + # Reference them using the nylas:// URL scheme: + # + # + # +
+

Hi there {@state.contact.name}!

+
+ + _renderPlaceholder: => +
No Data Available
+ + _onChange: => + @setState(@_getStateFromStores()) + + _getStateFromStores: => + contact: FocusedContactsStore.focusedContact() + + +module.exports = MyMessageSidebar diff --git a/static/package-template/spec/main-spec.coffee b/static/package-template/spec/main-spec.coffee index 6636dee73..169134c06 100644 --- a/static/package-template/spec/main-spec.coffee +++ b/static/package-template/spec/main-spec.coffee @@ -1,12 +1,19 @@ -describe "AccountSidebarStore", -> - xit "should update it's selected ID when the focusTag action fires", -> - true +{ComponentRegistry} = require 'nylas-exports' +{activate, deactivate} = require '../lib/main' - xit "should update when the DatabaseStore emits changes to tags", -> - true +MyMessageSidebar = require '../lib/my-message-sidebar' +MyComposerButton = require '../lib/my-composer-button' - xit "should update when the NamespaceStore emits", -> - true +describe "activate", -> + 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", -> - true +describe "deactivate", -> + it "should unregister the composer button and sidebar", -> + spyOn(ComponentRegistry, 'unregister') + deactivate() + expect(ComponentRegistry.unregister).toHaveBeenCalledWith(MyComposerButton) + expect(ComponentRegistry.unregister).toHaveBeenCalledWith(MyMessageSidebar) diff --git a/static/package-template/spec/my-composer-button-spec.cjsx b/static/package-template/spec/my-composer-button-spec.cjsx new file mode 100644 index 000000000..76f4f68ac --- /dev/null +++ b/static/package-template/spec/my-composer-button-spec.cjsx @@ -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( + + ) + + 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() \ No newline at end of file