1
0
mirror of https://github.com/BoostIo/Boostnote synced 2026-01-27 23:57:17 +00:00

add Finder & update main.js & cleanup some old files

This commit is contained in:
Rokt33r
2015-07-28 23:56:50 +09:00
parent d20f005c5d
commit b1be92e6c9
26 changed files with 811 additions and 250 deletions

View File

@@ -0,0 +1,43 @@
var React = require('react/addons')
var CodeViewer = require('../../main/Components/CodeViewer')
var Markdown = require('../../main/Mixins/Markdown')
module.exports = React.createClass({
mixins: [Markdown],
propTypes: {
currentArticle: React.PropTypes.object
},
render: function () {
var article = this.props.currentArticle
if (article != null) {
if (article.type === 'snippet') {
return (
<div className='FinderDetail'>
<div className='header'>{article.callSign}</div>
<div className='content'>
<CodeViewer code={article.content} mode={article.mode}/>
</div>
</div>
)
} else if (article.type === 'blueprint') {
return (
<div className='FinderDetail'>
<div className='header'>{article.title}</div>
<div className='content'>
<div className='marked' dangerouslySetInnerHTML={{__html: ' ' + this.markdown(article.content)}}></div>
</div>
</div>
)
}
}
return (
<div className='FinderDetail'>
<div className='nothing'>Nothing selected</div>
</div>
)
}
})

View File

@@ -0,0 +1,15 @@
var React = require('react/addons')
module.exports = React.createClass({
propTypes: {
onChange: React.PropTypes.func,
search: React.PropTypes.string
},
render: function () {
return (
<div className='FinderInput'>
<input value={this.props.search} onChange={this.props.onChange} type='text'/>
</div>
)
}
})

View File

@@ -0,0 +1,69 @@
var React = require('react/addons')
module.exports = React.createClass({
propTypes: {
articles: React.PropTypes.arrayOf,
currentArticle: React.PropTypes.shape({
id: React.PropTypes.number,
type: React.PropTypes.string
})
},
componentDidUpdate: function () {
var index = this.props.articles.indexOf(this.props.currentArticle)
var el = React.findDOMNode(this)
var li = el.querySelectorAll('li')[index]
var overflowBelow = el.clientHeight + el.scrollTop < li.offsetTop + li.clientHeight
if (overflowBelow) {
el.scrollTop = li.offsetTop + li.clientHeight - el.clientHeight
}
var overflowAbove = el.scrollTop > li.offsetTop
if (overflowAbove) {
el.scrollTop = li.offsetTop
}
},
render: function () {
var list = this.props.articles.map(function (article) {
if (article == null) {
return (
<li className={isActive ? 'active' : ''}>
<div className='articleItem'>Undefined</div>
<div className='divider'/>
</li>
)
}
var isActive = this.props.currentArticle != null && (article.type === this.props.currentArticle.type && article.id === this.props.currentArticle.id)
if (article.type === 'snippet') {
return (
<li className={isActive ? 'active' : ''}>
<div className='articleItem'><i className='fa fa-code fa-fw'/> {article.callSign} / {article.description.substring(0, 10)}</div>
<div className='divider'/>
</li>
)
}
if (article.type === 'blueprint') {
return (
<li className={isActive ? 'active' : ''}>
<div className='articleItem'><i className='fa fa-file-text-o fa-fw'/> {article.title}</div>
<div className='divider'/>
</li>
)
}
return (
<li className={isActive ? 'active' : ''}>
<div className='articleItem'>Undefined</div>
<div className='divider'/>
</li>
)
}.bind(this))
return (
<div className='FinderList'>
<ul>
{list}
</ul>
</div>
)
}
})

View File

@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<title>CodeXen Popup</title>
<link rel="stylesheet" href="../vendor/fontawesome/css/font-awesome.min.css" media="screen" title="no title" 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;
}
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;
}
});
}
require('electron-stylus')(__dirname + '/../styles/finder/index.styl')
</script>
</head>
<body>
<div id="content"></div>
<script src="../ace/src-min/ace.js"></script>
<script>
require('node-jsx').install({ harmony: true, extension: '.jsx' })
require('./index.jsx')
</script>
</body>
</html>

View File

188
browser/finder/index.jsx Normal file
View File

@@ -0,0 +1,188 @@
/* global localStorage */
var remote = require('remote')
var hideFinder = remote.getGlobal('hideFinder')
var clipboard = require('clipboard')
var React = require('react/addons')
var FinderInput = require('./Components/FinderInput')
var FinderList = require('./Components/FinderList')
var FinderDetail = require('./Components/FinderDetail')
// filter start
function basicFilter (keyword, articles) {
if (keyword === '' || keyword == null) return articles
var firstFiltered = articles.filter(function (article) {
var first = article.type === 'snippet' ? article.callSign : article.title
if (first.match(new RegExp(keyword, 'i'))) return true
return false
})
var secondFiltered = articles.filter(function (article) {
var second = article.type === 'snippet' ? article.description : article.content
if (second.match(new RegExp(keyword, 'i'))) return true
return false
})
var thirdFiltered = articles.filter(function (article) {
if (article.type === 'snippet') {
if (article.content.match(new RegExp(keyword, 'i'))) return true
}
return false
})
return firstFiltered.concat(secondFiltered, thirdFiltered).filter(function (value, index, self) {
return self.indexOf(value) === index
})
}
function snippetFilter (articles) {
return articles.filter(function (article) {
return article.type === 'snippet'
})
}
function blueprintFilter (articles) {
return articles.filter(function (article) {
return article.type === 'blueprint'
})
}
function tagFilter (keyword, articles) {
return articles.filter(function (article) {
return article.Tags.some(function (tag) {
return tag.name.match(new RegExp('^' + keyword, 'i'))
})
})
}
function searchArticle (search, articles) {
var keywords = search.split(' ')
for (var keyword of keywords) {
if (keyword.match(/^\$s/, 'i')) {
articles = snippetFilter(articles)
continue
} else if (keyword.match(/^\$b/, 'i')) {
articles = blueprintFilter(articles)
continue
} else if (keyword.match(/^#[A-Za-z0-9]+/)) {
articles = tagFilter(keyword.substring(1, keyword.length), articles)
continue
}
articles = basicFilter(keyword, articles)
}
return articles
}
// Filter end
function fetchArticles () {
var user = JSON.parse(localStorage.getItem('user'))
if (user == null) {
console.log('need to login')
return []
}
var articles = []
user.Planets.forEach(function (planet) {
var _planet = JSON.parse(localStorage.getItem('planet-' + planet.id))
articles = articles.concat(_planet.Snippets, _planet.Blueprints)
})
console.log(articles.length + ' articles')
return articles
}
var Finder = React.createClass({
getInitialState: function () {
var articles = fetchArticles()
return {
articles: articles,
currentArticle: articles[0],
search: ''
}
},
componentDidMount: function () {
document.addEventListener('keydown', this.handleKeyDown)
document.addEventListener('click', this.handleClick)
window.addEventListener('focus', this.handleFinderFocus)
},
componentWillUnmount: function () {
document.removeEventListener('keydown', this.handleKeyDown)
document.removeEventListener('click', this.handleClick)
window.removeEventListener('focus', this.handleFinderFocus)
},
handleFinderFocus: function () {
console.log('focusseeddddd')
this.focusInput()
var articles = fetchArticles()
this.setState({
articles: articles,
currentArticle: articles[0],
search: ''
})
},
handleKeyDown: function (e) {
if (e.keyCode === 38) {
this.selectPrevious()
e.preventDefault()
}
if (e.keyCode === 40) {
this.selectNext()
e.preventDefault()
}
if (e.keyCode === 13) {
var article = this.state.currentArticle
if (article.type === 'snippet') {
clipboard.writeText(article.content)
hideFinder()
e.preventDefault()
}
}
if (e.keyCode === 27) {
hideFinder()
e.preventDefault()
}
},
focusInput: function () {
React.findDOMNode(this.refs.finderInput).querySelector('input').focus()
},
handleClick: function () {
this.focusInput()
},
selectPrevious: function () {
var index = this.refs.finderList.props.articles.indexOf(this.state.currentArticle)
if (index > 0) {
this.setState({currentArticle: this.refs.finderList.props.articles[index - 1]})
}
},
selectNext: function () {
var index = this.refs.finderList.props.articles.indexOf(this.state.currentArticle)
if (index > -1 && index < this.refs.finderList.props.articles.length - 1) {
this.setState({currentArticle: this.refs.finderList.props.articles[index + 1]})
}
},
handleChange: function (e) {
this.setState({search: e.target.value}, function () {
this.setState({currentArticle: this.refs.finderList.props.articles[0]})
})
},
render: function () {
var articles = searchArticle(this.state.search, this.state.articles)
return (
<div className='Finder'>
<FinderInput ref='finderInput' onChange={this.handleChange} search={this.state.search}/>
<FinderList ref='finderList' currentArticle={this.state.currentArticle} articles={articles}/>
<FinderDetail currentArticle={this.state.currentArticle}/>
</div>
)
}
})
React.render(<Finder/>, document.getElementById('content'))

View File

@@ -7,9 +7,11 @@ var Select = require('react-select')
var request = require('superagent')
var PlanetActions = require('../Actions/PlanetActions')
var apiUrl = require('../../../config').apiUrl
var getOptions = function (input, callback) {
request
.get('http://localhost:8000/tags/search')
.get(apiUrl + 'tags/search')
.query({name: input})
.send()
.end(function (err, res) {

View File

@@ -8,7 +8,7 @@ var AuthActions = require('../Actions/AuthActions')
var AuthStore = require('../Stores/AuthStore')
var apiUrl = 'http://localhost:8000/'
var apiUrl = require('../../../config').apiUrl
module.exports = React.createClass({
mixins: [Catalyst.LinkedStateMixin],

View File

@@ -7,9 +7,11 @@ var Catalyst = require('../Mixins/Catalyst')
var PlanetActions = require('../Actions/PlanetActions')
var apiUrl = require('../../../config').apiUrl
var getOptions = function (input, callback) {
request
.get('http://localhost:8000/users/search')
.get(apiUrl + 'users/search')
.query({name: input})
.send()
.end(function (err, res) {

View File

@@ -6,9 +6,11 @@ var Catalyst = require('../Mixins/Catalyst')
var PlanetActions = require('../Actions/PlanetActions')
var apiUrl = require('../../../config').apiUrl
var getOptions = function (input, callback) {
request
.get('http://localhost:8000/users/search')
.get(apiUrl + 'users/search')
.query({name: input})
.send()
.end(function (err, res) {

View File

@@ -6,9 +6,11 @@ var Select = require('react-select')
var request = require('superagent')
var PlanetActions = require('../Actions/PlanetActions')
var apiUrl = require('../../../config').apiUrl
var getOptions = function (input, callback) {
request
.get('http://localhost:8000/tags/search')
.get(apiUrl + 'tags/search')
.query({name: input})
.send()
.end(function (err, res) {
@@ -22,7 +24,7 @@ var getOptions = function (input, callback) {
label: tag.name,
value: tag.name
}
}),
}),
complete: false
})
})

View File

@@ -1,47 +1,87 @@
/* global localStorage */
var React = require('react/addons')
var ReactRouter = require('react-router')
var RouteHandler = ReactRouter.RouteHandler
var request = require('superagent')
var AuthStore = require('../Stores/AuthStore')
var apiUrl = require('../../../config').apiUrl
function fetchPlanet (planet) {
request
.get(apiUrl + planet.userName + '/' + planet.name)
.send()
.end(function (err, res) {
if (err) {
console.error(err)
return
}
var _planet = res.body
_planet.userName = planet.userName
_planet.Snippets = _planet.Snippets.map(function (snippet) {
snippet.type = 'snippet'
return snippet
})
_planet.Blueprints = _planet.Blueprints.map(function (blueprint) {
blueprint.type = 'blueprint'
return blueprint
})
localStorage.setItem('planet-' + _planet.id, JSON.stringify(_planet))
console.log('planet-' + _planet.id + ' fetched')
})
}
module.exports = React.createClass({
mixins: [ReactRouter.Navigation, ReactRouter.State],
componentDidMount: function () {
this.unsubscribe = AuthStore.listen(this.onListen)
},
componentWillUnmount: function () {
this.unsubscribe()
},
onListen: function (res) {
if (res == null || res.status == null) {
return
}
mixins: [ReactRouter.Navigation, ReactRouter.State],
componentDidMount: function () {
this.unsubscribe = AuthStore.listen(this.onListen)
if (res.status === 'loggedIn' || res.status === 'registered') {
var user = res.data
var planet = user.Planets.length > 0 ? user.Planets[0] : null
if (planet == null) {
this.transitionTo('user', {userName: user.name})
return
}
this.transitionTo('planetHome', {userName: user.name, planetName: planet.name})
return
}
if (res.status === 'loggedOut') {
this.transitionTo('login')
return
}
},
render: function () {
// Redirect Login state
if (this.getPath() === '/') {
this.transitionTo('/login')
}
return (
<div className='Main'>
<RouteHandler/>
</div>
)
var user = JSON.parse(localStorage.getItem('user'))
if (user != null) {
user.Planets.forEach(fetchPlanet)
return
}
this.transitionTo('login')
},
componentWillUnmount: function () {
this.unsubscribe()
},
onListen: function (res) {
if (res == null || res.status == null) {
return
}
if (res.status === 'loggedIn' || res.status === 'registered') {
var user = res.data
var planet = user.Planets.length > 0 ? user.Planets[0] : null
if (planet == null) {
this.transitionTo('user', {userName: user.name})
return
}
this.transitionTo('planetHome', {userName: user.name, planetName: planet.name})
return
}
if (res.status === 'loggedOut') {
this.transitionTo('login')
return
}
},
render: function () {
// Redirect Login state
if (this.getPath() === '/') {
this.transitionTo('/login')
}
return (
<div className='Main'>
<RouteHandler/>
</div>
)
}
})

View File

@@ -10,6 +10,8 @@ var UserNavigator = require('../Components/UserNavigator')
var AuthStore = require('../Stores/AuthStore')
var PlanetStore = require('../Stores/PlanetStore')
var apiUrl = require('../../../config').apiUrl
module.exports = React.createClass({
mixins: [React.addons.LinkedStateMixin, ReactRouter.Navigation, ReactRouter.State],
propTypes: {
@@ -44,7 +46,7 @@ module.exports = React.createClass({
},
fetchUser: function (userName) {
request
.get('http://localhost:8000/' + userName)
.get(apiUrl + userName)
.send()
.end(function (err, res) {
if (err) {

View File

@@ -4,7 +4,7 @@ var request = require('superagent')
var AuthActions = require('../Actions/AuthActions')
var apiUrl = 'http://localhost:8000/'
var apiUrl = require('../../../config').apiUrl
var AuthStore = Reflux.createStore({
init: function () {

View File

@@ -4,7 +4,7 @@ var request = require('superagent')
var PlanetActions = require('../Actions/PlanetActions')
var apiUrl = 'http://localhost:8000/'
var apiUrl = require('../../../config').apiUrl
var PlanetStore = Reflux.createStore({
init: function () {
@@ -70,6 +70,8 @@ var PlanetStore = Reflux.createStore({
return blueprint
})
localStorage.setItem('planet-' + planet.id, JSON.stringify(planet))
planet.Articles = planet.Snippets.concat(planet.Blueprints).sort(function (a, b) {
a = new Date(a.updatedAt)
b = new Date(b.updatedAt)
@@ -94,6 +96,7 @@ var PlanetStore = Reflux.createStore({
}
var planet = res.body
localStorage.remove('planet-' + planet.id)
this.trigger({
status: 'planetDeleted',
@@ -186,6 +189,11 @@ var PlanetStore = Reflux.createStore({
.end(function (req, res) {
var snippet = res.body
snippet.type = 'snippet'
var planet = JSON.parse(localStorage.getItem('planet-' + snippet.PlanetId))
planet.Snippets.unshift(snippet)
localStorage.setItem('planet-' + snippet.PlanetId, JSON.stringify(planet))
this.trigger({
status: 'articleCreated',
data: snippet
@@ -209,6 +217,17 @@ var PlanetStore = Reflux.createStore({
var snippet = res.body
snippet.type = 'snippet'
var planet = JSON.parse(localStorage.getItem('planet-' + snippet.PlanetId))
planet.Snippets.some(function (_snippet, index) {
if (snippet.id === _snippet) {
planet.Snippets[index] = snippet
return true
}
return false
})
localStorage.setItem('planet-' + snippet.PlanetId, JSON.stringify(planet))
this.trigger({
status: 'articleUpdated',
data: snippet
@@ -230,6 +249,17 @@ var PlanetStore = Reflux.createStore({
}
var snippet = res.body
var planet = JSON.parse(localStorage.getItem('planet-' + snippet.PlanetId))
planet.Snippets.some(function (_snippet, index) {
if (snippet.id === _snippet) {
planet.splice(index, 1)
return true
}
return false
})
localStorage.setItem('planet-' + snippet.PlanetId, JSON.stringify(planet))
this.trigger({
status: 'articleDeleted',
data: snippet
@@ -247,6 +277,11 @@ var PlanetStore = Reflux.createStore({
.end(function (req, res) {
var blueprint = res.body
blueprint.type = 'blueprint'
var planet = JSON.parse(localStorage.getItem('planet-' + blueprint.PlanetId))
planet.Blueprints.unshift(blueprint)
localStorage.setItem('planet-' + blueprint.PlanetId, JSON.stringify(planet))
this.trigger({
status: 'articleCreated',
data: blueprint
@@ -270,6 +305,17 @@ var PlanetStore = Reflux.createStore({
var blueprint = res.body
blueprint.type = 'blueprint'
var planet = JSON.parse(localStorage.getItem('planet-' + blueprint.PlanetId))
planet.Blueprints.some(function (_blueprint, index) {
if (blueprint.id === _blueprint) {
planet.Blueprints[index] = blueprint
return true
}
return false
})
localStorage.setItem('planet-' + blueprint.PlanetId, JSON.stringify(blueprint))
this.trigger({
status: 'articleUpdated',
data: blueprint
@@ -291,6 +337,17 @@ var PlanetStore = Reflux.createStore({
}
var blueprint = res.body
var planet = JSON.parse(localStorage.getItem('planet-' + blueprint.PlanetId))
planet.Blueprints.some(function (_blueprint, index) {
if (blueprint.id === _blueprint) {
planet.splice(index, 1)
return true
}
return false
})
localStorage.setItem('planet-' + blueprint.PlanetId, JSON.stringify(planet))
this.trigger({
status: 'articleDeleted',
data: blueprint

View File

@@ -5,7 +5,7 @@
<link rel="stylesheet" href="../vendor/fontawesome/css/font-awesome.min.css" media="screen" title="no title" charset="utf-8">
<link rel="shortcut icon" href="favicon.ico">
<link rel="stylesheet" href="style.css">
<script>
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
@@ -39,6 +39,8 @@
}
});
}
require('electron-stylus')(__dirname + '/../styles/main/index.styl')
</script>
</head>
<body>

View File

@@ -0,0 +1,77 @@
@import '../../../node_modules/nib/lib/nib'
@import '../vars'
@import '../mixins/*'
global-reset()
@import '../shared/*'
body
font-family "Lato"
color textColor
font-size fontSize
.Finder
absolute top bottom left right
.FinderInput
position absolute
top 11px
left 11px
right 11px
margin 0 auto
height 44px
box-sizing border-box
border-bottom solid 1px borderColor
input
display block
width 100%
border solid 1px borderColor
padding 0 10px
font-size 1em
height 33px
border-radius 5px
box-sizing border-box
border-radius 16.5px
&:focus, &.focus
border-color brandBorderColor
outline none
.FinderList
absolute left bottom
top 55px
border-right solid 1px borderColor
box-sizing border-box
width 250px
overflow-y auto
&>ul>li
.articleItem
padding 10px
border solid 2px transparent
box-sizing border-box
.divider
box-sizing border-box
border-bottom solid 1px borderColor
&.active
.articleItem
border-color brandColor
.FinderDetail
absolute right bottom
top 55px
left 250px
.header
absolute top left right
height 44px
box-sizing border-box
padding 0 10px
border-bottom solid 1px borderColor
line-height 44px
font-size 1.3em
.content
.ace_editor, .marked
position absolute
top 49px
left 5px
right 5px
bottom 5px
box-sizing border-box
.marked
marked()
overflow-y auto