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

before applying redux

This commit is contained in:
Rokt33r
2015-10-08 20:40:19 +09:00
parent 116ddf345d
commit 979dcead49
86 changed files with 5445 additions and 2323 deletions

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ electron_build/
.env
dist/
vendor/
Boost-darwin-x64/

4
.gitmodules vendored
View File

@@ -0,0 +1,4 @@
[submodule "submodules/ace"]
path = submodules/ace
url = https://github.com/ajaxorg/ace-builds.git
branch = master

BIN
app.icns Normal file

Binary file not shown.

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
var React = require('react')
var CodeViewer = require('../../main/Components/CodeViewer')

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
var React = require('react')
module.exports = React.createClass({
propTypes: {

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
var React = require('react')
module.exports = React.createClass({
propTypes: {

View File

@@ -1,72 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>CodeXen Popup</title>
<meta charset="utf-8">
<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="shortcut icon" href="favicon.ico">
<style>
@font-face {
font-family: 'Lato';
src: url('../../resources/Lato-Regular.woff2') format('woff2'), /* Modern Browsers */
url('../../resources/Lato-Regular.woff') format('woff'), /* Modern Browsers */
url('../../resources/Lato-Regular.ttf') format('truetype');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
</style>
<script>
document.addEventListener('mousewheel', function(e) {
if(e.deltaY % 1 !== 0) {
e.preventDefault()
}
})
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource);
var keysArray = Object.keys(Object(nextSource));
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
</script>
</head>
<body>
<div id="content"></div>
<script src="../ace/src-min/ace.js"></script>
<script>
require('../electron-stylus')(__dirname + '/../styles/finder/index.styl', 'finderCss')
require('node-jsx').install({ harmony: true, extension: '.jsx' })
require('./index.jsx')
</script>
</body>
</html>

View File

@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<title>CodeXen Popup</title>
<meta charset="utf-8">
<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="shortcut icon" href="favicon.ico">
<style>
@font-face {
font-family: 'Lato';
src: url('../../resources/Lato-Regular.woff2') format('woff2'), /* Modern Browsers */
url('../../resources/Lato-Regular.woff') format('woff'), /* Modern Browsers */
url('../../resources/Lato-Regular.ttf') format('truetype');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
</style>
<script>
document.addEventListener('mousewheel', function(e) {
if(e.deltaY % 1 !== 0) {
e.preventDefault()
}
})
</script>
</head>
<body>
<div id="content"></div>
<script src="../ace/src-min/ace.js"></script>
<script>
require("babel-core/register")
require('./index.jsx')
</script>
</body>
</html>

View File

@@ -3,7 +3,7 @@ var remote = require('remote')
var hideFinder = remote.getGlobal('hideFinder')
var clipboard = require('clipboard')
var React = require('react/addons')
var React = require('react')
var ArticleFilter = require('../main/Mixins/ArticleFilter')

View File

@@ -1,11 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<a href="/main">Go Main</a>
<a href="/main">Go Popup</a>
</body>
</html>

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
var React = require('react')
var ExternalLink = require('../Mixins/ExternalLink')
var KeyCaster = require('../Mixins/KeyCaster')

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
var React = require('react')
var Select = require('react-select')
var LinkedState = require('../Mixins/LinkedState')

View File

@@ -0,0 +1,209 @@
var React = require('react')
var moment = require('moment')
var _ = require('lodash')
var CodeEditor = require('./CodeEditor')
var MarkdownPreview = require('./MarkdownPreview')
var ModeIcon = require('./ModeIcon')
var Select = require('react-select')
var Modal = require('../Mixins/Modal')
var ForceUpdate = require('../Mixins/ForceUpdate')
var LinkedState = require('../Mixins/LinkedState')
var aceModes = require('../../../modules/ace-modes')
var modeOptions = aceModes.map(function (mode) {
return {
label: mode,
value: mode
}
})
module.exports = React.createClass({
mixins: [ForceUpdate(60000), Modal, LinkedState],
propTypes: {
currentArticle: React.PropTypes.object,
showOnlyWithTag: React.PropTypes.func,
planet: React.PropTypes.object,
switchDetailMode: React.PropTypes.func,
user: React.PropTypes.shape({
id: React.PropTypes.number,
name: React.PropTypes.string,
Folders: React.PropTypes.array
}),
article: React.PropTypes.object,
saveCurrentArticle: React.PropTypes.func,
detailMode: React.PropTypes.string
},
getInitialState: function () {
var article = this.props.currentArticle != null ? {
id: this.props.currentArticle.id,
title: this.props.currentArticle.title,
content: this.props.currentArticle.CurrentRevision.title,
tags: this.props.currentArticle.Tags.map(function (tag) {
return tag.name
}),
mode: this.props.currentArticle.mode,
status: this.props.currentArticle.status
} : null
// console.log('init staet')
// console.log(article)
return {
isEditModalOpen: false,
article: article
}
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.detailMode === 'edit') {
var article = {
id: nextProps.currentArticle.id,
title: nextProps.currentArticle.title,
content: nextProps.currentArticle.CurrentRevision.content,
tags: nextProps.currentArticle.Tags.map(function (tag) {
return tag.name
}),
mode: nextProps.currentArticle.mode,
FolderId: nextProps.currentArticle.FolderId,
status: nextProps.currentArticle.status
}
this.setState({article: article})
}
},
openDeleteModal: function () {
if (this.props.article == null) return
},
handleFolderIdChange: function (FolderId) {
this.state.article.FolderId = FolderId
this.setState({article: this.state.article})
},
handleTagsChange: function (tag, tags) {
tags = _.uniq(tags, function (tag) {
return tag.value
})
this.state.article.tags = tags.map(function (tag) {
return tag.value
})
this.setState({article: this.state.article})
},
handleModeChange: function (mode) {
this.state.article.mode = mode
this.setState({article: this.state.article})
},
handleContentChange: function (e, value) {
var article = this.state.article
article.content = value
this.setState({article: article})
},
saveArticle: function () {
if (this.state.article.mode === '') {
return this.refs.mode.focus()
}
if (this.state.article.FolderId === '') {
return this.refs.folder.focus()
}
this.props.saveCurrentArticle(this.state.article)
},
render: function () {
if (this.props.currentArticle == null) {
return (
<div className='ArticleDetail'>
Nothing selected
</div>
)
}
if (this.props.detailMode === 'show') {
return this.renderViewer()
}
if (this.state.article == null) {
return (
<div className='ArticleDetail'>
Nothing selected
</div>
)
}
return this.renderEditor()
},
renderEditor: function () {
var article = this.state.article
var folderOptions = this.props.user.Folders.map(function (folder) {
return {
label: folder.name,
value: folder.id
}
})
return (
<div className='ArticleDetail edit'>
<div className='detailInfo'>
<div className='left'>
<Select ref='folder' onChange={this.handleFolderIdChange} clearable={false} options={folderOptions} placeholder='select folder...' value={article.FolderId} className='folder'/>
<Select onChange={this.handleTagsChange} clearable={false} multi={true} placeholder='add some tags...' allowCreate={true} value={article.tags} className='tags'/>
</div>
<div className='right'>
<button onClick={this.props.switchDetailMode('show')}>Cancel</button>
<button onClick={this.saveArticle} className='primary'>Save</button>
</div>
</div>
<div className='detailBody'>
<div className='detailPanel'>
<div className='header'>
<div className='title'>
<input ref='title' valueLink={this.linkState('article.title')}/>
</div>
<Select ref='mode' onChange={this.handleModeChange} clearable={false} options={modeOptions}placeholder='select mode...' value={article.mode} className='mode'/>
</div>
<CodeEditor onChange={this.handleContentChange} mode={article.mode} code={article.content}/>
</div>
</div>
</div>
)
},
renderViewer: function () {
var article = this.props.currentArticle
var tags = article.Tags.length > 0 ? article.Tags.map(function (tag) {
return (
<a key={tag.id}>{tag.name}</a>
)
}) : (
<span className='noTags'>Not tagged yet</span>
)
var folder = _.findWhere(this.props.user.Folders, {id: article.FolderId})
var folderName = folder != null ? folder.name : null
return (
<div className='ArticleDetail show'>
<div className='detailInfo'>
<div className='left'>
<div className='info'>
<i className='fa fa-fw fa-square'/> {folderName}&nbsp;
by {article.User.profileName}&nbsp;
Created {moment(article.createdAt).format('YYYY/MM/DD')}&nbsp;
Updated {moment(article.updatedAt).format('YYYY/MM/DD')}
</div>
<div className='tags'><i className='fa fa-fw fa-tags'/>{tags}</div>
</div>
<div className='right'>
<button onClick={this.props.switchDetailMode('edit')}><i className='fa fa-fw fa-edit'/></button>
<button><i className='fa fa-fw fa-trash'/></button>
<button><i className='fa fa-fw fa-share-alt'/></button>
</div>
</div>
<div className='detailBody'>
<div className='detailPanel'>
<div className='header'>
<ModeIcon className='mode' mode={article.mode}/>
<div className='title'>{article.title}</div>
</div>
{article.mode === 'markdown' ? <MarkdownPreview content={article.CurrentRevision.content}/> : <CodeEditor readOnly={true} onChange={this.handleContentChange} mode={article.mode} code={article.CurrentRevision.content}/>}
</div>
</div>
</div>
)
}
})

View File

@@ -0,0 +1,63 @@
var React = require('react')
var ReactRouter = require('react-router')
var moment = require('moment')
var _ = require('lodash')
var ForceUpdate = require('../Mixins/ForceUpdate')
var Markdown = require('../Mixins/Markdown')
var ProfileImage = require('../Components/ProfileImage')
var ModeIcon = require('../Components/ModeIcon')
module.exports = React.createClass({
mixins: [ReactRouter.Navigation, ReactRouter.State, ForceUpdate(60000), Markdown],
propTypes: {
articles: React.PropTypes.array
},
handleArticleClick: function (article) {
return function () {
this.props.selectArticle(article.id)
}.bind(this)
},
render: function () {
var articles = this.props.articles.map(function (article) {
if (article == null) return null
var tags = _.isArray(article.Tags) && article.Tags.length > 0 ? article.Tags.map(function (tag) {
return (
<a key={tag.id}>#{tag.name}</a>
)
}.bind(this)) : (
<span>Not tagged yet</span>
)
var params = this.getParams()
var isActive = this.props.currentArticle.id === article.id
return (
<li key={'article-' + article.id}>
<div onClick={this.handleArticleClick(article)} className={'articleItem' + (isActive ? ' active' : '')}>
<div className='top'>
<i className='fa fa-fw fa-square'/>
by <ProfileImage className='profileImage' size='20' email={article.User.email}/> {article.User.profileName}
<span className='updatedAt'>{article.status != null ? article.status : moment(article.updatedAt).fromNow()}</span>
</div>
<div className='middle'>
<ModeIcon className='mode' mode={article.mode}/> <div className='title'>{article.status !== 'new' ? article.title : '(New article)'}</div>
</div>
<div className='bottom'>
<div className='tags'><i className='fa fa-fw fa-tags'/>{tags}</div>
</div>
</div>
<div className='divider'></div>
</li>
)
}.bind(this))
return (
<div className='ArticleList'>
<ul ref='articles'>
{articles}
</ul>
</div>
)
}
})

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
var React = require('react')
var ace = window.ace
@@ -7,7 +7,13 @@ module.exports = React.createClass({
code: React.PropTypes.string,
mode: React.PropTypes.string,
className: React.PropTypes.string,
onChange: React.PropTypes.func
onChange: React.PropTypes.func,
readOnly: React.PropTypes.bool
},
getDefaultProps: function () {
return {
readOnly: false
}
},
componentDidMount: function () {
var el = React.findDOMNode(this.refs.target)
@@ -17,6 +23,9 @@ module.exports = React.createClass({
editor.renderer.setShowGutter(true)
editor.setTheme('ace/theme/xcode')
editor.clearSelection()
if (this.props.readOnly) {
editor.setReadOnly(true)
}
var session = editor.getSession()
if (this.props.mode != null && this.props.mode.length > 0) {
@@ -53,7 +62,7 @@ module.exports = React.createClass({
},
render: function () {
return (
<div ref='target' className={this.props.className}></div>
<div ref='target' className={this.props.className == null ? 'CodeEditor' : 'CodeEditor ' + this.props.className}></div>
)
}
})

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
var React = require('react')
var CodeEditor = require('./CodeEditor')
var Select = require('react-select')

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
var React = require('react')
var ace = window.ace

View File

@@ -1,25 +1,23 @@
var React = require('react')
var LinkedState = require('../Mixins/LinkedState')
var KeyCaster = require('../Mixins/KeyCaster')
import React, { PropTypes, findDOMNode } from 'react'
import linkState from '../helpers/linkState'
var Hq = require('../Services/Hq')
module.exports = React.createClass({
mixins: [LinkedState, KeyCaster('contactModal')],
propTypes: {
close: React.PropTypes.func
},
getInitialState: function () {
return {
export default class ContactModal extends React.Component {
constructor (props) {
super(props)
this.linkState = linkState
this.state = {
isSent: false,
mail: {
title: '',
content: ''
}
}
},
onKeyCast: function (e) {
}
onKeyCast (e) {
switch (e.status) {
case 'closeModal':
this.props.close()
@@ -32,11 +30,13 @@ module.exports = React.createClass({
this.sendEmail()
break
}
},
componentDidMount: function () {
React.findDOMNode(this.refs.title).focus()
},
sendEmail: function () {
}
componentDidMount () {
findDOMNode(this.refs.title).focus()
}
sendEmail () {
Hq.sendEmail(this.state.mail)
.then(function (res) {
this.setState({isSent: !this.state.isSent})
@@ -44,8 +44,9 @@ module.exports = React.createClass({
.catch(function (err) {
console.error(err)
})
},
render: function () {
}
render () {
return (
<div className='ContactModal modal'>
<div className='modal-header'><h1>Contact form</h1></div>
@@ -77,4 +78,8 @@ module.exports = React.createClass({
</div>
)
}
})
}
ContactModal.propTypes = {
close: PropTypes.func
}

View File

@@ -1,6 +1,4 @@
/* global localStorage */
var React = require('react/addons')
var React = require('react')
var Hq = require('../Services/Hq')

View File

@@ -1,11 +1,10 @@
/* global localStorage */
var React = require('react/addons')
var React = require('react')
var ReactRouter = require('react-router')
var Navigation = ReactRouter.Navigation
var State = ReactRouter.State
var Link = ReactRouter.Link
var Reflux = require('reflux')
var _ = require('lodash')
var Modal = require('../Mixins/Modal')
@@ -65,30 +64,12 @@ module.exports = React.createClass({
openTeamCreateModal: function () {
this.openModal(TeamCreateModal, {user: this.state.currentUser, transitionTo: this.transitionTo})
},
openPreferencesModal: function () {
this.openModal(PreferencesModal)
},
openPlanetCreateModal: function () {
this.openModal(PlanetCreateModal, {transitionTo: this.transitionTo})
},
toggleProfilePopup: function () {
this.openProfilePopup()
},
openProfilePopup: function () {
this.setState({isProfilePopupOpen: true}, function () {
document.addEventListener('click', this.closeProfilePopup)
})
},
closeProfilePopup: function () {
document.removeEventListener('click', this.closeProfilePopup)
this.setState({isProfilePopupOpen: false})
},
handleLogoutClick: function () {
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})
switchUserByIndex: function (index) {
var userProp = this.refs.users.props.children[index - 1].props
this.transitionTo('user', {userId: userProp.id})
},
render: function () {
var params = this.getParams()
@@ -96,88 +77,33 @@ module.exports = React.createClass({
if (this.state.currentUser == null) {
return null
}
console.log(this.state.currentUser.Teams)
var planets = this.state.currentUser.Planets.map(function (planet) {
planet.userName = this.state.currentUser.name
return planet
}.bind(this)).concat(this.state.currentUser.Teams.reduce(function (_planets, team) {
return _planets.concat(team.Planets == null ? [] : team.Planets.map(function (planet) {
planet.userName = team.name
return planet
}))
}, [])).map(function (planet, index) {
var users = [this.state.currentUser]
if (_.isArray(this.state.currentUser.Teams)) users = users.concat(this.state.currentUser.Teams)
var userButtons = users.map(function (user, index) {
return (
<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}}>
{planet.name[0]}
<div className='planetTooltip'>{planet.userName}/{planet.name}</div>
<li key={'user-' + user.id}>
<Link to='user' params={{userId: user.id}}>
{user.userType === 'person' ? (<ProfileImage email={user.email} size='44'/>): user.name[0]}
<div className='userTooltip'>{user.name}</div>
</Link>
{index < 9 ? (<div className='shortCut'>{index + 1}</div>) : null}
</li>
)
})
var popup = this.renderPopup()
return (
<div className='HomeNavigator'>
<button onClick={this.toggleProfilePopup} className='profileButton'>
<ProfileImage size='55' email={this.state.currentUser.email}/>
</button>
{popup}
<ul ref='planets' className='planetList'>
{planets}
<ul ref='users' className='userList'>
{userButtons}
</ul>
<button onClick={this.openPlanetCreateModal} className='newPlanet'>
<button onClick={this.openTeamCreateModal} className='newTeamButton'>
<i className='fa fa-plus'/>
<div className='tooltip'>Create new planet</div>
<div className='tooltip'>Create new team</div>
</button>
</div>
)
},
renderPopup: function () {
var teams = this.state.currentUser.Teams == null ? [] : this.state.currentUser.Teams.map(function (team) {
return (
<li key={'user-' + team.id}>
<Link to='userHome' params={{userName: team.name}} className='userName'>{team.profileName} ({team.name})</Link>
</li>
)
})
return (
<div ref='profilePopup' className={'profilePopup' + (this.state.isProfilePopupOpen ? '' : ' close')}>
<div className='profileGroup'>
<div className='profileGroupLabel'>
<span>You</span>
</div>
<ul className='profileGroupList'>
<li>
<Link to='userHome' params={{userName: this.state.currentUser.name}} className='userName'>Profile ({this.state.currentUser.name})</Link>
</li>
</ul>
</div>
<div className='profileGroup'>
<div className='profileGroupLabel'>
<span>Team</span>
</div>
<ul className='profileGroupList'>
{teams}
<li>
<button onClick={this.openTeamCreateModal} className='createNewTeam'><i className='fa fa-plus-square-o'/> create new team</button>
</li>
</ul>
</div>
<ul className='controlGroup'>
<li>
<button onClick={this.openPreferencesModal}><i className='fa fa-gears fa-fw'/> Preferences</button>
</li>
<li>
<button onClick={this.handleLogoutClick}><i className='fa fa-sign-out fa-fw'/> Log out</button>
</li>
</ul>
</div>
)
}
})

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
var React = require('react')
var CodeForm = require('./CodeForm')
var NoteForm = require('./NoteForm')

View File

@@ -1,5 +1,3 @@
/* global localStorage */
var React = require('react')
var socket = require('../Services/socket')

View File

@@ -0,0 +1,79 @@
var React = require('react')
module.exports = React.createClass({
propTypes: {
className: React.PropTypes.string,
mode: React.PropTypes.string
},
getClassName: function () {
var mode = this.props.mode
switch (mode) {
// Script
case 'javascript':
return 'devicon-javascript-plain'
case 'jsx':
return 'devicon-react-original'
case 'coffee':
return 'devicon-coffeescript-original'
case 'ruby':
return 'devicon-ruby-plain'
case 'erlang':
return 'devicon-erlang-plain'
case 'php':
return 'devicon-php-plain'
// HTML
case 'html':
return 'devicon-html5-plain'
// Stylesheet
case 'css':
return 'devicon-css3-plain'
case 'less':
return 'devicon-less-plain-wordmark'
case 'sass':
case 'scss':
return 'devicon-sass-original'
// Compile
case 'c_cpp':
return 'devicon-c-plain'
case 'csharp':
return 'devicon-csharp-plain'
case 'objc':
return 'devicon-apple-original'
case 'golang':
return 'devicon-go-plain'
case 'java':
return 'devicon-java-plain'
// Framework
case 'django':
return 'devicon-django-plain'
// Config
case 'dockerfile':
return 'devicon-docker-plain'
case 'gitignore':
return 'devicon-git-plain'
// Shell
case 'sh':
case 'batchfile':
case 'powershell':
return 'fa fa-fw fa-terminal'
case 'text':
case 'plain_text':
case 'markdown':
return 'fa fa-fw fa-file-text-o'
}
return 'fa fa-fw fa-code'
},
render: function () {
var className = this.getClassName()
return (
<i className={this.props.className + ' ' + className}/>
)
}
})

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
var React = require('react')
var Select = require('react-select')
var Hq = require('../Services/Hq')

View File

@@ -1,126 +0,0 @@
var React = require('react/addons')
var moment = require('moment')
var CodeViewer = require('./CodeViewer')
var CodeEditModal = require('./CodeEditModal')
var CodeDeleteModal = require('./CodeDeleteModal')
var NoteEditModal = require('./NoteEditModal')
var NoteDeleteModal = require('./NoteDeleteModal')
var MarkdownPreview = require('./MarkdownPreview')
var ProfileImage = require('./ProfileImage')
var Modal = require('../Mixins/Modal')
var ForceUpdate = require('../Mixins/ForceUpdate')
module.exports = React.createClass({
mixins: [ForceUpdate(60000), Modal],
propTypes: {
article: React.PropTypes.object,
showOnlyWithTag: React.PropTypes.func,
planet: React.PropTypes.object
},
getInitialState: function () {
return {
isEditModalOpen: false
}
},
openEditModal: function () {
if (this.props.article == null) return
switch (this.props.article.type) {
case 'code' :
this.openModal(CodeEditModal, {code: this.props.article, planet: this.props.planet})
break
case 'note' :
this.openModal(NoteEditModal, {note: this.props.article, planet: this.props.planet})
}
},
openDeleteModal: function () {
if (this.props.article == null) return
switch (this.props.article.type) {
case 'code' :
this.openModal(CodeDeleteModal, {code: this.props.article, planet: this.props.planet})
break
case 'note' :
this.openModal(NoteDeleteModal, {note: this.props.article, planet: this.props.planet})
}
},
render: function () {
var article = this.props.article
if (article == null) {
return (
<div className='PlanetArticleDetail'>
Nothing selected
</div>
)
}
var tags = article.Tags.length > 0 ? article.Tags.map(function (tag) {
return (
<a onClick={this.props.showOnlyWithTag(tag.name)} key={tag.id}>#{tag.name}</a>
)
}.bind(this)) : (
<a className='noTag'>Not tagged yet</a>
)
if (article.type === 'code') {
return (
<div className='PlanetArticleDetail codeDetail'>
<div className='detailHeader'>
<div className='itemLeft'>
<ProfileImage className='profileImage' size='25' email={article.User.email}/>
<i className='fa fa-code fa-fw'></i>
</div>
<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='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>
</div>
<div className='detailBody'>
<CodeViewer className='content' code={article.content} mode={article.mode}/>
</div>
</div>
)
}
return (
<div className='PlanetArticleDetail noteDetail'>
<div className='detailHeader'>
<div className='itemLeft'>
<ProfileImage className='profileImage' size='25' email={article.User.email}/>
<i className='fa fa-file-text-o fa-fw'></i>
</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>
</div>
<div className='detailBody'>
<MarkdownPreview className='content' content={article.content}/>
</div>
</div>
)
}
})

View File

@@ -1,100 +0,0 @@
var React = require('react/addons')
var ReactRouter = require('react-router')
var moment = require('moment')
var ForceUpdate = require('../Mixins/ForceUpdate')
var Markdown = require('../Mixins/Markdown')
var ProfileImage = require('../Components/ProfileImage')
module.exports = React.createClass({
mixins: [ReactRouter.Navigation, ReactRouter.State, ForceUpdate(60000), Markdown],
propTypes: {
articles: React.PropTypes.array,
showOnlyWithTag: React.PropTypes.func
},
handleArticleClikck: function (article) {
if (article.type === 'code') {
return function (e) {
var params = this.getParams()
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()
this.transitionTo('notes', {
userName: params.userName,
planetName: params.planetName,
localId: article.localId
})
}.bind(this)
}
},
render: function () {
var articles = this.props.articles.map(function (article) {
var tags = article.Tags.length > 0 ? article.Tags.map(function (tag) {
return (
<a onClick={this.props.showOnlyWithTag(tag.name)} key={tag.id}>#{tag.name}</a>
)
}.bind(this)) : (
<a className='noTag'>Not tagged yet</a>
)
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
if (article.type === 'code') {
return (
<li onClick={this.handleArticleClikck(article)} key={'code-' + article.id}>
<div className={'articleItem' + (isActive ? ' active' : '')}>
<div className='itemLeft'>
<ProfileImage className='profileImage' size='25' email={article.User.email}/>
<i className='fa fa-code fa-fw'></i>
</div>
<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='tags'><i className='fa fa-tags'/>{tags}</div>
</div>
</div>
<div className='divider'></div>
</li>
)
}
return (
<li onClick={this.handleArticleClikck(article)} key={'note-' + article.id}>
<div className={'articleItem blueprintItem' + (isActive ? ' active' : '')}>
<div className='itemLeft'>
<ProfileImage className='profileImage' size='25' email={article.User.email}/>
<i className='fa fa-file-text-o fa-fw'></i>
</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>
</div>
<div className='divider'></div>
</li>
)
}.bind(this))
return (
<div className='PlanetArticleList'>
<ul ref='articles'>
{articles}
</ul>
</div>
)
}
})

View File

@@ -1,6 +1,6 @@
/* global localStorage */
var React = require('react/addons')
var React = require('react')
var Hq = require('../Services/Hq')

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
var React = require('react')
var ReactRouter = require('react-router')
var Link = ReactRouter.Link

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
var React = require('react')
var ReactRouter = require('react-router')
var Navigation = ReactRouter.Navigation

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
var React = require('react')
var Hq = require('../Services/Hq')

View File

@@ -1,7 +1,7 @@
var ipc = require('ipc')
var remote = require('remote')
var React = require('react/addons')
var React = require('react')
var LinkedState = require('../Mixins/LinkedState')
var ExternalLink = require('../Mixins/ExternalLink')

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
var React = require('react')
var md5 = require('md5')
module.exports = React.createClass({

View File

@@ -1,6 +1,6 @@
/* global localStorage */
var React = require('react/addons')
var React = require('react')
var Hq = require('../Services/Hq')
var socket = require('../Services/socket')

View File

@@ -1,6 +1,6 @@
/* global localStorage */
var React = require('react/addons')
var React = require('react')
var Reflux = require('reflux')
var Select = require('react-select')

View File

@@ -0,0 +1,30 @@
var React = require('react')
var ExternalLink = require('../Mixins/ExternalLink')
module.exports = React.createClass({
mixins: [ExternalLink],
propTypes: {
search: React.PropTypes.string,
changeSearch: React.PropTypes.func
},
render: function () {
return (
<div className='TopBar'>
<div className='left'>
<div className='search'>
<i className='fa fa-search'/>
<input value={this.props.search} onChange={this.props.changeSearch} className='searchInput' placeholder='Search...'/>
</div>
</div>
<div className='right'>
<a onClick={this.openExternal} href='http://b00st.io' className='logo'>
<img width='44' height='44' src='resources/favicon-230x230.png'/>
<div className='tooltip'>Boost official page</div>
</a>
</div>
</div>
)
}
})

View File

@@ -0,0 +1,53 @@
var React = require('react')
var _ = require('lodash')
module.exports = React.createClass({
propTypes: {
createNewArticle: React.PropTypes.func,
search: React.PropTypes.string,
user: React.PropTypes.object
},
render: function () {
var user = this.props.user
var folders = _.isArray(user.Folders) ? user.Folders.map(function (folder) {
var isActive = this.props.search.match(new RegExp('in:' + folder.name))
return (
<button className={'folderButton' + (isActive ? ' active' : '')}><i className='fa fa-fw fa-square'/> {folder.name}</button>
)
}.bind(this)) : null
var members = _.isArray(user.Members) ? user.Members.map(function (member) {
return <button className='memberButton'>{member.profileName}</button>
}) : null
return (
<div className='UserNavigator'>
<div className='profile'>
<div className='profileName'>{user.profileName}</div>
<div className='name'>{user.name}</div>
<div className='dropdownIcon'><i className='fa fa-chevron-down'/></div>
</div>
<div className='control'>
<button onClick={this.props.createNewArticle} className='newPostButton'>New Post</button>
</div>
<div className='menu'>
<div className='menuGruop folders'>
<div className='label'>
Folders
<button className='plusButton'><i className='fa fa-plus'/></button>
</div>
<button className={'folderButton' + (this.props.search.match(/in:[a-z0-9-_]/) ? '' : ' active')}>All Folders</button>
{folders}
</div>
{user.userType === 'team' ? (
<div className='members'>{members}</div>
) : null}
</div>
</div>
)
}
})

View File

@@ -1,41 +0,0 @@
/* global localStorage */
var React = require('react/addons')
var ReactRouter = require('react-router')
var RouteHandler = ReactRouter.RouteHandler
var State = ReactRouter.State
var Navigation = ReactRouter.Navigation
var AuthFilter = require('../Mixins/AuthFilter')
var KeyCaster = require('../Mixins/KeyCaster')
var HomeNavigator = require('../Components/HomeNavigator')
module.exports = React.createClass({
mixins: [AuthFilter.OnlyUser, State, Navigation, KeyCaster('homeContainer')],
componentDidMount: function () {
if (this.isActive('homeEmpty')) {
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})
}
},
onKeyCast: function (e) {
switch (e.status) {
case 'switchPlanet':
this.refs.navigator.switchPlanetByIndex(e.data)
break
}
},
render: function () {
return (
<div className='HomeContainer'>
<HomeNavigator ref='navigator'/>
<RouteHandler/>
</div>
)
}
})

View File

@@ -0,0 +1,92 @@
var Hq = require('../Services/Hq')
var socket = require('../Services/socket')
import React, { PropTypes } from 'react'
import { Link } from 'react-router'
import linkState from '../helpers/linkState'
export default class LoginPage extends React.Component {
constructor (props) {
super(props)
this.state = {
user: {},
isSending: false,
error: null
}
this.linkState = linkState
}
handleSubmit (e) {
e.preventDefault()
this.setState({
isSending: true,
error: null
}, function () {
console.log(this.state.user)
Hq.login(this.state.user)
.then(function (res) {
localStorage.setItem('token', res.body.token)
localStorage.setItem('currentUser', JSON.stringify(res.body.user))
try {
this.props.history.pushState('home')
} catch(e) {
console.error(e)
}
}.bind(this))
.catch(function (err) {
console.error(err)
if (err.response == null) {
return this.setState({
error: {name: 'CunnectionRefused', message: 'API server doesn\'t respond. Check your internet connection.'},
isSending: false
})
}
var res = err.response
// Connection Failed or Whatever
this.setState({
error: err.response.body,
isSending: false
})
}.bind(this))
})
}
render () {
return (
<div className='LoginContainer'>
<img className='logo' src='resources/favicon-230x230.png'/>
<nav className='authNavigator text-center'><Link to='/login' activeClassName='active'>Log In</Link> / <Link to='/signup' activeClassName='active'>Sign Up</Link></nav>
<form onSubmit={e => this.handleSubmit(e)}>
<div className='formField'>
<input valueLink={this.linkState('user.email')} type='text' placeholder='E-mail'/>
</div>
<div className='formField'>
<input valueLink={this.linkState('user.password')} onChange={this.handleChange} type='password' placeholder='Password'/>
</div>
{this.state.isSending ? (
<p className='alertInfo'>Logging in...</p>
) : null}
{this.state.error != null ? <p className='alertError'>{this.state.error.message}</p> : null}
<div className='formField'>
<button className='logInButton' type='submit'>Log In</button>
</div>
</form>
</div>
)
}
}
LoginPage.propTypes = {
history: PropTypes.shape({
pushState: PropTypes.func
})
}

View File

@@ -1,108 +0,0 @@
/* global localStorage */
var React = require('react/addons')
var ReactRouter = require('react-router')
var Link = ReactRouter.Link
var AuthFilter = require('../Mixins/AuthFilter')
var LinkedState = require('../Mixins/LinkedState')
var Hq = require('../Services/Hq')
var socket = require('../Services/socket')
module.exports = React.createClass({
mixins: [LinkedState, ReactRouter.Navigation, AuthFilter.OnlyGuest],
getInitialState: function () {
return {
user: {},
authenticationFailed: false,
connectionFailed: false,
isSending: false
}
},
onListen: function (res) {
if (res.status === 'failedToLogIn') {
if (res.data.status === 401) {
// Wrong E-mail or Password
this.setState({
authenticationFailed: true,
connectionFailed: false,
isSending: false
})
return
}
// Connection Failed or Whatever
this.setState({
authenticationFailed: false,
connectionFailed: true,
isSending: false
})
return
}
},
handleSubmit: function (e) {
this.setState({
authenticationFailed: false,
connectionFailed: false,
isSending: true
}, function () {
Hq.login(this.state.user)
.then(function (res) {
localStorage.setItem('token', res.body.token)
localStorage.setItem('currentUser', JSON.stringify(res.body.user))
socket.reconnect()
this.transitionTo('userHome', {userName: res.body.user.name})
}.bind(this))
.catch(function (err) {
if (err.status === 401) {
this.setState({
authenticationFailed: true,
connectionFailed: false,
isSending: false
})
return
}
this.setState({
authenticationFailed: false,
connectionFailed: true,
isSending: false
})
}.bind(this))
})
e.preventDefault()
},
render: function () {
return (
<div className='LoginContainer'>
<img className='logo' src='resources/favicon-230x230.png'/>
<nav className='authNavigator text-center'><Link to='login'>Log In</Link> / <Link to='signup'>Sign Up</Link></nav>
<form onSubmit={this.handleSubmit}>
<div className='form-group'>
<input className='stripInput' valueLink={this.linkState('user.email')} type='text' placeholder='E-mail'/>
</div>
<div className='form-group'>
<input className='stripInput' valueLink={this.linkState('user.password')} onChange={this.handleChange} type='password' placeholder='Password'/>
</div>
{this.state.isSending ? (
<p className='alertInfo'>Logging in...</p>
) : null}
{this.state.connectionFailed ? (
<p className='alertError'>Please try again.</p>
) : null}
{this.state.authenticationFailed ? (
<p className='alertError'>Wrong E-mail or Password.</p>
) : null}
<div className='form-group'>
<button className='logInButton' type='submit'>Log In</button>
</div>
</form>
</div>
)
}
})

View File

@@ -0,0 +1,45 @@
var ipc = require('ipc')
import React, { PropTypes } from 'react'
var ContactModal = require('../Components/ContactModal')
export default class MainContainer extends React.Component {
// mixins: [Modal],
constructor (props) {
super(props)
this.state = {updateAvailable: false}
}
componentDidMount () {
ipc.on('update-available', function (message) {
this.setState({updateAvailable: true})
}.bind(this))
}
updateApp () {
ipc.send('update-app', 'Deal with it.')
}
openContactModal () {
this.openModal(ContactModal)
}
render () {
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'/>
<div className='tooltip'>Contact us</div>
</button>
{this.props.children}
</div>
)
}
}
MainContainer.propTypes = {
children: PropTypes.element
}

View File

@@ -1,107 +0,0 @@
/* global localStorage */
var ipc = require('ipc')
var React = require('react/addons')
var ReactRouter = require('react-router')
var RouteHandler = ReactRouter.RouteHandler
var Navigation = ReactRouter.Navigation
var State = ReactRouter.State
var Hq = require('../Services/Hq')
var socket = require('../Services/socket')
var Modal = require('../Mixins/Modal')
var UserStore = require('../Stores/UserStore')
var ContactModal = require('../Components/ContactModal')
function fetchPlanet (userName, planetName) {
return 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'
})
localStorage.setItem('planet-' + planet.id, JSON.stringify(planet))
return planet
})
.catch(function (err) {
console.error(err)
})
}
module.exports = React.createClass({
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')
return
} else {
this.transitionTo('home')
}
}
Hq.getUser()
.then(function (res) {
var user = res.body
UserStore.Actions.update(user)
user.Planets.forEach(function (planet) {
fetchPlanet(user.name, planet.name)
})
user.Teams.forEach(function (team) {
team.Planets.forEach(function (planet) {
fetchPlanet(team.name, planet.name)
})
})
})
.catch(function (err) {
if (err.status === 401) {
console.log('Not logged in yet')
localStorage.removeItem('currentUser')
this.transitionTo('login')
return
}
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'/>
<div className='tooltip'>Contact us</div>
</button>
<RouteHandler/>
</div>
)
}
})

View File

@@ -1,6 +1,5 @@
/* global localStorage*/
'strict'
var React = require('react/addons')
var React = require('react')
var ReactRouter = require('react-router')
var Reflux = require('reflux')

View File

@@ -0,0 +1,98 @@
import React, { PropTypes } from 'react'
import { Link } from 'react-router'
import linkState from '../helpers/linkState'
import openExternal from '../helpers/openExternal'
var Hq = require('../Services/Hq')
export default class SignupContainer extends React.Component {
constructor (props) {
super(props)
this.state = {
user: {},
connectionFailed: false,
emailConflicted: false,
nameConflicted: false,
validationFailed: false,
isSending: false,
error: null
}
this.linkState = linkState
this.openExternal = openExternal
}
handleSubmit (e) {
this.setState({
isSending: true,
error: null
}, function () {
Hq.signup(this.state.user)
.then(res => {
localStorage.setItem('token', res.body.token)
localStorage.setItem('currentUser', JSON.stringify(res.body.user))
this.props.history.pushState('userHome', {userId: res.body.user.id})
})
.catch(function (err) {
console.error(err)
if (err.response == null) {
return this.setState({
error: {name: 'CunnectionRefused', message: 'API server doesn\'t respond. Check your internet connection.'},
isSending: false
})
}
// Connection Failed or Whatever
this.setState({
error: err.response.body,
isSending: false
})
}.bind(this))
})
e.preventDefault()
}
render () {
return (
<div className='SignupContainer'>
<img className='logo' src='resources/favicon-230x230.png'/>
<nav className='authNavigator text-center'><Link to='/login' activeClassName='active'>Log In</Link> / <Link to='/signup' activeClassName='active'>Sign Up</Link></nav>
<form onSubmit={e => this.handleSubmit(e)}>
<div className='formField'>
<input valueLink={this.linkState('user.email')} type='text' placeholder='E-mail'/>
</div>
<div className='formField'>
<input valueLink={this.linkState('user.password')} type='password' placeholder='Password'/>
</div>
<div className='formField'>
<input valueLink={this.linkState('user.name')} type='text' placeholder='name'/>
</div>
<div className='formField'>
<input valueLink={this.linkState('user.profileName')} type='text' placeholder='Profile name'/>
</div>
{this.state.isSending ? (
<p className='alertInfo'>Signing up...</p>
) : null}
{this.state.error != null ? <p className='alertError'>{this.state.error.message}</p> : null}
<div className='formField'>
<button className='logInButton' type='submit'>Sign Up</button>
</div>
</form>
<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>
)
}
}
SignupContainer.propTypes = {
history: PropTypes.shape({
pushState: PropTypes.func
})
}

View File

@@ -1,139 +0,0 @@
/* global localStorage */
var React = require('react/addons')
var ReactRouter = require('react-router')
var Link = ReactRouter.Link
var AuthFilter = require('../Mixins/AuthFilter')
var LinkedState = require('../Mixins/LinkedState')
var ExternalLink = require('../Mixins/ExternalLink')
var Hq = require('../Services/Hq')
var socket = require('../Services/socket')
module.exports = React.createClass({
mixins: [LinkedState, ReactRouter.Navigation, AuthFilter.OnlyGuest, ExternalLink],
getInitialState: function () {
return {
user: {},
connectionFailed: false,
emailConflicted: false,
nameConflicted: false,
validationFailed: false,
isSending: false
}
},
handleSubmit: function (e) {
this.setState({
connectionFailed: false,
emailConflicted: false,
nameConflicted: false,
validationFailed: false,
isSending: true
}, function () {
Hq.signup(this.state.user)
.then(function (res) {
localStorage.setItem('token', res.body.token)
localStorage.setItem('currentUser', JSON.stringify(res.body.user))
socket.reconnect()
this.transitionTo('userHome', {userName: res.body.user.name})
}.bind(this))
.catch(function (err) {
console.error(err)
var res = err.response
if (err.status === 409) {
// Confliction
var emailConflicted = res.body.errors[0].path === 'email'
var nameConflicted = res.body.errors[0].path === 'name'
this.setState({
connectionFailed: false,
emailConflicted: emailConflicted,
nameConflicted: nameConflicted,
validationFailed: false,
isSending: false
})
return
}
if (err.status === 422) {
// Validation Failed
this.setState({
connectionFailed: false,
emailConflicted: false,
nameConflicted: false,
validationFailed: {
errors: res.body.errors.map(function (error) {
return error.path
})
},
isSending: false
})
return
}
// Connection Failed or Whatever
this.setState({
connectionFailed: true,
emailConflicted: false,
nameConflicted: false,
validationFailed: false,
isSending: false
})
return
}.bind(this))
})
e.preventDefault()
},
render: function () {
return (
<div className='SignupContainer'>
<img className='logo' src='resources/favicon-230x230.png'/>
<nav className='authNavigator text-center'><Link to='login'>Log In</Link> / <Link to='signup'>Sign Up</Link></nav>
<form onSubmit={this.handleSubmit}>
<div className='form-group'>
<input className='stripInput' valueLink={this.linkState('user.email')} type='text' placeholder='E-mail'/>
</div>
<div className='form-group'>
<input className='stripInput' valueLink={this.linkState('user.password')} type='password' placeholder='Password'/>
</div>
<div className='form-group'>
<input className='stripInput' valueLink={this.linkState('user.name')} type='text' placeholder='name'/>
</div>
<div className='form-group'>
<input className='stripInput' valueLink={this.linkState('user.profileName')} type='text' placeholder='Profile name'/>
</div>
{this.state.isSending ? (
<p className='alertInfo'>Signing up...</p>
) : null}
{this.state.connectionFailed ? (
<p className='alertError'>Please try again.</p>
) : null}
{this.state.emailConflicted ? (
<p className='alertError'>E-mail already exists.</p>
) : null}
{this.state.nameConflicted ? (
<p className='alertError'>Username already exists.</p>
) : null}
{this.state.validationFailed ? (
<p className='alertError'>Please fill every field correctly: {this.state.validationFailed.errors.join(', ')}</p>
) : null}
<div className='form-group'>
<button className='logInButton' type='submit'>Sign Up</button>
</div>
</form>
<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>
)
}
})

View File

@@ -1,367 +0,0 @@
/* global localStorage */
var React = require('react/addons')
var ReactRouter = require('react-router')
var Navigation = ReactRouter.Navigation
var State = ReactRouter.State
var RouteHandler = ReactRouter.RouteHandler
var Link = ReactRouter.Link
var Reflux = require('reflux')
var LinkedState = require('../Mixins/LinkedState')
var Modal = require('../Mixins/Modal')
var Helper = require('../Mixins/Helper')
var Hq = require('../Services/Hq')
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')
var PlanetStore = require('../Stores/PlanetStore')
module.exports = React.createClass({
mixins: [LinkedState, State, Navigation, Modal, Reflux.listenTo(UserStore, 'onUserChange'), Reflux.listenTo(PlanetStore, 'onPlanetChange'), Helper],
propTypes: {
params: React.PropTypes.shape({
userName: React.PropTypes.string,
planetName: React.PropTypes.string
})
},
getInitialState: function () {
return {
user: null
}
},
componentDidMount: function () {
this.fetchUser()
},
componentWillReceiveProps: function (nextProps) {
if (this.state.user == null) {
this.fetchUser(nextProps.params.userName)
return
}
if (nextProps.params.userName !== this.state.user.name) {
this.setState({
user: null
}, function () {
this.fetchUser(nextProps.params.userName)
})
}
},
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) {
if (this.state.user == null) return
var currentUser, planet, isOwner, team
switch (res.status) {
case 'updated':
// if state.user is currentUser, planet will be fetched by UserStore
currentUser = JSON.parse(localStorage.getItem('currentUser'))
if (currentUser.id === this.state.user.id) return
planet = res.data
isOwner = planet.Owner.id === this.state.user.id
if (isOwner) {
this.state.user.Planets = this.updateItemToTargetArray(planet, this.state.user.Planets)
this.setState({user: this.state.user})
return
}
// check if team of user has this planet
team = null
this.state.user.userType !== 'team' && this.state.user.Teams.some(function (_team) {
if (planet.Owner.id === _team.id) {
team = _team
return true
}
return false
})
if (team != null) {
team.Planets = this.updateItemToTargetArray(planet, team.Planets)
this.setState({user: this.state.user})
return
}
break
case 'destroyed':
// if state.user is currentUser, planet will be fetched by UserStore
currentUser = JSON.parse(localStorage.getItem('currentUser'))
if (currentUser.id === this.state.user.id) return
planet = res.data
isOwner = planet.Owner.id === this.state.user.id
if (isOwner) {
this.state.user.Planets = this.deleteItemFromTargetArray(planet, this.state.user.Planets)
this.setState({user: this.state.user})
return
}
// check if team of user has this planet
team = null
this.state.user.userType !== 'team' && this.state.user.Teams.some(function (_team) {
if (planet.Owner.id === _team.id) {
team = _team
return true
}
return false
})
if (team != null) {
team.Planets = this.deleteItemFromTargetArray(planet, team.Planets)
this.setState({user: this.state.user})
return
}
break
}
},
fetchUser: function (userName) {
if (userName == null) userName = this.props.params.userName
Hq.fetchUser(userName)
.then(function (res) {
this.setState({user: res.body})
}.bind(this))
.catch(function (err) {
console.error(err)
})
},
openEditProfileModal: function () {
this.openModal(EditProfileModal, {user: this.state.user})
},
openTeamSettingsModal: function () {
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})
},
openPlanetCreateModalWithOwnerName: function (name) {
return function () {
this.openModal(PlanetCreateModal, {ownerName: name})
}.bind(this)
},
render: function () {
var user = this.state.user
var currentUser = JSON.parse(localStorage.getItem('currentUser'))
if (this.isActive('userHome')) {
if (user == null) {
return (
<div className='UserContainer'>
User Loading...
</div>
)
} else if (user.userType === 'team') {
return this.renderTeamHome(currentUser)
} else {
return this.renderUserHome(currentUser)
}
} else if (this.isActive('planet') && user != null && user.userType === 'team') {
var members = user.Members.map(function (member) {
return (
<li key={'user-' + member.id}><Link to='userHome' params={{userName: member.name}}>
<ProfileImage className='memberImage' size='22' email={member.email}/>
<div className='memberInfo'>
<div className='memberProfileName'>{member.profileName}</div>
<div className='memberName'>@{member.name}</div>
</div>
</Link></li>
)
})
return (
<div className='UserContainer'>
<RouteHandler/>
<div className='memberPopup'>
<div className='label'>Members</div>
<ul className='members'>
{members}
</ul>
</div>
</div>
)
} else {
return (
<div className='UserContainer'>
<RouteHandler/>
</div>
)
}
},
renderTeamHome: function (currentUser) {
var user = this.state.user
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) {
return (
<li key={'planet-' + planet.id}>
<Link to='planet' params={{userName: user.name, planetName: planet.name}}>{user.name}/{planet.name}</Link>
&nbsp;{!planet.public ? (<i className='fa fa-lock'/>) : null}
</li>
)
})
var members = user.Members == null ? [] : user.Members.map(function (member) {
return (
<li key={'user-' + member.id}>
<Link to='userHome' params={{userName: member.name}}>
<ProfileImage size='22' className='memberImage' email={member.email}/>
<div className='memberInfo'>
<div className='memberProfileName'>{member.profileName} <span className='memberRole'>({member.TeamMember.role})</span></div>
<div className='memberName'>@{member.name}</div>
</div>
</Link>
<div className='role'></div>
</li>
)
})
return (
<div className='UserContainer'>
<div className='userProfile'>
<ProfileImage className='userPhoto' size='75' email={user.email}/>
<div className='userInfo'>
<div className='userProfileName'>{user.profileName}</div>
<div className='userName'>{user.name}</div>
</div>
{isOwner ? (<button onClick={this.openTeamSettingsModal} className='editProfileButton'>Team settings</button>) : null}
</div>
<div className='memberList'>
<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'>
<div className='planetLabel'>{userPlanets.length} {userPlanets.length > 0 ? 'Planets' : 'Planet'}</div>
<div className='planetGroup'>
<ul className='planets'>
{userPlanets}
{isOwner ? (<li><button onClick={this.openPlanetCreateModalWithOwnerName(user.name)} className='createPlanetButton'><i className='fa fa-plus-square-o'/> Create new planet</button></li>) : null}
</ul>
</div>
</div>
</div>
)
},
renderUserHome: function (currentUser) {
var user = this.state.user
var isOwner = currentUser.id === user.id
var userPlanets = user.Planets.map(function (planet) {
return (
<li key={'planet-' + planet.id}>
<Link to='planet' params={{userName: user.name, planetName: planet.name}}>{user.name}/{planet.name}</Link>
&nbsp;{!planet.public ? (<i className='fa fa-lock'/>) : null}
</li>
)
})
var teams = user.Teams == null ? [] : user.Teams.map(function (team) {
return (
<li key={'user-' + team.id}>
<Link to='userHome' params={{userName: team.name}}>
<div className='teamInfo'>
<div className='teamProfileName'>{team.profileName}</div>
<div className='teamName'>@{team.name}</div>
</div>
</Link>
</li>
)
})
var teamPlanets = user.Teams == null ? [] : user.Teams.map(function (team) {
var planets = (team.Planets == null ? [] : team.Planets).map(function (planet) {
return (
<li key={'planet-' + planet.id}>
<Link to='planet' params={{userName: team.name, planetName: planet.name}}>{team.name}/{planet.name}</Link>
&nbsp;{!planet.public ? (<i className='fa fa-lock'/>) : null}
</li>
)
})
return (
<div key={'user-' + team.id} className='planetGroup'>
<div className='planetGroupLabel'>{team.profileName} <small>@{team.name}</small></div>
<ul className='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}
</ul>
</div>
)
}.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'>
<ProfileImage className='userPhoto' size='75' email={user.email}/>
<div className='userInfo'>
<div className='userProfileName'>{user.profileName}</div>
<div className='userName'>{user.name}</div>
</div>
{isOwner ? (
<button onClick={this.openEditProfileModal} className='editProfileButton'>Edit profile</button>) : null}
</div>
<div className='teamList'>
<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'>{planetCount} {planetCount > 1 ? 'Planets' : 'Planet'}</div>
<div className='planetGroup'>
<div className='planetGroupLabel'>{user.profileName} <small>@{user.name}</small></div>
<ul className='planets'>
{userPlanets}
{isOwner ? (<li><button onClick={this.openPlanetCreateModalWithOwnerName(user.name)} className='createPlanetButton'><i className='fa fa-plus-square-o'/> Create new planet</button></li>) : null}
</ul>
</div>
{teamPlanets}
</div>
</div>
)
}
})

View File

@@ -0,0 +1,9 @@
import React, { PropTypes } from 'react'
export default class ArticleDetail extends React.Component {
render () {
return (
<div></div>
)
}
}

View File

@@ -0,0 +1,11 @@
import React, { PropTypes } from 'react'
class ArticleList extends React.Component {
render() {
return (
<div className='ArticleList'></div>
)
}
}
export default ArticleList

View File

@@ -0,0 +1,12 @@
import React, { PropTypes } from 'react'
class ArticleNavigator extends React.Component {
render () {
return (
<div className='ArticleNavigator'>
</div>
)
}
}
export default ArticleNavigator

View File

@@ -0,0 +1,33 @@
import React, { Component, PropTypes } from 'react'
import { Link } from 'react-router'
export default class UserNavigator extends Component {
renderUserList () {
var users = this.props.users.map(user => (
<li key={'user-' + user.id}>
<Link to={'/users/' + user.id}>
<div className='userTooltip'>{user.name}</div>
</Link>
</li>
))
return (
<div className='userList'>
{users}
</div>
)
}
render () {
return (
<div className='UserNavigator'>
{this.renderUserList()}
</div>
)
}
}
UserNavigator.propTypes = {
users: PropTypes.array
}

View File

@@ -0,0 +1,10 @@
function updateUser (user) {
return {
type: 'USER_UPDATE',
data: user
}
}
module.exports = {
updateUser: updateUser
}

View File

@@ -0,0 +1,50 @@
import React from 'react'
// import { connect } from 'react-redux'
// import actionss....
import UserNavigator from './Components/UserNavigator'
import ArticleNavigator from './Components/ArticleNavigator'
import ArticleList from './Components/ArticleList'
import ArticleDetail from './Components/ArticleDetail'
// var AuthFilter = require('../Mixins/AuthFilter')
// var KeyCaster = require('../Mixins/KeyCaster')
class HomeContainer extends React.Component {
componentDidMount () {
// if (!this.isActive('user')) {
// console.log('redirect to user home')
// var user = JSON.parse(localStorage.getItem('currentUser'))
// this.transitionTo('userHome', {userId: user.id})
// }
}
render () {
let users = [
{
id: 1,
name: 'me',
email: 'fll@eme.com'
},
{
id: 2,
name: 'me',
email: 'fll@eme.com'
}
]
return (
<div className='HomeContainer'>
<UserNavigator users={users} />
<ArticleNavigator/>
<ArticleList/>
<ArticleDetail/>
</div>
)
}
}
// function remap (state) {
// console.log('mapped')
// console.log(state)
// return {}
// }
export default HomeContainer

View File

@@ -0,0 +1,15 @@
import {combineReducers} from 'redux'
const initialCurrentUser = JSON.parse(localStorage.getItem('currentUser'))
function currentUser (state, action) {
switch (action.type) {
default:
return initialCurrentUser
}
}
export default combineReducers({
currentUser
})

View File

@@ -4,12 +4,12 @@ var mixin = {}
mixin.OnlyGuest = {
componentDidMount: function () {
var currentUser = localStorage.getItem('currentUser')
var currentUser = JSON.parse(localStorage.getItem('currentUser'))
if (currentUser == null) {
return
}
this.transitionTo('userHome', {userName: currentUser.name})
this.transitionTo('homeDefault')
}
}

View File

@@ -6,6 +6,7 @@ var md = markdownit({
var Markdown = {
markdown: function (content) {
if (content == null) content = ''
return md.render(content)
}
}

View File

@@ -1,4 +1,4 @@
var React = require('react/addons')
import React from 'react'
var ModalBase = React.createClass({
getInitialState: function () {
return {

View File

@@ -21,11 +21,9 @@ function setPartialState (component, path, value) {
updateIn(component.state, path, value))
}
module.exports = {
linkState: function (path) {
export default function linkState (path) {
return {
value: getIn(this.state, path),
requestChange: setPartialState.bind(null, this, path)
}
}
}

View File

@@ -7,12 +7,12 @@ module.exports = {
// Auth
login: function (input) {
return request
.post(apiUrl + 'auth')
.post(apiUrl + 'auth/login')
.send(input)
},
signup: function (input) {
return request
.post(apiUrl + 'auth/signup')
.post(apiUrl + 'auth/register')
.send(input)
},
getUser: function () {
@@ -30,124 +30,36 @@ module.exports = {
})
.send(input)
},
// Resources
fetchUser: function (userName) {
fetchArticles: function (userId) {
return request
.get(apiUrl + 'resources/' + userName)
.get(apiUrl + 'teams/' + userId +'/articles')
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
},
updateUser: function (userName, input) {
fetchArticlesByFolderId: function (folderId) {
return request
.put(apiUrl + 'resources/' + userName)
.get(apiUrl + 'folders/' + folderId +'/articles')
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
},
createArticle: function (input) {
return request
.post(apiUrl + 'folders/' + input.FolderId + '/articles')
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.send(input)
},
createTeam: function (userName, input) {
updateArticle: function (articleId, input) {
return request
.post(apiUrl + 'resources/' + userName + '/teams')
.put(apiUrl + 'articles/' + articleId)
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.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')
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.send(input)
},
fetchPlanet: function (userName, planetName) {
return request
.get(apiUrl + 'resources/' + userName + '/planets/' + planetName)
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
},
updatePlanet: function (userName, planetName, input) {
return request
.put(apiUrl + 'resources/' + userName + '/planets/' + planetName)
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.send(input)
},
destroyPlanet: function (userName, planetName) {
return request
.del(apiUrl + 'resources/' + userName + '/planets/' + planetName)
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
},
createCode: function (userName, planetName, input) {
return request
.post(apiUrl + 'resources/' + userName + '/planets/' + planetName + '/codes')
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.send(input)
},
updateCode: function (userName, planetName, localId, input) {
return request
.put(apiUrl + 'resources/' + userName + '/planets/' + planetName + '/codes/' + localId)
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.send(input)
},
destroyCode: function (userName, planetName, localId) {
return request
.del(apiUrl + 'resources/' + userName + '/planets/' + planetName + '/codes/' + localId)
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
},
createNote: function (userName, planetName, input) {
return request
.post(apiUrl + 'resources/' + userName + '/planets/' + planetName + '/notes')
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.send(input)
},
updateNote: function (userName, planetName, localId, input) {
return request
.put(apiUrl + 'resources/' + userName + '/planets/' + planetName + '/notes/' + localId)
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.send(input)
},
destroyNote: function (userName, planetName, localId) {
return request
.del(apiUrl + 'resources/' + userName + '/planets/' + planetName + '/notes/' + localId)
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
},
// Search
searchTag: function (tagName) {
return request

View File

@@ -1,64 +1,17 @@
/* global localStorage */
var config = require('../../../config')
var UserStore = require('../Stores/UserStore')
var PlanetStore = require('../Stores/PlanetStore')
var io = require('socket.io-client')(config.apiUrl)
io.on('connected', function (data) {
console.log('connected by WS')
reconnect()
})
io.on('userUpdated', function (data) {
console.log('userUpdated')
UserStore.Actions.update(data)
})
// Planet
io.on('planetUpdated', function (data) {
console.log('planetUpdated')
PlanetStore.Actions.update(data)
})
io.on('planetDestroyed', function (data) {
console.log('planetDestroyed')
PlanetStore.Actions.destroy(data)
})
// Article
io.on('codeUpdated', function (data) {
console.log('codeUpdated')
PlanetStore.Actions.updateCode(data)
})
io.on('codeDestroyed', function (data) {
console.log('codeDestroyed')
PlanetStore.Actions.destroyCode(data)
})
io.on('noteUpdated', function (data) {
console.log('noteUpdated')
PlanetStore.Actions.updateNote(data)
})
io.on('noteDestroyed', function (data) {
console.log('noteDestroyed')
PlanetStore.Actions.destroyNote(data)
})
var reconnect = function (currentUser) {
if (currentUser == null) currentUser = JSON.parse(localStorage.getItem('currentUser'))
if (currentUser != null) {
var rooms = ['user:' + currentUser.id].concat(currentUser.Teams.map(function (team) {
return 'user:' + team.id
}))
io.emit('room:sync', {rooms: rooms})
} else {
io.emit('room:sync', {rooms: []})
}
}
module.exports = {
io: io,
reconnect: reconnect
io: io
}

View File

@@ -1,131 +0,0 @@
/* global localStorage */
var Reflux = require('reflux')
var request = require('superagent')
var apiUrl = require('../../../config').apiUrl
var AuthStore = Reflux.createStore({
init: function () {
},
// Reflux Store
login: function (input) {
request
.post(apiUrl + 'auth/login')
.send(input)
.set('Accept', 'application/json')
.end(function (err, res) {
if (err) {
console.error(err)
this.trigger({
status: 'failedToLogIn',
data: res
})
return
}
var user = res.body.user
localStorage.setItem('token', res.body.token)
localStorage.setItem('user', JSON.stringify(res.body.user))
this.trigger({
status: 'loggedIn',
data: user
})
}.bind(this))
},
register: function (input) {
request
.post(apiUrl + 'auth/signup')
.send(input)
.set('Accept', 'application/json')
.end(function (err, res) {
if (err) {
console.error(res)
this.trigger({
status: 'failedToRegister',
data: res
})
return
}
var user = res.body.user
localStorage.setItem('token', res.body.token)
localStorage.setItem('user', JSON.stringify(res.body.user))
this.trigger({
status: 'registered',
data: user
})
}.bind(this))
},
refreshUser: function () {
request
.get(apiUrl + 'auth/user')
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.end(function (err, res) {
if (err) {
console.error(err)
if (res.status === 401 || res.status === 403) {
AuthActions.logout()
}
return
}
var user = res.body
localStorage.setItem('user', JSON.stringify(user))
this.trigger({
status: 'userRefreshed',
data: user
})
}.bind(this))
},
logout: function () {
localStorage.removeItem('token')
localStorage.removeItem('currentUser')
this.trigger({
status: 'loggedOut'
})
},
updateProfile: function (input) {
request
.put(apiUrl + 'auth/user')
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.send(input)
.end(function (err, res) {
if (err) {
console.error(err)
this.trigger({
status: 'userProfileUpdatingFailed',
data: err
})
return
}
var user = res.body
localStorage.setItem('user', JSON.stringify(user))
this.trigger({
status: 'userProfileUpdated',
data: user
})
}.bind(this))
},
// Methods
check: function () {
if (localStorage.getItem('token')) return true
return false
},
getUser: function () {
var userJSON = localStorage.getItem('currentUser')
if (userJSON == null) return null
return JSON.parse(userJSON)
}
})
module.exports = AuthStore

View File

@@ -1,179 +0,0 @@
/* global localStorage */
var Reflux = require('reflux')
var UserStore = require('./UserStore')
var Helper = require('../Mixins/Helper')
var actions = Reflux.createActions([
'update',
'destroy',
'updateCode',
'destroyCode',
'updateNote',
'destroyNote'
])
module.exports = Reflux.createStore({
mixins: [Helper],
listenables: [actions],
Actions: actions,
/*
Planet must be updated like below
Planet
Codes
Tags
User
Notes
Tags
User
Owner
*/
onUpdate: function (planet) {
// Copy the planet object
var aPlanet = Object.assign({}, planet)
delete aPlanet.Codes
delete aPlanet.Notes
delete aPlanet.Owner
// Check if the planet should be updated to currentUser
var currentUser = JSON.parse(localStorage.getItem('currentUser'))
var currentUserMustBeUpdated = false
var ownedByCurrentUser = currentUser.id === aPlanet.OwnerId
if (ownedByCurrentUser) {
currentUser.Planets = this.updateItemToTargetArray(aPlanet, currentUser.Planets)
currentUserMustBeUpdated = true
} else {
var team = null
if (currentUser.Teams.some(function (_team) {
if (_team.id === aPlanet.OwnerId) {
team = _team
return true
}
return false
})) {
team.Planets = this.updateItemToTargetArray(aPlanet, team.Planets)
currentUserMustBeUpdated = true
}
}
// Update currentUser
if (currentUserMustBeUpdated) {
UserStore.Actions.update(currentUser)
}
planet.Codes.forEach(function (code) {
code.type = 'code'
})
planet.Notes.forEach(function (note) {
note.type = 'note'
})
// Update the planet
localStorage.setItem('planet-' + planet.id, JSON.stringify(planet))
this.trigger({
status: 'updated',
data: planet
})
},
onDestroy: function (planet) {
// Check if the planet should be updated to currentUser
var currentUser = JSON.parse(localStorage.getItem('currentUser'))
var ownedByCurrentUser = currentUser.id === planet.OwnerId
if (ownedByCurrentUser) {
currentUser.Planets = this.deleteItemFromTargetArray(planet, currentUser.Planets)
}
if (!ownedByCurrentUser) {
var team = null
currentUser.Teams.some(function (_team) {
if (_team.id === planet.OwnerId) {
team = _team
return true
}
return
})
if (team) {
team.Planets = this.deleteItemFromTargetArray(planet, team.Planets)
}
}
// Update currentUser
localStorage.setItem('currentUser', JSON.stringify(currentUser))
UserStore.Actions.update(currentUser)
// Update the planet
localStorage.setItem('planet-' + planet.id, JSON.stringify(planet))
this.trigger({
status: 'destroyed',
data: planet
})
},
onUpdateCode: function (code) {
code.type = 'code'
var planet = JSON.parse(localStorage.getItem('planet-' + code.PlanetId))
if (planet != null) {
planet.Codes = this.updateItemToTargetArray(code, planet.Codes)
localStorage.setItem('planet-' + code.PlanetId, JSON.stringify(planet))
}
this.trigger({
status: 'codeUpdated',
data: code
})
},
onDestroyCode: function (code) {
var planet = JSON.parse(localStorage.getItem('planet-' + code.PlanetId))
if (planet != null) {
planet.Codes = this.deleteItemFromTargetArray(code, planet.Codes)
localStorage.setItem('planet-' + code.PlanetId, JSON.stringify(planet))
}
code.type = 'code'
this.trigger({
status: 'codeDestroyed',
data: code
})
},
onUpdateNote: function (note) {
note.type = 'note'
var planet = JSON.parse(localStorage.getItem('planet-' + note.PlanetId))
if (planet != null) {
planet.Notes = this.updateItemToTargetArray(note, planet.Notes)
localStorage.setItem('planet-' + note.PlanetId, JSON.stringify(planet))
}
this.trigger({
status: 'noteUpdated',
data: note
})
},
onDestroyNote: function (note) {
var planet = JSON.parse(localStorage.getItem('planet-' + note.PlanetId))
if (planet != null) {
planet.Notes = this.deleteItemFromTargetArray(note, planet.Notes)
localStorage.setItem('planet-' + note.PlanetId, JSON.stringify(planet))
}
note.type = 'note'
this.trigger({
status: 'noteDestroyed',
data: note
})
}
})

View File

@@ -1,58 +0,0 @@
/* global localStorage */
var Reflux = require('reflux')
var actions = Reflux.createActions([
'update',
'destroy'
])
module.exports = Reflux.createStore({
listenables: [actions],
onUpdate: function (user) {
if (this.socket == null) this.socket = require('../Services/socket')
var currentUser = JSON.parse(localStorage.getItem('currentUser'))
if (currentUser.id === user.id) {
localStorage.setItem('currentUser', JSON.stringify(user))
this.socket.reconnect(user)
}
if (user.userType === 'team') {
var isMyTeam = user.Members.some(function (member) {
if (currentUser.id === member.id) {
return true
}
return false
})
if (isMyTeam) {
var isNew = !currentUser.Teams.some(function (team, index) {
if (user.id === team.id) {
currentUser.Teams.splice(index, 1, user)
return true
}
return false
})
if (isNew) {
currentUser.Teams.push(user)
}
localStorage.setItem('currentUser', JSON.stringify(currentUser))
}
}
this.trigger({
status: 'userUpdated',
data: user
})
},
onDestroy: function (user) {
this.trigger({
status: 'userDestroyed',
data: user
})
},
Actions: actions
})

View File

@@ -0,0 +1,29 @@
function getIn (object, path) {
var stack = path.split('.')
while (stack.length > 1) {
object = object[stack.shift()]
}
return object[stack.shift()]
}
function updateIn (object, path, value) {
var current = object
var stack = path.split('.')
while (stack.length > 1) {
current = current[stack.shift()]
}
current[stack.shift()] = value
return object
}
function setPartialState (component, path, value) {
component.setState(
updateIn(component.state, path, value))
}
export default function linkState (path) {
return {
value: getIn(this.state, path),
requestChange: setPartialState.bind(null, this, path)
}
}

View File

@@ -0,0 +1,6 @@
var shell = require('shell')
export default function (e) {
shell.openExternal(e.currentTarget.href)
e.preventDefault()
}

View File

@@ -1,99 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<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="shortcut icon" href="favicon.ico">
<style>
@font-face {
font-family: 'Lato';
src: url('../../resources/Lato-Regular.woff2') format('woff2'), /* Modern Browsers */
url('../../resources/Lato-Regular.woff') format('woff'), /* Modern Browsers */
url('../../resources/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>
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource);
var keysArray = Object.keys(Object(nextSource));
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
</script>
<script src="../ace/src-min/ace.js"></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('./index.jsx')
</script>
</body>
</html>

View File

@@ -1,56 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<title>CodeXen</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
<link rel="stylesheet" href="../vendor/fontawesome/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" charset="utf-8">
<link rel="stylesheet" href="../../node_modules/devicon/devicon.min.css">
<link rel="stylesheet" href="../styles/main/index.css" media="screen" charset="utf-8">
<link rel="shortcut icon" href="favicon.ico">
<script>
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
<style>
@font-face {
font-family: 'Lato';
src: url('../../resources/Lato-Regular.woff2') format('woff2'), /* Modern Browsers */
url('../../resources/Lato-Regular.woff') format('woff'), /* Modern Browsers */
url('../../resources/Lato-Regular.ttf') format('truetype');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
nextSource = Object(nextSource);
var keysArray = Object.keys(Object(nextSource));
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
#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;
}
return to;
}
});
}
</script>
<script src="../vendor/moment/min/moment.min.js"></script>
<script src="../vendor/markdown-it/dist/markdown-it.min.js"></script>
<script src="../vendor/react/react-with-addons.js"></script>
<script src="../vendor/react-router/build/umd/ReactRouter.js"></script>
<script src="../vendor/reflux/dist/reflux.js"></script>
<script src="../ace/src-min/ace.js"></script>
</style>
</head>
<body>
<div id="loadingCover">
<img src="resources/favicon-230x230.png">
<div class='message'>Loading...</div>
</div>
<div id="content"></div>
<script src="http://localhost:8090/webpack-dev-server.js"></script>
<script type="text/javascript" src="http://localhost:8090/assets/main.js"></script>
<script type="text/javascript" src="http://localhost:8090/assets/main-style.js"></script>
<script src="../../submodules/ace/src-min/ace.js"></script>
<script>
var version = require('remote').require('app').getVersion()
global.version = version
document.title = 'Boost' + ((version == null || version.length === 0) ? ' DEV' : '')
require("babel-core/register")
require('./index')
</script>
</body>
</html>

29
browser/main/index.js Normal file
View File

@@ -0,0 +1,29 @@
import React from 'react'
import { Router, Route, IndexRoute } from 'react-router'
import MainContainer from './Containers/MainContainer'
import LoginContainer from './Containers/LoginContainer'
import SignupContainer from './Containers/SignupContainer'
import HomeContainer from './HomeContainer'
function onlyUser (state, replaceState) {
var currentUser = JSON.parse(localStorage.getItem('currentUser'))
if (currentUser == null) replaceState('login', '/login')
}
let routes = (
<Route path='/' component={MainContainer}>
<Route name='login' path='login' component={LoginContainer}/>
<Route name='signup' path='signup' component={SignupContainer}/>
<IndexRoute name='home' component={HomeContainer} onEnter={onlyUser}>
<IndexRoute name='homeDefault'/>
<Route name='user' path=':userId'/>
</IndexRoute>
</Route>
)
let el = document.getElementById('content')
React.render(<Router>{routes}</Router>, el, function () {
let loadingCover = document.getElementById('loadingCover')
loadingCover.parentNode.removeChild(loadingCover)
})

View File

@@ -1,46 +0,0 @@
var React = require('react/addons')
var ReactRouter = require('react-router')
var Route = ReactRouter.Route
var DefaultRoute = ReactRouter.DefaultRoute
var MainContainer = require('./Containers/MainContainer')
var LoginContainer = require('./Containers/LoginContainer')
var SignupContainer = require('./Containers/SignupContainer')
var HomeContainer = require('./Containers/HomeContainer')
var UserContainer = require('./Containers/UserContainer')
var PlanetContainer = require('./Containers/PlanetContainer')
var routes = (
<Route path='/' handler={MainContainer}>
<DefaultRoute name='root'/>
<Route name='login' path='login' handler={LoginContainer}/>
<Route name='signup' path='signup' handler={SignupContainer}/>
<Route name='home' path='home' handler={HomeContainer}>
<DefaultRoute name='homeEmpty'/>
<Route name='user' path=':userName' handler={UserContainer}>
<DefaultRoute name='userHome'/>
<Route name='planet' path=':planetName' handler={PlanetContainer}>
<DefaultRoute name='planetHome'/>
<Route name='codes' path='codes/:localId'/>
<Route name='notes' path='notes/:localId'/>
</Route>
</Route>
</Route>
</Route>
)
var loadingCover = document.getElementById('loadingCover')
ReactRouter.run(routes, ReactRouter.HashLocation, function (Root) {
React.render(<Root/>, document.getElementById('content'))
if (loadingCover != null) {
loadingCover.parentNode.removeChild(loadingCover)
loadingCover = null
}
})

View File

@@ -1,2 +0,0 @@
require('../styles/main/index.styl')
require('react-select/dist/default.css')

View File

@@ -0,0 +1,169 @@
noTagsColor = #999
.ArticleDetail
absolute right bottom
top 60px
left 250px
padding 10px
*
-webkit-user-select all
.detailInfo
height 70px
width 100%
transition 0.1s
font-size 12px
position relative
.left
absolute top left bottom
right 120px
.right
absolute top right
.detailBody
absolute left right bottom
top 70px
overflow-x hidden
overflow-y auto
.detailPanel
absolute top
left 10px
right 10px
bottom 10px
background-color white
border-radius 5px
border solid 1px borderColor
&>.header
absolute top left right
height 60px
.MarkdownPreview
absolute left right bottom
top 60px
marked()
box-sizing border-box
padding 5px 15px
border-top solid 1px borderColor
overflow-y auto
.CodeEditor
absolute left right bottom
top 60px
border-top solid 1px borderColor
min-height 300px
border-bottom-left-radius 5px
border-bottom-right-radius 5px
&.edit
.detailInfo
.left
.Select
.Select-control
border none
background-color transparent
.folder.Select
width 150px
.Select-control
&:hover
background-color darken(white, 5%)
&.is-focused
.Select-control
background-color white
.tags.Select
.Select-control
white-space nowrap
overflow-x auto
position relative
.Select-arrow-zone, .Select-arrow
display none
.right
button
cursor pointer
height 33px
width 55px
margin-left 5px
font-size 14px
color inactiveTextColor
background-color darken(white, 5%)
border solid 1px borderColor
border-radius 5px
&:hover
background-color white
&.primary
border none
background-color brandColor
color white
&:hover
color white
background-color lighten(brandColor, 10%)
.detailBody
.detailPanel
&>.header
.mode
absolute top bottom right
display block
height 33px
margin-top 12px
width 120px
margin-right 15px
.title
absolute left top bottom
right 120px
padding 0 15px
input
width 100%
border none
background-color transparent
line-height 60px
font-size 32px
font-weight bold
outline none
&.show
.detailInfo
.left
right 99px
.info
padding 5px
overflow ellipsis
.tags
padding 10px 10px 5px
color articleItemColor
a
background-color brandColor
color white
border-radius 2px
padding 1.5px 5px
margin 2px
font-size 10px
opacity 0.8
&:hover
opacity 1
span.noTags
color noTagsColor
.right
button
cursor pointer
height 33px
width 33px
border none
font-size 18px
color inactiveTextColor
background-color transparent
padding 0
&:hover
color inherit
.detailBody
.detailPanel
&>.header
.mode
display block
line-height 60px
width 45px
height 60px
font-size 18px
text-align center
.title
absolute top bottom
left 45px
right 15px
font-size 32px
line-height 60px
font-weight bold
white-space nowrap
overflow-x auto
overflow-y hidden

View File

@@ -0,0 +1,69 @@
articleItemHoverBgColor = darken(white, 5%)
articleItemColor = #777
.ArticleList
absolute left bottom
top 60px
width 250px
border-right solid 1px highlightenBorderColor
&>ul
absolute top bottom left right
overflow-y auto
noSelect()
li
.articleItem
border solid 2px transparent
position relative
height 88px
width 100%
cursor pointer
transition 0.1s
background-color white
padding 0 10px
font-size 12px
.top
clearfix()
line-height 20px
padding 5px 0
color articleItemColor
.profileImage
vertical-align middle
.updatedAt
float right
line-height 20px
.middle
clearfix()
padding 3px 0 7px
font-size 16px
.mode
float left
font-size 12px
line-height 16px
.title
float left
overflow ellipsis
padding 0 5px
.bottom
padding 5px 0
overflow-x auto
white-space nowrap
.tags
color articleItemColor
a
background-color brandColor
color white
border-radius 2px
padding 1.5px 5px
margin 2px
font-size 10px
opacity 0.8
&:hover
opacity 1
&:hover, &.hover
background-color articleItemHoverBgColor
&:active, &.active
background-color white
&:active, &.active
border-color brandBorderColor
.divider
border-bottom solid 1px borderColor

View File

@@ -23,7 +23,7 @@
transition: all 200ms ease;
}
.Select-control:hover {
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06);
// box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06);
}
.is-searchable.is-open > .Select-control {
cursor: text;
@@ -42,8 +42,8 @@
cursor: text;
}
.is-focused:not(.is-open) > .Select-control {
border-color: #0088cc #0099e6 #0099e6;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 0 5px -1px rgba(0, 136, 204, 0.5);
// border-color: #0088cc #0099e6 #0099e6;
// box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 0 5px -1px rgba(0, 136, 204, 0.5);
}
.Select-placeholder {
color: #aaaaaa;
@@ -196,10 +196,10 @@
padding: 3px 0;
}
.Select-item {
background-color: #f2f9fc;
background-color: brandColor;
border-radius: 2px;
border: 1px solid #c9e6f2;
color: #0088cc;
// border: 1px solid #c9e6f2;
color: white;
display: inline-block;
font-size: 1em;
margin: 2px;
@@ -216,20 +216,19 @@
padding: 3px 5px;
}
.Select-item-label .Select-item-label__a {
color: #0088cc;
cursor: pointer;
color: white;
cursor: white;
}
.Select-item-icon {
cursor: pointer;
border-bottom-left-radius: 2px;
border-top-left-radius: 2px;
border-right: 1px solid #c9e6f2;
border-right: 1px solid darken(brandColor, 10%)
padding: 2px 5px 4px;
}
.Select-item-icon:hover,
.Select-item-icon:focus {
background-color: #ddeff7;
color: #0077b3;
background-color: lighten(brandColor, 10%)
}
.Select-item-icon:active {
background-color: #c9e6f2;

View File

@@ -0,0 +1,43 @@
.TopBar
absolute top left right
height 60px
border-bottom solid 1px borderColor
noSelect()
.left
float left
.search
position absolute
top 13.5px
left 15px
height 33px
i.fa
position absolute
line-height 33px
z-index 1
width 33px
text-align center
input.searchInput
absolute top left
background-color white
borderInput()
width 350px
padding-left 30px
border-radius 16.5px
font-size 14px
height 33px
line-height 33px
outline none
&:focus
border-color brandColor
.right
float right
.logo
&>img
margin-top 7px
margin-right 15px
.tooltip
tooltip()
right 5px
&:hover
.tooltip
opacity 1.0

View File

@@ -0,0 +1,153 @@
homeNavigatorBgColor = #1B1C1C
homeNavigatorColor = #DDD
userAnchorColor = #979797
userAnchorBgColor = #BEBEBE
userAnchorActiveColor = textColor
userAnchorActiveBgColor = white
.HomeContainer
.HomeNavigator
noSelect()
background-color homeNavigatorBgColor
absolute left top bottom
width 60px
text-align center
box-sizing border-box
// must be moved
// .profilePopup
// position fixed
// left 35px
// top 35px
// z-index popupZIndex
// width 200px
// background-color backgroundColor
// box-shadow popupShadow
// border-radius 10px
// padding 10px 0 0px
// &.close
// display none
// .profileGroup
// margin-bottom 10px
// .profileGroupLabel
// text-align left
// height 1em
// padding 0 15px
// span
// position absolute
// z-index 2
// background-color backgroundColor
// padding-right 5px
// color inactiveTextColor
// font-size 0.8em
// &::before
// content ''
// position absolute
// display block
// z-index 1
// height 0.5em
// width 175px
// border-bottom solid 1px borderColor
// .profileGroupList
// li
// clearfix()
// &:hover
// background-color hoverBackgroundColor
// .userName
// width 155px
// padding 10px 15px
// text-align left
// display block
// text-decoration none
// cursor pointer
// .createNewTeam
// btnStripDefault()
// width 100%
// padding 10px 20px
// font-size 1em
// cursor pointer
// text-align left
// .controlGroup
// list-style none
// border-top solid 1px borderColor
// padding 10px 0
// li
// &:hover
// background-color hoverBackgroundColor
// button
// btnStripDefault()
// width 100%
// padding 10px 20px
// font-size 1em
// cursor pointer
// text-align left
ul.userList
margin-top 25px
&>li
.shortCut
margin-top 5px
font-size 0.8em
color homeNavigatorColor
a
display block
width 44px
height 44px
margin 0 auto
text-align center
background-color userAnchorBgColor
text-decoration none
color userAnchorColor
line-height 44px
font-size 1.1em
cursor pointer
circle()
img
width 44px
height 44px
transition 0.1s
&:hover, &.active
background-color userAnchorActiveBgColor
color userAnchorActiveColor
.userTooltip
position absolute
z-index popupZIndex
background-color transparentify(invBackgroundColor, 80%)
color invTextColor
padding 10px
line-height 1em
border-radius 5px
margin-top -52px
margin-left 52px
white-space nowrap
opacity 0
transition 0.1s
pointer-events none
&:hover .userTooltip
opacity 1
button.newTeamButton
display block
margin 0 auto
width 30px
height 30px
circle()
border solid 1px lightButtonColor
color lightButtonColor
text-align center
background-image none
background-color transparent
box-sizing border-box
absolute left bottom right
bottom 15px
&:hover, &.hover, &:focus, &.focus
border-color darken(lightButtonColor, 50%)
color darken(lightButtonColor, 50%)
&:active, &.active
border-color darken(brandBorderColor, 10%)
background-color brandColor
color white
.tooltip
tooltip()
margin-top -22px
margin-left 33px
font-size 14px
&:hover .tooltip
opacity 1

View File

@@ -1,6 +1,6 @@
.LoginContainer, .SignupContainer
margin 0 auto
padding 25px 15px
padding 105px 15px
box-sizing border-box
color inactiveTextColor
.logo
@@ -58,16 +58,23 @@
.alertInfo, .alertError
margin-top 15px
margin-bottom 15px
height 44px
padding 5px
border-radius 10px
line-height 44px
padding 10px
border-radius 5px
line-height 1.6
text-align center
.alertInfo
alertInfo()
.alertError
alertError()
div.form-group:last-child
div.formField
input
stripInput()
height 33px
width 100%
margin-bottom 10px
text-align center
font-size 1.1em
&:last-child
margin-top 15px
button.logInButton
btnPrimary()

View File

@@ -1,310 +1,123 @@
.HomeContainer
.HomeNavigator
noSelect()
background-color planetNavBgColor
absolute left top bottom
width 55px
text-align center
box-sizing border-box
border-right solid 1px borderColor
.profileButton
display block
width 55px
height 55px
border-bottom solid 1px borderColor
overflow hidden
background-color black
margin 0
padding 0
cursor pointer
box-sizing border-box
border none
img
transition 0.1s
opacity 0.9
&.vivid.active, &.focus, &:focus, &.hover, &:hover
img
opacity 1
.profilePopup
position fixed
left 35px
top 35px
z-index popupZIndex
width 200px
background-color backgroundColor
box-shadow popupShadow
border-radius 10px
padding 10px 0 0px
&.close
display none
.profileGroup
margin-bottom 10px
.profileGroupLabel
text-align left
height 1em
padding 0 15px
span
position absolute
z-index 2
background-color backgroundColor
padding-right 5px
color inactiveTextColor
font-size 0.8em
&::before
content ''
position absolute
display block
z-index 1
height 0.5em
width 175px
border-bottom solid 1px borderColor
.profileGroupList
li
clearfix()
&:hover
background-color hoverBackgroundColor
.userName
width 155px
padding 10px 15px
text-align left
display block
text-decoration none
cursor pointer
.createNewTeam
btnStripDefault()
width 100%
padding 10px 20px
font-size 1em
cursor pointer
text-align left
.controlGroup
list-style none
border-top solid 1px borderColor
padding 10px 0
li
&:hover
background-color hoverBackgroundColor
button
btnStripDefault()
width 100%
padding 10px 20px
font-size 1em
cursor pointer
text-align left
userNavigatorWidth = 200px
userNavigatorBgColor = #333
userNavigatorColor = #DDD
userNavigatorProfileNameColor = brandColor
userNavigatorBorderColor = #666
userContentBgColor = #E6E6E6
ul.planetList>li
margin 15px 0
.shortCut
margin-top 5px
color lighten(textColor, 5%)
font-size 0.8em
&.active
a
background-color planetAnchorActiveBgColor
color planetAnchorActiveColor
a
display block
width 44px
height 44px
margin 0 auto
text-align center
background-color planetAnchorBgColor
text-decoration none
color planetAnchorColor
line-height 44px
font-size 1.1em
cursor pointer
circle()
transition 0.1s
&:hover, &:active
background-color white
.planetTooltip
position absolute
z-index popupZIndex
background-color transparentify(invBackgroundColor, 80%)
color invTextColor
padding 10px
line-height 1em
border-radius 5px
margin-top -41px
margin-left 52px
white-space nowrap
opacity 0
transition 0.1s
pointer-events none
&:hover .planetTooltip
opacity 1
img
circle()
width 55px
height 55px
button.newPlanet
display block
margin 0 auto
width 30px
height 30px
circle()
border solid 1px lightButtonColor
color lightButtonColor
text-align center
font-size 1
background-image none
background-color transparent
box-sizing border-box
absolute left bottom right
bottom 15px
&:hover, &.hover, &:focus, &.focus
border-color darken(lightButtonColor, 50%)
color darken(lightButtonColor, 50%)
&:active, &.active
border-color darken(brandBorderColor, 10%)
background-color brandColor
color white
.tooltip
tooltip()
margin-top -22px
margin-left 33px
&:hover .tooltip
opacity 1
.UserContainer
absolute top bottom right
left 55px
.memberPopup
absolute left
top 235px
z-index 1
padding 0 15px 10px
width 200px
.label
padding 10px 0
font-size 0.9em
border-bottom solid 1px borderColor
margin-bottom 15px
.members
li
padding 0 10px
margin-bottom 15px
clearfix()
.memberImage
float left
margin-right 7px
circle()
.memberInfo
float left
.memberProfileName
margin-bottom 5px
font-size 1.05em
.memberName
margin-left 5px
font-size 0.9em
color inactiveTextColor
a:hover .memberProfileName, a:hover .memberName
text-decoration underline
.userProfile
absolute top left right
padding 15px
border-bottom solid 1px borderColor
height 125px
clearfix()
.userPhoto
circle()
float left
margin 5px 15px 15px
.userInfo
float left
margin-top 15px
.userProfileName
font-size 1.5em
color brandColor
margin-bottom 10px
.userName
font-size 1.1em
.editProfileButton
left 60px
.content
absolute top bottom right
left userNavigatorWidth
background-color userContentBgColor
.UserNavigator
absolute left top bottom
width userNavigatorWidth
background-color userNavigatorBgColor
color userNavigatorColor
noSelect()
&>.profile
height 60px
padding 10px 15px 0
box-sizing border-box
position relative
border-bottom solid 1px userNavigatorBorderColor
cursor pointer
&>.profileName
color userNavigatorProfileNameColor
font-size 22px
cursor pointer
transition 0.1s
&>.name
padding 5px 10px
font-size 14px
color userNavigatorColor
cursor pointer
transition 0.1s
&>.dropdownIcon
position absolute
top 20px
right 25px
float right
btnDefault()
margin-top 25px
padding 10px 15px
border-radius 5px
.teamList, .memberList
absolute left bottom
top 125px
width 200px
padding 15px
border-right solid 1px borderColor
overflow-y auto
.teamLabel, .memberLabel
font-size 1.2em
margin-bottom 15px
.teams
li
padding 0 10px
margin-bottom 15px
clearfix()
.teamInfo
float left
.teamProfileName
margin-bottom 5px
font-size 1.05em
.teamName
margin-left 5px
font-size 0.9em
color inactiveTextColor
a:hover .teamProfileName, a:hover .teamName
text-decoration underline
margin-bottom 10px
font-size 1.1em
.createTeamButton, .addMemberButton
btnStripDefault()
.members
li
padding 0 10px
margin-bottom 15px
clearfix()
.memberImage
float left
margin-right 7px
circle()
.memberInfo
float left
.memberProfileName
margin-bottom 5px
font-size 1.05em
.memberRole
font-size 0.9em
color inactiveTextColor
.memberName
margin-left 5px
font-size 0.9em
color inactiveTextColor
.createTeamButton, .addMemberButton
btnStripDefault()
a:hover .memberProfileName, a:hover .memberName
text-decoration underline
.planetList
absolute right bottom
top 125px
left 200px
padding 15px
overflow-y auto
.planetLabel
font-size 1.2em
margin-bottom 15px
.planetGroup
margin-left 15px
.planetGroupLabel
font-size 1.1em
margin-bottom 15px
small
font-size 0.8em
color inactiveTextColor
.planets
margin-left 15px
li
a
font-size 1.1em
text-decoration none
width 20px
height 20px
line-height 20px
font-size 8px
border solid 1px userNavigatorColor
border-radius 12.5px
text-align center
transition 0.1s
&:hover
text-decoration underline
&>.profileName
color lighten(brandColor, 10%)
&>.name
color white
&>.dropdownIcon
border-color white
&:active
&>.dropdownIcon
background-color brandColor
border-color brandColor
&>.control
padding 15px 15px
&>.newPostButton
background-color brandColor
color white
height 44px
width 100%
border none
border-radius 5px
font-size 16px
font-weight 600
transition 0.1s
&:hover
background-color lighten(brandColor, 10%)
&>.menu
absolute left right bottom
top 134px
padding 15px 0
overflow auto
&>.menuGruop
&>.label
border-bottom 1px solid userNavigatorBorderColor
padding 10px 15px
font-size 18px
margin-bottom 10px
.createPlanetButton
btnStripDefault()
&>.plusButton
float right
width 20px
height 20px
margin-top -2.5px
margin-right -5px
line-height 15px
font-size 8px
border solid 1px userNavigatorColor
border-radius 10px
background-color transparent
text-align center
color userNavigatorColor
&:hover
border-color white
color white
&:active
background-color brandColor
border-color brandColor
&>.folders
.folderButton
padding 10px 25px
width 100%
background-color transparent
border none
font-size 14px
color userNavigatorColor
transition 0.1s
text-align left
&:hover
background-color transparentify(white, 20%)
color white
&.active
background-color brandColor
color white

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@ global-reset()
*
-webkit-app-region no-drag
-webkit-user-select none
html, body
width 100%
@@ -19,24 +20,12 @@ body
color textColor
font-size fontSize
font-weight 400
button
button, input, select
font-family "Lato"
div, span, a, button, input, textarea
box-sizing border-box
h1
font-size 2em
h2
font-size 1.5em
h3
font-size 1.17em
h4
font-size 1em
h5
font-size 0.83em
h6
font-size 0.67em
a
color brandColor
&:hover
@@ -55,6 +44,9 @@ button
&:focus, &.focus
outline none
.noSelect
noSelect()
.text-center
text-align center
@@ -64,13 +56,6 @@ button
display block
margin-bottom 5px
.stripInput
stripInput()
display block
width 100%
font-size 1em
height 33px
.block-input, .inline-input
border solid 1px borderColor
padding 0 10px
@@ -110,19 +95,29 @@ textarea.block-input
z-index 2000
bottom 5px
right 53px
btnPrimary()
padding 10px 15px
border none
border-radius 5px
background-color backgroundColor
background-color brandColor
color white
opacity 0.7
&:hover
opacity 1
background-color lighten(brandColor, 10%)
.contactButton
position fixed
z-index 2000
bottom 5px
right 5px
btnPrimary()
padding 10px 15px
border none
border-radius 5px
background-color backgroundColor
background-color brandColor
color white
opacity 0.7
&:hover
opacity 1
background-color lighten(brandColor, 10%)
.tooltip
tooltip()
margin-top -22px

View File

@@ -6,28 +6,28 @@ marked()
h1
font-size 2em
border-bottom solid 2px borderColor
margin 0.67em auto
margin 0.33em auto 0.67em
h2
font-size 1.5em
margin 0.83em auto
margin 0.42em auto 0.83em
h3
font-size 1.17em
margin 1em auto
margin 0.5em auto 1em
h4
font-size 1em
margin 1.33em auto
margin 0.67em auto 1.33em
h5
font-size 0.83em
margin 1.67em auto
margin 0.84em auto 1.67em
h6
font-size 0.67em
margin 2.33em auto
margin 1.16em auto 2.33em
h1, h2, h3, h4, h5, h6
font-weight 700
line-height 1.8em
p
line-height 1.8em
margin-bottom 25px
margin 15px 0 25px
img
max-width 100%
strong
@@ -38,12 +38,12 @@ marked()
text-decoration line-through
blockquote
border-left solid 4px brandBorderColor
margin 15px 0 15px
margin 15px 0 25px
padding 0 25px
ul
list-style-type disc
padding-left 35px
margin-bottom 25px
margin-bottom 35px
li
display list-item
margin 15px 0
@@ -54,7 +54,7 @@ marked()
ol
list-style-type decimal
padding-left 35px
margin-bottom 25px
margin-bottom 35px
li
display list-item
margin 15px 0
@@ -72,7 +72,7 @@ marked()
border solid 1px borderColor
border-radius 5px
overflow-x auto
margin-bottom 25px
margin 15px 0 25px
background-color #F6F6F6
&>code
padding 0

View File

@@ -3,5 +3,4 @@ borderBox()
noSelect()
-webkit-user-select none
-webkit-app-region drag
cursor default

View File

@@ -1,4 +1,4 @@
borderColor = #E8E8E8
borderColor = #D0D0D0
highlightenBorderColor = darken(borderColor, 20%)
invBorderColor = #404849
brandBorderColor = #3FB399
@@ -24,12 +24,6 @@ btnHighlightenColor = #000
brandColor = #2BAC8F
planetNavBgColor = #ECECEC
planetAnchorColor = #979797
planetAnchorBgColor = #BEBEBE
planetAnchorActiveColor = textColor
planetAnchorActiveBgColor = white
popupShadow = 0 0 5px 0 #888
modalBackColor = transparentify(white, 65%)

View File

@@ -1,5 +1,5 @@
module.exports = {
// apiUrl: 'https://api.b00st.io/'
apiUrl: 'https://api2.b00st.io/'
// apiUrl: 'http://localhost:8000/'
// apiUrl: 'https://api2.b00st.io/'
apiUrl: 'http://localhost:8000/'
}

View File

@@ -6,16 +6,16 @@ var finderWindow = new BrowserWindow({
show: false,
frame: false,
resizable: false,
'zoom-factor': 1.0,
'always-on-top': true,
'web-preferences': {
'zoom-factor': 1.0,
'overlay-scrollbars': true,
'skip-taskbar': true
},
'standard-window': false
})
finderWindow.loadUrl('file://' + __dirname + '/browser/finder/index.electron.html')
finderWindow.loadUrl('file://' + __dirname + '/browser/finder/index.html')
finderWindow.on('blur', function () {
finderWindow.hide()

7
lib/key-gen.js Normal file
View File

@@ -0,0 +1,7 @@
var crypto = require('crypto')
module.exports = function () {
var shasum = crypto.createHash('sha1')
shasum.update(((new Date()).getTime()).toString())
return shasum.digest('hex')
}

View File

@@ -3,15 +3,14 @@ var BrowserWindow = require('browser-window')
var mainWindow = new BrowserWindow({
width: 1080,
height: 720,
// frame: false,
'web-preferences': {
'zoom-factor': 1.0,
'web-preferences': {
'overlay-scrollbars': true
},
'standard-window': false
})
mainWindow.loadUrl('file://' + __dirname + '/browser/main/index.electron.html')
mainWindow.loadUrl('file://' + __dirname + '/browser/main/index.html')
mainWindow.setVisibleOnAllWorkspaces(true)

View File

@@ -1,13 +1,16 @@
{
"name": "boost",
"version": "0.3.1",
"version": "0.4.0",
"description": "Boost App",
"main": "main.js",
"scripts": {
"start": "electron ./main.js",
"web": "npm run serve | npm run dev",
"serve": "./node_modules/.bin/http-server ./browser -p 8080",
"dev": "webpack-dev-server --progress --colors --port 8090"
"build": "electron-packager ./ Boost $npm_package_config_platform $npm_package_config_version $npm_package_config_ignore --overwrite"
},
"config": {
"version": "--version=0.33.0 --app-version=$npm_package_version --app-bundle-id=com.maisin.boost",
"platform": "--platform=darwin --arch=x64 --prune --icon=app.icns",
"ignore": "--ignore=Boost-darwin-x64 --ignore=node_modules/devicon/icons --ignore=submodules\/ace\/(?!src-min)|submodules\/ace\/(?=src-min-noconflict)"
},
"repository": {
"type": "git",
@@ -31,38 +34,38 @@
},
"homepage": "https://github.com/Rokt33r/codexen-app#readme",
"dependencies": {
"babel-core": "^5.8.25",
"devicon": "^2.0.0",
"font-awesome": "^4.3.0",
"fs-jetpack": "^0.7.0",
"lodash": "^3.10.1",
"markdown-it": "^4.3.1",
"md5": "^2.0.0",
"moment": "^2.10.3",
"nib": "^1.1.0",
"node-jsx": "^0.13.3",
"node-notifier": "^4.2.3",
"react": "^0.13.3",
"react-router": "^0.13.3",
"react-select": "^0.5.4",
"react-redux": "^3.1.0",
"react-router": "^1.0.0-rc1",
"react-select": "^0.6.10",
"redux": "^3.0.2",
"reflux": "^0.2.8",
"stylus": "^0.52.0",
"socket.io-client": "^1.3.6",
"superagent": "^1.2.0",
"superagent-promise": "^1.0.3",
"titlebar": "^1.3.0"
},
"devDependencies": {
"css-loader": "^0.15.1",
"http-server": "^0.8.0",
"jsx-loader": "^0.13.2",
"node-libs-browser": "^0.5.2",
"style-loader": "^0.12.3",
"stylus-loader": "^1.2.1",
"webpack": "^1.10.0",
"webpack-dev-server": "^1.10.1"
"electron-packager": "^5.1.0",
"electron-prebuilt": "^0.33.6",
"nib": "^1.1.0",
"standard": "^5.3.1",
"stylus": "^0.52.4"
},
"standard": {
"ignore": [
"/browser/ace/"
],
"global": [
"globals": [
"localStorage"
]
}

1
submodules/ace Submodule

Submodule submodules/ace added at b082bcb4bf