mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-30 11:59:02 +08:00
fix(github): fix view on Github buttton
Error due to action import. Convert to ES6
This commit is contained in:
parent
54a74e317d
commit
53eb7f58aa
3 changed files with 166 additions and 142 deletions
|
@ -52,10 +52,6 @@ export function activate() {
|
|||
});
|
||||
}
|
||||
|
||||
export function serialize() {
|
||||
return {};
|
||||
}
|
||||
|
||||
export function deactivate() {
|
||||
ComponentRegistry.unregister(ViewOnGithubButton);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
<KeyCommandsRegion globalHandlers={@_keymapHandlers()}>
|
||||
<button className="btn btn-toolbar btn-view-on-github"
|
||||
onClick={@_openLink}
|
||||
title={"Visit Thread on GitHub"}>
|
||||
<RetinaImg
|
||||
mode={RetinaImg.Mode.ContentIsMask}
|
||||
url="nylas://message-view-on-github/assets/github@2x.png" />
|
||||
</button>
|
||||
</KeyCommandsRegion>
|
||||
|
||||
|
||||
#### 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
|
|
@ -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 (
|
||||
<KeyCommandsRegion globalHandlers={this._keymapHandlers()}>
|
||||
<button
|
||||
className="btn btn-toolbar btn-view-on-github"
|
||||
onClick={this._openLink}
|
||||
title={"Visit Thread on GitHub"}
|
||||
>
|
||||
<RetinaImg
|
||||
mode={RetinaImg.Mode.ContentIsMask}
|
||||
url="nylas://message-view-on-github/assets/github@2x.png"
|
||||
/>
|
||||
</button>
|
||||
</KeyCommandsRegion>
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue