1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 01:36:22 +00:00

Contact Modal追加

This commit is contained in:
Rokt33r
2015-08-20 01:30:57 +09:00
parent 0b4cfd6563
commit 3d9a631786
22 changed files with 645 additions and 278 deletions

View File

@@ -13,20 +13,20 @@ module.exports = React.createClass({
var article = this.props.currentArticle
if (article != null) {
if (article.type === 'snippet') {
if (article.type === 'code') {
return (
<div className='FinderDetail'>
<div className='header'>{article.callSign}</div>
<div className='header'><i className='fa fa-code fa-fw'/> {article.description}</div>
<div className='content'>
<CodeViewer code={article.content} mode={article.mode}/>
</div>
</div>
)
} else if (article.type === 'blueprint') {
} else if (article.type === 'note') {
return (
<div className='FinderDetail'>
<div className='header'>{article.title}</div>
<div className='header'><i className='fa fa-file-text-o fa-fw'/> {article.title}</div>
<div className='content'>
<div className='marked' dangerouslySetInnerHTML={{__html: ' ' + this.markdown(article.content)}}></div>
</div>

View File

@@ -6,7 +6,8 @@ module.exports = React.createClass({
currentArticle: React.PropTypes.shape({
id: React.PropTypes.number,
type: React.PropTypes.string
})
}),
selectArticle: React.PropTypes.func
},
componentDidUpdate: function () {
var index = this.props.articles.indexOf(this.props.currentArticle)
@@ -26,6 +27,11 @@ module.exports = React.createClass({
el.scrollTop = li.offsetTop
}
},
handleArticleClick: function (article) {
return function () {
this.props.selectArticle(article)
}.bind(this)
},
render: function () {
var list = this.props.articles.map(function (article) {
if (article == null) {
@@ -38,17 +44,17 @@ module.exports = React.createClass({
}
var isActive = this.props.currentArticle != null && (article.type === this.props.currentArticle.type && article.id === this.props.currentArticle.id)
if (article.type === 'snippet') {
if (article.type === 'code') {
return (
<li className={isActive ? 'active' : ''}>
<div className='articleItem'><i className='fa fa-code fa-fw'/> {article.callSign} / {article.description.substring(0, 10)}</div>
<li onClick={this.handleArticleClick(article)} className={isActive ? 'active' : ''}>
<div className='articleItem'><i className='fa fa-code fa-fw'/> {article.description}</div>
<div className='divider'/>
</li>
)
}
if (article.type === 'blueprint') {
if (article.type === 'note') {
return (
<li className={isActive ? 'active' : ''}>
<li onClick={this.handleArticleClick(article)} className={isActive ? 'active' : ''}>
<div className='articleItem'><i className='fa fa-file-text-o fa-fw'/> {article.title}</div>
<div className='divider'/>
</li>

View File

@@ -5,79 +5,12 @@ var clipboard = require('clipboard')
var React = require('react/addons')
var ArticleFilter = require('../main/Mixins/ArticleFilter')
var FinderInput = require('./Components/FinderInput')
var FinderList = require('./Components/FinderList')
var FinderDetail = require('./Components/FinderDetail')
// filter start
function basicFilter (keyword, articles) {
if (keyword === '' || keyword == null) return articles
var firstFiltered = articles.filter(function (article) {
var first = article.type === 'snippet' ? article.callSign : article.title
if (first.match(new RegExp(keyword, 'i'))) return true
return false
})
var secondFiltered = articles.filter(function (article) {
var second = article.type === 'snippet' ? article.description : article.content
if (second.match(new RegExp(keyword, 'i'))) return true
return false
})
var thirdFiltered = articles.filter(function (article) {
if (article.type === 'snippet') {
if (article.content.match(new RegExp(keyword, 'i'))) return true
}
return false
})
return firstFiltered.concat(secondFiltered, thirdFiltered).filter(function (value, index, self) {
return self.indexOf(value) === index
})
}
function snippetFilter (articles) {
return articles.filter(function (article) {
return article.type === 'snippet'
})
}
function blueprintFilter (articles) {
return articles.filter(function (article) {
return article.type === 'blueprint'
})
}
function tagFilter (keyword, articles) {
return articles.filter(function (article) {
return article.Tags.some(function (tag) {
return tag.name.match(new RegExp('^' + keyword, 'i'))
})
})
}
function searchArticle (search, articles) {
var keywords = search.split(' ')
for (var keyword of keywords) {
if (keyword.match(/^\$s/, 'i')) {
articles = snippetFilter(articles)
continue
} else if (keyword.match(/^\$b/, 'i')) {
articles = blueprintFilter(articles)
continue
} else if (keyword.match(/^#[A-Za-z0-9]+/)) {
articles = tagFilter(keyword.substring(1, keyword.length), articles)
continue
}
articles = basicFilter(keyword, articles)
}
return articles
}
// Filter end
function fetchArticles () {
@@ -90,14 +23,20 @@ function fetchArticles () {
var articles = []
user.Planets.forEach(function (planet) {
var _planet = JSON.parse(localStorage.getItem('planet-' + planet.id))
articles = articles.concat(_planet.Snippets, _planet.Blueprints)
articles = articles.concat(_planet.Codes, _planet.Notes)
})
user.Teams.forEach(function (team) {
team.Planets.forEach(function (planet) {
var _planet = JSON.parse(localStorage.getItem('planet-' + planet.id))
articles = articles.concat(_planet.Codes, _planet.Notes)
})
})
console.log(articles.length + ' articles')
return articles
}
var Finder = React.createClass({
mixins: [ArticleFilter],
getInitialState: function () {
var articles = fetchArticles()
return {
@@ -122,8 +61,14 @@ var Finder = React.createClass({
var articles = fetchArticles()
this.setState({
articles: articles,
currentArticle: articles[0],
search: ''
}, function () {
var firstArticle = this.refs.finderList.props.articles[0]
if (firstArticle) {
this.setState({
currentArticle: firstArticle
})
}
})
},
handleKeyDown: function (e) {
@@ -139,11 +84,9 @@ var Finder = React.createClass({
if (e.keyCode === 13) {
var article = this.state.currentArticle
if (article.type === 'snippet') {
clipboard.writeText(article.content)
hideFinder()
e.preventDefault()
}
clipboard.writeText(article.content)
hideFinder()
e.preventDefault()
}
if (e.keyCode === 27) {
hideFinder()
@@ -168,17 +111,20 @@ var Finder = React.createClass({
this.setState({currentArticle: this.refs.finderList.props.articles[index + 1]})
}
},
selectArticle: function (article) {
this.setState({currentArticle: article})
},
handleChange: function (e) {
this.setState({search: e.target.value}, function () {
this.setState({currentArticle: this.refs.finderList.props.articles[0]})
})
},
render: function () {
var articles = searchArticle(this.state.search, this.state.articles)
var articles = this.searchArticle(this.state.search, this.state.articles)
return (
<div className='Finder'>
<FinderInput ref='finderInput' onChange={this.handleChange} search={this.state.search}/>
<FinderList ref='finderList' currentArticle={this.state.currentArticle} articles={articles}/>
<FinderList ref='finderList' currentArticle={this.state.currentArticle} articles={articles} selectArticle={this.selectArticle}/>
<FinderDetail currentArticle={this.state.currentArticle}/>
</div>
)

View File

@@ -15,7 +15,7 @@ module.exports = React.createClass({
<div className='AboutModal modal'>
<div className='about1'>
<img className='logo' src='resources/favicon-230x230.png'/>
<div className='appInfo'>Boost {version == null ? 'DEV version' : 'v' + version}</div>
<div className='appInfo'>Boost {version == null || version.length === 0 ? 'DEV version' : 'v' + version}</div>
</div>
<div className='about2'>

View File

@@ -0,0 +1,83 @@
var React = require('react/addons')
var Select = require('react-select')
var LinkedState = require('../Mixins/LinkedState')
var Hq = require('../Services/Hq')
var UserStore = require('../Stores/UserStore')
var getOptions = function (input, callback) {
Hq.searchUser(input)
.then(function (res) {
callback(null, {
options: res.body.map(function (user) {
return {
label: user.name,
value: user.name
}
}),
complete: false
})
})
.catch(function (err) {
console.error(err)
})
}
module.exports = React.createClass({
mixins: [LinkedState],
propTypes: {
team: React.PropTypes.object,
close: React.PropTypes.func
},
getInitialState: function () {
return {
userName: '',
role: 'member'
}
},
handleSubmit: function () {
Hq
.addMember(this.props.team.name, {
userName: this.state.userName,
role: this.state.role
})
.then(function (res) {
console.log(res.body)
UserStore.Actions.addMember(res.body)
this.props.close()
}.bind(this))
.catch(function (err) {
console.error(err)
})
},
handleChange: function (value) {
this.setState({userName: value})
},
render: function () {
return (
<div className='AddMemberModal modal'>
<Select
name='userName'
value={this.state.userName}
placeholder='Username to add'
asyncOptions={getOptions}
onChange={this.handleChange}
className='userNameSelect'
/>
<div className='formField'>
Add member as
<select valueLink={this.linkState('role')}>
<option value={'member'}>Member</option>
<option value={'owner'}>Owner</option>
</select>
role
</div>
<button onClick={this.handleSubmit} className='submitButton'><i className='fa fa-check'/></button>
</div>
)
}
})

View File

@@ -0,0 +1,62 @@
var React = require('react')
var LinkedState = require('../Mixins/LinkedState')
var Hq = require('../Services/Hq')
module.exports = React.createClass({
mixins: [LinkedState],
propTypes: {
close: React.PropTypes.func
},
getInitialState: function () {
return {
isSent: false,
mail: {
title: '',
content: ''
}
}
},
sendEmail: function () {
Hq.sendEmail(this.state.mail)
.then(function (res) {
this.setState({isSent: !this.state.isSent})
}.bind(this))
.catch(function (err) {
console.error(err)
})
},
render: function () {
return (
<div className='ContactModal modal'>
<div className='modal-header'><h1>Contact form</h1></div>
{!this.state.isSent ? (
<div className='contactForm'>
<div className='modal-body'>
<div className='formField'>
<input valueLink={this.linkState('mail.title')} placeholder='Title'/>
</div>
<div className='formField'>
<textarea valueLink={this.linkState('mail.content')} placeholder='Content'/>
</div>
</div>
<div className='modal-footer'>
<div className='formControl'>
<button onClick={this.sendEmail} className='sendButton'>Send</button>
<button onClick={this.props.close}>Cancel</button>
</div>
</div>
</div>
) : (
<div className='confirmation'>
<div className='confirmationMessage'>Thanks for sharing your opinion!</div>
<button className='doneButton' onClick={this.props.close}>Done</button>
</div>
)}
</div>
)
}
})

View File

@@ -22,7 +22,6 @@ module.exports = React.createClass({
return {
currentTab: 'userInfo',
user: {
name: user.name,
profileName: user.profileName,
email: user.email
},
@@ -116,10 +115,6 @@ module.exports = React.createClass({
<label>Profile Name</label>
<input valueLink={this.linkState('user.profileName')}/>
</div>
<div className='formField'>
<label>Name</label>
<input valueLink={this.linkState('user.name')}/>
</div>
<div className='formField'>
<label>E-mail</label>
<input valueLink={this.linkState('user.email')}/>

View File

@@ -1,77 +0,0 @@
var React = require('react/addons')
var ReactRouter = require('react-router')
var Select = require('react-select')
var request = require('superagent')
var Catalyst = require('../Mixins/Catalyst')
var PlanetActions = require('../Actions/PlanetActions')
var apiUrl = require('../../../config').apiUrl
var getOptions = function (input, callback) {
request
.get(apiUrl + 'users/search')
.query({name: input})
.send()
.end(function (err, res) {
if (err) {
callback(err)
return
}
callback(null, {
options: res.body.map(function (user) {
return {
label: user.name,
value: user.name
}
}),
complete: false
})
})
}
module.exports = React.createClass({
mixins: [Catalyst.LinkedStateMixin, ReactRouter.State],
propTypes: {
close: React.PropTypes.func
},
getInitialState: function () {
return {
userName: ''
}
},
componentDidMount: function () {
window.ns = React.findDOMNode(this).querySelector('.Select')
},
handleSubmit: function () {
var userName = this.state.userName
var params = this.getParams()
var ownerName = params.userName
var planetName = params.planetName
PlanetActions.addUser(ownerName + '/' + planetName, userName)
},
handleChange: function (value) {
this.setState({userName: value})
},
stopPropagation: function (e) {
e.stopPropagation()
},
render: function () {
return (
<div onClick={this.stopPropagation} className='PlanetAddUserModal modal'>
<Select
name='userName'
value={this.state.userName}
placeholder='Username'
asyncOptions={getOptions}
onChange={this.handleChange}
className='userNameSelect'
/>
<button onClick={this.handleSubmit} className='submitButton'><i className='fa fa-check'/></button>
</div>
)
}
})

View File

@@ -1,20 +1,43 @@
/* global localStorage */
var React = require('react/addons')
var Reflux = require('reflux')
var Select = require('react-select')
var Hq = require('../Services/Hq')
var LinkedState = require('../Mixins/LinkedState')
var Helper = require('../Mixins/Helper')
var UserStore = require('../Stores/UserStore')
var getOptions = function (input, callback) {
Hq.searchUser(input)
.then(function (res) {
callback(null, {
options: res.body.map(function (user) {
return {
label: user.name,
value: user.name
}
}),
complete: false
})
})
.catch(function (err) {
console.error(err)
})
}
module.exports = React.createClass({
mixins: [LinkedState],
mixins: [LinkedState, Reflux.listenTo(UserStore, 'onUserChange'), Helper],
propTypes: {
team: React.PropTypes.shape({
id: React.PropTypes.number,
name: React.PropTypes.string,
profileName: React.PropTypes.string,
email: React.PropTypes.string
email: React.PropTypes.string,
Members: React.PropTypes.array
})
},
getInitialState: function () {
@@ -22,10 +45,31 @@ module.exports = React.createClass({
return {
currentTab: 'teamInfo',
team: {
name: team.name,
profileName: team.profileName
},
userSubmitStatus: null
userSubmitStatus: null,
member: {
name: '',
role: 'member'
},
updatingMember: false
}
},
onUserChange: function (res) {
var member
switch (res.status) {
case 'memberAdded':
member = res.data
if (member.TeamMember.TeamId === this.props.team.id) {
this.forceUpdate()
}
break
case 'memberRemoved':
member = res.data
if (member.TeamMember.TeamId === this.props.team.id) {
this.forceUpdate()
}
break
}
},
selectTab: function (tabName) {
@@ -41,6 +85,7 @@ module.exports = React.createClass({
.then(function (res) {
this.setState({userSubmitStatus: 'done'}, function () {
UserStore.Actions.update(res.body)
this.forceUpdate()
})
}.bind(this))
.catch(function (err) {
@@ -49,6 +94,66 @@ module.exports = React.createClass({
}.bind(this))
})
},
handleMemberNameChange: function (value) {
var member = this.state.member
member.name = value
this.setState({member: member})
},
addMember: function () {
this.setState({updatingMember: true}, function () {
Hq
.addMember(this.props.team.name, {
userName: this.state.member.name,
role: this.state.member.role
})
.then(function (res) {
UserStore.Actions.addMember(res.body)
this.setState({updatingMember: false})
}.bind(this))
.catch(function (err) {
console.error(err)
this.setState({updatingMember: false})
}.bind(this))
})
},
roleChange: function (memberName) {
return function (e) {
var role = e.target.value
this.setState({updatingMember: true}, function () {
Hq
.addMember(this.props.team.name, {
userName: memberName,
role: role
})
.then(function (res) {
UserStore.Actions.addMember(res.body)
this.setState({updatingMember: false})
}.bind(this))
.catch(function (err) {
console.error(err)
this.setState({updatingMember: false})
}.bind(this))
})
}.bind(this)
},
removeMember: function (memberName) {
return function () {
this.setState({updatingMember: true}, function () {
Hq
.removeMember(this.props.team.name, {
userName: memberName
})
.then(function (res) {
UserStore.Actions.removeMember(res.body)
this.setState({updatingMember: false})
}.bind(this))
.catch(function (err) {
console.error(err)
this.setState({updatingMember: false})
}.bind(this))
})
}.bind(this)
},
render: function () {
var content
@@ -62,7 +167,7 @@ module.exports = React.createClass({
}
return (
<div className='EditProfileModal modal tabModal'>
<div className='TeamSettingsModal modal tabModal'>
<div className='leftPane'>
<div className='tabLabel'>Team settings</div>
<div className='tabList'>
@@ -83,10 +188,6 @@ module.exports = React.createClass({
<label>Profile Name</label>
<input valueLink={this.linkState('team.profileName')}/>
</div>
<div className='formField'>
<label>Name</label>
<input valueLink={this.linkState('team.name')}/>
</div>
<div className='formConfirm'>
<button disabled={this.state.userSubmitStatus === 'sending'} onClick={this.saveUserInfo}>Save</button>
@@ -100,8 +201,72 @@ module.exports = React.createClass({
)
},
renderMembersTab: function () {
var currentUser = JSON.parse(localStorage.getItem('currentUser'))
var members = this.props.team.Members.map(function (member) {
var isCurrentUser = currentUser.id === member.id
return (
<tr>
<td>{member.profileName}({member.name})</td>
<td>
{isCurrentUser ? (
'Owner'
) : (
<select disabled={this.state.updatingMember} onChange={this.roleChange(member.name)} className='roleSelect' value={member.TeamMember.role}>
<option value='owner'>Owner</option>
<option value='member'>Member</option>
</select>
)}
</td>
<td>
{isCurrentUser ? '-' : (
<button disabled={this.state.updatingMember} onClick={this.removeMember(member.name)}><i className='fa fa-close fa-fw'/></button>
)}
</td>
</tr>
)
}.bind(this))
var belowLimit = members.length < 5
return (
<div className='membersTab'>
<table className='memberTable'>
<thead>
<tr>
<th>Username</th>
<th>Role</th>
<th>Control</th>
</tr>
</thead>
<tbody>
{members}
</tbody>
</table>
{belowLimit ? (
<div className='addMemberForm'>
<div className='formLabel'>Add Member</div>
<div className='formGroup'>
<Select
name='userName'
value={this.state.member.name}
placeholder='Username to add'
asyncOptions={getOptions}
onChange={this.handleMemberNameChange}
className='userNameSelect'
/>
<select valueLink={this.linkState('member.role')} className='roleSelect'>
<option value={'member'}>Member</option>
<option value={'owner'}>Owner</option>
</select>
<button disabled={this.state.updatingMember} onClick={this.addMember} className='confirmButton'>Add Member</button>
</div>
</div>
) : (
<div>
Maximum numbr of members is 5 on Beta version. Please contact us if you want futher use.
</div>
)}
</div>
)
}

View File

@@ -1,5 +1,7 @@
/* global localStorage */
var ipc = require('ipc')
var React = require('react/addons')
var ReactRouter = require('react-router')
var RouteHandler = ReactRouter.RouteHandler
@@ -8,35 +10,45 @@ var State = ReactRouter.State
var Hq = require('../Services/Hq')
var Modal = require('../Mixins/Modal')
var UserStore = require('../Stores/UserStore')
// function fetchPlanet (planet) {
// return Hq.fetchPlanet(planet.userName, planet.name)
// .then(function (res) {
// var _planet = res.body
// _planet.userName = planet.userName
//
// _planet.Snippets = _planet.Snippets.map(function (snippet) {
// snippet.type = 'snippet'
// return snippet
// })
//
// _planet.Blueprints = _planet.Blueprints.map(function (blueprint) {
// blueprint.type = 'blueprint'
// return blueprint
// })
//
// localStorage.setItem('planet-' + _planet.id, JSON.stringify(_planet))
// console.log('planet-' + _planet.id + ' fetched')
// })
// .catch(function (err) {
// console.error(err)
// })
// }
var ContactModal = require('../Components/ContactModal')
function fetchPlanet (userName, planetName) {
Hq.fetchPlanet(userName, planetName)
.then(function (res) {
var planet = res.body
planet.Codes.forEach(function (code) {
code.type = 'code'
})
planet.Notes.forEach(function (note) {
note.type = 'note'
})
console.log('planet-' + planet.id + ' fetched!')
localStorage.setItem('planet-' + planet.id, JSON.stringify(planet))
})
.catch(function (err) {
console.error(err)
})
}
module.exports = React.createClass({
mixins: [State, Navigation],
mixins: [State, Navigation, Modal],
getInitialState: function () {
return {
updateAvailable: false
}
},
componentDidMount: function () {
ipc.on('update-available', function (message) {
this.setState({updateAvailable: true})
}.bind(this))
if (this.isActive('root')) {
if (localStorage.getItem('currentUser') == null) {
this.transitionTo('login')
@@ -49,9 +61,19 @@ module.exports = React.createClass({
Hq.getUser()
.then(function (res) {
console.log(res.body)
localStorage.setItem('currentUser', JSON.stringify(res.body))
UserStore.Actions.update(res.body)
var user = res.body
localStorage.setItem('currentUser', JSON.stringify(user))
UserStore.Actions.update(user)
user.Planets.forEach(function (planet) {
fetchPlanet(planet.userName, planet.name)
})
user.Teams.forEach(function (team) {
team.Planets.forEach(function (planet) {
fetchPlanet(planet.userName, planet.name)
})
})
})
.catch(function (err) {
if (err.status === 401) {
@@ -63,9 +85,19 @@ module.exports = React.createClass({
console.error(err)
}.bind(this))
},
updateApp: function () {
ipc.send('update-app', 'Deal with it.')
},
openContactModal: function () {
this.openModal(ContactModal)
},
render: function () {
return (
<div className='Main'>
{this.state.updateAvailable ? (
<button onClick={this.updateApp} className='appUpdateButton'><i className='fa fa-cloud-download'/> Update available!</button>
) : null}
<button onClick={this.openContactModal} className='contactButton'><i className='fa fa-paper-plane-o'/></button>
<RouteHandler/>
</div>
)

View File

@@ -9,42 +9,17 @@ var PlanetNavigator = require('../Components/PlanetNavigator')
var PlanetArticleList = require('../Components/PlanetArticleList')
var PlanetArticleDetail = require('../Components/PlanetArticleDetail')
var Hq = require('../Services/Hq')
var Modal = require('../Mixins/Modal')
var ArticleFilter = require('../Mixins/ArticleFilter')
var Hq = require('../Services/Hq')
var Helper = require('../Mixins/Helper')
var UserStore = require('../Stores/UserStore')
var PlanetStore = require('../Stores/PlanetStore')
function deleteItemFromTargetArray (item, targetArray) {
targetArray.some(function (_item, index) {
if (_item.id === item.id) {
targetArray.splice(index, 1)
return true
}
return false
})
return targetArray
}
function updateItemToTargetArray (item, targetArray) {
var isNew = !targetArray.some(function (_item, index) {
if (_item.id === item.id) {
targetArray.splice(index, 1, item)
return true
}
return false
})
if (isNew) targetArray.push(item)
return targetArray
}
module.exports = React.createClass({
mixins: [ReactRouter.Navigation, ReactRouter.State, Modal, Reflux.listenTo(UserStore, 'onUserChange'), Reflux.listenTo(PlanetStore, 'onPlanetChange'), ArticleFilter],
mixins: [ReactRouter.Navigation, ReactRouter.State, Modal, Reflux.listenTo(UserStore, 'onUserChange'), Reflux.listenTo(PlanetStore, 'onPlanetChange'), ArticleFilter, Helper],
propTypes: {
params: React.PropTypes.object,
planetName: React.PropTypes.string
@@ -111,7 +86,7 @@ module.exports = React.createClass({
case 'codeUpdated':
code = res.data
if (code.PlanetId === this.state.planet.id) {
this.state.planet.Codes = updateItemToTargetArray(code, this.state.planet.Codes)
this.state.planet.Codes = this.updateItemToTargetArray(code, this.state.planet.Codes)
this.setState({planet: this.state.planet})
}
@@ -119,7 +94,7 @@ module.exports = React.createClass({
case 'noteUpdated':
note = res.data
if (note.PlanetId === this.state.planet.id) {
this.state.planet.Notes = updateItemToTargetArray(note, this.state.planet.Notes)
this.state.planet.Notes = this.updateItemToTargetArray(note, this.state.planet.Notes)
this.setState({planet: this.state.planet})
}
@@ -127,7 +102,7 @@ module.exports = React.createClass({
case 'codeDestroyed':
code = res.data
if (code.PlanetId === this.state.planet.id) {
this.state.planet.Codes = deleteItemFromTargetArray(code, this.state.planet.Codes)
this.state.planet.Codes = this.deleteItemFromTargetArray(code, this.state.planet.Codes)
if (this.refs.detail.props.article != null && this.refs.detail.props.article.type === code.type && this.refs.detail.props.article.localId === code.localId) {
articleIndex = this.getFilteredIndexOfCurrentArticle()
@@ -151,7 +126,7 @@ module.exports = React.createClass({
case 'noteDestroyed':
note = res.data
if (note.PlanetId === this.state.planet.id) {
this.state.planet.Notes = deleteItemFromTargetArray(note, this.state.planet.Notes)
this.state.planet.Notes = this.deleteItemFromTargetArray(note, this.state.planet.Notes)
if (this.refs.detail.props.article != null && this.refs.detail.props.article.type === note.type && this.refs.detail.props.article.localId === note.localId) {
articleIndex = this.getFilteredIndexOfCurrentArticle()

View File

@@ -18,6 +18,7 @@ var ProfileImage = require('../Components/ProfileImage')
var EditProfileModal = require('../Components/EditProfileModal')
var TeamSettingsModal = require('../Components/TeamSettingsModal')
var PlanetCreateModal = require('../Components/PlanetCreateModal')
var AddMemberModal = require('../Components/AddMemberModal')
var TeamCreateModal = require('../Components/TeamCreateModal')
var UserStore = require('../Stores/UserStore')
@@ -56,12 +57,29 @@ module.exports = React.createClass({
onUserChange: function (res) {
if (this.state.user == null) return
var member
switch (res.status) {
case 'userUpdated':
if (this.state.user.id === res.data.id) {
this.setState({user: res.data})
}
break
case 'memberAdded':
member = res.data
if (this.state.user.userType === 'team' && member.TeamMember.TeamId === this.state.user.id) {
this.state.user.Members = this.updateItemToTargetArray(member, this.state.user.Members)
this.setState({user: this.state.user})
}
break
case 'memberRemoved':
member = res.data
if (this.state.user.userType === 'team' && member.TeamMember.TeamId === this.state.user.id) {
this.state.user.Members = this.deleteItemFromTargetArray(member, this.state.user.Members)
this.setState({user: this.state.user})
}
break
}
},
onPlanetChange: function (res) {
@@ -123,6 +141,7 @@ module.exports = React.createClass({
this.setState({user: this.state.user})
return
}
break
}
},
fetchUser: function (userName) {
@@ -143,7 +162,7 @@ module.exports = React.createClass({
this.openModal(TeamSettingsModal, {team: this.state.user})
},
openAddUserModal: function () {
this.openModal(AddMemberModal, {team: this.state.user})
},
openTeamCreateModal: function () {
this.openModal(TeamCreateModal, {user: this.state.user})
@@ -212,9 +231,10 @@ module.exports = React.createClass({
<button onClick={this.openTeamSettingsModal} className='editProfileButton'>Team settings</button>
</div>
<div className='memberList'>
<div className='memberLabel'>{members.length} {members.length > 0 ? 'Members' : 'Member'}</div>
<div className='memberLabel'>{members.length} {members.length > 1 ? 'Members' : 'Member'}</div>
<ul className='members'>
{members}
{isOwner ? (<li><button onClick={this.openAddUserModal} className='addMemberButton'><i className='fa fa-plus-square-o'/> add Member</button></li>) : null}
</ul>
</div>
<div className='planetList'>
@@ -271,6 +291,10 @@ module.exports = React.createClass({
)
}.bind(this))
var planetCount = userPlanets.length + user.Teams.reduce(function (sum, team) {
return sum + (team.Planets != null ? team.Planets.length : 0)
}, 0)
return (
<div className='UserContainer'>
<div className='userProfile'>
@@ -284,16 +308,14 @@ module.exports = React.createClass({
<button onClick={this.openEditProfileModal} className='editProfileButton'>Edit profile</button>) : null}
</div>
<div className='teamList'>
<div className='teamLabel'>{teams.length} {teams.length > 0 ? 'Teams' : 'Team'}</div>
<div className='teamLabel'>{teams.length} {teams.length > 1 ? 'Teams' : 'Team'}</div>
<ul className='teams'>
{teams}
{isOwner ? (<li><button onClick={this.openTeamCreateModal} className='createTeamButton'><i className='fa fa-plus-square-o'/> Create new team</button></li>) : null}
</ul>
</div>
<div className='planetList'>
<div className='planetLabel'>{userPlanets.length + user.Teams.reduce(function (sum, team) {
return sum + (team.Planets != null ? team.Planets.length : 0)
}, 0)} {userPlanets.length > 0 ? 'Planets' : 'Planet'}</div>
<div className='planetLabel'>{planetCount} {planetCount > 1 ? 'Planets' : 'Planet'}</div>
<div className='planetGroup'>
<div className='planetGroupLabel'>{user.profileName}</div>
<ul className='planets'>

View File

@@ -52,6 +52,22 @@ module.exports = {
})
.send(input)
},
addMember: function (userName, input) {
return request
.post(apiUrl + 'resources/' + userName + '/members')
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.send(input)
},
removeMember: function (userName, input) {
return request
.del(apiUrl + 'resources/' + userName + '/members')
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.send(input)
},
createPlanet: function (userName, input) {
return request
.post(apiUrl + 'resources/' + userName + '/planets')
@@ -131,5 +147,20 @@ module.exports = {
return request
.get(apiUrl + 'search/tags')
.query({name: tagName})
},
searchUser: function (userName) {
return request
.get(apiUrl + 'search/users')
.query({name: userName})
},
// Mail
sendEmail: function (input) {
return request
.post(apiUrl + 'mail')
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.send(input)
}
}

View File

@@ -4,7 +4,9 @@ var Reflux = require('reflux')
var actions = Reflux.createActions([
'update',
'destroy'
'destroy',
'addMember',
'removeMember'
])
module.exports = Reflux.createStore({
@@ -51,5 +53,17 @@ module.exports = Reflux.createStore({
data: user
})
},
onAddMember: function (member) {
this.trigger({
status: 'memberAdded',
data: member
})
},
onRemoveMember: function (member) {
this.trigger({
status: 'memberRemoved',
data: member
})
},
Actions: actions
})

View File

@@ -1,7 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>CodeXen v0.2.1</title>
<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 name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>

View File

@@ -45,6 +45,7 @@ body
padding 10px
border solid 2px transparent
box-sizing border-box
cursor pointer
.divider
box-sizing border-box
border-bottom solid 1px borderColor

View File

@@ -1,15 +0,0 @@
.DashboardContainer
fullsize()
box-sizing border-box
overflow-y auto
padding 10px
.jumbotron
background-color brandColor
color white
padding 65px 20px
font-size 3.5em
margin-bottom 25px
h2, h3
margin-bottom 15px
p
margin-bottom 25px

View File

@@ -222,6 +222,7 @@
font-size 1.2em
margin-bottom 15px
.teams, .members
margin-left 10px
li
margin-bottom 10px
font-size 1.1em

View File

@@ -17,7 +17,7 @@ body
font-size fontSize
font-weight 400
div, span, a, button, input
div, span, a, button, input, textarea
box-sizing border-box
h1
@@ -98,3 +98,21 @@ textarea.block-input
#content
fullsize()
.Main
.appUpdateButton
position fixed
z-index 2000
bottom 5px
right 53px
btnDefault()
padding 10px 15px
border-radius 5px
.contactButton
position fixed
z-index 2000
bottom 5px
right 5px
btnDefault()
padding 10px 15px
border-radius 5px

View File

@@ -56,8 +56,9 @@
absolute top bottom right
left 175px
padding 15px
overflow-y auto
.EditProfileModal, .PlanetSettingModal
.EditProfileModal, .PlanetSettingModal, .TeamSettingsModal
.userInfoTab, .passwordTab, .planetProfileTab, .userInfoTab, .membersTab
padding-top 45px
.formField
@@ -75,7 +76,7 @@
borderInput()
height 33px
font-size 1em
border-radius 10px
border-radius 5px
float left
.formRadioField
margin-bottom 15px
@@ -152,6 +153,48 @@
alertSuccess()
.alertError
alertError()
.membersTab
.memberTable
width 100%
margin-bottom 25px
th
border-bottom solid 2px borderColor
td
border-bottom solid 1px borderColor
height 38px
button
btnDefault()
padding 5px
border-radius 5px
.roleSelect
height 33px
border solid 1px borderColor
background-color backgroundColor
th, td
padding 5px 0
.addMemberForm
.formLabel
margin-bottom 5px
.formGroup
clearfix()
.userNameSelect
display block
width 200px
margin-right 5px
float left
.roleSelect
display block
height 33px
border solid 1px borderColor
background-color backgroundColor
float left
margin-right 5px
.confirmButton
display block
height 33px
btnDefault()
border-radius 5px
float left
.LaunchModal
@@ -240,8 +283,7 @@
li
margin-bottom 15px
.PlanetCreateModal.modal, .TeamCreateModal.modal
.PlanetCreateModal.modal, .TeamCreateModal.modal, .AddMemberModal.modal
padding 60px 0
.nameInput
width 80%
@@ -264,7 +306,6 @@
background-color white
padding 0 10px
margin 0 15px
.submitButton
display block
margin 0 auto
@@ -273,3 +314,44 @@
height 55px
circle()
btnPrimary()
.ContactModal
padding 15px
.contactForm
.formField
width 100%
margin-bottom 10px
input, textarea
display block
width 100%
borderInput()
border-radius 5px
input
height 33px
font-size 1em
textarea
height 175px
font-size 1em
.formControl
clearfix()
button
float right
btnDefault()
height 44px
padding 0 15px
border-radius 5px
margin-left 5px
button.sendButton
btnPrimary()
.confirmation
.confirmationMessage
padding 35px 0
text-align center
font-size 1.1em
.doneButton
btnDefault()
height 44px
padding 0 35px
border-radius 5px
display block
margin 0 auto 25px

View File

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

35
main.js
View File

@@ -3,6 +3,7 @@ var BrowserWindow = require('browser-window')
var Menu = require('menu')
var MenuItem = require('menu-item')
var Tray = require('tray')
var ipc = require('ipc')
require('crash-reporter').start()
@@ -19,49 +20,66 @@ var update = null
var version = app.getVersion()
global.version = version
var versionText = (version == null || version.length === 0) ? 'DEV version' : 'v' + version
var nn = require('node-notifier')
var autoUpdater = require('auto-updater')
var path = require('path')
autoUpdater
.on('error', function (err, message) {
nn.notify({
title: 'Error! Ver.' + version,
title: 'Error! ' + versionText,
icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'),
message: message
})
})
.on('checking-for-update', function () {
nn.notify({
title: 'Boost launched!! Ver. ' + version,
title: 'Boost launched!! ' + versionText,
icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'),
message: 'Checking update is available....'
})
})
.on('update-available', function () {
nn.notify({
title: 'Update is available!! Ver. ' + version,
title: 'Update is available!! ' + versionText,
icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'),
message: 'Download started.. wait for the update ready.'
})
})
.on('update-not-available', function () {
nn.notify({
title: 'Latest Build!! Ver. ' + version,
title: 'Latest Build!! ' + versionText,
icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'),
message: 'Hope you to enjoy our app :D'
})
})
.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
nn.notify({
title: 'Ready to Update!! v' + version,
title: 'Ready to Update!! ' + versionText,
icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'),
message: 'Click tray icon to update app: ' + releaseName
})
update = quitAndUpdate
if (mainWindow != null && !mainWindow.webContents.isLoading()) {
mainWindow.webContents.send('update-available', 'Update available!')
}
})
app.on('ready', function () {
console.log('Version ' + version)
autoUpdater.setFeedUrl('http://localhost:8000/testcat/test/latest?version=' + version)
autoUpdater.setFeedUrl('http://orbital.b00st.io/rokt33r/boost/latest?version=' + version)
autoUpdater.checkForUpdates()
// menu start
var template = require('./modules/menu-template')
ipc.on('update-app', function (event, msg) {
if (update != null) {
update()
}
})
menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
@@ -152,6 +170,11 @@ function makeNewMainWindow () {
'overlay-scrollbars': true
}
})
if (update != null) {
mainWindow.webContents.on('did-finish-load', function () {
mainWindow.webContents.send('update-available', 'whoooooooh!')
})
}
mainWindow.loadUrl('file://' + __dirname + '/browser/main/index.electron.html')