1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-14 18:26:26 +00:00

Compare commits

...

36 Commits

Author SHA1 Message Date
Rokt33r
5fa37dbffb Merge branch 'dev'
* dev:
  version 0.2.10 - Hotkeyの設定機能 - Stylus refactor
2015-09-02 01:02:32 +09:00
Rokt33r
c6307e4ad3 version 0.2.10
- Hotkeyの設定機能
- Stylus refactor
2015-09-02 01:02:04 +09:00
Rokt33r
06a54d451c Merge branch 'dev'
* dev:
  0.2.9 API server changed, bump electron version 0.31.0
2015-08-31 01:34:15 +09:00
Rokt33r
e317075815 0.2.9 API server changed, bump electron version 0.31.0 2015-08-31 01:33:32 +09:00
Rokt33r
45541a255b Merge branch 'dev'
* dev:
  - StylusでコンパイルされたCSSをCachingする(ロディングが短くなる) - Planet name changeのときにエラーハンドリング追加 + Bug fix - TeamのMemberを編集する場合、自分を編集することはできない - FinderにMarkdownのリンクがちゃんと外部に飛ぶように - Tray iconがちゃんと表示 - ArticleDetailのCodeアイコンがちゃんと表示されない
2015-08-30 05:31:13 +09:00
Rokt33r
3ab423d695 - StylusでコンパイルされたCSSをCachingする(ロディングが短くなる)
- Planet name changeのときにエラーハンドリング追加 + Bug fix
- TeamのMemberを編集する場合、自分を編集することはできない
- FinderにMarkdownのリンクがちゃんと外部に飛ぶように
- Tray iconがちゃんと表示
- ArticleDetailのCodeアイコンがちゃんと表示されない
2015-08-30 05:30:54 +09:00
Rokt33r
345d7b427a Merge branch 'dev'
* dev:
  Loading font 微調整
2015-08-26 18:27:42 +09:00
Rokt33r
de6d6b692e Loading font 微調整 2015-08-26 18:27:19 +09:00
Rokt33r
b2845e2284 Merge branch 'dev'
* dev:
  version 0.2.7  - Planet, Team作成の時Error message表示  - MarkdownのCode blockの背景を薄い灰色にする  - 権限なしのPlanetには  - SignUpに規約/Privacyの外部リンク追加  - Loading画面追加  - Font 添付(Lato regular)  - UserContainerでのTeam Label変更
2015-08-26 18:23:04 +09:00
Rokt33r
47383c347c version 0.2.7
- Planet, Team作成の時Error message表示
 - MarkdownのCode blockの背景を薄い灰色にする
 - 権限なしのPlanetには
 - SignUpに規約/Privacyの外部リンク追加
 - Loading画面追加
 - Font 添付(Lato regular)
 - UserContainerでのTeam Label変更
2015-08-26 18:22:46 +09:00
Rokt33r
4bda84d69c cleanup old files 2015-08-24 17:42:43 +09:00
Rokt33r
b510aa11f5 Merge branch 'dev'
* dev:
  fix bugs - ArticleList text overflow behaviour in Finder, PlanetContainer - No DevTools
2015-08-24 17:42:00 +09:00
Rokt33r
8dab6d5e04 fix bugs
- ArticleList text overflow behaviour in Finder, PlanetContainer
- No DevTools
2015-08-24 17:41:38 +09:00
Rokt33r
85f833c865 Merge branch 'dev'
* dev:
  v0.2.5 - bugfix - Alert message added(Private planet/Team member)

Conflicts:
	package.json
2015-08-24 13:46:32 +09:00
Rokt33r
15133d00c7 v0.2.5
- bugfix
- Alert message added(Private planet/Team member)
2015-08-24 13:45:28 +09:00
Rokt33r
b93990d10b bump version 2015-08-24 06:23:41 +09:00
Rokt33r
a0bcb8edbe Merge branch 'dev'
* dev:
  実装 - MemberがOwnerではないときにTeam設定ボターンを全部隠す
  v0.2.4 - Minor fix
  fix minor bug
2015-08-24 06:22:32 +09:00
Rokt33r
bfdf691bed 実装 - MemberがOwnerではないときにTeam設定ボターンを全部隠す 2015-08-24 06:22:16 +09:00
Rokt33r
f60856b998 v0.2.4 - Minor fix 2015-08-24 06:14:13 +09:00
Rokt33r
3308eeaf82 fix minor bug 2015-08-23 04:00:18 +09:00
Rokt33r
19930a2472 fix minor bug 2015-08-23 03:49:19 +09:00
Rokt33r
e75d95b1fc Merge branch 'dev'
* dev:
  実装 - PlanetArticleListへのAuto scroll追加
  実装 - tooltip (Refresh, Planet setting, Planet refresh, Edit article, Delete article, Contact)
  #14 改善適用 - アプリが立ち上がったら最初はmy planetが表示されるようにしたい - Noteがスクロールできない - Note行間がなくて読みづらい
  実装 - Hotkey
2015-08-23 03:42:40 +09:00
Rokt33r
4319711dc6 実装 - PlanetArticleListへのAuto scroll追加 2015-08-23 03:42:00 +09:00
Rokt33r
9712be909d 実装 - tooltip (Refresh, Planet setting, Planet refresh, Edit article, Delete article, Contact) 2015-08-23 03:05:30 +09:00
Rokt33r
04060ce252 #14 改善適用
- アプリが立ち上がったら最初はmy planetが表示されるようにしたい
- Noteがスクロールできない
- Note行間がなくて読みづらい
2015-08-23 00:52:03 +09:00
Rokt33r
da066fe694 実装 - Hotkey 2015-08-22 23:57:37 +09:00
Rokt33r
3b7215b36a Merge branch 'dev'
* dev:
  改善 - Finder descriptionのTextoverflow対策, 最初起動の時のFinderの異常振る舞い
2015-08-22 03:46:51 +09:00
Rokt33r
b88d5cfb06 改善 - Finder descriptionのTextoverflow対策, 最初起動の時のFinderの異常振る舞い 2015-08-22 03:46:30 +09:00
Rokt33r
8ab96cf2fb fix version 2015-08-22 03:36:04 +09:00
Rokt33r
63af2fd8b1 fix submodule setting 2015-08-22 03:33:32 +09:00
Rokt33r
fcf26f7844 Merge branch 'dev'
* dev:
  改善 - PlanetArticleList, PlanetArticleDetail
  #14 改善点4つ適用  - Team member 管理画面でのスペルミス  - Member profile imageを丸にする  - Profile popup 改善  - 文字サイズ大きく(UserContainerのみ)
2015-08-22 03:28:56 +09:00
Rokt33r
657ebc99df 改善 - PlanetArticleList, PlanetArticleDetail 2015-08-22 03:27:42 +09:00
Rokt33r
9500f9a8c9 #14 改善点4つ適用
- Team member 管理画面でのスペルミス
 - Member profile imageを丸にする
 - Profile popup 改善
 - 文字サイズ大きく(UserContainerのみ)
2015-08-22 00:27:05 +09:00
Rokt33r
a05bba15e7 Merge branch 'dev'
* dev: (79 commits)
  version 0.2.1
  修正 - Member list, Team listのデザイン修正
  実装 - Team pageとPlanet pageにMember List表示
  実装 - PlanetHeaderにPrivate鍵表示
  Noteから外部Linkを開くときにBrowserを使う
  LogoutModal実装
  Contact Modal追加
  on Refactor... #4
  on Refactor... #3
  on Refactor... #2
  on Refactoring...
  test 0.2.0
  Fix: Personal Settingボターンを右にする。写真からまっすぐProfile pageに入れるようにする
  Add: Refresh button
  Fix: Design changed
  Fix: Disable pinch to zoom
  Fix: Preview button修正
  Fix: PlanetNavigatorのHome削除 & SnippetsとBlueprintsはToggleができるように
  Fix: minor features 設定ボタンアイコンの変更 削除Modalでcmd+enterの使用 検索バーデザイン
  Add: Log in / Sign upの時にエラーが出たらAlertを表示する Debug: Tray Icon, PopUpWindow, Menuがいつの間にか消える
  ...

Conflicts:
	src/browser/main/controllers/AppController.js
	src/browser/main/controllers/directives/SideNavController.js
	src/browser/main/controllers/states/AuthRegisterController.js
	src/browser/main/index.html
	src/browser/main/services/Modal.js
	src/browser/main/styles/app.css
	src/browser/main/styles/app.styl
	src/browser/main/styles/directives/side-nav.styl
	src/browser/main/tpls/directives/side-nav.tpl.html
	src/browser/main/tpls/states/home.tpl.html
2015-08-21 04:58:01 +09:00
Rokt33r
3b907627f7 fix links 2015-07-03 18:35:10 +09:00
Rokt33r
2284fd41b9 add agreement 2015-07-03 16:41:42 +09:00
57 changed files with 1191 additions and 406 deletions

3
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "browser/ace"] [submodule "browser/ace"]
path = browser/ace path = browser/ace
url = https://github.com/ajaxorg/ace-builds.git url = https://github.com/ajaxorg/ace-builds.git
[submodule "browser/electron-stylus"]
path = browser/electron-stylus
url = https://github.com/Rokt33r/electron-stylus.git

BIN
Lato-Regular.ttf Normal file

Binary file not shown.

BIN
Lato-Regular.woff Normal file

Binary file not shown.

BIN
Lato-Regular.woff2 Normal file

Binary file not shown.

View File

@@ -2,10 +2,9 @@ var React = require('react/addons')
var CodeViewer = require('../../main/Components/CodeViewer') var CodeViewer = require('../../main/Components/CodeViewer')
var Markdown = require('../../main/Mixins/Markdown') var MarkdownPreview = require('../../main/Components/MarkdownPreview')
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [Markdown],
propTypes: { propTypes: {
currentArticle: React.PropTypes.object currentArticle: React.PropTypes.object
}, },
@@ -28,7 +27,7 @@ module.exports = React.createClass({
<div className='FinderDetail'> <div className='FinderDetail'>
<div className='header'><i className='fa fa-file-text-o fa-fw'/> {article.title}</div> <div className='header'><i className='fa fa-file-text-o fa-fw'/> {article.title}</div>
<div className='content'> <div className='content'>
<div className='marked' dangerouslySetInnerHTML={{__html: ' ' + this.markdown(article.content)}}></div> <MarkdownPreview className='marked' content={article.content}/>
</div> </div>
</div> </div>
) )

View File

@@ -8,6 +8,17 @@
<link rel="stylesheet" href="../../node_modules/font-awesome/css/font-awesome.min.css" media="screen" title="no title" charset="utf-8"> <link rel="stylesheet" href="../../node_modules/font-awesome/css/font-awesome.min.css" media="screen" title="no title" charset="utf-8">
<link rel="shortcut icon" href="favicon.ico"> <link rel="shortcut icon" href="favicon.ico">
<style>
@font-face {
font-family: 'Lato';
src: url('../../Lato-Regular.woff2') format('woff2'), /* Modern Browsers */
url('../../Lato-Regular.woff') format('woff'), /* Modern Browsers */
url('../../Lato-Regular.ttf') format('truetype');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
</style>
<script> <script>
document.addEventListener('mousewheel', function(e) { document.addEventListener('mousewheel', function(e) {
if(e.deltaY % 1 !== 0) { if(e.deltaY % 1 !== 0) {
@@ -47,14 +58,13 @@
} }
}); });
} }
require('electron-stylus')(__dirname + '/../styles/finder/index.styl')
</script> </script>
</head> </head>
<body> <body>
<div id="content"></div> <div id="content"></div>
<script src="../ace/src-min/ace.js"></script> <script src="../ace/src-min/ace.js"></script>
<script> <script>
require('../electron-stylus')(__dirname + '/../styles/finder/index.styl', 'finderCss')
require('node-jsx').install({ harmony: true, extension: '.jsx' }) require('node-jsx').install({ harmony: true, extension: '.jsx' })
require('./index.jsx') require('./index.jsx')
</script> </script>

View File

@@ -49,6 +49,7 @@ var Finder = React.createClass({
document.addEventListener('keydown', this.handleKeyDown) document.addEventListener('keydown', this.handleKeyDown)
document.addEventListener('click', this.handleClick) document.addEventListener('click', this.handleClick)
window.addEventListener('focus', this.handleFinderFocus) window.addEventListener('focus', this.handleFinderFocus)
this.handleFinderFocus()
}, },
componentWillUnmount: function () { componentWillUnmount: function () {
document.removeEventListener('keydown', this.handleKeyDown) document.removeEventListener('keydown', this.handleKeyDown)

View File

@@ -1,18 +1,24 @@
var remote = require('remote')
var version = remote.getGlobal('version')
var React = require('react/addons') var React = require('react/addons')
var ExternalLink = require('../Mixins/ExternalLink') var ExternalLink = require('../Mixins/ExternalLink')
var KeyCaster = require('../Mixins/KeyCaster')
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [ExternalLink], mixins: [ExternalLink, KeyCaster('aboutModal')],
propTypes: { propTypes: {
close: React.PropTypes.func close: React.PropTypes.func
}, },
onKeyCast: function (e) {
switch (e.status) {
case 'closeModal':
this.props.close()
break
}
},
render: function () { render: function () {
var version = global.version
return ( return (
<div className='AboutModal modal'> <div className='PreferencesModal sideNavModal modal'>
<div className='about1'> <div className='about1'>
<img className='logo' src='resources/favicon-230x230.png'/> <img className='logo' src='resources/favicon-230x230.png'/>
<div className='appInfo'>Boost {version == null || version.length === 0 ? 'DEV version' : 'v' + version}</div> <div className='appInfo'>Boost {version == null || version.length === 0 ? 'DEV version' : 'v' + version}</div>

View File

@@ -5,6 +5,8 @@ var LinkedState = require('../Mixins/LinkedState')
var Hq = require('../Services/Hq') var Hq = require('../Services/Hq')
var KeyCaster = require('../Mixins/KeyCaster')
var UserStore = require('../Stores/UserStore') var UserStore = require('../Stores/UserStore')
var getOptions = function (input, callback) { var getOptions = function (input, callback) {
@@ -26,7 +28,7 @@ var getOptions = function (input, callback) {
} }
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [LinkedState], mixins: [LinkedState, KeyCaster('addMemberModal')],
propTypes: { propTypes: {
team: React.PropTypes.object, team: React.PropTypes.object,
close: React.PropTypes.func close: React.PropTypes.func
@@ -37,7 +39,18 @@ module.exports = React.createClass({
role: 'member' role: 'member'
} }
}, },
onKeyCast: function (e) {
switch (e.status) {
case 'closeModal':
this.props.close()
break
case 'submitAddMemberModal':
this.handleSubmit()
break
}
},
handleSubmit: function () { handleSubmit: function () {
this.setState({errorMessage: null}, function () {
Hq Hq
.addMember(this.props.team.name, { .addMember(this.props.team.name, {
userName: this.state.userName, userName: this.state.userName,
@@ -50,6 +63,10 @@ module.exports = React.createClass({
}.bind(this)) }.bind(this))
.catch(function (err) { .catch(function (err) {
console.error(err) console.error(err)
if (err.status === 403) {
this.setState({errorMessage: err.response.body.message})
}
}.bind(this))
}) })
}, },
handleChange: function (value) { handleChange: function (value) {
@@ -76,6 +93,8 @@ module.exports = React.createClass({
role role
</div> </div>
{this.state.errorMessage != null ? (<p className='errorAlert'>{this.state.errorMessage}</p>) : null}
<button onClick={this.handleSubmit} className='submitButton'><i className='fa fa-check'/></button> <button onClick={this.handleSubmit} className='submitButton'><i className='fa fa-check'/></button>
</div> </div>
) )

View File

@@ -2,19 +2,26 @@ var React = require('react')
var Hq = require('../Services/Hq') var Hq = require('../Services/Hq')
var KeyCaster = require('../Mixins/KeyCaster')
var PlanetStore = require('../Stores/PlanetStore') var PlanetStore = require('../Stores/PlanetStore')
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [KeyCaster('codeDeleteModal')],
propTypes: { propTypes: {
planet: React.PropTypes.object, planet: React.PropTypes.object,
code: React.PropTypes.object, code: React.PropTypes.object,
close: React.PropTypes.func close: React.PropTypes.func
}, },
componentDidMount: function () { onKeyCast: function (e) {
React.findDOMNode(this).focus() switch (e.status) {
}, case 'submitCodeDeleteModal':
stopPropagation: function (e) { this.submit()
e.stopPropagation() break
case 'closeModal':
this.props.close()
break
}
}, },
submit: function () { submit: function () {
var planet = this.props.planet var planet = this.props.planet

View File

@@ -7,13 +7,19 @@ module.exports = React.createClass({
code: React.PropTypes.object, code: React.PropTypes.object,
planet: React.PropTypes.object planet: React.PropTypes.object
}, },
componentDidMount: function () {
// TODO: Hacked!! should fix later
setTimeout(function () {
React.findDOMNode(this.refs.form.refs.description).focus()
}.bind(this), 1)
},
render: function () { render: function () {
return ( return (
<div className='CodeEditModal modal'> <div className='CodeEditModal modal'>
<div className='modal-header'> <div className='modal-header'>
<h1>Edit Code</h1> <h1>Edit Code</h1>
</div> </div>
<CodeForm code={this.props.code} planet={this.props.planet} close={this.props.close}/> <CodeForm ref='form' code={this.props.code} planet={this.props.planet} close={this.props.close}/>
</div> </div>
) )
} }

View File

@@ -6,6 +6,7 @@ module.exports = React.createClass({
propTypes: { propTypes: {
code: React.PropTypes.string, code: React.PropTypes.string,
mode: React.PropTypes.string, mode: React.PropTypes.string,
className: React.PropTypes.string,
onChange: React.PropTypes.func onChange: React.PropTypes.func
}, },
componentDidMount: function () { componentDidMount: function () {
@@ -52,7 +53,7 @@ module.exports = React.createClass({
}, },
render: function () { render: function () {
return ( return (
<div ref='target'></div> <div ref='target' className={this.props.className}></div>
) )
} }
}) })

View File

@@ -5,6 +5,7 @@ var Select = require('react-select')
var Hq = require('../Services/Hq') var Hq = require('../Services/Hq')
var LinkedState = require('../Mixins/LinkedState') var LinkedState = require('../Mixins/LinkedState')
var KeyCaster = require('../Mixins/KeyCaster')
var PlanetStore = require('../Stores/PlanetStore') var PlanetStore = require('../Stores/PlanetStore')
@@ -29,7 +30,7 @@ var getOptions = function (input, callback) {
} }
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [LinkedState], mixins: [LinkedState, KeyCaster('codeForm')],
propTypes: { propTypes: {
planet: React.PropTypes.object, planet: React.PropTypes.object,
close: React.PropTypes.func, close: React.PropTypes.func,
@@ -55,6 +56,16 @@ module.exports = React.createClass({
code: code code: code
} }
}, },
onKeyCast: function (e) {
switch (e.status) {
case 'submitCodeForm':
this.submit()
break
case 'closeModal':
this.props.close()
break
}
},
handleModeChange: function (selected) { handleModeChange: function (selected) {
var code = this.state.code var code = this.state.code
code.mode = selected code.mode = selected

View File

@@ -5,7 +5,8 @@ var ace = window.ace
module.exports = React.createClass({ module.exports = React.createClass({
propTypes: { propTypes: {
code: React.PropTypes.string, code: React.PropTypes.string,
mode: React.PropTypes.string mode: React.PropTypes.string,
className: React.PropTypes.string
}, },
componentDidMount: function () { componentDidMount: function () {
var el = React.findDOMNode(this.refs.target) var el = React.findDOMNode(this.refs.target)
@@ -46,7 +47,7 @@ module.exports = React.createClass({
}, },
render: function () { render: function () {
return ( return (
<div ref='target'></div> <div ref='target' className={this.props.className}></div>
) )
} }
}) })

View File

@@ -1,11 +1,12 @@
var React = require('react') var React = require('react')
var LinkedState = require('../Mixins/LinkedState') var LinkedState = require('../Mixins/LinkedState')
var KeyCaster = require('../Mixins/KeyCaster')
var Hq = require('../Services/Hq') var Hq = require('../Services/Hq')
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [LinkedState], mixins: [LinkedState, KeyCaster('contactModal')],
propTypes: { propTypes: {
close: React.PropTypes.func close: React.PropTypes.func
}, },
@@ -18,6 +19,23 @@ module.exports = React.createClass({
} }
} }
}, },
onKeyCast: function (e) {
switch (e.status) {
case 'closeModal':
this.props.close()
break
case 'submitContactModal':
if (this.state.isSent) {
this.props.close()
return
}
this.sendEmail()
break
}
},
componentDidMount: function () {
React.findDOMNode(this.refs.title).focus()
},
sendEmail: function () { sendEmail: function () {
Hq.sendEmail(this.state.mail) Hq.sendEmail(this.state.mail)
.then(function (res) { .then(function (res) {
@@ -36,7 +54,7 @@ module.exports = React.createClass({
<div className='contactForm'> <div className='contactForm'>
<div className='modal-body'> <div className='modal-body'>
<div className='formField'> <div className='formField'>
<input valueLink={this.linkState('mail.title')} placeholder='Title'/> <input ref='title' valueLink={this.linkState('mail.title')} placeholder='Title'/>
</div> </div>
<div className='formField'> <div className='formField'>
<textarea valueLink={this.linkState('mail.content')} placeholder='Content'/> <textarea valueLink={this.linkState('mail.content')} placeholder='Content'/>

View File

@@ -5,17 +5,19 @@ var React = require('react/addons')
var Hq = require('../Services/Hq') var Hq = require('../Services/Hq')
var LinkedState = require('../Mixins/LinkedState') var LinkedState = require('../Mixins/LinkedState')
var KeyCaster = require('../Mixins/KeyCaster')
var UserStore = require('../Stores/UserStore') var UserStore = require('../Stores/UserStore')
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [LinkedState], mixins: [LinkedState, KeyCaster('editProfileModal')],
propTypes: { propTypes: {
user: React.PropTypes.shape({ user: React.PropTypes.shape({
name: React.PropTypes.string, name: React.PropTypes.string,
profileName: React.PropTypes.string, profileName: React.PropTypes.string,
email: React.PropTypes.string email: React.PropTypes.string
}) }),
close: React.PropTypes.func
}, },
getInitialState: function () { getInitialState: function () {
var user = this.props.user var user = this.props.user
@@ -34,6 +36,13 @@ module.exports = React.createClass({
passwordSubmitStatus: null passwordSubmitStatus: null
} }
}, },
onKeyCast: function (e) {
switch (e.status) {
case 'closeModal':
this.props.close()
break
}
},
selectTab: function (tabName) { selectTab: function (tabName) {
return function () { return function () {
this.setState({currentTab: tabName}) this.setState({currentTab: tabName})
@@ -60,7 +69,6 @@ module.exports = React.createClass({
this.setState({ this.setState({
passwordSubmitStatus: 'sending' passwordSubmitStatus: 'sending'
}, function () { }, function () {
console.log(this.state.password)
Hq.changePassword(this.state.password) Hq.changePassword(this.state.password)
.then(function (res) { .then(function (res) {
this.setState({ this.setState({
@@ -94,9 +102,9 @@ module.exports = React.createClass({
} }
return ( return (
<div className='EditProfileModal modal tabModal'> <div className='EditProfileModal sideNavModal modal'>
<div className='leftPane'> <div className='leftPane'>
<div className='tabLabel'>Edit profile</div> <div className='modalLabel'>Edit profile</div>
<div className='tabList'> <div className='tabList'>
<button className={this.state.currentTab === 'userInfo' ? 'active' : ''} onClick={this.selectTab('userInfo')}><i className='fa fa-user fa-fw'/> User Info</button> <button className={this.state.currentTab === 'userInfo' ? 'active' : ''} onClick={this.selectTab('userInfo')}><i className='fa fa-user fa-fw'/> User Info</button>
<button className={this.state.currentTab === 'password' ? 'active' : ''} onClick={this.selectTab('password')}><i className='fa fa-lock fa-fw'/> Password</button> <button className={this.state.currentTab === 'password' ? 'active' : ''} onClick={this.selectTab('password')}><i className='fa fa-lock fa-fw'/> Password</button>
@@ -110,7 +118,7 @@ module.exports = React.createClass({
}, },
renderUserInfoTab: function () { renderUserInfoTab: function () {
return ( return (
<div className='userInfoTab'> <div className='userInfoTab tab'>
<div className='formField'> <div className='formField'>
<label>Profile Name</label> <label>Profile Name</label>
<input valueLink={this.linkState('user.profileName')}/> <input valueLink={this.linkState('user.profileName')}/>
@@ -133,18 +141,18 @@ module.exports = React.createClass({
}, },
renderPasswordTab: function () { renderPasswordTab: function () {
return ( return (
<div className='passwordTab'> <div className='passwordTab tab'>
<div className='formField'> <div className='formField'>
<label>Current password</label> <label>Current password</label>
<input valueLink={this.linkState('password.currentPassword')}/> <input type='password' valueLink={this.linkState('password.currentPassword')}/>
</div> </div>
<div className='formField'> <div className='formField'>
<label>New password</label> <label>New password</label>
<input valueLink={this.linkState('password.newPassword')}/> <input type='password' valueLink={this.linkState('password.newPassword')}/>
</div> </div>
<div className='formField'> <div className='formField'>
<label>Confirmation</label> <label>Confirmation</label>
<input valueLink={this.linkState('password.passwordConfirmation')}/> <input type='password' valueLink={this.linkState('password.passwordConfirmation')}/>
</div> </div>
<div className='formConfirm'> <div className='formConfirm'>

View File

@@ -11,7 +11,7 @@ var Modal = require('../Mixins/Modal')
var UserStore = require('../Stores/UserStore') var UserStore = require('../Stores/UserStore')
var AboutModal = require('./AboutModal') var PreferencesModal = require('./PreferencesModal')
var PlanetCreateModal = require('./PlanetCreateModal') var PlanetCreateModal = require('./PlanetCreateModal')
var TeamCreateModal = require('./TeamCreateModal') var TeamCreateModal = require('./TeamCreateModal')
var LogoutModal = require('./LogoutModal') var LogoutModal = require('./LogoutModal')
@@ -65,22 +65,12 @@ module.exports = React.createClass({
openTeamCreateModal: function () { openTeamCreateModal: function () {
this.openModal(TeamCreateModal, {user: this.state.currentUser, transitionTo: this.transitionTo}) this.openModal(TeamCreateModal, {user: this.state.currentUser, transitionTo: this.transitionTo})
}, },
openAboutModal: function () { openPreferencesModal: function () {
this.openModal(AboutModal) this.openModal(PreferencesModal)
}, },
openPlanetCreateModal: function () { openPlanetCreateModal: function () {
this.openModal(PlanetCreateModal, {transitionTo: this.transitionTo}) this.openModal(PlanetCreateModal, {transitionTo: this.transitionTo})
}, },
handleKeyDown: function (e) {
if (this.state.currentUser == null) return
if (e.metaKey && e.keyCode > 48 && e.keyCode < 58) {
var planet = this.state.currentUser.Planets[e.keyCode - 49]
if (planet != null) {
this.transitionTo('planet', {userName: planet.userName, planetName: planet.name})
}
e.preventDefault()
}
},
toggleProfilePopup: function () { toggleProfilePopup: function () {
this.openProfilePopup() this.openProfilePopup()
}, },
@@ -96,6 +86,10 @@ module.exports = React.createClass({
handleLogoutClick: function () { handleLogoutClick: function () {
this.openModal(LogoutModal, {transitionTo: this.transitionTo}) this.openModal(LogoutModal, {transitionTo: this.transitionTo})
}, },
switchPlanetByIndex: function (index) {
var planetProps = this.refs.planets.props.children[index - 1].props
this.transitionTo('planet', {userName: planetProps.userName, planetName: planetProps.planetName})
},
render: function () { render: function () {
var params = this.getParams() var params = this.getParams()
@@ -110,12 +104,12 @@ module.exports = React.createClass({
return team.Planets == null ? planets : planets.concat(team.Planets) return team.Planets == null ? planets : planets.concat(team.Planets)
}, []))).map(function (planet, index) { }, []))).map(function (planet, index) {
return ( return (
<li key={planet.id} className={params.userName === planet.userName && params.planetName === planet.name ? 'active' : ''}> <li userName={planet.userName} planetName={planet.name} key={planet.id} className={params.userName === planet.userName && params.planetName === planet.name ? 'active' : ''}>
<Link to='planet' params={{userName: planet.userName, planetName: planet.name}}> <Link to='planet' params={{userName: planet.userName, planetName: planet.name}}>
{planet.name[0]} {planet.name[0]}
<div className='planetTooltip'>{planet.userName}/{planet.name}</div> <div className='planetTooltip'>{planet.userName}/{planet.name}</div>
</Link> </Link>
<div className='shortCut'>{index + 1}</div> {index < 9 ? (<div className='shortCut'>{index + 1}</div>) : null}
</li> </li>
) )
}) })
@@ -128,12 +122,12 @@ module.exports = React.createClass({
<ProfileImage size='55' email={this.state.currentUser.email}/> <ProfileImage size='55' email={this.state.currentUser.email}/>
</button> </button>
{popup} {popup}
<ul className='planetList'> <ul ref='planets' className='planetList'>
{planets} {planets}
</ul> </ul>
<button onClick={this.openPlanetCreateModal} className='newPlanet'> <button onClick={this.openPlanetCreateModal} className='newPlanet'>
<i className='fa fa-plus'/> <i className='fa fa-plus'/>
<div className='newPlanetTooltip'>Create new planet</div> <div className='tooltip'>Create new planet</div>
</button> </button>
</div> </div>
) )
@@ -143,7 +137,6 @@ module.exports = React.createClass({
return ( return (
<li key={'user-' + team.id}> <li key={'user-' + team.id}>
<Link to='userHome' params={{userName: team.name}} className='userName'>{team.profileName} ({team.name})</Link> <Link to='userHome' params={{userName: team.name}} className='userName'>{team.profileName} ({team.name})</Link>
<div className='userSetting'><i className='fa fa-gear'/></div>
</li> </li>
) )
}) })
@@ -157,7 +150,6 @@ module.exports = React.createClass({
<ul className='profileGroupList'> <ul className='profileGroupList'>
<li> <li>
<Link to='userHome' params={{userName: this.state.currentUser.name}} className='userName'>Profile ({this.state.currentUser.name})</Link> <Link to='userHome' params={{userName: this.state.currentUser.name}} className='userName'>Profile ({this.state.currentUser.name})</Link>
<div className='userSetting'><i className='fa fa-gear'/></div>
</li> </li>
</ul> </ul>
</div> </div>
@@ -176,7 +168,7 @@ module.exports = React.createClass({
<ul className='controlGroup'> <ul className='controlGroup'>
<li> <li>
<button onClick={this.openAboutModal}><i className='fa fa-info-circle fa-fw'/> About this app</button> <button onClick={this.openPreferencesModal}><i className='fa fa-gears fa-fw'/> Preferences</button>
</li> </li>
<li> <li>
<button onClick={this.handleLogoutClick}><i className='fa fa-sign-out fa-fw'/> Log out</button> <button onClick={this.handleLogoutClick}><i className='fa fa-sign-out fa-fw'/> Log out</button>

View File

@@ -15,34 +15,52 @@ module.exports = React.createClass({
} }
}, },
componentDidMount: function () { componentDidMount: function () {
var codeButton = React.findDOMNode(this.refs.codeButton)
codeButton.addEventListener('keydown', this.handleKeyDown)
React.findDOMNode(this.refs.noteButton).addEventListener('keydown', this.handleKeyDown)
codeButton.focus()
}, },
stopPropagation: function (e) { componentWillUnmount: function () {
e.stopPropagation() React.findDOMNode(this.refs.codeButton).removeEventListener('keydown', this.handleKeyDown)
}, React.findDOMNode(this.refs.noteButton).removeEventListener('keydown', this.handleKeyDown)
selectCodeTab: function () {
this.setState({currentTab: 'code'})
},
selectNoteTab: function () {
this.setState({currentTab: 'note'})
}, },
handleKeyDown: function (e) { handleKeyDown: function (e) {
if (e.keyCode === 37 && e.metaKey) { if (e.keyCode === 37 && e.metaKey) {
this.selectCodeTab() this.selectCodeTab()
e.stopPropagation()
return
} }
if (e.keyCode === 39 && e.metaKey) { if (e.keyCode === 39 && e.metaKey) {
this.selectNoteTab() this.selectNoteTab()
e.stopPropagation()
return
} }
if (e.keyCode === 9) {
if (this.state.currentTab === 'code') React.findDOMNode(this.refs.form.refs.description).focus()
else React.findDOMNode(this.refs.form.refs.title).focus()
e.preventDefault()
}
},
selectCodeTab: function () {
this.setState({currentTab: 'code'}, function () {
React.findDOMNode(this.refs.codeButton).focus()
})
},
selectNoteTab: function () {
this.setState({currentTab: 'note'}, function () {
React.findDOMNode(this.refs.noteButton).focus()
})
}, },
render: function () { render: function () {
var modalBody var modalBody
if (this.state.currentTab === 'code') { if (this.state.currentTab === 'code') {
modalBody = ( modalBody = (
<CodeForm planet={this.props.planet} transitionTo={this.props.transitionTo} close={this.props.close}/> <CodeForm ref='form' planet={this.props.planet} transitionTo={this.props.transitionTo} close={this.props.close}/>
) )
} else { } else {
modalBody = ( modalBody = (
<NoteForm planet={this.props.planet} transitionTo={this.props.transitionTo} close={this.props.close}/> <NoteForm ref='form' planet={this.props.planet} transitionTo={this.props.transitionTo} close={this.props.close}/>
) )
} }
@@ -50,7 +68,8 @@ module.exports = React.createClass({
<div className='LaunchModal modal'> <div className='LaunchModal modal'>
<div className='modal-header'> <div className='modal-header'>
<div className='modal-tab'> <div className='modal-tab'>
<button className={this.state.currentTab === 'code' ? 'btn-primary active' : 'btn-default'} onClick={this.selectCodeTab}>Code</button><button className={this.state.currentTab === 'note' ? 'btn-primary active' : 'btn-default'} onClick={this.selectNoteTab}>Note</button> <button ref='codeButton' className={this.state.currentTab === 'code' ? 'btn-primary active' : 'btn-default'} onClick={this.selectCodeTab}>Code</button>
<button ref='noteButton' className={this.state.currentTab === 'note' ? 'btn-primary active' : 'btn-default'} onClick={this.selectNoteTab}>Note</button>
</div> </div>
</div> </div>
{modalBody} {modalBody}

View File

@@ -2,11 +2,24 @@
var React = require('react') var React = require('react')
var KeyCaster = require('../Mixins/KeyCaster')
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [KeyCaster('logoutModal')],
propTypes: { propTypes: {
transitionTo: React.PropTypes.func, transitionTo: React.PropTypes.func,
close: React.PropTypes.func close: React.PropTypes.func
}, },
onKeyCast: function (e) {
switch (e.status) {
case 'closeModal':
this.props.close()
break
case 'submitLogoutModal':
this.logout()
break
}
},
logout: function () { logout: function () {
localStorage.removeItem('currentUser') localStorage.removeItem('currentUser')
localStorage.removeItem('token') localStorage.removeItem('token')

View File

@@ -2,16 +2,26 @@ var React = require('react')
var Hq = require('../Services/Hq') var Hq = require('../Services/Hq')
var KeyCaster = require('../Mixins/KeyCaster')
var PlanetStore = require('../Stores/PlanetStore') var PlanetStore = require('../Stores/PlanetStore')
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [KeyCaster('noteDeleteModal')],
propTypes: { propTypes: {
planet: React.PropTypes.object, planet: React.PropTypes.object,
note: React.PropTypes.object, note: React.PropTypes.object,
close: React.PropTypes.func close: React.PropTypes.func
}, },
componentDidMount: function () { onKeyCast: function (e) {
React.findDOMNode(this).focus() switch (e.status) {
case 'submitNoteDeleteModal':
this.submit()
break
case 'closeModal':
this.props.close()
break
}
}, },
submit: function () { submit: function () {
var planet = this.props.planet var planet = this.props.planet

View File

@@ -8,13 +8,19 @@ module.exports = React.createClass({
note: React.PropTypes.object, note: React.PropTypes.object,
planet: React.PropTypes.object planet: React.PropTypes.object
}, },
componentDidMount: function () {
// TODO: Hacked!! should fix later
setTimeout(function () {
React.findDOMNode(this.refs.form.refs.title).focus()
}.bind(this), 1)
},
render: function () { render: function () {
return ( return (
<div className='NoteEditModal modal'> <div className='NoteEditModal modal'>
<div className='modal-header'> <div className='modal-header'>
<h1>Edit Note</h1> <h1>Edit Note</h1>
</div> </div>
<NoteForm note={this.props.note} planet={this.props.planet} close={this.props.close}/> <NoteForm ref='form' note={this.props.note} planet={this.props.planet} close={this.props.close}/>
</div> </div>
) )
} }

View File

@@ -1,11 +1,11 @@
var React = require('react/addons') var React = require('react/addons')
var ReactRouter = require('react-router')
var Select = require('react-select') var Select = require('react-select')
var Hq = require('../Services/Hq') var Hq = require('../Services/Hq')
var LinkedState = require('../Mixins/LinkedState') var LinkedState = require('../Mixins/LinkedState')
var Markdown = require('../Mixins/Markdown') var Markdown = require('../Mixins/Markdown')
var KeyCaster = require('../Mixins/KeyCaster')
var PlanetStore = require('../Stores/PlanetStore') var PlanetStore = require('../Stores/PlanetStore')
@@ -34,7 +34,7 @@ var EDIT_MODE = 0
var PREVIEW_MODE = 1 var PREVIEW_MODE = 1
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [LinkedState, Markdown], mixins: [LinkedState, Markdown, KeyCaster('noteForm')],
propTypes: { propTypes: {
planet: React.PropTypes.object, planet: React.PropTypes.object,
close: React.PropTypes.func, close: React.PropTypes.func,
@@ -58,8 +58,15 @@ module.exports = React.createClass({
mode: EDIT_MODE mode: EDIT_MODE
} }
}, },
componentDidMount: function () { onKeyCast: function (e) {
React.findDOMNode(this.refs.title).focus() switch (e.status) {
case 'submitNoteForm':
this.submit()
break
case 'closeModal':
this.props.close()
break
}
}, },
handleTagsChange: function (selected, all) { handleTagsChange: function (selected, all) {
var note = this.state.note var note = this.state.note

View File

@@ -1,13 +1,13 @@
var React = require('react/addons') var React = require('react/addons')
var moment = require('moment') var moment = require('moment')
var CodeViewer = require('../Components/CodeViewer') var CodeViewer = require('./CodeViewer')
var CodeEditModal = require('./CodeEditModal')
var CodeEditModal = require('../Components/CodeEditModal') var CodeDeleteModal = require('./CodeDeleteModal')
var CodeDeleteModal = require('../Components/CodeDeleteModal') var NoteEditModal = require('./NoteEditModal')
var NoteEditModal = require('../Components/NoteEditModal') var NoteDeleteModal = require('./NoteDeleteModal')
var NoteDeleteModal = require('../Components/NoteDeleteModal') var MarkdownPreview = require('./MarkdownPreview')
var MarkdownPreview = require('../Components/MarkdownPreview') var ProfileImage = require('./ProfileImage')
var Modal = require('../Mixins/Modal') var Modal = require('../Mixins/Modal')
var ForceUpdate = require('../Mixins/ForceUpdate') var ForceUpdate = require('../Mixins/ForceUpdate')
@@ -25,6 +25,7 @@ module.exports = React.createClass({
} }
}, },
openEditModal: function () { openEditModal: function () {
if (this.props.article == null) return
switch (this.props.article.type) { switch (this.props.article.type) {
case 'code' : case 'code' :
this.openModal(CodeEditModal, {code: this.props.article, planet: this.props.planet}) this.openModal(CodeEditModal, {code: this.props.article, planet: this.props.planet})
@@ -34,6 +35,7 @@ module.exports = React.createClass({
} }
}, },
openDeleteModal: function () { openDeleteModal: function () {
if (this.props.article == null) return
switch (this.props.article.type) { switch (this.props.article.type) {
case 'code' : case 'code' :
this.openModal(CodeDeleteModal, {code: this.props.article, planet: this.props.planet}) this.openModal(CodeDeleteModal, {code: this.props.article, planet: this.props.planet})
@@ -61,36 +63,61 @@ module.exports = React.createClass({
if (article.type === 'code') { if (article.type === 'code') {
return ( return (
<div className='PlanetArticleDetail codeDetail'> <div className='PlanetArticleDetail codeDetail'>
<div className='viewer-header'> <div className='detailHeader'>
<i className='fa fa-code fa-fw'></i> {article.callSign} <small className='updatedAt'>{moment(article.updatedAt).fromNow()}</small> <div className='itemLeft'>
<span className='control-group'> <ProfileImage className='profileImage' size='25' email={article.User.email}/>
<button onClick={this.openEditModal} className='btn-default btn-square btn-sm'><i className='fa fa-edit fa-fw'></i></button> <i className='fa fa-code fa-fw'></i>
<button onClick={this.openDeleteModal} className='btn-default btn-square btn-sm'><i className='fa fa-trash fa-fw'></i></button>
</span>
</div> </div>
<div className='viewer-body'>
<div className='viewer-detail'> <div className='itemRight'>
<div className='itemInfo'>{moment(article.updatedAt).fromNow()} by <span className='userProfileName'>{article.User.profileName}</span></div>
<div className='description'>{article.description}</div> <div className='description'>{article.description}</div>
<div className='tags'><i className='fa fa-tags'/>{tags}</div> <div className='tags'><i className='fa fa-tags'/>{tags}</div>
</div> </div>
<div className='content'>
<CodeViewer code={article.content} mode={article.mode}/> <span className='itemControl'>
<button id='articleEditButton' onClick={this.openEditModal} className='editButton'>
<i className='fa fa-edit fa-fw'></i>
<div className='tooltip'>Edit</div>
</button>
<button onClick={this.openDeleteModal} className='deleteButton'>
<i className='fa fa-trash fa-fw'></i>
<div className='tooltip'>Delete</div>
</button>
</span>
</div> </div>
<div className='detailBody'>
<CodeViewer className='content' code={article.content} mode={article.mode}/>
</div> </div>
</div> </div>
) )
} }
return ( return (
<div className='PlanetArticleDetail noteDetail'> <div className='PlanetArticleDetail noteDetail'>
<div className='viewer-header'> <div className='detailHeader'>
<i className='fa fa-file-text-o fa-fw'></i> {article.title} <small className='updatedAt'>{moment(article.updatedAt).fromNow()}</small> <div className='itemLeft'>
<span className='control-group'> <ProfileImage className='profileImage' size='25' email={article.User.email}/>
<button onClick={this.openEditModal} className='btn-default btn-square btn-sm'><i className='fa fa-edit fa-fw'></i></button> <i className='fa fa-file-text-o fa-fw'></i>
<button onClick={this.openDeleteModal} className='btn-default btn-square btn-sm'><i className='fa fa-trash fa-fw'></i></button> </div>
<div className='itemRight'>
<div className='itemInfo'>{moment(article.updatedAt).fromNow()} by <span className='userProfileName'>{article.User.profileName}</span></div>
<div className='description'>{article.title}</div>
<div className='tags'><i className='fa fa-tags'/>{tags}</div>
</div>
<span className='itemControl'>
<button id='articleEditButton' onClick={this.openEditModal} className='editButton'>
<i className='fa fa-edit fa-fw'></i>
<div className='tooltip'>Edit</div>
</button>
<button onClick={this.openDeleteModal} className='deleteButton'>
<i className='fa fa-trash fa-fw'></i>
<div className='tooltip'>Delete</div>
</button>
</span> </span>
</div> </div>
<div className='viewer-body'> <div className='detailBody'>
<div className='tags'><i className='fa fa-tags'/>{tags}</div>
<MarkdownPreview className='content' content={article.content}/> <MarkdownPreview className='content' content={article.content}/>
</div> </div>
</div> </div>

View File

@@ -13,6 +13,33 @@ module.exports = React.createClass({
articles: React.PropTypes.array, articles: React.PropTypes.array,
showOnlyWithTag: React.PropTypes.func showOnlyWithTag: React.PropTypes.func
}, },
handleArticleClikck: function (article) {
if (article.type === 'code') {
return function (e) {
var params = this.getParams()
document.getElementById('articleEditButton').focus()
this.transitionTo('codes', {
userName: params.userName,
planetName: params.planetName,
localId: article.localId
})
}.bind(this)
}
if (article.type === 'note') {
return function (e) {
var params = this.getParams()
document.getElementById('articleEditButton').focus()
this.transitionTo('notes', {
userName: params.userName,
planetName: params.planetName,
localId: article.localId
})
}.bind(this)
}
},
render: function () { render: function () {
var articles = this.props.articles.map(function (article) { var articles = this.props.articles.map(function (article) {
var tags = article.Tags.length > 0 ? article.Tags.map(function (tag) { var tags = article.Tags.length > 0 ? article.Tags.map(function (tag) {
@@ -25,28 +52,17 @@ module.exports = React.createClass({
var params = this.getParams() var params = this.getParams()
var isActive = article.type === 'code' ? this.isActive('codes') && parseInt(params.localId, 10) === article.localId : this.isActive('notes') && parseInt(params.localId, 10) === article.localId var isActive = article.type === 'code' ? this.isActive('codes') && parseInt(params.localId, 10) === article.localId : this.isActive('notes') && parseInt(params.localId, 10) === article.localId
var handleClick
if (article.type === 'code') { if (article.type === 'code') {
handleClick = function () {
this.transitionTo('codes', {
userName: params.userName,
planetName: params.planetName,
localId: article.localId
})
}.bind(this)
return ( return (
<li onClick={handleClick} key={'code-' + article.id}> <li onClick={this.handleArticleClikck(article)} key={'code-' + article.id}>
<div className={'articleItem' + (isActive ? ' active' : '')}> <div className={'articleItem' + (isActive ? ' active' : '')}>
<div className='itemLeft'> <div className='itemLeft'>
<ProfileImage className='profileImage' size='25' email={article.User.email}/> <ProfileImage className='profileImage' size='25' email={article.User.email}/>
<i className='fa fa-code fa-fw'></i> <i className='fa fa-code fa-fw'></i>
</div> </div>
<div className='itemRight'> <div className='itemRight'>
<div className='updatedAt'>{moment(article.updatedAt).fromNow()}</div> <div className='itemInfo'>{moment(article.updatedAt).fromNow()} by <span className='userProfileName'>{article.User.profileName}</span></div>
<div className='description'>{article.description.length > 50 ? article.description.substring(0, 50) + ' …' : article.description}</div> <div className='description'>{article.description}</div>
<div className='tags'><i className='fa fa-tags'/>{tags}</div> <div className='tags'><i className='fa fa-tags'/>{tags}</div>
</div> </div>
</div> </div>
@@ -55,16 +71,8 @@ module.exports = React.createClass({
) )
} }
handleClick = function () {
this.transitionTo('notes', {
userName: params.userName,
planetName: params.planetName,
localId: article.localId
})
}.bind(this)
return ( return (
<li onClick={handleClick} key={'note-' + article.id}> <li onClick={this.handleArticleClikck(article)} key={'note-' + article.id}>
<div className={'articleItem blueprintItem' + (isActive ? ' active' : '')}> <div className={'articleItem blueprintItem' + (isActive ? ' active' : '')}>
<div className='itemLeft'> <div className='itemLeft'>
<ProfileImage className='profileImage' size='25' email={article.User.email}/> <ProfileImage className='profileImage' size='25' email={article.User.email}/>
@@ -72,7 +80,7 @@ module.exports = React.createClass({
</div> </div>
<div className='itemRight'> <div className='itemRight'>
<div className='updatedAt'>{moment(article.updatedAt).fromNow()}</div> <div className='itemInfo'>{moment(article.updatedAt).fromNow()} by <span className='userProfileName'>{article.User.profileName}</span></div>
<div className='description'>{article.title}</div> <div className='description'>{article.title}</div>
<div className='tags'><i className='fa fa-tags'/>{tags}</div> <div className='tags'><i className='fa fa-tags'/>{tags}</div>
</div> </div>
@@ -85,7 +93,7 @@ module.exports = React.createClass({
return ( return (
<div className='PlanetArticleList'> <div className='PlanetArticleList'>
<ul> <ul ref='articles'>
{articles} {articles}
</ul> </ul>
</div> </div>

View File

@@ -5,11 +5,12 @@ var React = require('react/addons')
var Hq = require('../Services/Hq') var Hq = require('../Services/Hq')
var LinkedState = require('../Mixins/LinkedState') var LinkedState = require('../Mixins/LinkedState')
var KeyCaster = require('../Mixins/KeyCaster')
var PlanetStore = require('../Stores/PlanetStore') var PlanetStore = require('../Stores/PlanetStore')
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [LinkedState], mixins: [LinkedState, KeyCaster('planetCreateModal')],
propTypes: { propTypes: {
ownerName: React.PropTypes.string, ownerName: React.PropTypes.string,
transitionTo: React.PropTypes.func, transitionTo: React.PropTypes.func,
@@ -24,18 +25,25 @@ module.exports = React.createClass({
name: '', name: '',
public: true public: true
}, },
ownerName: ownerName ownerName: ownerName,
error: null
} }
}, },
componentDidMount: function () { componentDidMount: function () {
React.findDOMNode(this.refs.name).focus() React.findDOMNode(this.refs.name).focus()
}, },
onListen: function (res) { onKeyCast: function (e) {
if (res.status === 'planetCreated') { switch (e.status) {
case 'closeModal':
this.props.close() this.props.close()
break
case 'submitPlanetCreateModal':
this.handleSubmit()
break
} }
}, },
handleSubmit: function () { handleSubmit: function () {
this.setState({error: null}, function () {
Hq.createPlanet(this.state.ownerName, this.state.planet) Hq.createPlanet(this.state.ownerName, this.state.planet)
.then(function (res) { .then(function (res) {
var planet = res.body var planet = res.body
@@ -50,6 +58,23 @@ module.exports = React.createClass({
}.bind(this)) }.bind(this))
.catch(function (err) { .catch(function (err) {
console.error(err) console.error(err)
if (err.status == null) return this.setState({error: {message: 'Check your network connection'}})
switch (err.status) {
case 403:
this.setState({error: err.response.body})
break
case 422:
this.setState({error: {message: 'Planet name should be Alphanumeric with _, -'}})
break
case 409:
this.setState({error: {message: 'The entered name already in use'}})
break
default:
this.setState({error: {message: 'Unexpected error occured! please try again'}})
}
}.bind(this))
}) })
}, },
render: function () { render: function () {
@@ -75,6 +100,8 @@ module.exports = React.createClass({
</select> </select>
</div> </div>
{this.state.error != null ? (<p className='errorAlert'>{this.state.error.message != null ? this.state.error.message : 'Error message undefined'}</p>) : null}
<button onClick={this.handleSubmit} className='submitButton'> <button onClick={this.handleSubmit} className='submitButton'>
<i className='fa fa-check'/> <i className='fa fa-check'/>
</button> </button>

View File

@@ -21,7 +21,22 @@ module.exports = React.createClass({
} }
}, },
componentDidMount: function () { componentDidMount: function () {
React.findDOMNode(this.refs.search).focus() var search = React.findDOMNode(this.refs.search)
search.addEventListener('keydown', this.handleSearchKeyDown)
},
componentWillUnmount: function () {
var search = React.findDOMNode(this.refs.search)
search.removeEventListener('keydown', this.handleSearchKeyDown)
},
handleSearchKeyDown: function (e) {
if (e.keyCode === 38 || e.keyCode === 40) {
var search = React.findDOMNode(this.refs.search)
search.blur()
e.preventDefault()
}
if (e.keyCode !== 27 && (e.keyCode !== 13 || !e.metaKey)) {
e.stopPropagation()
}
}, },
openPlanetSettingModal: function () { openPlanetSettingModal: function () {
this.openModal(PlanetSettingModal, {planet: this.props.currentPlanet}) this.openModal(PlanetSettingModal, {planet: this.props.currentPlanet})
@@ -42,12 +57,13 @@ module.exports = React.createClass({
{this.props.currentPlanet.public ? null : ( {this.props.currentPlanet.public ? null : (
<div className='private'> <div className='private'>
<i className='fa fa-lock'/> <i className='fa fa-lock'/>
<div className='privateTooltip'>Private planet</div> <div className='tooltip'>Private planet</div>
</div> </div>
)} )}
<button onClick={this.openPlanetSettingModal} className='menuBtn'> <button onClick={this.openPlanetSettingModal} className='planetSettingButton'>
<i className='fa fa-chevron-down'></i> <i className='fa fa-chevron-down'></i>
<div className='tooltip'>Planet setting</div>
</button> </button>
</div> </div>
<div className='headerControl'> <div className='headerControl'>
@@ -55,9 +71,13 @@ module.exports = React.createClass({
<i className='fa fa-search'/> <i className='fa fa-search'/>
<input onChange={this.props.onSearchChange} value={this.props.search} ref='search' type='text' className='inline-input circleInput' placeholder='Search...'/> <input onChange={this.props.onSearchChange} value={this.props.search} ref='search' type='text' className='inline-input circleInput' placeholder='Search...'/>
</div> </div>
<button onClick={this.refresh} className='refreshButton'><i className='fa fa-refresh'/></button> <button onClick={this.refresh} className='refreshButton'>
<i className='fa fa-refresh'/>
<div className='tooltip'>Refresh planet</div>
</button>
<a onClick={this.openExternal} href='http://b00st.io' className='logo'> <a onClick={this.openExternal} href='http://b00st.io' className='logo'>
<img width='44' height='44' src='resources/favicon-230x230.png'/> <img width='44' height='44' src='resources/favicon-230x230.png'/>
<div className='tooltip'>Boost official page</div>
</a> </a>
</div> </div>
</div> </div>

View File

@@ -3,29 +3,47 @@ var ReactRouter = require('react-router')
var Navigation = ReactRouter.Navigation var Navigation = ReactRouter.Navigation
var Modal = require('../Mixins/Modal') var Modal = require('../Mixins/Modal')
var LaunchModal = require('../Components/LaunchModal') var LaunchModal = require('../Components/LaunchModal')
var PlanetNavigator = React.createClass({ module.exports = React.createClass({
mixins: [Modal, Navigation], mixins: [Modal, Navigation],
propTypes: { propTypes: {
planet: React.PropTypes.shape({ planet: React.PropTypes.shape({
name: React.PropTypes.string name: React.PropTypes.string,
Owner: React.PropTypes.shape({
id: React.PropTypes.number,
userType: React.PropTypes.string
})
}), }),
search: React.PropTypes.string, search: React.PropTypes.string,
toggleCodeFilter: React.PropTypes.func, toggleCodeFilter: React.PropTypes.func,
toggleNoteFilter: React.PropTypes.func toggleNoteFilter: React.PropTypes.func,
currentUser: React.PropTypes.shape({
id: React.PropTypes.number,
userType: React.PropTypes.string,
Teams: React.PropTypes.array
})
}, },
getInitialState: function () { getInitialState: function () {
return { return {
isLaunchModalOpen: false isLaunchModalOpen: false
} }
}, },
submitLaunchModal: function (ret) {
this.setState({isLaunchModalOpen: false})
},
openLaunchModal: function () { openLaunchModal: function () {
this.openModal(LaunchModal, {planet: this.props.planet, transitionTo: this.transitionTo}) this.openModal(LaunchModal, {planet: this.props.planet, transitionTo: this.transitionTo})
}, },
isMyPlanet: function () {
if (this.props.currentUser == null) return false
if (this.props.planet.Owner.userType === 'person' && this.props.planet.Owner.id !== this.props.currentUser.id) return false
if (this.props.planet.Owner.userType === 'team' && !this.props.currentUser.Teams.some(function (team) {
if (team.id === this.props.planet.Owner.id) return true
return false
}.bind(this))) return false
return true
},
render: function () { render: function () {
var keywords = this.props.search.split(' ') var keywords = this.props.search.split(' ')
var usingCodeFilter = keywords.some(function (keyword) { var usingCodeFilter = keywords.some(function (keyword) {
@@ -39,9 +57,11 @@ var PlanetNavigator = React.createClass({
return ( return (
<div className='PlanetNavigator'> <div className='PlanetNavigator'>
{this.isMyPlanet() ? (
<button onClick={this.openLaunchModal} className='launchButton btn-primary btn-block'> <button onClick={this.openLaunchModal} className='launchButton btn-primary btn-block'>
<i className='fa fa-rocket fa-fw'/> Launch <i className='fa fa-rocket fa-fw'/> Launch
</button> </button>
) : null}
<nav className='articleFilters'> <nav className='articleFilters'>
<a className={usingCodeFilter && !usingNoteFilter ? 'active' : ''} onClick={this.props.toggleCodeFilter}> <a className={usingCodeFilter && !usingNoteFilter ? 'active' : ''} onClick={this.props.toggleCodeFilter}>
<i className='fa fa-code fa-fw'/> Codes <i className='fa fa-code fa-fw'/> Codes
@@ -54,5 +74,3 @@ var PlanetNavigator = React.createClass({
) )
} }
}) })
module.exports = PlanetNavigator

View File

@@ -3,11 +3,12 @@ var React = require('react/addons')
var Hq = require('../Services/Hq') var Hq = require('../Services/Hq')
var LinkedState = require('../Mixins/LinkedState') var LinkedState = require('../Mixins/LinkedState')
var KeyCaster = require('../Mixins/KeyCaster')
var PlanetStore = require('../Stores/PlanetStore') var PlanetStore = require('../Stores/PlanetStore')
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [LinkedState], mixins: [LinkedState, KeyCaster('planetSettingModal')],
propTypes: { propTypes: {
close: React.PropTypes.func, close: React.PropTypes.func,
planet: React.PropTypes.shape({ planet: React.PropTypes.shape({
@@ -35,6 +36,13 @@ module.exports = React.createClass({
deleteConfirmation: '' deleteConfirmation: ''
} }
}, },
onKeyCast: function (e) {
switch (e.status) {
case 'closeModal':
this.props.close()
break
}
},
activePlanetProfile: function () { activePlanetProfile: function () {
this.setState({currentTab: 'profile'}) this.setState({currentTab: 'profile'})
}, },
@@ -50,18 +58,44 @@ module.exports = React.createClass({
handleSavePlanetProfile: function (e) { handleSavePlanetProfile: function (e) {
var planet = this.props.planet var planet = this.props.planet
this.setState({profileSubmitStatus: 'sending'}, function () { this.setState({profileFormStatus: 'sending', profileFormError: null}, function () {
Hq.updatePlanet(planet.userName, planet.name, this.state.planet) Hq.updatePlanet(planet.userName, planet.name, this.state.planet)
.then(function (res) { .then(function (res) {
var planet = res.body var planet = res.body
console.log(planet)
this.setState({profileSubmitStatus: 'done'}) this.setState({profileFormStatus: 'done'})
PlanetStore.Actions.update(planet) PlanetStore.Actions.update(planet)
this.props.close()
}.bind(this)) }.bind(this))
.catch(function (err) { .catch(function (err) {
this.setState({profileSubmitStatus: 'error'})
console.error(err) console.error(err)
var newState = {
profileFormStatus: 'error'
}
if (err.status == null) {
newState.profileFormError = {message: 'Check your network connection'}
return this.setState(newState)
}
switch (err.status) {
case 403:
newState.profileFormError = err.response.body
this.setState(newState)
break
case 422:
newState.profileFormError = {message: 'Planet name should be Alphanumeric with _, -'}
this.setState(newState)
break
case 409:
newState.profileFormError = {message: 'The entered name already in use'}
this.setState(newState)
break
default:
newState.profileFormError = {message: 'Undefined error please try again'}
this.setState(newState)
}
}.bind(this)) }.bind(this))
}) })
}, },
@@ -91,9 +125,9 @@ module.exports = React.createClass({
content = this.state.currentTab === 'profile' ? this.renderPlanetProfileTab() : this.renderPlanetDeleteTab() content = this.state.currentTab === 'profile' ? this.renderPlanetProfileTab() : this.renderPlanetDeleteTab()
return ( return (
<div className='PlanetSettingModal modal tabModal'> <div className='PlanetSettingModal sideNavModal modal'>
<div className='leftPane'> <div className='leftPane'>
<h1 className='tabLabel'>Planet setting</h1> <h1 className='modalLabel'>Planet setting</h1>
<nav className='tabList'> <nav className='tabList'>
<button onClick={this.activePlanetProfile} className={this.state.currentTab === 'profile' ? 'active' : ''}><i className='fa fa-globe fa-fw'/> Planet profile</button> <button onClick={this.activePlanetProfile} className={this.state.currentTab === 'profile' ? 'active' : ''}><i className='fa fa-globe fa-fw'/> Planet profile</button>
<button onClick={this.activePlanetDelete} className={this.state.currentTab === 'delete' ? 'active' : ''}><i className='fa fa-trash fa-fw'/> Delete Planet</button> <button onClick={this.activePlanetDelete} className={this.state.currentTab === 'delete' ? 'active' : ''}><i className='fa fa-trash fa-fw'/> Delete Planet</button>
@@ -108,7 +142,7 @@ module.exports = React.createClass({
}, },
renderPlanetProfileTab: function () { renderPlanetProfileTab: function () {
return ( return (
<div className='planetProfileTab'> <div className='planetProfileTab tab'>
<div className='formField'> <div className='formField'>
<label>Planet name </label> <label>Planet name </label>
<input valueLink={this.linkState('planet.name')}/> <input valueLink={this.linkState('planet.name')}/>
@@ -122,11 +156,11 @@ module.exports = React.createClass({
<div className='formConfirm'> <div className='formConfirm'>
<button onClick={this.handleSavePlanetProfile} className='saveButton btn-primary'>Save</button> <button onClick={this.handleSavePlanetProfile} className='saveButton btn-primary'>Save</button>
<div className={'alertInfo' + (this.state.profileSubmitStatus === 'sending' ? '' : ' hide')}>on Sending...</div> <div className={'alertInfo' + (this.state.profileFormStatus === 'sending' ? '' : ' hide')}>on Sending...</div>
<div className={'alertError' + (this.state.profileSubmitStatus === 'error' ? '' : ' hide')}>Connection failed.. Try again.</div> <div className={'alertError' + (this.state.profileFormStatus === 'error' ? '' : ' hide')}>{this.state.profileFormError != null ? this.state.profileFormError.message : 'Unexpected error occured! please try again'}</div>
<div className={'alertSuccess' + (this.state.profileSubmitStatus === 'done' ? '' : ' hide')}>Successfully done!!</div> <div className={'alertSuccess' + (this.state.profileFormStatus === 'done' ? '' : ' hide')}>Successfully done!!</div>
</div> </div>
</div> </div>
) )
@@ -135,7 +169,7 @@ module.exports = React.createClass({
var disabled = !this.state.deleteConfirmation.match(new RegExp('^' + this.props.planet.userName + '/' + this.props.planet.name + '$')) var disabled = !this.state.deleteConfirmation.match(new RegExp('^' + this.props.planet.userName + '/' + this.props.planet.name + '$'))
return ( return (
<div className='planetDeleteTab'> <div className='planetDeleteTab tab'>
<p>Are you sure to destroy <strong>'{this.props.planet.userName + '/' + this.props.planet.name}'</strong>?</p> <p>Are you sure to destroy <strong>'{this.props.planet.userName + '/' + this.props.planet.name}'</strong>?</p>
<p>If you are sure, write <strong>'{this.props.planet.userName + '/' + this.props.planet.name}'</strong> to input below and click <strong>'{this.state.randomDeleteText}'</strong> button.</p> <p>If you are sure, write <strong>'{this.props.planet.userName + '/' + this.props.planet.name}'</strong> to input below and click <strong>'{this.state.randomDeleteText}'</strong> button.</p>
<input valueLink={this.linkState('deleteConfirmation')} placeholder='userName/planetName'/> <input valueLink={this.linkState('deleteConfirmation')} placeholder='userName/planetName'/>

View File

@@ -0,0 +1,112 @@
var ipc = require('ipc')
var remote = require('remote')
var React = require('react/addons')
var LinkedState = require('../Mixins/LinkedState')
var ExternalLink = require('../Mixins/ExternalLink')
var KeyCaster = require('../Mixins/KeyCaster')
module.exports = React.createClass({
mixins: [LinkedState, ExternalLink, KeyCaster('aboutModal')],
propTypes: {
close: React.PropTypes.func
},
getInitialState: function () {
var keymap = remote.getGlobal('keymap')
console.log(keymap)
return {
currentTab: 'settings',
keymap: keymap
}
},
onKeyCast: function (e) {
switch (e.status) {
case 'closeModal':
this.props.close()
break
}
},
activeSettings: function () {
this.setState({currentTab: 'settings'})
},
activeAbout: function () {
this.setState({currentTab: 'about'})
},
saveKeymap: function () {
ipc.send('hotkeyUpdated', JSON.stringify(this.state.keymap))
},
render: function () {
var content = this.state.currentTab === 'settings' ? this.renderSettingsTab() : this.renderAboutTab()
return (
<div className='PreferencesModal sideNavModal modal'>
<div className='leftPane'>
<h1 className='modalLabel'>Preferences</h1>
<nav className='tabList'>
<button onClick={this.activeSettings} className={this.state.currentTab === 'settings' ? 'active' : ''}><i className='fa fa-gear fa-fw'/> Settings</button>
<button onClick={this.activeAbout} className={this.state.currentTab === 'about' ? 'active' : ''}><i className='fa fa-info-circle fa-fw'/> About this app</button>
</nav>
</div>
<div className='rightPane'>
{content}
</div>
</div>
)
},
renderSettingsTab: function () {
return (
<div className='settingsTab tab'>
<div className='categoryLabel'>Hotkey</div>
<div className='formField'>
<label>Toggle finder</label>
<input valueLink={this.linkState('keymap.toggleFinder')}/>
</div>
<div className='formConfirm'>
<button onClick={this.saveKeymap}>Save</button>
</div>
<div className='example'>
<h3>Example</h3>
<ul>
<li><code>0</code> to <code>9</code></li>
<li><code>A</code> to <code>Z</code></li>
<li><code>F1</code> to <code>F24</code></li>
<li>Punctuations like <code>~</code>, <code>!</code>, <code>@</code>, <code>#</code>, <code>$</code>, etc.</li>
<li><code>Plus</code></li>
<li><code>Space</code></li>
<li><code>Backspace</code></li>
<li><code>Delete</code></li>
<li><code>Insert</code></li>
<li><code>Return</code> (or <code>Enter</code> as alias)</li>
<li><code>Up</code>, <code>Down</code>, <code>Left</code> and <code>Right</code></li>
<li><code>Home</code> and <code>End</code></li>
<li><code>PageUp</code> and <code>PageDown</code></li>
<li><code>Escape</code> (or <code>Esc</code> for short)</li>
<li><code>VolumeUp</code>, <code>VolumeDown</code> and <code>VolumeMute</code></li>
<li><code>MediaNextTrack</code>, <code>MediaPreviousTrack</code>, <code>MediaStop</code> and <code>MediaPlayPause</code></li>
</ul>
</div>
</div>
)
},
renderAboutTab: function () {
var version = global.version
return (
<div className='aboutTab tab'>
<div className='about1'>
<img className='logo' src='resources/favicon-230x230.png'/>
<div className='appInfo'>Boost {version == null || version.length === 0 ? 'DEV version' : 'v' + version}</div>
</div>
<div className='about2'>
<div className='externalLabel'>External links</div>
<ul className='externalList'>
<li><a onClick={this.openExternal} href='http://b00st.io'>Boost Homepage <i className='fa fa-external-link'/></a></li>
<li><a onClick={this.openExternal} href='http://boostio.github.io/regulations.html'>Regulation <i className='fa fa-external-link'/></a></li>
<li><a onClick={this.openExternal} href='http://boostio.github.io/privacypolicies.html'>Private policy <i className='fa fa-external-link'/></a></li>
</ul>
</div>
</div>
)
}
})

View File

@@ -5,11 +5,12 @@ var React = require('react/addons')
var Hq = require('../Services/Hq') var Hq = require('../Services/Hq')
var LinkedState = require('../Mixins/LinkedState') var LinkedState = require('../Mixins/LinkedState')
var KeyCaster = require('../Mixins/KeyCaster')
var UserStore = require('../Stores/UserStore') var UserStore = require('../Stores/UserStore')
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [LinkedState], mixins: [LinkedState, KeyCaster('teamCreateModal')],
propTypes: { propTypes: {
user: React.PropTypes.shape({ user: React.PropTypes.shape({
name: React.PropTypes.string name: React.PropTypes.string
@@ -21,10 +22,25 @@ module.exports = React.createClass({
return { return {
team: { team: {
name: '' name: ''
},
error: null
} }
},
componentDidMount: function () {
React.findDOMNode(this.refs.teamName).focus()
},
onKeyCast: function (e) {
switch (e.status) {
case 'closeModal':
this.props.close()
break
case 'submitTeamCreateModal':
this.handleSubmit()
break
} }
}, },
handleSubmit: function () { handleSubmit: function () {
this.setState({error: null}, function () {
Hq.createTeam(this.props.user.name, this.state.team) Hq.createTeam(this.props.user.name, this.state.team)
.then(function (res) { .then(function (res) {
var currentUser = JSON.parse(localStorage.getItem('currentUser')) var currentUser = JSON.parse(localStorage.getItem('currentUser'))
@@ -41,13 +57,27 @@ module.exports = React.createClass({
}.bind(this)) }.bind(this))
.catch(function (err) { .catch(function (err) {
console.error(err) console.error(err)
if (err.status == null) return this.setState({error: {message: 'Check your network connection'}})
switch (err.status) {
case 422:
this.setState({error: {message: 'Team name should be Alphanumeric with _, -'}})
break
case 409:
this.setState({error: {message: 'The entered name already in use'}})
break
default:
this.setState({error: {message: 'Error message undefined'}})
}
}.bind(this))
}) })
}, },
render: function () { render: function () {
return ( return (
<div className='TeamCreateModal modal'> <div className='TeamCreateModal modal'>
<input valueLink={this.linkState('team.name')} className='nameInput stripInput' placeholder='Create new team'/> <input ref='teamName' valueLink={this.linkState('team.name')} className='nameInput stripInput' placeholder='Create new team'/>
{this.state.error != null ? (<p className='errorAlert'>{this.state.error.message != null ? this.state.error.message : 'Unintended error occured'}</p>) : null}
<button onClick={this.handleSubmit} className='submitButton'> <button onClick={this.handleSubmit} className='submitButton'>
<i className='fa fa-check'/> <i className='fa fa-check'/>
</button> </button>

View File

@@ -8,6 +8,7 @@ var Hq = require('../Services/Hq')
var LinkedState = require('../Mixins/LinkedState') var LinkedState = require('../Mixins/LinkedState')
var Helper = require('../Mixins/Helper') var Helper = require('../Mixins/Helper')
var KeyCaster = require('../Mixins/KeyCaster')
var UserStore = require('../Stores/UserStore') var UserStore = require('../Stores/UserStore')
@@ -30,7 +31,7 @@ var getOptions = function (input, callback) {
} }
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [LinkedState, Reflux.listenTo(UserStore, 'onUserChange'), Helper], mixins: [LinkedState, Reflux.listenTo(UserStore, 'onUserChange'), Helper, KeyCaster('teamSettingsModal')],
propTypes: { propTypes: {
team: React.PropTypes.shape({ team: React.PropTypes.shape({
id: React.PropTypes.number, id: React.PropTypes.number,
@@ -38,7 +39,8 @@ module.exports = React.createClass({
profileName: React.PropTypes.string, profileName: React.PropTypes.string,
email: React.PropTypes.string, email: React.PropTypes.string,
Members: React.PropTypes.array Members: React.PropTypes.array
}) }),
close: React.PropTypes.func
}, },
getInitialState: function () { getInitialState: function () {
var team = this.props.team var team = this.props.team
@@ -55,6 +57,13 @@ module.exports = React.createClass({
updatingMember: false updatingMember: false
} }
}, },
onKeyCast: function (e) {
switch (e.status) {
case 'closeModal':
this.props.close()
break
}
},
onUserChange: function (res) { onUserChange: function (res) {
var member var member
switch (res.status) { switch (res.status) {
@@ -167,9 +176,9 @@ module.exports = React.createClass({
} }
return ( return (
<div className='TeamSettingsModal modal tabModal'> <div className='TeamSettingsModal sideNavModal modal'>
<div className='leftPane'> <div className='leftPane'>
<div className='tabLabel'>Team settings</div> <div className='modalLabel'>Team settings</div>
<div className='tabList'> <div className='tabList'>
<button className={this.state.currentTab === 'teamInfo' ? 'active' : ''} onClick={this.selectTab('teamInfo')}><i className='fa fa-info-circle fa-fw'/> Team Info</button> <button className={this.state.currentTab === 'teamInfo' ? 'active' : ''} onClick={this.selectTab('teamInfo')}><i className='fa fa-info-circle fa-fw'/> Team Info</button>
<button className={this.state.currentTab === 'members' ? 'active' : ''} onClick={this.selectTab('members')}><i className='fa fa-users fa-fw'/> Members</button> <button className={this.state.currentTab === 'members' ? 'active' : ''} onClick={this.selectTab('members')}><i className='fa fa-users fa-fw'/> Members</button>
@@ -183,7 +192,7 @@ module.exports = React.createClass({
}, },
renderTeamInfoTab: function () { renderTeamInfoTab: function () {
return ( return (
<div className='userInfoTab'> <div className='userInfoTab tab'>
<div className='formField'> <div className='formField'>
<label>Profile Name</label> <label>Profile Name</label>
<input valueLink={this.linkState('team.profileName')}/> <input valueLink={this.linkState('team.profileName')}/>
@@ -230,7 +239,7 @@ module.exports = React.createClass({
var belowLimit = members.length < 5 var belowLimit = members.length < 5
return ( return (
<div className='membersTab'> <div className='membersTab tab'>
<table className='memberTable'> <table className='memberTable'>
<thead> <thead>
<tr> <tr>
@@ -264,7 +273,7 @@ module.exports = React.createClass({
</div> </div>
) : ( ) : (
<div> <div>
Maximum numbr of members is 5 on Beta version. Please contact us if you want futher use. Maximum number of members is 5 on Beta version. Please contact us if you want futher use.
</div> </div>
)} )}
</div> </div>

View File

@@ -7,21 +7,33 @@ var State = ReactRouter.State
var Navigation = ReactRouter.Navigation var Navigation = ReactRouter.Navigation
var AuthFilter = require('../Mixins/AuthFilter') var AuthFilter = require('../Mixins/AuthFilter')
var KeyCaster = require('../Mixins/KeyCaster')
var HomeNavigator = require('../Components/HomeNavigator') var HomeNavigator = require('../Components/HomeNavigator')
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [AuthFilter.OnlyUser, State, Navigation], mixins: [AuthFilter.OnlyUser, State, Navigation, KeyCaster('homeContainer')],
componentDidMount: function () { componentDidMount: function () {
if (this.isActive('homeEmpty')) { if (this.isActive('homeEmpty')) {
var user = JSON.parse(localStorage.getItem('currentUser')) var user = JSON.parse(localStorage.getItem('currentUser'))
if (user.Planets != null && user.Planets.length > 0) {
this.transitionTo('planet', {userName: user.name, planetName: user.Planets[0].name})
return
}
this.transitionTo('userHome', {userName: user.name}) this.transitionTo('userHome', {userName: user.name})
} }
}, },
onKeyCast: function (e) {
switch (e.status) {
case 'switchPlanet':
this.refs.navigator.switchPlanetByIndex(e.data)
break
}
},
render: function () { render: function () {
return ( return (
<div className='HomeContainer'> <div className='HomeContainer'>
<HomeNavigator/> <HomeNavigator ref='navigator'/>
<RouteHandler/> <RouteHandler/>
</div> </div>
) )

View File

@@ -55,7 +55,6 @@ module.exports = React.createClass({
return return
} else { } else {
this.transitionTo('home') this.transitionTo('home')
return
} }
} }
@@ -96,7 +95,10 @@ module.exports = React.createClass({
{this.state.updateAvailable ? ( {this.state.updateAvailable ? (
<button onClick={this.updateApp} className='appUpdateButton'><i className='fa fa-cloud-download'/> Update available!</button> <button onClick={this.updateApp} className='appUpdateButton'><i className='fa fa-cloud-download'/> Update available!</button>
) : null} ) : null}
<button onClick={this.openContactModal} className='contactButton'><i className='fa fa-paper-plane-o'/></button> <button onClick={this.openContactModal} className='contactButton'>
<i className='fa fa-paper-plane-o'/>
<div className='tooltip'>Contact us</div>
</button>
<RouteHandler/> <RouteHandler/>
</div> </div>
) )

View File

@@ -14,12 +14,13 @@ var Hq = require('../Services/Hq')
var Modal = require('../Mixins/Modal') var Modal = require('../Mixins/Modal')
var ArticleFilter = require('../Mixins/ArticleFilter') var ArticleFilter = require('../Mixins/ArticleFilter')
var Helper = require('../Mixins/Helper') var Helper = require('../Mixins/Helper')
var KeyCaster = require('../Mixins/KeyCaster')
var UserStore = require('../Stores/UserStore') var UserStore = require('../Stores/UserStore')
var PlanetStore = require('../Stores/PlanetStore') var PlanetStore = require('../Stores/PlanetStore')
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [ReactRouter.Navigation, ReactRouter.State, Modal, Reflux.listenTo(UserStore, 'onUserChange'), Reflux.listenTo(PlanetStore, 'onPlanetChange'), ArticleFilter, Helper], mixins: [ReactRouter.Navigation, ReactRouter.State, Modal, Reflux.listenTo(UserStore, 'onUserChange'), Reflux.listenTo(PlanetStore, 'onPlanetChange'), ArticleFilter, Helper, KeyCaster('planetContainer')],
propTypes: { propTypes: {
params: React.PropTypes.object, params: React.PropTypes.object,
planetName: React.PropTypes.string planetName: React.PropTypes.string
@@ -62,6 +63,28 @@ module.exports = React.createClass({
}) })
} }
}, },
onKeyCast: function (e) {
switch (e.status) {
case 'openLaunchModal':
this.refs.navigator.openLaunchModal()
break
case 'selectNextArticle':
this.selectNextArticle()
break
case 'selectPriorArticle':
this.selectPriorArticle()
break
case 'toggleFocusSearchInput':
this.toggleFocusSearchInput()
break
case 'openEditModal':
this.refs.detail.openEditModal()
break
case 'openDeleteModal':
this.refs.detail.openDeleteModal()
break
}
},
onPlanetChange: function (res) { onPlanetChange: function (res) {
if (this.state.planet == null) return if (this.state.planet == null) return
@@ -207,6 +230,18 @@ module.exports = React.createClass({
return return
} }
var listElement = this.refs.list.refs.articles.getDOMNode()
var articleElement = listElement.querySelectorAll('li')[index]
var overflowBelow = listElement.clientHeight + listElement.scrollTop < articleElement.offsetTop + articleElement.clientHeight
if (overflowBelow) {
listElement.scrollTop = articleElement.offsetTop + articleElement.clientHeight - listElement.clientHeight
}
var overflowAbove = listElement.scrollTop > articleElement.offsetTop
if (overflowAbove) {
listElement.scrollTop = articleElement.offsetTop
}
if (article.type === 'code') { if (article.type === 'code') {
params.localId = article.localId params.localId = article.localId
this.transitionTo('codes', params) this.transitionTo('codes', params)
@@ -237,9 +272,17 @@ module.exports = React.createClass({
if (index > 0) { if (index > 0) {
this.selectArticleByListIndex(index - 1) this.selectArticleByListIndex(index - 1)
} else { } else {
React.findDOMNode(this).querySelector('.PlanetHeader .searchInput input').focus() React.findDOMNode(this.refs.header.refs.search).focus()
} }
}, },
toggleFocusSearchInput: function () {
var search = React.findDOMNode(this.refs.header.refs.search)
if (document.activeElement === search) {
React.findDOMNode(this.refs.header.refs.search).blur()
return
}
React.findDOMNode(this.refs.header.refs.search).focus()
},
handleSearchChange: function (e) { handleSearchChange: function (e) {
this.setState({search: e.target.value}, function () { this.setState({search: e.target.value}, function () {
this.selectArticleByListIndex(0) this.selectArticleByListIndex(0)
@@ -309,9 +352,6 @@ module.exports = React.createClass({
this.setState({search: '#' + tag}) this.setState({search: '#' + tag})
}.bind(this) }.bind(this)
}, },
focus: function () {
React.findDOMNode(this).focus()
},
render: function () { render: function () {
if (this.state.planet == null) return (<div/>) if (this.state.planet == null) return (<div/>)
@@ -346,6 +386,7 @@ module.exports = React.createClass({
return ( return (
<div className='PlanetContainer'> <div className='PlanetContainer'>
<PlanetHeader <PlanetHeader
ref='header'
search={this.state.search} search={this.state.search}
fetchPlanet={this.fetchPlanet} fetchPlanet={this.fetchPlanet}
onSearchChange={this.handleSearchChange} onSearchChange={this.handleSearchChange}
@@ -358,7 +399,8 @@ module.exports = React.createClass({
showAll={this.showAll} showAll={this.showAll}
toggleCodeFilter={this.toggleCodeFilter} toggleCodeFilter={this.toggleCodeFilter}
toggleNoteFilter={this.toggleNoteFilter} toggleNoteFilter={this.toggleNoteFilter}
planet={this.state.planet}/> planet={this.state.planet}
currentUser={this.state.currentUser}/>
<PlanetArticleList showOnlyWithTag={this.applyTagFilter} ref='list' articles={filteredArticles}/> <PlanetArticleList showOnlyWithTag={this.applyTagFilter} ref='list' articles={filteredArticles}/>

View File

@@ -6,10 +6,11 @@ var Link = ReactRouter.Link
var AuthFilter = require('../Mixins/AuthFilter') var AuthFilter = require('../Mixins/AuthFilter')
var LinkedState = require('../Mixins/LinkedState') var LinkedState = require('../Mixins/LinkedState')
var ExternalLink = require('../Mixins/ExternalLink')
var Hq = require('../Services/Hq') var Hq = require('../Services/Hq')
module.exports = React.createClass({ module.exports = React.createClass({
mixins: [LinkedState, ReactRouter.Navigation, AuthFilter.OnlyGuest], mixins: [LinkedState, ReactRouter.Navigation, AuthFilter.OnlyGuest, ExternalLink],
getInitialState: function () { getInitialState: function () {
return { return {
user: {}, user: {},
@@ -129,7 +130,7 @@ module.exports = React.createClass({
</div> </div>
</form> </form>
<p className='alert'>会員登録することで当サイトの利用規約及びCookieの使用を含むデータに関するポリシーに同意するものとします</p> <p className='alert'>会員登録することで<a onClick={this.openExternal} href='http://boostio.github.io/regulations.html'>当サイトの利用規約</a>及び<a onClick={this.openExternal} href='http://boostio.github.io/privacypolicies.html'>Cookieの使用を含むデータに関するポリシー</a>に同意するものとします</p>
</div> </div>
) )
} }

View File

@@ -190,7 +190,6 @@ module.exports = React.createClass({
return this.renderUserHome(currentUser) return this.renderUserHome(currentUser)
} }
} else if (this.isActive('planet') && user != null && user.userType === 'team') { } else if (this.isActive('planet') && user != null && user.userType === 'team') {
console.log(user.Members)
var members = user.Members.map(function (member) { var members = user.Members.map(function (member) {
return ( return (
<li key={'user-' + member.id}><Link to='userHome' params={{userName: member.name}}> <li key={'user-' + member.id}><Link to='userHome' params={{userName: member.name}}>
@@ -224,7 +223,9 @@ module.exports = React.createClass({
renderTeamHome: function (currentUser) { renderTeamHome: function (currentUser) {
var user = this.state.user var user = this.state.user
var isOwner = true var isOwner = user.Members == null ? false : user.Members.some(function (member) {
return member.id === currentUser.id && member.TeamMember.role === 'owner'
})
var userPlanets = user.Planets.map(function (planet) { var userPlanets = user.Planets.map(function (planet) {
return ( return (
@@ -258,7 +259,7 @@ module.exports = React.createClass({
<div className='userName'>{user.name}</div> <div className='userName'>{user.name}</div>
</div> </div>
<button onClick={this.openTeamSettingsModal} className='editProfileButton'>Team settings</button> {isOwner ? (<button onClick={this.openTeamSettingsModal} className='editProfileButton'>Team settings</button>) : null}
</div> </div>
<div className='memberList'> <div className='memberList'>
<div className='memberLabel'>{members.length} {members.length > 1 ? 'Members' : 'Member'}</div> <div className='memberLabel'>{members.length} {members.length > 1 ? 'Members' : 'Member'}</div>
@@ -317,7 +318,7 @@ module.exports = React.createClass({
}) })
return ( return (
<div key={'user-' + team.id} className='planetGroup'> <div key={'user-' + team.id} className='planetGroup'>
<div className='planetGroupLabel'>{team.name}</div> <div className='planetGroupLabel'>{team.profileName} <small>@{team.name}</small></div>
<ul className='planets'> <ul className='planets'>
{planets} {planets}
{isOwner ? (<li><button onClick={this.openPlanetCreateModalWithOwnerName(team.name)} className='createPlanetButton'><i className='fa fa-plus-square-o'/> Create new planet</button></li>) : null} {isOwner ? (<li><button onClick={this.openPlanetCreateModalWithOwnerName(team.name)} className='createPlanetButton'><i className='fa fa-plus-square-o'/> Create new planet</button></li>) : null}
@@ -352,7 +353,7 @@ module.exports = React.createClass({
<div className='planetList'> <div className='planetList'>
<div className='planetLabel'>{planetCount} {planetCount > 1 ? 'Planets' : 'Planet'}</div> <div className='planetLabel'>{planetCount} {planetCount > 1 ? 'Planets' : 'Planet'}</div>
<div className='planetGroup'> <div className='planetGroup'>
<div className='planetGroupLabel'>{user.profileName}</div> <div className='planetGroupLabel'>{user.profileName} <small>@{user.name}</small></div>
<ul className='planets'> <ul className='planets'>
{userPlanets} {userPlanets}
{isOwner ? (<li><button onClick={this.openPlanetCreateModalWithOwnerName(user.name)} className='createPlanetButton'><i className='fa fa-plus-square-o'/> Create new planet</button></li>) : null} {isOwner ? (<li><button onClick={this.openPlanetCreateModalWithOwnerName(user.name)} className='createPlanetButton'><i className='fa fa-plus-square-o'/> Create new planet</button></li>) : null}

View File

@@ -1,4 +1,5 @@
function deleteItemFromTargetArray (item, targetArray) { function deleteItemFromTargetArray (item, targetArray) {
if (targetArray == null) targetArray = []
targetArray.some(function (_item, index) { targetArray.some(function (_item, index) {
if (_item.id === item.id) { if (_item.id === item.id) {
targetArray.splice(index, 1) targetArray.splice(index, 1)
@@ -11,6 +12,8 @@ function deleteItemFromTargetArray (item, targetArray) {
} }
function updateItemToTargetArray (item, targetArray) { function updateItemToTargetArray (item, targetArray) {
if (targetArray == null) targetArray = []
var isNew = !targetArray.some(function (_item, index) { var isNew = !targetArray.some(function (_item, index) {
if (_item.id === item.id) { if (_item.id === item.id) {
targetArray.splice(index, 1, item) targetArray.splice(index, 1, item)

View File

@@ -0,0 +1,100 @@
var Reflux = require('reflux')
var state = {
}
var keyDown = Reflux.createAction()
var KeyStore = Reflux.createStore({
init: function () {
this.listenTo(keyDown, this.onKeyDown)
document.addEventListener('keydown', function (e) {
keyDown(e)
})
},
setState: function (newState, cb) {
for (var key in newState) {
state[key] = newState[key]
}
if (typeof cb === 'function') cb()
},
onKeyDown: function (e) {
/*
Modals
*/
if (state.codeForm || state.noteForm || state.noteDeleteModal || state.codeDeleteModal || state.addMemberModal || state.aboutModal || state.editProfileModal || state.contactModal || state.teamCreateModal || state.planetCreateModal || state.planetSettingModal || state.teamSettingsModal || state.logoutModal) {
// ESC
if (e.keyCode === 27) this.cast('closeModal')
// Cmd + Enter
if (e.keyCode === 13 && e.metaKey) {
if (state.codeForm) this.cast('submitCodeForm')
if (state.noteForm) this.cast('submitNoteForm')
if (state.codeDeleteModal) this.cast('submitCodeDeleteModal')
if (state.noteDeleteModal) this.cast('submitNoteDeleteModal')
if (state.addMemberModal) this.cast('submitAddMemberModal')
if (state.contactModal) this.cast('submitContactModal')
if (state.teamCreateModal) this.cast('submitTeamCreateModal')
if (state.planetCreateModal) this.cast('submitPlanetCreateModal')
if (state.logoutModal) this.cast('submitLogoutModal')
}
return
}
/*
PlanetContainer
*/
if (state.planetContainer) {
// Cmd + Enter, A
if ((e.keyCode === 13 && e.metaKey) || e.keyCode === 65) this.cast('openLaunchModal')
// Esc
if (e.keyCode === 27) this.cast('toggleFocusSearchInput')
// Up
if (e.keyCode === 38) this.cast('selectPriorArticle')
// Down
if (e.keyCode === 40) this.cast('selectNextArticle')
// E
if (e.keyCode === 69) this.cast('openEditModal')
// D
if (e.keyCode === 68) this.cast('openDeleteModal')
}
/*
HomeContainer
*/
if (state.homeContainer) {
if (e.keyCode > 48 && e.keyCode < 58 && e.metaKey) {
this.cast('switchPlanet', e.keyCode - 48)
}
}
},
cast: function (status, data) {
this.trigger({
status: status,
data: data
})
}
})
module.exports = function (stateKey) {
return {
mixins: [Reflux.listenTo(KeyStore, 'onKeyCast')],
componentDidMount: function () {
var newState = {}
newState[stateKey] = true
KeyStore.setState(newState)
},
componentWillUnmount: function () {
var newState = {}
newState[stateKey] = false
KeyStore.setState(newState)
}
}
}

View File

@@ -35,6 +35,9 @@ module.exports = {
fetchUser: function (userName) { fetchUser: function (userName) {
return request return request
.get(apiUrl + 'resources/' + userName) .get(apiUrl + 'resources/' + userName)
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
}, },
updateUser: function (userName, input) { updateUser: function (userName, input) {
return request return request
@@ -79,6 +82,9 @@ module.exports = {
fetchPlanet: function (userName, planetName) { fetchPlanet: function (userName, planetName) {
return request return request
.get(apiUrl + 'resources/' + userName + '/planets/' + planetName) .get(apiUrl + 'resources/' + userName + '/planets/' + planetName)
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
}, },
updatePlanet: function (userName, planetName, input) { updatePlanet: function (userName, planetName, input) {
return request return request

View File

@@ -53,6 +53,14 @@ module.exports = Reflux.createStore({
localStorage.setItem('currentUser', JSON.stringify(currentUser)) localStorage.setItem('currentUser', JSON.stringify(currentUser))
UserStore.Actions.update(currentUser) UserStore.Actions.update(currentUser)
planet.Codes.forEach(function (code) {
code.type = 'code'
})
planet.Notes.forEach(function (note) {
note.type = 'note'
})
// Update the planet // Update the planet
localStorage.setItem('planet-' + planet.id, JSON.stringify(planet)) localStorage.setItem('planet-' + planet.id, JSON.stringify(planet))

View File

@@ -1,16 +1,55 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<script>
var version = require('remote').getGlobal('version')
document.title = 'Boost ' + ((version == null || version.length === 0) ? 'DEV version' : 'v' + version)
</script>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
<link rel="stylesheet" href="../../node_modules/font-awesome/css/font-awesome.min.css" media="screen" title="no title" charset="utf-8"> <link rel="stylesheet" href="../../node_modules/font-awesome/css/font-awesome.min.css" media="screen" title="no title" charset="utf-8">
<link rel="shortcut icon" href="favicon.ico"> <link rel="shortcut icon" href="favicon.ico">
<style>
@font-face {
font-family: 'Lato';
src: url('../../Lato-Regular.woff2') format('woff2'), /* Modern Browsers */
url('../../Lato-Regular.woff') format('woff'), /* Modern Browsers */
url('../../Lato-Regular.ttf') format('truetype');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
#loadingCover{
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
box-sizing: border-box;
padding: 65px 0;
font-family: sans-serif;
}
#loadingCover img{
display: block;
margin: 75px auto 5px;
width: 160px;
height: 160px;
}
#loadingCover .message{
font-size: 30px;
text-align: center;
line-height: 1.6;
font-weight: 100;
color: #888;
}
</style>
</head>
<body>
<div id="loadingCover">
<img src="resources/favicon-230x230.png">
<div class='message'>Loading...</div>
</div>
<div id="content"></div>
<script> <script>
if (!Object.assign) { if (!Object.assign) {
Object.defineProperty(Object, 'assign', { Object.defineProperty(Object, 'assign', {
@@ -45,13 +84,14 @@
}); });
} }
require('electron-stylus')(__dirname + '/../styles/main/index.styl')
</script> </script>
</head>
<body>
<div id="content"></div>
<script src="../ace/src-min/ace.js"></script> <script src="../ace/src-min/ace.js"></script>
<script> <script>
var version = require('remote').require('app').getVersion()
global.version = version
document.title = 'Boost ' + ((version == null || version.length === 0) ? 'DEV version' : 'v' + version)
require('../electron-stylus')(__dirname + '/../styles/main/index.styl', 'mainCss')
// require('../electron-stylus')(__dirname + '/../styles/main/index.styl')
require('node-jsx').install({ harmony: true, extension: '.jsx' }) require('node-jsx').install({ harmony: true, extension: '.jsx' })
require('./index.jsx') require('./index.jsx')
</script> </script>

View File

@@ -34,7 +34,13 @@ var routes = (
</Route> </Route>
</Route> </Route>
) )
var loadingCover = document.getElementById('loadingCover')
ReactRouter.run(routes, ReactRouter.HashLocation, function (Root) { ReactRouter.run(routes, ReactRouter.HashLocation, function (Root) {
React.render(<Root/>, document.getElementById('content')) React.render(<Root/>, document.getElementById('content'))
if (loadingCover != null) {
loadingCover.parentNode.removeChild(loadingCover)
loadingCover = null
}
}) })

View File

@@ -46,6 +46,9 @@ body
border solid 2px transparent border solid 2px transparent
box-sizing border-box box-sizing border-box
cursor pointer cursor pointer
white-space nowrap
overflow-x hidden
text-overflow ellipsis
.divider .divider
box-sizing border-box box-sizing border-box
border-bottom solid 1px borderColor border-bottom solid 1px borderColor
@@ -65,6 +68,9 @@ body
border-bottom solid 1px borderColor border-bottom solid 1px borderColor
line-height 44px line-height 44px
font-size 1.3em font-size 1.3em
white-space nowrap
text-overflow ellipsis
overflow-x hidden
.content .content
.ace_editor, .marked .ace_editor, .marked
position absolute position absolute

View File

@@ -10,7 +10,7 @@ articleListWidth= 275px
margin 0 2px margin 0 2px
text-decoration underline text-decoration underline
cursor pointer cursor pointer
font-size 0.9em font-size 0.95em
&.noTag &.noTag
color inactiveTextColor color inactiveTextColor
font-size 0.8em font-size 0.8em
@@ -64,40 +64,35 @@ articleListWidth= 275px
color inactiveColor color inactiveColor
&:hover &:hover
color textColor color textColor
.privateTooltip .tooltip
position fixed tooltip()
z-index popupZIndex
background-color transparentify(invBackgroundColor, 80%)
color invTextColor
padding 10px
font-size 0.9em
line-height 0.9em
border-radius 5px
white-space nowrap
opacity 0
transition 0.1s
pointer-events none
margin-left -30px margin-left -30px
&:hover .privateTooltip &:hover .tooltip
opacity 1 opacity 1
.menuBtn .planetSettingButton
position absolute position absolute
top 12px top 15px
right 5px right 5px
font-size 1em font-size 0.8em
btnDefault() btnDefault()
box-sizing border-box box-sizing border-box
circle() circle()
width 33px width 26px
height 33px height 26px
text-align center text-align center
cursor pointer cursor pointer
transition 0.1s transition 0.1s
transform scale(0.8)
&:focus, &.focus &:focus, &.focus
outline none outline none
.tooltip
tooltip()
margin-top 11px
margin-left -36px
&:hover .tooltip
opacity 1
.headerControl .headerControl
noSelect() noSelect()
absolute top bottom right absolute top bottom right
@@ -118,10 +113,11 @@ articleListWidth= 275px
.refreshButton .refreshButton
display block display block
position absolute position absolute
top 12px top 15px
right 55px right 55px
width 28px width 26px
height 28px height 26px
font-size 0.8em
btnDefault() btnDefault()
circle() circle()
text-align center text-align center
@@ -129,16 +125,28 @@ articleListWidth= 275px
transition 0.1s transition 0.1s
&:focus, &.focus &:focus, &.focus
outline none outline none
.tooltip
tooltip()
margin-top 11px
margin-left -39px
&:hover .tooltip
opacity 1
.logo .logo
display block display block
position absolute position absolute
top 4px top 4px
right 10px right 10px
cursor pointer cursor pointer
img
transition 0.1s transition 0.1s
opacity 0.9 opacity 0.9
&:hover, &.hover &:hover img, &:hover .tooltip
opacity 1 opacity 1
.tooltip
tooltip()
margin-top -5px
margin-left -67px
.PlanetNavigator .PlanetNavigator
absolute bottom left absolute bottom left
@@ -178,33 +186,51 @@ articleListWidth= 275px
overflow-y auto overflow-y auto
li li
.articleItem .articleItem
user-select none noSelect()
border solid 2px transparent border solid 2px transparent
padding 10px position relative
height 94px
width 100%
cursor pointer cursor pointer
transition 0.1s transition 0.1s
clearfix()
.itemLeft .itemLeft
float left position absolute
width 25px top 4px
bottom 4px
width 38px
padding 3px 0 3px 3px
text-align center text-align center
.profileImage .profileImage
margin-bottom 5px margin-bottom 5px
circle()
.fa .fa
line-height 25px line-height 25px
.itemRight .itemRight
float left position absolute
width 225px top 4px
bottom 4px
right 2px
left 40px
overflow-x hidden overflow-x hidden
padding-left 10px padding 3px 10px 3px 3px
.updatedAt .itemInfo
margin-bottom 10px margin 5px 0 13px
color lighten(textColor, 25%) color lighten(textColor, 25%)
font-size 0.7em font-size 0.7em
.userProfileName
color brandColor
font-size 1.2em
.description .description
line-height 120% line-height 120%
margin-bottom 15px margin-bottom 10px
font-size 1em font-size 1em
overflow-x hidden
white-space nowrap
text-overflow ellipsis
.tags
position absolute
bottom 5px
font-size 0.9em
&:hover, &.hover &:hover, &.hover
background-color hoverBackgroundColor background-color hoverBackgroundColor
&:active, &.active &:active, &.active
@@ -218,57 +244,89 @@ articleListWidth= 275px
absolute right bottom absolute right bottom
top 55px top 55px
left navigationWidth + articleListWidth left navigationWidth + articleListWidth
&>.viewer-header .detailHeader
height 44px border solid 2px transparent
line-height 44px position relative
padding 0 15px height 105px
box-sizing border-box width 100%
font-size 1.2em transition 0.1s
small .itemLeft
font-size 0.8em position absolute
top 7px
bottom 4px
width 38px
padding 3px 0 3px 3px
text-align center
.profileImage
margin-bottom 5px
circle()
.fa
line-height 25px
.itemRight
position absolute
top 7px
bottom 4px
right 2px
left 40px
overflow-x hidden
padding 3px 10px 3px 3px
.itemInfo
margin 5px 0 13px
color lighten(textColor, 25%) color lighten(textColor, 25%)
.control-group font-size 0.7em
float right .userProfileName
button color brandColor
margin 10px 3px font-size 1.2em
.viewer-body
absolute bottom right
left 1px
top 44px
&.codeDetail>.viewer-body
.viewer-detail
border-bottom solid 1px borderColor
height 150px
box-sizing border-box
padding 10px
.description .description
height 100px line-height 120%
line-height 1.4em margin-bottom 10px
overflow-y auto font-size 1em
overflow-x auto
white-space nowrap
.tags .tags
position absolute position absolute
left 15px
right 15px
top 120px
.content
.ace_editor
absolute left right
top 155px
bottom 5px bottom 5px
&.noteDetail>.viewer-body font-size 0.9em
.tags .itemControl
absolute top position absolute
left 15px z-index 1
right 15px top 2px
height 24px right 2px
line-height 24px .deleteButton, .editButton
.content btnDefault()
text-align center
width 33px
height 33px
border-radius 16.5px
font-size 15px
margin 0 3px
.tooltip
tooltip()
margin-top 10px
&:hover .tooltip
opacity 1
.editButton .tooltip
margin-left -12px
.deleteButton .tooltip
margin-left -26px
.detailBody
absolute left right bottom absolute left right bottom
top 30px top 105px
.content
position absolute
top 5px
bottom 5px
left 2px
right 2px
box-sizing border-box box-sizing border-box
padding 5px padding 5px
border-top solid 1px borderColor border-top solid 1px borderColor
padding 10px &.noteDetail
.detailBody .content
overflow-x hidden overflow-x hidden
overflow-y auto overflow-y auto
marked() marked()
&.codeDetail
.detailBody .content
.ace_editor
absolute left right top bottom

View File

@@ -1,5 +1,6 @@
.HomeContainer .HomeContainer
.HomeNavigator .HomeNavigator
noSelect()
background-color planetNavBgColor background-color planetNavBgColor
absolute left top bottom absolute left top bottom
width 55px width 55px
@@ -63,19 +64,12 @@
&:hover &:hover
background-color hoverBackgroundColor background-color hoverBackgroundColor
.userName .userName
float left
width 155px width 155px
padding 10px 15px padding 10px 15px
text-align left text-align left
display block display block
text-decoration none text-decoration none
cursor pointer cursor pointer
.userSetting
float right
display block
padding 10px 0
width 45px
cursor pointer
.createNewTeam .createNewTeam
btnStripDefault() btnStripDefault()
width 100% width 100%
@@ -166,22 +160,11 @@
border-color darken(brandBorderColor, 10%) border-color darken(brandBorderColor, 10%)
background-color brandColor background-color brandColor
color white color white
.newPlanetTooltip .tooltip
position fixed tooltip()
z-index 500 margin-top -22px
background-color transparentify(invBackgroundColor, 80%)
color invTextColor
padding 10px
line-height 1em
border-radius 5px
margin-top -23px
margin-left 33px margin-left 33px
white-space nowrap &:hover .tooltip
font-size 1.1em
opacity 0
transition 0.1s
pointer-events none
&:hover .newPlanetTooltip
opacity 1 opacity 1
.UserContainer .UserContainer
absolute top bottom right absolute top bottom right
@@ -189,7 +172,7 @@
.memberPopup .memberPopup
absolute left absolute left
top 235px top 235px
z-index popupZIndex z-index 1
padding 0 15px 10px padding 0 15px 10px
width 200px width 200px
.label .label
@@ -205,13 +188,15 @@
.memberImage .memberImage
float left float left
margin-right 7px margin-right 7px
circle()
.memberInfo .memberInfo
float left float left
.memberProfileName .memberProfileName
margin-bottom 5px margin-bottom 5px
font-size 1.05em
.memberName .memberName
margin-left 5px margin-left 5px
font-size 0.8em font-size 0.9em
color inactiveTextColor color inactiveTextColor
a:hover .memberProfileName, a:hover .memberName a:hover .memberProfileName, a:hover .memberName
text-decoration underline text-decoration underline
@@ -259,9 +244,10 @@
float left float left
.teamProfileName .teamProfileName
margin-bottom 5px margin-bottom 5px
font-size 1.05em
.teamName .teamName
margin-left 5px margin-left 5px
font-size 0.8em font-size 0.9em
color inactiveTextColor color inactiveTextColor
a:hover .teamProfileName, a:hover .teamName a:hover .teamProfileName, a:hover .teamName
text-decoration underline text-decoration underline
@@ -277,16 +263,18 @@
.memberImage .memberImage
float left float left
margin-right 7px margin-right 7px
circle()
.memberInfo .memberInfo
float left float left
.memberProfileName .memberProfileName
margin-bottom 5px margin-bottom 5px
font-size 1.05em
.memberRole .memberRole
font-size 0.8em font-size 0.9em
color inactiveTextColor color inactiveTextColor
.memberName .memberName
margin-left 5px margin-left 5px
font-size 0.8em font-size 0.9em
color inactiveTextColor color inactiveTextColor
.createTeamButton, .addMemberButton .createTeamButton, .addMemberButton
btnStripDefault() btnStripDefault()
@@ -306,6 +294,9 @@
.planetGroupLabel .planetGroupLabel
font-size 1.1em font-size 1.1em
margin-bottom 15px margin-bottom 15px
small
font-size 0.8em
color inactiveTextColor
.planets .planets
margin-left 15px margin-left 15px
li li

View File

@@ -16,6 +16,8 @@ body
color textColor color textColor
font-size fontSize font-size fontSize
font-weight 400 font-weight 400
button
font-family "Lato"
div, span, a, button, input, textarea div, span, a, button, input, textarea
box-sizing border-box box-sizing border-box
@@ -105,14 +107,22 @@ textarea.block-input
z-index 2000 z-index 2000
bottom 5px bottom 5px
right 53px right 53px
btnDefault() btnPrimary()
padding 10px 15px padding 10px 15px
border-radius 5px border-radius 5px
background-color backgroundColor
.contactButton .contactButton
position fixed position fixed
z-index 2000 z-index 2000
bottom 5px bottom 5px
right 5px right 5px
btnDefault() btnPrimary()
padding 10px 15px padding 10px 15px
border-radius 5px border-radius 5px
background-color backgroundColor
.tooltip
tooltip()
margin-top -22px
margin-left -97px
&:hover .tooltip
opacity 1

View File

@@ -22,10 +22,11 @@ marked()
font-size 0.67em font-size 0.67em
margin 2.33em auto margin 2.33em auto
h1, h2, h3, h4, h5, h6 h1, h2, h3, h4, h5, h6
font-weight font-weight 400 font-weight 400
line-height 1.2em line-height 1.4em
p p
line-height 1.2em line-height 1.4em
margin-bottom 15px
img img
max-width 100% max-width 100%
strong strong
@@ -36,14 +37,14 @@ marked()
text-decoration line-through text-decoration line-through
blockquote blockquote
border-left solid 4px brandBorderColor border-left solid 4px brandBorderColor
margin 1em 0 margin 15px 0 15px
padding 0 25px padding 0 25px
ul ul
list-style-type disc list-style-type disc
padding-left 35px padding-left 35px
li li
display list-item display list-item
margin 0.5em 0 margin 15px 0
&>li>ul &>li>ul
list-style-type circle list-style-type circle
&>li>ul &>li>ul
@@ -53,33 +54,38 @@ marked()
padding-left 35px padding-left 35px
li li
display list-item display list-item
margin 0.5em 0 margin 15px 0
code code
font-family monospace font-family monospace
padding 2px 4px padding 2px 4px
border solid 1px borderColor border solid 1px borderColor
border-radius 4px border-radius 4px
font-size 0.9em font-size 0.9em
color black
text-decoration none
background-color #F6F6F6
pre pre
padding 5px padding 5px
border solid 1px borderColor border solid 1px borderColor
border-radius 5px border-radius 5px
margin 0.5em 0
overflow-x auto overflow-x auto
margin-bottom 15px
background-color #F6F6F6
&>code &>code
padding 0 padding 0
border none border none
border-radius 0 border-radius 0
color black
table table
width 100% width 100%
margin 15px 0 margin 15px 0 25px
thead thead
tr tr
background-color tableHeadBgColor background-color tableHeadBgColor
th th
border-style: solid; border-style solid
padding: 5px; padding 15px 5px
border-width: 1px 0 2px 1px; border-width 1px 0 2px 1px
border-color borderColor border-color borderColor
&:last-child &:last-child
border-right solid 1px borderColor border-right solid 1px borderColor
@@ -89,9 +95,9 @@ marked()
tr:nth-child(2n) tr:nth-child(2n)
background-color tableEvenBgColor background-color tableEvenBgColor
td td
border-style: solid; border-style solid
padding: 5px; padding 15px 5px
border-width: 0 0 1px 1px; border-width 0 0 1px 1px
border-color borderColor border-color borderColor
&:last-child &:last-child
border-right solid 1px borderColor border-right solid 1px borderColor

View File

@@ -0,0 +1,13 @@
tooltip()
position fixed
z-index popupZIndex
background-color transparentify(invBackgroundColor, 80%)
color invTextColor
padding 10px
font-size 12px
line-height 12px
border-radius 5px
white-space nowrap
opacity 0
transition 0.1s
pointer-events none

View File

@@ -32,14 +32,14 @@
.modal-control .modal-control
float right float right
.tabModal .sideNavModal
height 500px height 500px
.leftPane .leftPane
absolute top bottom left absolute top bottom left
width 175px width 175px
padding 20px padding 20px
border-right solid 1px borderColor border-right solid 1px borderColor
.tabLabel .modalLabel
font-size 1.5em font-size 1.5em
margin-top 25px margin-top 25px
margin-bottom 35px margin-bottom 35px
@@ -57,9 +57,7 @@
left 175px left 175px
padding 15px padding 15px
overflow-y auto overflow-y auto
.tab
.EditProfileModal, .PlanetSettingModal, .TeamSettingsModal
.userInfoTab, .passwordTab, .planetProfileTab, .userInfoTab, .membersTab
padding-top 45px padding-top 45px
.formField .formField
position relative position relative
@@ -97,7 +95,7 @@
float right float right
padding 12px 10px padding 12px 10px
border-radius 5px border-radius 5px
width 200px width 320px
font-size 1em font-size 1em
overflow-x hidden overflow-x hidden
white-space nowrap white-space nowrap
@@ -111,6 +109,33 @@
alertSuccess() alertSuccess()
.alertError .alertError
alertError() alertError()
.PreferencesModal
.settingsTab
.categoryLabel
font-size 1.5em
margin-bottom 25px
.example
marked()
.aboutTab
padding-top 30px
.about1
margin-bottom 25px
.logo
display block
margin 0 auto
.appInfo
font-size 1.5em
text-align center
.about2
width 200px
margin 0 auto
.externalLabel
font-size 1.2em
margin-bottom 15px
.externalList
li
margin-bottom 15px
.PlanetSettingModal
.planetDeleteTab .planetDeleteTab
padding-top 65px padding-top 65px
p p
@@ -139,7 +164,7 @@
float right float right
padding 12px 10px padding 12px 10px
border-radius 5px border-radius 5px
width 200px width 320px
font-size 1em font-size 1em
overflow-x hidden overflow-x hidden
white-space nowrap white-space nowrap
@@ -153,6 +178,7 @@
alertSuccess() alertSuccess()
.alertError .alertError
alertError() alertError()
.TeamSettingsModal
.membersTab .membersTab
.memberTable .memberTable
width 100% width 100%
@@ -196,7 +222,6 @@
border-radius 5px border-radius 5px
float left float left
.LaunchModal .LaunchModal
.modal-tab .modal-tab
text-align center text-align center
@@ -263,25 +288,6 @@
border-radius 5px border-radius 5px
marked() marked()
.AboutModal
width 320px
.about1
margin-bottom 25px
.logo
display block
margin 0 auto
.appInfo
font-size 1.5em
text-align center
.about2
width 200px
margin 0 auto
.externalLabel
font-size 1.2em
margin-bottom 15px
.externalList
li
margin-bottom 15px
.PlanetCreateModal.modal, .TeamCreateModal.modal, .AddMemberModal.modal .PlanetCreateModal.modal, .TeamCreateModal.modal, .AddMemberModal.modal
padding 60px 0 padding 60px 0
@@ -314,6 +320,14 @@
height 55px height 55px
circle() circle()
btnPrimary() btnPrimary()
.errorAlert
alertError()
padding 12px 10px
border-radius 5px
text-align center
display block
width 360px
margin 0 auto 15px
.ContactModal .ContactModal
padding 15px padding 15px
@@ -341,6 +355,7 @@
padding 0 15px padding 0 15px
border-radius 5px border-radius 5px
margin-left 5px margin-left 5px
font-size 1em
button.sendButton button.sendButton
btnPrimary() btnPrimary()
.confirmation .confirmation

View File

@@ -1,4 +1,4 @@
module.exports = { module.exports = {
apiUrl: 'http://codexen-server-dev2.elasticbeanstalk.com/' apiUrl: 'https://api.b00st.io/'
// apiUrl: 'http://localhost:8000/' // apiUrl: 'http://localhost:8000/'
} }

61
main.js
View File

@@ -4,6 +4,7 @@ var Menu = require('menu')
var MenuItem = require('menu-item') var MenuItem = require('menu-item')
var Tray = require('tray') var Tray = require('tray')
var ipc = require('ipc') var ipc = require('ipc')
var jetpack = require('fs-jetpack')
require('crash-reporter').start() require('crash-reporter').start()
@@ -19,7 +20,6 @@ var update = null
// }) // })
var version = app.getVersion() var version = app.getVersion()
global.version = version
var versionText = (version == null || version.length === 0) ? 'DEV version' : 'v' + version var versionText = (version == null || version.length === 0) ? 'DEV version' : 'v' + version
var nn = require('node-notifier') var nn = require('node-notifier')
var autoUpdater = require('auto-updater') var autoUpdater = require('auto-updater')
@@ -27,6 +27,7 @@ var path = require('path')
autoUpdater autoUpdater
.on('error', function (err, message) { .on('error', function (err, message) {
console.error(message)
nn.notify({ nn.notify({
title: 'Error! ' + versionText, title: 'Error! ' + versionText,
icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'), icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'),
@@ -34,11 +35,7 @@ autoUpdater
}) })
}) })
.on('checking-for-update', function () { .on('checking-for-update', function () {
nn.notify({ // Connecting
title: 'Boost launched!! ' + versionText,
icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'),
message: 'Checking update is available....'
})
}) })
.on('update-available', function () { .on('update-available', function () {
nn.notify({ nn.notify({
@@ -85,7 +82,7 @@ app.on('ready', function () {
Menu.setApplicationMenu(menu) Menu.setApplicationMenu(menu)
// menu end // menu end
appIcon = new Tray(__dirname + '/tray-icon.png') appIcon = new Tray(__dirname + '/tray-icon.png')
appIcon.setToolTip('Codexen') appIcon.setToolTip('Boost')
var trayMenu = new Menu() var trayMenu = new Menu()
trayMenu.append(new MenuItem({ trayMenu.append(new MenuItem({
@@ -133,7 +130,8 @@ app.on('ready', function () {
'web-preferences': { 'web-preferences': {
'overlay-scrollbars': true, 'overlay-scrollbars': true,
'skip-taskbar': true 'skip-taskbar': true
} },
'standard-window': false
}) })
popUpWindow.loadUrl('file://' + __dirname + '/browser/finder/index.electron.html') popUpWindow.loadUrl('file://' + __dirname + '/browser/finder/index.electron.html')
@@ -141,16 +139,54 @@ app.on('ready', function () {
popUpWindow.on('blur', function () { popUpWindow.on('blur', function () {
popUpWindow.hide() popUpWindow.hide()
}) })
popUpWindow.setVisibleOnAllWorkspaces(true) popUpWindow.setVisibleOnAllWorkspaces(true)
var globalShortcut = require('global-shortcut') var globalShortcut = require('global-shortcut')
console.log('jetpack launch')
var userDataPath = app.getPath('userData')
if (!jetpack.cwd(userDataPath).exists('keymap.json')) {
jetpack.cwd(userDataPath).file('keymap.json', {content: '{}'})
}
try {
global.keymap = JSON.parse(jetpack.cwd(userDataPath).read('keymap.json', 'utf-8'))
} catch (err) {
jetpack.cwd(userDataPath).file('keymap.json', {content: '{}'})
global.keymap = {}
}
if (global.keymap.toggleFinder == null) global.keymap.toggleFinder = 'ctrl+tab+shift'
var toggleFinderKey = global.keymap.toggleFinder
globalShortcut.register('ctrl+tab+shift', function () { try {
globalShortcut.register(toggleFinderKey, function () {
if (mainWindow != null && !mainWindow.isFocused()) { if (mainWindow != null && !mainWindow.isFocused()) {
mainWindow.hide() mainWindow.hide()
} }
popUpWindow.show() popUpWindow.show()
}) })
} catch (err) {
console.log(err.name)
}
ipc.on('hotkeyUpdated', function (event, newKeymap) {
console.log('got new keymap')
console.log(newKeymap)
globalShortcut.unregisterAll()
global.keymap = JSON.parse(newKeymap)
jetpack.cwd(userDataPath).file('keymap.json', {content: JSON.stringify(global.keymap)})
var toggleFinderKey = global.keymap.toggleFinder != null ? global.keymap.toggleFinder : 'ctrl+tab+shift'
try {
globalShortcut.register(toggleFinderKey, function () {
if (mainWindow != null && !mainWindow.isFocused()) {
mainWindow.hide()
}
popUpWindow.show()
})
} catch (err) {
console.log(err.name)
}
})
global.hideFinder = function () { global.hideFinder = function () {
if (mainWindow == null || !mainWindow.isVisible()) { if (mainWindow == null || !mainWindow.isVisible()) {
@@ -168,7 +204,8 @@ function makeNewMainWindow () {
'zoom-factor': 1.0, 'zoom-factor': 1.0,
'web-preferences': { 'web-preferences': {
'overlay-scrollbars': true 'overlay-scrollbars': true
} },
'standard-window': false
}) })
if (update != null) { if (update != null) {
mainWindow.webContents.on('did-finish-load', function () { mainWindow.webContents.on('did-finish-load', function () {
@@ -178,6 +215,10 @@ function makeNewMainWindow () {
mainWindow.loadUrl('file://' + __dirname + '/browser/main/index.electron.html') mainWindow.loadUrl('file://' + __dirname + '/browser/main/index.electron.html')
mainWindow.webContents.on('new-window', function (e) {
e.preventDefault()
})
mainWindow.on('closed', function () { mainWindow.on('closed', function () {
console.log('main closed') console.log('main closed')
mainWindow = null mainWindow = null

View File

@@ -5,7 +5,7 @@ module.exports = [
label: 'Electron', label: 'Electron',
submenu: [ submenu: [
{ {
label: 'About Electron', label: 'About Boost',
selector: 'orderFrontStandardAboutPanel:' selector: 'orderFrontStandardAboutPanel:'
}, },
{ {
@@ -19,7 +19,7 @@ module.exports = [
type: 'separator' type: 'separator'
}, },
{ {
label: 'Hide Electron', label: 'Hide Boost',
accelerator: 'Command+H', accelerator: 'Command+H',
selector: 'hide:' selector: 'hide:'
}, },

View File

@@ -1,6 +1,6 @@
{ {
"name": "boost", "name": "boost",
"version": "0.2.1", "version": "0.2.10",
"description": "Boost App", "description": "Boost App",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
@@ -31,8 +31,8 @@
}, },
"homepage": "https://github.com/Rokt33r/codexen-app#readme", "homepage": "https://github.com/Rokt33r/codexen-app#readme",
"dependencies": { "dependencies": {
"electron-stylus": "^0.1.0",
"font-awesome": "^4.3.0", "font-awesome": "^4.3.0",
"fs-jetpack": "^0.7.0",
"markdown-it": "^4.3.1", "markdown-it": "^4.3.1",
"md5": "^2.0.0", "md5": "^2.0.0",
"moment": "^2.10.3", "moment": "^2.10.3",
@@ -43,6 +43,7 @@
"react-router": "^0.13.3", "react-router": "^0.13.3",
"react-select": "^0.5.4", "react-select": "^0.5.4",
"reflux": "^0.2.8", "reflux": "^0.2.8",
"stylus": "^0.52.0",
"superagent": "^1.2.0", "superagent": "^1.2.0",
"superagent-promise": "^1.0.3" "superagent-promise": "^1.0.3"
}, },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 463 B

After

Width:  |  Height:  |  Size: 959 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 924 B

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB