Skip to content

Commit 6b66893

Browse files
committed
Merge branch 'dev'
* dev: fix typo shareWith -> shareVia bump up version to 0.4.6 Finderを開き直したら内容初期化 ARTICLE_SHARE イベント追跡 debug - 新規投稿が不可能 enable copy (finder) modify dock.menu Folderの位置修正の保存 add hot key:Navigate up(Ctrl + P) for CodeEditor switch API URL submit user name Url share done Conflicts: package.json
2 parents f3732c7 + 529c27a commit 6b66893

16 files changed

Lines changed: 372 additions & 310 deletions

File tree

atom-lib/menu-template.js

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const electron = require('electron')
22
const BrowserWindow = electron.BrowserWindow
3+
const shell = electron.shell
34

45
module.exports = [
56
{
@@ -90,7 +91,17 @@ module.exports = [
9091
click: function () {
9192
BrowserWindow.getFocusedWindow().reload()
9293
}
93-
}
94+
},
95+
// {
96+
// label: 'Toggle Developer Tools',
97+
// accelerator: (function () {
98+
// if (process.platform === 'darwin') return 'Alt+Command+I'
99+
// else return 'Ctrl+Shift+I'
100+
// })(),
101+
// click: function (item, focusedWindow) {
102+
// if (focusedWindow) BrowserWindow.getFocusedWindow().toggleDevTools()
103+
// }
104+
// }
94105
]
95106
},
96107
{
@@ -117,6 +128,24 @@ module.exports = [
117128
},
118129
{
119130
label: 'Help',
120-
submenu: []
131+
role: 'help',
132+
submenu: [
133+
{
134+
label: 'Boost official site',
135+
click: function () { shell.openExternal('https://b00st.io/') }
136+
},
137+
{
138+
label: 'Tutorial page',
139+
click: function () { shell.openExternal('https://b00st.io/tutorial.html') }
140+
},
141+
{
142+
label: 'Discussions',
143+
click: function () { shell.openExternal('https://github.com/BoostIO/boost-app-discussions/issues') }
144+
},
145+
{
146+
label: 'Changelog',
147+
click: function () { shell.openExternal('https://github.com/BoostIO/boost-releases/blob/master/changelog.md') }
148+
}
149+
]
121150
}
122151
]

browser/finder/index.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,20 @@ class FinderMain extends React.Component {
3232
}
3333

3434
componentDidMount () {
35+
this.keyDownHandler = e => this.handleKeyDown(e)
36+
document.addEventListener('keydown', this.keyDownHandler)
3537
ReactDOM.findDOMNode(this.refs.finderInput.refs.input).focus()
38+
this.focusHandler = e => {
39+
let { dispatch } = this.props
40+
41+
dispatch(searchArticle(''))
42+
}
43+
window.addEventListener('focus', this.focusHandler)
3644
}
3745

38-
handleClick (e) {
39-
ReactDOM.findDOMNode(this.refs.finderInput.refs.input).focus()
46+
componentWillUnmount () {
47+
document.removeEventListener('keydown', this.keyDownHandler)
48+
window.removeEventListener('focus', this.focusHandler)
4049
}
4150

4251
handleKeyDown (e) {
@@ -58,6 +67,11 @@ class FinderMain extends React.Component {
5867
hideFinder()
5968
e.preventDefault()
6069
}
70+
if (e.keyCode === 91 || e.metaKey) {
71+
return
72+
}
73+
74+
ReactDOM.findDOMNode(this.refs.finderInput.refs.input).focus()
6175
}
6276

6377
saveToClipboard () {
@@ -99,7 +113,7 @@ class FinderMain extends React.Component {
99113
let { articles, activeArticle, status, dispatch } = this.props
100114
let saveToClipboard = () => this.saveToClipboard()
101115
return (
102-
<div onClick={e => this.handleClick(e)} onKeyDown={e => this.handleKeyDown(e)} className='Finder'>
116+
<div onClick={e => this.handleClick(e)} className='Finder'>
103117
<FinderInput
104118
handleSearchChange={e => this.handleSearchChange(e)}
105119
ref='finderInput'

browser/main/HomePage.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import _ from 'lodash'
99
import { isModalOpen, closeModal } from 'boost/modal'
1010

1111
const electron = require('electron')
12-
const BrowserWindow = electron.remote.BrowserWindow
12+
const remote = electron.remote
1313

1414
const TEXT_FILTER = 'TEXT_FILTER'
1515
const FOLDER_FILTER = 'FOLDER_FILTER'
@@ -29,13 +29,6 @@ class HomePage extends React.Component {
2929
}
3030

3131
handleKeyDown (e) {
32-
if (process.env.BOOST_ENV === 'development' && e.keyCode === 73 && e.metaKey && e.altKey) {
33-
e.preventDefault()
34-
e.stopPropagation()
35-
BrowserWindow.getFocusedWindow().toggleDevTools()
36-
return
37-
}
38-
3932
if (isModalOpen()) {
4033
if (e.keyCode === 27) closeModal()
4134
return
@@ -106,7 +99,7 @@ class HomePage extends React.Component {
10699
list.selectNextArticle()
107100
}
108101

109-
if (e.keyCode === 65 || (e.keyCode === 13 && e.metaKey) || (e.keyCode === 78 && e.metaKey)) {
102+
if ((e.keyCode === 65 && !e.metaKey && !e.ctrlKey) || (e.keyCode === 13 && e.metaKey) || (e.keyCode === 78 && e.metaKey)) {
110103
nav.handleNewPostButtonClick()
111104
e.preventDefault()
112105
}
@@ -142,6 +135,7 @@ class HomePage extends React.Component {
142135
<ArticleDetail
143136
ref='detail'
144137
dispatch={dispatch}
138+
user={user}
145139
activeArticle={activeArticle}
146140
folders={folders}
147141
status={status}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import React, { PropTypes } from 'react'
2+
import ReactDOM from 'react-dom'
3+
import api from 'boost/api'
4+
import clientKey from 'boost/clientKey'
5+
import activityRecord from 'boost/activityRecord'
6+
const clipboard = require('electron').clipboard
7+
8+
function getDefault () {
9+
return {
10+
openDropdown: false,
11+
isSharing: false,
12+
// Fetched url
13+
url: null,
14+
// for tooltip Copy -> Copied!
15+
copied: false,
16+
failed: false
17+
}
18+
}
19+
20+
export default class ShareButton extends React.Component {
21+
constructor (props) {
22+
super(props)
23+
this.state = getDefault()
24+
}
25+
26+
componentWillReceiveProps (nextProps) {
27+
this.setState(getDefault())
28+
}
29+
30+
componentDidMount () {
31+
this.dropdownInterceptor = e => {
32+
this.dropdownClicked = true
33+
}
34+
ReactDOM.findDOMNode(this.refs.dropdown).addEventListener('click', this.dropdownInterceptor)
35+
this.shareViaPublicURLHandler = e => {
36+
this.handleShareViaPublicURLClick(e)
37+
}
38+
}
39+
40+
componentWillUnmount () {
41+
document.removeEventListener('click', this.dropdownHandler)
42+
ReactDOM.findDOMNode(this.refs.dropdown).removeEventListener('click', this.dropdownInterceptor)
43+
}
44+
45+
handleOpenButtonClick (e) {
46+
this.openDropdown()
47+
if (this.dropdownHandler == null) {
48+
this.dropdownHandler = e => {
49+
if (!this.dropdownClicked) {
50+
this.closeDropdown()
51+
} else {
52+
this.dropdownClicked = false
53+
}
54+
}
55+
}
56+
document.removeEventListener('click', this.dropdownHandler)
57+
document.addEventListener('click', this.dropdownHandler)
58+
}
59+
60+
openDropdown () {
61+
this.setState({openDropdown: true})
62+
}
63+
64+
closeDropdown () {
65+
document.removeEventListener('click', this.dropdownHandler)
66+
this.setState({openDropdown: false})
67+
}
68+
69+
handleShareViaPublicURLClick (e) {
70+
let { user } = this.props
71+
let input = Object.assign({}, this.props.article, {
72+
clientKey: clientKey.get(),
73+
writerName: user.name
74+
})
75+
this.setState({
76+
isSharing: true,
77+
failed: false
78+
}, () => {
79+
api.shareViaPublicURL(input)
80+
.then(res => {
81+
let url = res.body.url
82+
this.setState({url: url, isSharing: false})
83+
activityRecord.emit('ARTICLE_SHARE')
84+
})
85+
.catch(err => {
86+
console.log(err)
87+
this.setState({isSharing: false, failed: true})
88+
})
89+
})
90+
}
91+
92+
handleCopyURLClick () {
93+
clipboard.writeText(this.state.url)
94+
this.setState({copied: true})
95+
}
96+
97+
// Restore copy url tooltip
98+
handleCopyURLMouseLeave () {
99+
this.setState({copied: false})
100+
}
101+
102+
render () {
103+
let hasPublicURL = this.state.url != null
104+
return (
105+
<div className='ShareButton'>
106+
<button ref='openButton' onClick={e => this.handleOpenButtonClick(e)} className='ShareButton-open-button'>
107+
<i className='fa fa-fw fa-share-alt'/>
108+
{
109+
this.state.openDropdown ? null : (
110+
<span className='tooltip'>Share</span>
111+
)
112+
}
113+
</button>
114+
<div ref='dropdown' className={'share-dropdown' + (this.state.openDropdown ? '' : ' hide')}>
115+
{
116+
!hasPublicURL ? (
117+
<button
118+
onClick={e => this.shareViaPublicURLHandler(e)}
119+
ref='sharePublicURL'
120+
disabled={this.state.isSharing}>
121+
<i className='fa fa-fw fa-external-link'/> {this.state.failed ? 'Failed : Click to Try again' : !this.state.isSharing ? 'Share via public URL' : 'Sharing...'}
122+
</button>
123+
) : (
124+
<div className='ShareButton-url'>
125+
<input className='ShareButton-url-input' value={this.state.url} readOnly/>
126+
<button
127+
onClick={e => this.handleCopyURLClick(e)}
128+
className='ShareButton-url-button'
129+
onMouseLeave={e => this.handleCopyURLMouseLeave(e)}
130+
>
131+
<i className='fa fa-fw fa-clipboard'/>
132+
<div className='ShareButton-url-button-tooltip'>{this.state.copied ? 'Copied!' : 'Copy URL'}</div>
133+
</button>
134+
<div className='ShareButton-url-alert'>This url is valid for 7 days.</div>
135+
</div>
136+
)
137+
}
138+
</div>
139+
</div>
140+
)
141+
}
142+
}
143+
144+
ShareButton.propTypes = {
145+
article: PropTypes.shape({
146+
publicURL: PropTypes.string
147+
}),
148+
user: PropTypes.shape({
149+
name: PropTypes.string
150+
})
151+
}

browser/main/HomePage/ArticleDetail.js renamed to browser/main/HomePage/ArticleDetail/index.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import TagLink from 'boost/components/TagLink'
2424
import TagSelect from 'boost/components/TagSelect'
2525
import ModeSelect from 'boost/components/ModeSelect'
2626
import activityRecord from 'boost/activityRecord'
27+
import api from 'boost/api'
28+
import ShareButton from './ShareButton'
2729

2830
const electron = require('electron')
2931
const clipboard = electron.clipboard
@@ -106,12 +108,16 @@ export default class ArticleDetail extends React.Component {
106108
isTagChanged: false,
107109
isTitleChanged: false,
108110
isContentChanged: false,
109-
isModeChanged: false
111+
isModeChanged: false,
112+
openShareDropdown: false
110113
}
111114
}
112115

113116
componentDidMount () {
114117
this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000)
118+
this.shareDropdownInterceptor = e => {
119+
e.stopPropagation()
120+
}
115121
}
116122

117123
componentWillUnmount () {
@@ -191,7 +197,7 @@ export default class ArticleDetail extends React.Component {
191197
}
192198

193199
renderIdle () {
194-
let { status, activeArticle, folders } = this.props
200+
let { status, activeArticle, folders, user } = this.props
195201

196202
let tags = activeArticle.tags != null ? activeArticle.tags.length > 0
197203
? activeArticle.tags.map(tag => {
@@ -234,9 +240,15 @@ export default class ArticleDetail extends React.Component {
234240
<div className='tags'><i className='fa fa-fw fa-tags'/>{tags}</div>
235241
</div>
236242
<div className='right'>
243+
<ShareButton
244+
article={activeArticle}
245+
user={user}
246+
/>
247+
237248
<button onClick={e => this.handleClipboardButtonClick(e)} className='editBtn'>
238249
<i className='fa fa-fw fa-clipboard'/><span className='tooltip'>Copy to clipboard</span>
239250
</button>
251+
240252
<button onClick={e => this.handleEditButtonClick(e)} className='editBtn'>
241253
<i className='fa fa-fw fa-edit'/><span className='tooltip'>Edit (e)</span>
242254
</button>
@@ -286,7 +298,7 @@ export default class ArticleDetail extends React.Component {
286298

287299
dispatch(unlockStatus())
288300

289-
delete newArticle.status
301+
newArticle.status = null
290302
newArticle.updatedAt = new Date()
291303
newArticle.title = newArticle.title.trim()
292304
if (newArticle.createdAt == null) {
@@ -586,7 +598,8 @@ export default class ArticleDetail extends React.Component {
586598
ArticleDetail.propTypes = {
587599
status: PropTypes.shape(),
588600
activeArticle: PropTypes.shape(),
589-
activeUser: PropTypes.shape(),
601+
user: PropTypes.shape(),
602+
folders: PropTypes.array,
590603
dispatch: PropTypes.func
591604
}
592605
ArticleDetail.prototype.linkState = linkState

browser/main/MainPage.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ const electron = require('electron')
22
const ipc = electron.ipcRenderer
33
import React, { PropTypes } from 'react'
44

5-
var ContactModal = require('boost/components/modal/ContactModal')
6-
75
export default class MainContainer extends React.Component {
86
constructor (props) {
97
super(props)
@@ -20,20 +18,12 @@ export default class MainContainer extends React.Component {
2018
ipc.send('update-app', 'Deal with it.')
2119
}
2220

23-
openContactModal () {
24-
this.openModal(ContactModal)
25-
}
26-
2721
render () {
2822
return (
2923
<div className='Main'>
3024
{this.state.updateAvailable ? (
3125
<button onClick={this.updateApp} className='appUpdateButton'><i className='fa fa-cloud-download'/> Update available!</button>
3226
) : null}
33-
{/* <button onClick={this.openContactModal} className='contactButton'>
34-
<i className='fa fa-paper-plane-o'/>
35-
<div className='tooltip'>Contact us</div>
36-
</button> */}
3727
{this.props.children}
3828
</div>
3929
)

0 commit comments

Comments
 (0)