From d9442aa23c7a72653bcddfd8f01d851aa7a827f1 Mon Sep 17 00:00:00 2001 From: Rokt33r Date: Fri, 30 Oct 2015 14:53:09 +0900 Subject: [PATCH] Going LIte --- atom-lib/finder-window.js | 7 +- browser/finder/Components/FinderDetail.jsx | 42 ---- browser/finder/Components/FinderInput.jsx | 15 -- browser/finder/Components/FinderList.jsx | 79 -------- browser/finder/FinderDetail.js | 34 ++++ browser/finder/FinderInput.js | 16 ++ browser/finder/FinderList.js | 71 +++++++ browser/finder/actions.js | 33 ++++ browser/finder/index.html | 29 +-- browser/finder/index.js | 183 ++++++++++++++++++ browser/finder/index.jsx | 135 ------------- browser/finder/reducer.js | 49 +++++ browser/main/HomePage.js | 110 ++++------- browser/main/HomePage/ArticleDetail.js | 176 ++++++----------- browser/main/HomePage/ArticleList.js | 39 ++-- browser/main/HomePage/ArticleNavigator.js | 47 ++--- browser/main/HomePage/ArticleTopBar.js | 2 - browser/main/HomePage/untitled | 0 browser/main/index.html | 5 +- browser/main/index.js | 51 +++-- browser/styles/finder/index.styl | 48 +++-- .../components/ArticleDetail.styl | 66 +++++-- .../HomeContainer/components/ArticleList.styl | 2 +- .../components/ArticleNavigator.styl | 3 +- .../components/ArticleTopBar.styl | 4 +- browser/styles/mixins/marked.styl | 3 + lib/actions.js | 45 ++--- lib/components/CodeEditor.js | 14 +- lib/components/FolderMark.js | 4 +- lib/components/TagLink.js | 8 +- lib/components/TagSelect.js | 77 ++++++++ lib/components/modal/CreateNewFolder.js | 74 +++---- .../modal/Preference/AppSettingTab.js | 26 ++- lib/components/modal/Preferences.js | 58 +----- lib/reducer.js | 122 ++++++------ lib/search.js | 40 ++++ lib/store.js | 1 - main.js | 99 +++++----- package.json | 2 +- webpack.config.js | 12 +- 40 files changed, 978 insertions(+), 853 deletions(-) delete mode 100644 browser/finder/Components/FinderDetail.jsx delete mode 100644 browser/finder/Components/FinderInput.jsx delete mode 100644 browser/finder/Components/FinderList.jsx create mode 100644 browser/finder/FinderDetail.js create mode 100644 browser/finder/FinderInput.js create mode 100644 browser/finder/FinderList.js create mode 100644 browser/finder/actions.js create mode 100644 browser/finder/index.js delete mode 100644 browser/finder/index.jsx create mode 100644 browser/finder/reducer.js create mode 100644 browser/main/HomePage/untitled create mode 100644 lib/components/TagSelect.js create mode 100644 lib/search.js diff --git a/atom-lib/finder-window.js b/atom-lib/finder-window.js index 1038456a..529f33a2 100644 --- a/atom-lib/finder-window.js +++ b/atom-lib/finder-window.js @@ -1,7 +1,8 @@ var BrowserWindow = require('browser-window') +var path = require('path') var finderWindow = new BrowserWindow({ - width: 600, + width: 640, height: 400, show: false, frame: false, @@ -15,7 +16,9 @@ var finderWindow = new BrowserWindow({ 'standard-window': false }) -finderWindow.loadUrl('file://' + __dirname + '/browser/finder/index.html') +var url = path.resolve(__dirname, '../browser/finder/index.html') + +finderWindow.loadUrl('file://' + url) finderWindow.on('blur', function () { finderWindow.hide() diff --git a/browser/finder/Components/FinderDetail.jsx b/browser/finder/Components/FinderDetail.jsx deleted file mode 100644 index 3be69f21..00000000 --- a/browser/finder/Components/FinderDetail.jsx +++ /dev/null @@ -1,42 +0,0 @@ -var React = require('react') - -var CodeViewer = require('../../main/Components/CodeViewer') - -var MarkdownPreview = require('../../main/Components/MarkdownPreview') - -module.exports = React.createClass({ - propTypes: { - currentArticle: React.PropTypes.object - }, - render: function () { - var article = this.props.currentArticle - - if (article != null) { - if (article.type === 'code') { - return ( -
-
{article.description}
-
- -
-
- ) - } else if (article.type === 'note') { - - return ( -
-
{article.title}
-
- -
-
- ) - } - } - return ( -
-
Nothing selected
-
- ) - } -}) diff --git a/browser/finder/Components/FinderInput.jsx b/browser/finder/Components/FinderInput.jsx deleted file mode 100644 index 893265c1..00000000 --- a/browser/finder/Components/FinderInput.jsx +++ /dev/null @@ -1,15 +0,0 @@ -var React = require('react') - -module.exports = React.createClass({ - propTypes: { - onChange: React.PropTypes.func, - search: React.PropTypes.string - }, - render: function () { - return ( -
- -
- ) - } -}) diff --git a/browser/finder/Components/FinderList.jsx b/browser/finder/Components/FinderList.jsx deleted file mode 100644 index cd704274..00000000 --- a/browser/finder/Components/FinderList.jsx +++ /dev/null @@ -1,79 +0,0 @@ -var React = require('react') - -module.exports = React.createClass({ - propTypes: { - articles: React.PropTypes.arrayOf, - currentArticle: React.PropTypes.shape({ - id: React.PropTypes.number, - type: React.PropTypes.string - }), - selectArticle: React.PropTypes.func - }, - componentDidUpdate: function () { - var index = this.props.articles.indexOf(this.props.currentArticle) - var el = React.findDOMNode(this) - var li = el.querySelectorAll('li')[index] - - if (li == null) { - return - } - - 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 - } - }, - handleArticleClick: function (article) { - return function () { - this.props.selectArticle(article) - }.bind(this) - }, - render: function () { - var list = this.props.articles.map(function (article) { - if (article == null) { - return ( -
  • -
    Undefined
    -
    -
  • - ) - } - - var isActive = this.props.currentArticle != null && (article.type === this.props.currentArticle.type && article.id === this.props.currentArticle.id) - if (article.type === 'code') { - return ( -
  • -
    {article.description}
    -
    -
  • - ) - } - if (article.type === 'note') { - return ( -
  • -
    {article.title}
    -
    -
  • - ) - } - return ( -
  • -
    Undefined
    -
    -
  • - ) - }.bind(this)) - - return ( -
    -
      - {list} -
    -
    - ) - } -}) diff --git a/browser/finder/FinderDetail.js b/browser/finder/FinderDetail.js new file mode 100644 index 00000000..b50392fa --- /dev/null +++ b/browser/finder/FinderDetail.js @@ -0,0 +1,34 @@ +import React, { PropTypes } from 'react' +import CodeEditor from 'boost/components/CodeEditor' +import MarkdownPreview from 'boost/components/MarkdownPreview' +import ModeIcon from 'boost/components/ModeIcon' + +export default class FinderDetail extends React.Component { + render () { + let { activeArticle } = this.props + + if (activeArticle != null) { + return ( +
    +
    + {activeArticle.title}
    +
    + {activeArticle.mode === 'markdown' + ? + : + } +
    +
    + ) + } + return ( +
    +
    Nothing selected
    +
    + ) + } +} + +FinderDetail.propTypes = { + activeArticle: PropTypes.shape() +} diff --git a/browser/finder/FinderInput.js b/browser/finder/FinderInput.js new file mode 100644 index 00000000..d23fa98d --- /dev/null +++ b/browser/finder/FinderInput.js @@ -0,0 +1,16 @@ +import React, { PropTypes } from 'react' + +export default class FinderInput extends React.Component { + render () { + return ( +
    + +
    + ) + } +} + +FinderInput.propTypes = { + handleSearchChange: PropTypes.func, + value: PropTypes.string +} diff --git a/browser/finder/FinderList.js b/browser/finder/FinderList.js new file mode 100644 index 00000000..1833ee4a --- /dev/null +++ b/browser/finder/FinderList.js @@ -0,0 +1,71 @@ +import React, { PropTypes } from 'react' +import ReactDOM from 'react-dom' +import ModeIcon from 'boost/components/ModeIcon' +import { selectArticle } from './actions' + +export default class FinderList extends React.Component { + componentDidUpdate () { + var index = this.props.articles.indexOf(this.props.activeArticle) + var el = ReactDOM.findDOMNode(this) + var li = el.querySelectorAll('li')[index] + + if (li == null) { + return + } + + 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 + } + } + + handleArticleClick (article) { + return (e) => { + let { dispatch } = this.props + dispatch(selectArticle(article.key)) + } + } + + render () { + let articleElements = this.props.articles.map(function (article) { + if (article == null) { + return ( +
  • +
    Undefined
    +
    +
  • + ) + } + + var isActive = this.props.activeArticle != null && (article.key === this.props.activeArticle.key) + return ( +
  • +
    + {article.title}
    +
    +
  • + ) + }.bind(this)) + + return ( +
    +
      + {articleElements} +
    +
    + ) + } +} + +FinderList.propTypes = { + articles: PropTypes.array, + activeArticle: PropTypes.shape({ + type: PropTypes.string, + key: PropTypes.string + }), + dispatch: PropTypes.func +} diff --git a/browser/finder/actions.js b/browser/finder/actions.js new file mode 100644 index 00000000..616d7953 --- /dev/null +++ b/browser/finder/actions.js @@ -0,0 +1,33 @@ +export const SELECT_ARTICLE = 'SELECT_ARTICLE' +export const SEARCH_ARTICLE = 'SEARCH_ARTICLE' +export const REFRESH_DATA = 'REFRESH_DATA' + +export function selectArticle (key) { + return { + type: SELECT_ARTICLE, + data: { key } + } +} + +export function searchArticle (input) { + return { + type: SEARCH_ARTICLE, + data: { input } + } +} + +export function refreshData () { + console.log('refreshing data') + let data = JSON.parse(localStorage.getItem('local')) + if (data == null) return null + + let { folders, articles } = data + + return { + type: REFRESH_DATA, + data: { + articles, + folders + } + } +} diff --git a/browser/finder/index.html b/browser/finder/index.html index c8ac50d1..8deba929 100644 --- a/browser/finder/index.html +++ b/browser/finder/index.html @@ -6,8 +6,11 @@ - + + + + -
    - + diff --git a/browser/finder/index.js b/browser/finder/index.js new file mode 100644 index 00000000..fb2a40a7 --- /dev/null +++ b/browser/finder/index.js @@ -0,0 +1,183 @@ +import React, { PropTypes } from 'react' +import ReactDOM from 'react-dom' +import { connect, Provider } from 'react-redux' +import reducer from './reducer' +import { createStore } from 'redux' +import FinderInput from './FinderInput' +import FinderList from './FinderList' +import FinderDetail from './FinderDetail' +import { selectArticle, searchArticle, refreshData } from './actions' +import _ from 'lodash' + +import remote from 'remote' +var hideFinder = remote.getGlobal('hideFinder') +import clipboard from 'clipboard' + +require('../styles/finder/index.styl') + +const FOLDER_FILTER = 'FOLDER_FILTER' +const TEXT_FILTER = 'TEXT_FILTER' +const TAG_FILTER = 'TAG_FILTER' + +class FinderMain extends React.Component { + constructor (props) { + super(props) + } + + componentDidMount () { + ReactDOM.findDOMNode(this.refs.finderInput.refs.input).focus() + } + + handleClick (e) { + ReactDOM.findDOMNode(this.refs.finderInput.refs.input).focus() + } + + handleKeyDown (e) { + if (e.keyCode === 38) { + this.selectPrevious() + e.preventDefault() + } + + if (e.keyCode === 40) { + this.selectNext() + e.preventDefault() + } + + if (e.keyCode === 13) { + let { activeArticle } = this.props + clipboard.writeText(activeArticle.content) + hideFinder() + e.preventDefault() + } + if (e.keyCode === 27) { + hideFinder() + e.preventDefault() + } + } + + handleSearchChange (e) { + let { dispatch } = this.props + + dispatch(searchArticle(e.target.value)) + } + + selectArticle (article) { + this.setState({currentArticle: article}) + } + + selectPrevious () { + let { activeArticle, dispatch } = this.props + let index = this.refs.finderList.props.articles.indexOf(activeArticle) + let previousArticle = this.refs.finderList.props.articles[index - 1] + if (previousArticle != null) dispatch(selectArticle(previousArticle.key)) + } + + selectNext () { + let { activeArticle, dispatch } = this.props + let index = this.refs.finderList.props.articles.indexOf(activeArticle) + let previousArticle = this.refs.finderList.props.articles[index + 1] + if (previousArticle != null) dispatch(selectArticle(previousArticle.key)) + } + + render () { + let { articles, activeArticle, status, dispatch } = this.props + return ( +
    this.handleClick(e)} onKeyDown={e => this.handleKeyDown(e)} className='Finder'> + this.handleSearchChange(e)} + ref='finderInput' + onChange={this.handleChange} + value={status.search} + /> + this.selectArticle(article)} + /> + +
    + ) + } +} + +FinderMain.propTypes = { + articles: PropTypes.array, + activeArticle: PropTypes.shape({ + key: PropTypes.string, + tags: PropTypes.array, + title: PropTypes.string, + content: PropTypes.string + }), + status: PropTypes.shape(), + dispatch: PropTypes.func +} + +function remap (state) { + let { articles, folders, status } = state + + let filters = status.search.split(' ').map(key => key.trim()).filter(key => key.length > 0 && !key.match(/^#$/)).map(key => { + if (key.match(/^in:.+$/)) { + return {type: FOLDER_FILTER, value: key.match(/^in:(.+)$/)[1]} + } + if (key.match(/^#(.+)/)) { + return {type: TAG_FILTER, value: key.match(/^#(.+)$/)[1]} + } + return {type: TEXT_FILTER, value: key} + }) + let folderFilters = filters.filter(filter => filter.type === FOLDER_FILTER) + let textFilters = filters.filter(filter => filter.type === TEXT_FILTER) + let tagFilters = filters.filter(filter => filter.type === TAG_FILTER) + + if (folders != null) { + let targetFolders = folders.filter(folder => { + return _.findWhere(folderFilters, {value: folder.name}) + }) + status.targetFolders = targetFolders + + if (targetFolders.length > 0) { + articles = articles.filter(article => { + return _.findWhere(targetFolders, {key: article.FolderKey}) + }) + } + + if (textFilters.length > 0) { + articles = textFilters.reduce((articles, textFilter) => { + return articles.filter(article => { + return article.title.match(new RegExp(textFilter.value, 'i')) || article.content.match(new RegExp(textFilter.value, 'i')) + }) + }, articles) + } + + if (tagFilters.length > 0) { + articles = tagFilters.reduce((articles, tagFilter) => { + return articles.filter(article => { + return _.find(article.tags, tag => tag.match(new RegExp(tagFilter.value, 'i'))) + }) + }, articles) + } + } + + let activeArticle = _.findWhere(articles, {key: status.articleKey}) + if (activeArticle == null) activeArticle = articles[0] + + return { + articles, + activeArticle, + status + } +} + +var Finder = connect(remap)(FinderMain) +var store = createStore(reducer) + +window.onfocus = e => { + store.dispatch(refreshData()) +} + +ReactDOM.render(( + + + +), document.getElementById('content')) diff --git a/browser/finder/index.jsx b/browser/finder/index.jsx deleted file mode 100644 index d3668aa8..00000000 --- a/browser/finder/index.jsx +++ /dev/null @@ -1,135 +0,0 @@ -/* global localStorage */ -var remote = require('remote') -var hideFinder = remote.getGlobal('hideFinder') -var clipboard = require('clipboard') - -var React = require('react') - -var ArticleFilter = require('../main/Mixins/ArticleFilter') - -var FinderInput = require('./Components/FinderInput') -var FinderList = require('./Components/FinderList') -var FinderDetail = require('./Components/FinderDetail') - -// Filter end - -function fetchArticles () { - var user = JSON.parse(localStorage.getItem('currentUser')) - 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.Codes, _planet.Notes) - }) - user.Teams.forEach(function (team) { - team.Planets.forEach(function (planet) { - var _planet = JSON.parse(localStorage.getItem('planet-' + planet.id)) - articles = articles.concat(_planet.Codes, _planet.Notes) - }) - }) - - return articles -} - -var Finder = React.createClass({ - mixins: [ArticleFilter], - 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) - 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, - search: '' - }, function () { - var firstArticle = this.refs.finderList.props.articles[0] - if (firstArticle) { - this.setState({ - currentArticle: firstArticle - }) - } - }) - }, - 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 - 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]}) - } - }, - selectArticle: function (article) { - this.setState({currentArticle: article}) - }, - handleChange: function (e) { - this.setState({search: e.target.value}, function () { - this.setState({currentArticle: this.refs.finderList.props.articles[0]}) - }) - }, - render: function () { - var articles = this.searchArticle(this.state.search, this.state.articles) - return ( -
    - - - -
    - ) - } -}) - -React.render(, document.getElementById('content')) diff --git a/browser/finder/reducer.js b/browser/finder/reducer.js new file mode 100644 index 00000000..72b087c3 --- /dev/null +++ b/browser/finder/reducer.js @@ -0,0 +1,49 @@ +import { combineReducers } from 'redux' +import { SELECT_ARTICLE, SEARCH_ARTICLE, REFRESH_DATA } from './actions' + +let data = JSON.parse(localStorage.getItem('local')) + +let initialArticles = data != null ? data.articles : [] +let initialFolders = data != null ? data.folders : [] +let initialStatus = { + articleKey: null, + search: '' +} + +function status (state = initialStatus, action) { + switch (action.type) { + case SELECT_ARTICLE: + state.articleKey = action.data.key + return state + case SEARCH_ARTICLE: + state.search = action.data.input + return state + default: + return state + } +} + +function articles (state = initialArticles, action) { + switch (action.type) { + case REFRESH_DATA: + return action.data.articles + default: + return state + } +} + +function folders (state = initialFolders, action) { + switch (action.type) { + case REFRESH_DATA: + console.log(action) + return action.data.folders + default: + return state + } +} + +export default combineReducers({ + status, + folders, + articles +}) diff --git a/browser/main/HomePage.js b/browser/main/HomePage.js index edc7f887..eb8f3f84 100644 --- a/browser/main/HomePage.js +++ b/browser/main/HomePage.js @@ -1,65 +1,43 @@ import React, { PropTypes} from 'react' import { connect } from 'react-redux' -import { CREATE_MODE, IDLE_MODE, switchUser, NEW, refreshArticles } from 'boost/actions' -import UserNavigator from './HomePage/UserNavigator' +import { CREATE_MODE, IDLE_MODE, NEW } from 'boost/actions' +// import UserNavigator from './HomePage/UserNavigator' import ArticleNavigator from './HomePage/ArticleNavigator' import ArticleTopBar from './HomePage/ArticleTopBar' import ArticleList from './HomePage/ArticleList' import ArticleDetail from './HomePage/ArticleDetail' -import _, { findWhere, findIndex, pick } from 'lodash' +import _ from 'lodash' import keygen from 'boost/keygen' -import api from 'boost/api' -import auth from 'boost/auth' -import io from 'boost/socket' const TEXT_FILTER = 'TEXT_FILTER' const FOLDER_FILTER = 'FOLDER_FILTER' const TAG_FILTER = 'TAG_FILTER' class HomePage extends React.Component { - componentDidMount () { - const { dispatch } = this.props - - dispatch(switchUser(this.props.params.userId)) - - let currentUser = auth.user() - - let users = currentUser.Teams != null ? [currentUser].concat(currentUser.Teams) : [currentUser] - users.forEach(user => { - api.fetchArticles(user.id) - .then(res => { - dispatch(refreshArticles(user.id, res.body)) - }) - .catch(err => { - if (err.status == null) throw err - console.error(err) - }) - }) - - let token = auth.token() - if (token != null) { - io.emit('JOIN', {token}) - } - } - - componentWillReceiveProps (nextProps) { - const { dispatch, status } = this.props - - if (nextProps.params.userId !== status.userId) { - dispatch(switchUser(nextProps.params.userId)) - } - } - render () { - const { dispatch, status, users, activeUser, articles, activeArticle } = this.props + let { dispatch, status, articles, activeArticle, folders } = this.props return (
    - - + - - + +
    ) } @@ -67,17 +45,9 @@ class HomePage extends React.Component { function remap (state) { let status = state.status - - let currentUser = state.currentUser - if (currentUser == null) return state - let teams = Array.isArray(currentUser.Teams) ? currentUser.Teams : [] - - let users = [currentUser, ...teams] - let activeUser = findWhere(users, {id: parseInt(status.userId, 10)}) - if (activeUser == null) activeUser = users[0] - // Fetch articles - let articles = state.articles['team-' + activeUser.id] + let data = JSON.parse(localStorage.getItem('local')) + let { folders, articles } = data if (articles == null) articles = [] articles.sort((a, b) => { return new Date(b.updatedAt) - new Date(a.updatedAt) @@ -97,17 +67,18 @@ function remap (state) { let textFilters = filters.filter(filter => filter.type === TEXT_FILTER) let tagFilters = filters.filter(filter => filter.type === TAG_FILTER) - if (activeUser.Folders != null) { - let targetFolders = activeUser.Folders.filter(folder => { - return findWhere(folderFilters, {value: folder.name}) + if (folders != null) { + let targetFolders = folders.filter(folder => { + return _.findWhere(folderFilters, {value: folder.name}) }) status.targetFolders = targetFolders if (targetFolders.length > 0) { articles = articles.filter(article => { - return findWhere(targetFolders, {id: article.FolderId}) + return _.findWhere(targetFolders, {key: article.FolderKey}) }) } + if (textFilters.length > 0) { articles = textFilters.reduce((articles, textFilter) => { return articles.filter(article => { @@ -119,19 +90,19 @@ function remap (state) { if (tagFilters.length > 0) { articles = tagFilters.reduce((articles, tagFilter) => { return articles.filter(article => { - return _.find(article.Tags, tag => tag.name.match(new RegExp(tagFilter.value, 'i'))) + return _.find(article.tags, tag => tag.match(new RegExp(tagFilter.value, 'i'))) }) }, articles) } } // Grab active article - let activeArticle = findWhere(articles, {key: status.articleKey}) + let activeArticle = _.findWhere(articles, {key: status.articleKey}) if (activeArticle == null) activeArticle = articles[0] // remove Unsaved new article if user is not CREATE_MODE if (status.mode !== CREATE_MODE) { - let targetIndex = findIndex(articles, article => article.status === NEW) + let targetIndex = _.findIndex(articles, article => article.status === NEW) if (targetIndex >= 0) articles.splice(targetIndex, 1) } @@ -140,8 +111,8 @@ function remap (state) { // restrict // 1. team have one folder at least // or Change IDLE MODE - if (status.mode === CREATE_MODE && activeUser.Folders.length > 0) { - var newArticle = findWhere(articles, {status: 'NEW'}) + if (status.mode === CREATE_MODE) { + var newArticle = _.findWhere(articles, {status: 'NEW'}) if (newArticle == null) { newArticle = { id: null, @@ -149,9 +120,8 @@ function remap (state) { title: '', content: '', mode: 'markdown', - Tags: [], - User: pick(currentUser, ['email', 'name', 'profileName']), - FolderId: activeUser.Folders[0].id, + tags: [], + FolderKey: folders[0].key, status: NEW } articles.unshift(newArticle) @@ -162,8 +132,7 @@ function remap (state) { } let props = { - users, - activeUser, + folders, status, articles, activeArticle @@ -173,8 +142,6 @@ function remap (state) { } HomePage.propTypes = { - users: PropTypes.array, - activeUser: PropTypes.object, params: PropTypes.shape({ userId: PropTypes.string }), @@ -183,7 +150,8 @@ HomePage.propTypes = { }), articles: PropTypes.array, activeArticle: PropTypes.shape(), - dispatch: PropTypes.func + dispatch: PropTypes.func, + folders: PropTypes.array } export default connect(remap)(HomePage) diff --git a/browser/main/HomePage/ArticleDetail.js b/browser/main/HomePage/ArticleDetail.js index 1b6d187e..0f2f2682 100644 --- a/browser/main/HomePage/ArticleDetail.js +++ b/browser/main/HomePage/ArticleDetail.js @@ -1,16 +1,16 @@ import React, { PropTypes } from 'react' import moment from 'moment' -import { findWhere, uniq } from 'lodash' +import _ from 'lodash' import ModeIcon from 'boost/components/ModeIcon' import MarkdownPreview from 'boost/components/MarkdownPreview' import CodeEditor from 'boost/components/CodeEditor' -import { UNSYNCED, IDLE_MODE, CREATE_MODE, EDIT_MODE, switchMode, switchArticle, updateArticle, destroyArticle } from 'boost/actions' +import { IDLE_MODE, CREATE_MODE, EDIT_MODE, switchMode, updateArticle, destroyArticle } from 'boost/actions' import aceModes from 'boost/ace-modes' import Select from 'react-select' import linkState from 'boost/linkState' -import api from 'boost/api' import FolderMark from 'boost/components/FolderMark' import TagLink from 'boost/components/TagLink' +import TagSelect from 'boost/components/TagSelect' var modeOptions = aceModes.map(function (mode) { return { @@ -20,9 +20,7 @@ var modeOptions = aceModes.map(function (mode) { }) function makeInstantArticle (article) { - let instantArticle = Object.assign({}, article) - instantArticle.Tags = Array.isArray(instantArticle.Tags) ? instantArticle.Tags.map(tag => tag.name) : [] - return instantArticle + return Object.assign({}, article) } export default class ArticleDetail extends React.Component { @@ -35,7 +33,7 @@ export default class ArticleDetail extends React.Component { } componentWillReceiveProps (nextProps) { - if (nextProps.activeArticle != null && nextProps.activeArticle.id !== this.state.article.id) { + if (nextProps.activeArticle != null && (nextProps.activeArticle.key !== this.state.article.key) || (nextProps.status.mode !== this.props.status.mode)) { this.setState({article: makeInstantArticle(nextProps.activeArticle)}, function () { console.log('receive props') }) @@ -44,8 +42,8 @@ export default class ArticleDetail extends React.Component { renderEmpty () { return ( -
    - Empty article +
    + Command(⌘) + Enter to create a new post
    ) } @@ -60,23 +58,9 @@ export default class ArticleDetail extends React.Component { } handleDeleteConfirmButtonClick (e) { - let { dispatch, activeUser, activeArticle } = this.props + let { dispatch, activeArticle } = this.props - api.destroyArticle(activeArticle.id) - .then(res => { - console.log(res.body) - }) - .catch(err => { - // connect failed need to queue data - if (err.code === 'ECONNREFUSED') { - return - } - - if (err.status != null) throw err - else console.log(err) - }) - - dispatch(destroyArticle(activeUser.id, activeArticle.id)) + dispatch(destroyArticle(activeArticle.key)) this.setState({openDeleteConfirmMenu: false}) } @@ -85,17 +69,16 @@ export default class ArticleDetail extends React.Component { } renderIdle () { - let { activeArticle, activeUser } = this.props + let { activeArticle, folders } = this.props - let tags = activeArticle.Tags.length > 0 - ? activeArticle.Tags.map(tag => { - return () + let tags = activeArticle.tags != null ? activeArticle.tags.length > 0 + ? activeArticle.tags.map(tag => { + return () }) : ( Not tagged yet - ) - let folder = findWhere(activeUser.Folders, {id: activeArticle.FolderId}) - let folderName = folder != null ? folder.name : '(unknown)' + ) : null + let folder = _.findWhere(folders, {key: activeArticle.FolderKey}) return (
    @@ -117,8 +100,7 @@ export default class ArticleDetail extends React.Component {
    - {folderName}  - by {activeArticle.User.profileName}  + {folder.name}  Created {moment(activeArticle.createdAt).format('YYYY/MM/DD')}  Updated {moment(activeArticle.updatedAt).format('YYYY/MM/DD')}
    @@ -127,7 +109,6 @@ export default class ArticleDetail extends React.Component {
    -
    ) @@ -141,7 +122,7 @@ export default class ArticleDetail extends React.Component {
    {activeArticle.mode === 'markdown' ? - : + : this.handleContentChange(e, value)} mode={activeArticle.mode} code={activeArticle.content}/> }
    @@ -154,86 +135,30 @@ export default class ArticleDetail extends React.Component { } handleSaveButtonClick (e) { - let { activeArticle } = this.props - - if (activeArticle.id == null) this.saveAsNew() - else this.save() - } - - saveAsNew () { - let { dispatch, activeUser } = this.props - let article = this.state.article - let newArticle = Object.assign({}, article) - article.tags = article.Tags - - api.createArticle(article) - .then(res => { - console.log('saved as new') - console.log(res.body) - }) - .catch(err => { - // connect failed need to queue data - if (err.code === 'ECONNREFUSED') { - return - } - - if (err.status != null) throw err - else console.log(err) - }) - - newArticle.status = UNSYNCED - newArticle.Tags = newArticle.Tags.map(tag => { return {name: tag} }) - - dispatch(updateArticle(activeUser.id, newArticle)) - dispatch(switchMode(IDLE_MODE)) - dispatch(switchArticle(article.id)) - } - - save () { - let { dispatch, activeUser } = this.props + let { dispatch, folders } = this.props let article = this.state.article let newArticle = Object.assign({}, article) - article.tags = article.Tags + let folder = _.findWhere(folders, {key: article.FolderKey}) + if (folder == null) return false - api.saveArticle(article) - .then(res => { - console.log('saved') - console.log(res.body) - }) - .catch(err => { - // connect failed need to queue data - if (err.code === 'ECONNREFUSED') { - return - } + delete newArticle.status + newArticle.updatedAt = new Date() - if (err.status != null) throw err - else console.log(err) - }) - - newArticle.status = UNSYNCED - newArticle.Tags = newArticle.Tags.map(tag => { return {name: tag} }) - - dispatch(updateArticle(activeUser.id, newArticle)) + dispatch(updateArticle(newArticle)) dispatch(switchMode(IDLE_MODE)) - dispatch(switchArticle(article.id)) } - handleFolderIdChange (value) { + handleFolderKeyChange (e) { let article = this.state.article - article.FolderId = value + article.FolderKey = e.target.value + this.setState({article: article}) } - handleTagsChange (tag, tags) { - tags = uniq(tags, function (tag) { - return tag.value - }) - - var article = this.state.article - article.Tags = tags.map(function (tag) { - return tag.value - }) + handleTagsChange (newTag, tags) { + let article = this.state.article + article.tags = tags this.setState({article: article}) } @@ -251,21 +176,31 @@ export default class ArticleDetail extends React.Component { } renderEdit () { - let { activeUser } = this.props + let { folders } = this.props - let folderOptions = activeUser.Folders.map(folder => { - return { - label: folder.name, - value: folder.id - } + let folderOptions = folders.map(folder => { + return ( + + ) }) + console.log('edit rendered') return (
    - this.handleTagsChange(tag, tags)} clearable={false} multi placeholder='add some tags...' allowCreate value={this.state.article.Tags} className='tags'/> + + + this.handleTagsChange(tags, tag)} + />
    @@ -278,9 +213,22 @@ export default class ArticleDetail extends React.Component {
    - this.handleModeChange(value)} + clearable={false} + options={modeOptions} + placeholder='select mode...' + value={this.state.article.mode} + className='mode' + />
    - this.handleContentChange(e, value)} mode={this.state.article.mode} code={this.state.article.content}/> + this.handleContentChange(e, value)} + readOnly={false} + mode={this.state.article.mode} + code={this.state.article.content} + />
    diff --git a/browser/main/HomePage/ArticleList.js b/browser/main/HomePage/ArticleList.js index 750a9fee..67442513 100644 --- a/browser/main/HomePage/ArticleList.js +++ b/browser/main/HomePage/ArticleList.js @@ -1,42 +1,54 @@ import React, { PropTypes } from 'react' -import ProfileImage from 'boost/components/ProfileImage' import ModeIcon from 'boost/components/ModeIcon' import moment from 'moment' import { switchArticle, NEW } from 'boost/actions' import FolderMark from 'boost/components/FolderMark' import TagLink from 'boost/components/TagLink' +import _ from 'lodash' export default class ArticleList extends React.Component { - handleArticleClick (key) { + componentDidMount () { + this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000) + } + + componentWillUnmount () { + clearInterval(this.refreshTimer) + } + + handleArticleClick (article) { let { dispatch } = this.props return function (e) { - dispatch(switchArticle(key)) + if (article.status === NEW) return null + dispatch(switchArticle(article.key)) } } render () { - let { articles, activeArticle } = this.props + let { articles, activeArticle, folders } = this.props - let articlesEl = articles.map(article => { - let tags = Array.isArray(article.Tags) && article.Tags.length > 0 - ? article.Tags.map(tag => { - return () + let articleElements = articles.map(article => { + let tagElements = Array.isArray(article.tags) && article.tags.length > 0 + ? article.tags.map(tag => { + return () }) : (Not tagged yet) + let folder = _.findWhere(folders, {key: article.FolderKey}) return (
    -
    this.handleArticleClick(article.key)(e)} className={'articleItem' + (activeArticle.key === article.key ? ' active' : '')}> +
    this.handleArticleClick(article)(e)} className={'articleItem' + (activeArticle.key === article.key ? ' active' : '')}>
    - - by {article.User.profileName} + {folder != null + ? {folder.name} + : Unknown + } {article.status != null ? article.status : moment(article.updatedAt).fromNow()}
    {article.status !== NEW ? article.title : '(New article)'}
    -
    {tags}
    +
    {tagElements}
    @@ -46,13 +58,14 @@ export default class ArticleList extends React.Component { return (
    - {articlesEl} + {articleElements}
    ) } } ArticleList.propTypes = { + folders: PropTypes.array, articles: PropTypes.array, activeArticle: PropTypes.shape(), dispatch: PropTypes.func diff --git a/browser/main/HomePage/ArticleNavigator.js b/browser/main/HomePage/ArticleNavigator.js index b277916c..a3a022ba 100644 --- a/browser/main/HomePage/ArticleNavigator.js +++ b/browser/main/HomePage/ArticleNavigator.js @@ -36,38 +36,25 @@ export default class ArticleNavigator extends React.Component { } render () { - let { activeUser, status } = this.props - if (activeUser == null) return (
    ) + let { status, folders } = this.props let { targetFolders } = status if (targetFolders == null) targetFolders = [] - let folders = activeUser.Folders != null - ? activeUser.Folders.map((folder, index) => { - let isActive = findWhere(targetFolders, {id: folder.id}) + let folderElememts = folders.map((folder, index) => { + let isActive = findWhere(targetFolders, {key: folder.key}) - return ( - - ) - }) - : [] - - let members = Array.isArray(activeUser.Members) ? activeUser.Members.sort((a, b) => { - return new Date(a._pivot_createdAt) - new Date(b._pivot_createdAt) - }).map(member => { return ( -
    - -
    {member.profileName}
    -
    + ) - }) : null + }) return (
    -
    {activeUser.profileName}
    -
    {activeUser.name}
    +
    {process.env.USER}
    +
    local
    @@ -82,22 +69,9 @@ export default class ArticleNavigator extends React.Component {
    - {folders} + {folderElememts}
    - - {activeUser.userType === 'team' ? ( -
    -
    -
    Members
    - -
    -
    - {members} -
    -
    - ) : null} -
    ) } @@ -105,6 +79,7 @@ export default class ArticleNavigator extends React.Component { ArticleNavigator.propTypes = { activeUser: PropTypes.object, + folders: PropTypes.array, status: PropTypes.shape({ folderId: PropTypes.number }), diff --git a/browser/main/HomePage/ArticleTopBar.js b/browser/main/HomePage/ArticleTopBar.js index 2b7e1cf7..cf612a24 100644 --- a/browser/main/HomePage/ArticleTopBar.js +++ b/browser/main/HomePage/ArticleTopBar.js @@ -17,11 +17,9 @@ export default class ArticleTopBar extends React.Component { this.handleSearchChange(e)} placeholder='Search' type='text'/>
    -
    - diff --git a/browser/main/HomePage/untitled b/browser/main/HomePage/untitled new file mode 100644 index 00000000..e69de29b diff --git a/browser/main/index.html b/browser/main/index.html index eb510dde..b3b2cbe8 100644 --- a/browser/main/index.html +++ b/browser/main/index.html @@ -6,7 +6,6 @@ -