From 53eb7f58aa1e5ee1b0ddae7a2dfa334f193ded79 Mon Sep 17 00:00:00 2001 From: Evan Morikawa Date: Fri, 17 Jun 2016 10:03:46 -0400 Subject: [PATCH] fix(github): fix view on Github buttton Error due to action import. Convert to ES6 --- .../message-view-on-github/lib/main.jsx | 4 - .../lib/view-on-github-button.cjsx | 138 --------------- .../lib/view-on-github-button.jsx | 166 ++++++++++++++++++ 3 files changed, 166 insertions(+), 142 deletions(-) delete mode 100644 internal_packages/message-view-on-github/lib/view-on-github-button.cjsx create mode 100644 internal_packages/message-view-on-github/lib/view-on-github-button.jsx diff --git a/internal_packages/message-view-on-github/lib/main.jsx b/internal_packages/message-view-on-github/lib/main.jsx index a5d14f60f..aa52b52a8 100644 --- a/internal_packages/message-view-on-github/lib/main.jsx +++ b/internal_packages/message-view-on-github/lib/main.jsx @@ -52,10 +52,6 @@ export function activate() { }); } -export function serialize() { - return {}; -} - export function deactivate() { ComponentRegistry.unregister(ViewOnGithubButton); } diff --git a/internal_packages/message-view-on-github/lib/view-on-github-button.cjsx b/internal_packages/message-view-on-github/lib/view-on-github-button.cjsx deleted file mode 100644 index e42a59a22..000000000 --- a/internal_packages/message-view-on-github/lib/view-on-github-button.cjsx +++ /dev/null @@ -1,138 +0,0 @@ -{shell} = require 'electron' -GithubStore = require('./github-store').default -{React, Actions} = require 'nylas-exports' -{RetinaImg, KeyCommandsRegion} = require 'nylas-component-kit' - -### -The `ViewOnGithubButton` displays a button whenever there's a relevant -Github asset to link to. - -When creating this React component the first consideration was when & -where we'd be rendered. The next consideration was what data we need to -display. - -Unlike a traditional React application, N1 components have very few -guarantees on who will render them and where they will be rendered. In our -`lib/main.cjsx` file we registered this component with our -{ComponentRegistry} for the `"ThreadActionsToolbarButton"` role. That means that -whenever the "ThreadActionsToolbarButton" region gets rendered, we'll render -everything registered with that area. Other buttons, such as "Archive" and -the "Change Label" button are reigstered with that role, so we should -expect ourselves to showup alongside them. - -The only data we need is a single relevant to Github. If we have one, -we'll open it up in a browser. If we don't have one, we'll hide the -component. - -Getting that url takes a bit of message parsing. We need to retrieve a -message body then implement some kind of regex to find and parse out that -link. - -We could have put all of that logic in this React Component, but that's -not what React components should be doing. In N1 a component's only job is -to display known data and be the first responders to user interaction. - -We instead create a {GithubStore} to handle the fetching and preparation -of the data. See that file's documentation for more on how that works. - -As far as this component is concerned, there will be an entity called -`GitHubStore` that will expose the correct `link`. That store will then -notify us when the `link` changes so we can update our state. - -Once we know our `link` our `render` method can simply be a description of -how we want to display that link. In this case we're going to make a -simple button with a GitHub logo in it. - -We'll also display nothing if there is no link. -### -class ViewOnGithubButton extends React.Component - @displayName: "ViewOnGithubButton" - @containerRequired: false - - @propTypes: - items: React.PropTypes.array - - #### React methods #### - # The following methods are React methods that we override. See {React} - # documentation for more info - - constructor: (@props) -> - @state = @_getStateFromStores() - - # When components mount, it's very common to have them listen to a - # `Store`. Since most of our React Components in N1 are registered into - # {ComponentRegistry} regions instead of manually rendered top-down much - # of our data is side-loaded from stores instead of passed in as props. - componentDidMount: -> - # The `listen` method of {NylasStore}s (which {GithubStore} - # subclasses) returns an "unlistener" function. When the unlistener is - # invoked (as it is in `componentWillUnmount`) the listener references - # are cleaned up. Every time the `GithubStore` calls its `trigger` - # method, the `_onStoreChanged` callback will be fired. - @_unlisten = GithubStore.listen(@_onStoreChanged) - - componentWillUnmount: -> - @_unlisten?() - - _keymapHandlers: -> - 'github:open': @_openLink - - render: -> - return null unless @props.items.length is 1 - return null unless @state.link - - - - - - #### Super common N1 Component private methods #### - - # An extremely common pattern for all N1 components are the methods - # `onStoreChanged` and `getStateFromStores`. - # - # Most N1 components listen to some source of data, which is usally a - # Store. When the store notifies that something has changed, we need to - # fetch the fresh data and updated our state. - # - # Note that when a Store updates it does not let us know what changed. - # This is intentional! This forces us to fresh the full latest state - # from the stores in a more declarative, easy-to-follow way. There are a - # couple rare exceptions that are only used for performance - # optimizations. - - # Note that we bind this method to the class instance's `this`. Any - # method used as a callback must be bound. In Coffeescript we use the - # fat arrow (`=>`) - _onStoreChanged: => - @setState(@_getStateFromStores()) - - # getStateFromStores fetches the data the view needs from the - # appropriate data source (our GithubStore). We return a basic object - # that can be passed directly into `setState`. - _getStateFromStores: -> - return {link: GithubStore.link()} - - - #### Other utility "private" methods #### - - # This responds to user interaction. Since it's a callback we have to - # bind it to the instances's `this` (Coffeescript fat arrow `=>`) - # - # In the case of this component we use the Electron `shell` module to - # request the computer to open the default browser. - # - # In other very common cases, user interaction handlers may fire an - # `Action` across the system for other Stores to respond to. They may - # also queue a {Task} to eventually perform a mutating API POST or PUT - # request. - _openLink: => - Actions.recordUserEvent("Github Thread Opened", {pageUrl: @state.link}) - shell.openExternal(@state.link) if @state.link - -module.exports = ViewOnGithubButton diff --git a/internal_packages/message-view-on-github/lib/view-on-github-button.jsx b/internal_packages/message-view-on-github/lib/view-on-github-button.jsx new file mode 100644 index 000000000..17f51116e --- /dev/null +++ b/internal_packages/message-view-on-github/lib/view-on-github-button.jsx @@ -0,0 +1,166 @@ +import {shell} from 'electron' +import GithubStore from './github-store' +import {Actions, React} from 'nylas-exports' +import {RetinaImg, KeyCommandsRegion} from 'nylas-component-kit' + +/** +The `ViewOnGithubButton` displays a button whenever there's a relevant +Github asset to link to. + +When creating this React component the first consideration was when & +where we'd be rendered. The next consideration was what data we need to +display. + +Unlike a traditional React application, N1 components have very few +guarantees on who will render them and where they will be rendered. In our +`lib/main.cjsx` file we registered this component with our +{ComponentRegistry} for the `"ThreadActionsToolbarButton"` role. That means that +whenever the "ThreadActionsToolbarButton" region gets rendered, we'll render +everything registered with that area. Other buttons, such as "Archive" and +the "Change Label" button are reigstered with that role, so we should +expect ourselves to showup alongside them. + +The only data we need is a single relevant to Github. If we have one, +we'll open it up in a browser. If we don't have one, we'll hide the +component. + +Getting that url takes a bit of message parsing. We need to retrieve a +message body then implement some kind of regex to find and parse out that +link. + +We could have put all of that logic in this React Component, but that's +not what React components should be doing. In N1 a component's only job is +to display known data and be the first responders to user interaction. + +We instead create a {GithubStore} to handle the fetching and preparation +of the data. See that file's documentation for more on how that works. + +As far as this component is concerned, there will be an entity called +`GitHubStore` that will expose the correct `link`. That store will then +notify us when the `link` changes so we can update our state. + +Once we know our `link` our `render` method can simply be a description of +how we want to display that link. In this case we're going to make a +simple button with a GitHub logo in it. + +We'll also display nothing if there is no link. +*/ +export default class ViewOnGithubButton extends React.Component { + static displayName = "ViewOnGithubButton" + + static containerRequired = false + + static propTypes = { + items: React.PropTypes.array, + } + + /** ** React methods **** + * The following methods are React methods that we override. See {React} + * documentation for more info + */ + + constructor(props) { + super(props) + this.state = this._getStateFromStores() + } + + /* + * When components mount, it's very common to have them listen to a + * `Store`. Since most of our React Components in N1 are registered into + * {ComponentRegistry} regions instead of manually rendered top-down much + * of our data is side-loaded from stores instead of passed in as props. + */ + componentDidMount() { + /* + * The `listen` method of {NylasStore}s (which {GithubStore} + * subclasses) returns an "unlistener" function. When the unlistener is + * invoked (as it is in `componentWillUnmount`) the listener references + * are cleaned up. Every time the `GithubStore` calls its `trigger` + * method, the `_onStoreChanged` callback will be fired. + */ + this._unlisten = GithubStore.listen(this._onStoreChanged) + } + + componentWillUnmount() { + this._unlisten() + } + + _keymapHandlers() { + return { + 'github:open': this._openLink, + } + } + + /** ** Super common N1 Component private methods **** + /* + * An extremely common pattern for all N1 components are the methods + * `onStoreChanged` and `getStateFromStores`. + * + * Most N1 components listen to some source of data, which is usally a + * Store. When the store notifies that something has changed, we need to + * fetch the fresh data and updated our state. + * + * Note that when a Store updates it does not let us know what changed. + * This is intentional! This forces us to fresh the full latest state + * from the stores in a more declarative, easy-to-follow way. There are a + * couple rare exceptions that are only used for performance + * optimizations. + + * Note that we bind this method to the class instance's `this`. Any + * method used as a callback must be bound. In Coffeescript we use the + * fat arrow (`=>`) + */ + _onStoreChanged = () => { + this.setState(this._getStateFromStores()) + } + + /* + * getStateFromStores fetches the data the view needs from the + * appropriate data source (our GithubStore). We return a basic object + * that can be passed directly into `setState`. + */ + _getStateFromStores() { + return { + link: GithubStore.link(), + } + } + + /** ** Other utility "private" methods **** + /* + * This responds to user interaction. Since it's a callback we have to + * bind it to the instances's `this` (Coffeescript fat arrow `=>`) + * + * In the case of this component we use the Electron `shell` module to + * request the computer to open the default browser. + * + * In other very common cases, user interaction handlers may fire an + * `Action` across the system for other Stores to respond to. They may + * also queue a {Task} to eventually perform a mutating API POST or PUT + * request. + */ + _openLink = () => { + Actions.recordUserEvent("Github Thread Opened", {pageUrl: this.state.link}) + if (this.state.link) { + shell.openExternal(this.state.link) + } + } + + render() { + if (this.props.items.length !== 1) { return false } + if (!this.state.link) { return false } + return ( + + + + ) + } +}