From 00360c77d2337b4efa8f1406defa726c936e7652 Mon Sep 17 00:00:00 2001 From: Rokt33r Date: Sat, 2 Jan 2016 04:42:04 +0900 Subject: [PATCH] add unsaved list & move new post button to top bar --- browser/main/HomePage/ArticleDetail/index.js | 29 ++-- browser/main/HomePage/ArticleList.js | 16 ++- browser/main/HomePage/ArticleNavigator.js | 121 +++++++++------- browser/main/HomePage/ArticleTopBar.js | 83 +++++------ browser/main/HomePage/index.js | 31 ++-- browser/styles/main/ArticleNavigator.styl | 144 +++++++++++-------- browser/styles/main/ArticleTopBar.styl | 45 ++---- 7 files changed, 240 insertions(+), 229 deletions(-) diff --git a/browser/main/HomePage/ArticleDetail/index.js b/browser/main/HomePage/ArticleDetail/index.js index a6c9285b..9a8d6c2f 100644 --- a/browser/main/HomePage/ArticleDetail/index.js +++ b/browser/main/HomePage/ArticleDetail/index.js @@ -138,22 +138,15 @@ export default class ArticleDetail extends React.Component { } componentWillReceiveProps (nextProps) { - if (nextProps.activeArticle == null || this.props.activeArticle == null || nextProps.activeArticle.key !== this.props.activeArticle.key) { - let nextArticle = nextProps.activeArticle - let nextModified = nextArticle != null ? _.findWhere(nextProps.modified, {key: nextArticle.key}) : null + let nextArticle = nextProps.activeArticle + let nextModified = nextArticle != null ? _.findWhere(nextProps.modified, {key: nextArticle.key}) : null - let article = Object.assign({content: ''}, nextProps.activeArticle, nextModified) - let nextState = { - article, - previewMode: false - } - - if (article.content.trim().length > 0 && article.mode === 'markdown') { - nextState.previewMode = true - } - - this.setState(nextState) + let article = Object.assign({content: ''}, nextProps.activeArticle, nextModified) + let nextState = { + article } + + this.setState(nextState) } editArticle () { @@ -404,12 +397,12 @@ export default class ArticleDetail extends React.Component { } ArticleDetail.propTypes = { + dispatch: PropTypes.func, status: PropTypes.shape(), - activeArticle: PropTypes.shape(), - modified: PropTypes.array, + tags: PropTypes.array, user: PropTypes.shape(), folders: PropTypes.array, - tags: PropTypes.array, - dispatch: PropTypes.func + modified: PropTypes.array, + activeArticle: PropTypes.shape() } ArticleDetail.prototype.linkState = linkState diff --git a/browser/main/HomePage/ArticleList.js b/browser/main/HomePage/ArticleList.js index b988d419..3ee3d462 100644 --- a/browser/main/HomePage/ArticleList.js +++ b/browser/main/HomePage/ArticleList.js @@ -85,12 +85,16 @@ export default class ArticleList extends React.Component { } handleArticleListKeyDown (e) { - console.log(e.keyCode) if (e.metaKey || e.ctrlKey) return true - if (e.keyCode === 65) { + if (e.keyCode === 65 && !e.shiftKey) { e.preventDefault() - remote.getCurrentWebContents().send('nav-new-post') + remote.getCurrentWebContents().send('top-new-post') + } + + if (e.keyCode === 65 && e.shiftKey) { + e.preventDefault() + remote.getCurrentWebContents().send('nav-new-folder') } if (e.keyCode === 68) { @@ -129,7 +133,7 @@ export default class ArticleList extends React.Component { article = Object.assign({}, article, modifiedArticle) } let tagElements = Array.isArray(article.tags) && article.tags.length > 0 - ? article.tags.map(tag => { + ? article.tags.slice().map(tag => { return () }) : (Not tagged yet) @@ -189,9 +193,9 @@ export default class ArticleList extends React.Component { } ArticleList.propTypes = { + dispatch: PropTypes.func, folders: PropTypes.array, articles: PropTypes.array, modified: PropTypes.array, - activeArticle: PropTypes.shape(), - dispatch: PropTypes.func + activeArticle: PropTypes.shape() } diff --git a/browser/main/HomePage/ArticleNavigator.js b/browser/main/HomePage/ArticleNavigator.js index cde86851..36128fae 100644 --- a/browser/main/HomePage/ArticleNavigator.js +++ b/browser/main/HomePage/ArticleNavigator.js @@ -1,15 +1,17 @@ import React, { PropTypes } from 'react' import { findWhere } from 'lodash' -import { setSearchFilter, switchFolder, saveArticle } from '../actions' +import { setSearchFilter, switchFolder, uncacheArticle, saveAllArticles, switchArticle, clearSearch } from '../actions' import { openModal, isModalOpen } from 'browser/lib/modal' import FolderMark from 'browser/components/FolderMark' import Preferences from '../modal/Preferences' import CreateNewFolder from '../modal/CreateNewFolder' -import keygen from 'browser/lib/keygen' +import _ from 'lodash' +import ModeIcon from 'browser/components/ModeIcon' const ipc = require('electron').ipcRenderer const BRAND_COLOR = '#18AF90' +const OSX = global.process.platform === 'darwin' const preferenceTutorialElement = ( @@ -29,7 +31,7 @@ c-4,0-7.9,0-11.9-0.1C164,294,164,297,165.9,297L165.9,297z'/> const newPostTutorialElement = ( Create a new post!! - + 0 - let FolderKey = isFolderFilterApplied - ? targetFolders[0].key - : folders[0].key - - let newArticle = { - key: keygen(), - title: '', - content: '', - mode: 'markdown', - tags: [], - FolderKey: FolderKey, - craetedAt: new Date(), - updatedAt: new Date() - } - - dispatch(saveArticle(newArticle.key, newArticle, true)) - if (isFolderFilterApplied) dispatch(switchFolder(targetFolders[0].name)) - } - handleNewFolderButton (e) { let { user } = this.props openModal(CreateNewFolder, {user: user}) @@ -127,11 +99,52 @@ export default class ArticleNavigator extends React.Component { dispatch(setSearchFilter('')) } + handleUnsavedItemClick (article) { + let { dispatch } = this.props + return e => { + let { articles } = this.props + let isInArticleList = articles.some(_article => _article.key === article.key) + if (!isInArticleList) dispatch(clearSearch()) + dispatch(switchArticle(article.key)) + } + } + + handleUncacheButtonClick (article) { + let { dispatch } = this.props + return e => { + dispatch(uncacheArticle(article.key)) + } + } + + handleSaveAllClick (e) { + let { dispatch } = this.props + dispatch(saveAllArticles()) + } + render () { - let { status, user, folders, allArticles } = this.props + let { status, user, folders, allArticles, modified, activeArticle } = this.props let { targetFolders } = status if (targetFolders == null) targetFolders = [] + let modifiedElements = modified.map(modifiedArticle => { + let originalArticle = _.findWhere(allArticles, {key: modifiedArticle.key}) + if (originalArticle == null) return false + let combinedArticle = Object.assign({}, originalArticle, modifiedArticle) + + let className = 'ArticleNavigator-unsaved-list-item' + if (activeArticle && activeArticle.key === combinedArticle.key) className += ' active' + + return ( +
this.handleUnsavedItemClick(combinedArticle)(e)} className={className}> +
+   + {combinedArticle.title} +
+ +
+ ) + }).filter(modifiedArticle => modifiedArticle).sort((a, b) => a.updatedAt - b.updatedAt) + let folderElememts = folders.map((folder, index) => { let isActive = findWhere(targetFolders, {key: folder.key}) let articleCount = allArticles.filter(article => article.FolderKey === folder.key && article.status !== 'NEW').length @@ -157,22 +170,27 @@ export default class ArticleNavigator extends React.Component { -
- - - {status.isTutorialOpen ? newPostTutorialElement : null} - +
+
Work in progress
+
+ {modifiedElements.length > 0 + ? modifiedElements + : ( +
Empty list
+ ) + } +
+
+ +
-
-
+
+
Folders
{status.isTutorialOpen ? newFolderTutorialElement : null} @@ -189,12 +207,17 @@ export default class ArticleNavigator extends React.Component { } ArticleNavigator.propTypes = { - user: PropTypes.object, - folders: PropTypes.array, - allArticles: PropTypes.array, + dispatch: PropTypes.func, status: PropTypes.shape({ folderId: PropTypes.number }), - dispatch: PropTypes.func + user: PropTypes.object, + folders: PropTypes.array, + allArticles: PropTypes.array, + articles: PropTypes.array, + modified: PropTypes.array, + activeArticle: PropTypes.shape({ + key: PropTypes.string + }) } diff --git a/browser/main/HomePage/ArticleTopBar.js b/browser/main/HomePage/ArticleTopBar.js index 0b235123..9950ae8b 100644 --- a/browser/main/HomePage/ArticleTopBar.js +++ b/browser/main/HomePage/ArticleTopBar.js @@ -1,39 +1,15 @@ import React, { PropTypes } from 'react' import ReactDOM from 'react-dom' import ExternalLink from 'browser/components/ExternalLink' -import { setSearchFilter, clearSearch, toggleOnlyUnsavedFilter, toggleTutorial, saveAllArticles, switchArticle } from '../actions' -import store from '../store' +import { setSearchFilter, clearSearch, toggleTutorial, saveArticle, switchFolder } from '../actions' import { isModalOpen } from 'browser/lib/modal' +import keygen from 'browser/lib/keygen' const electron = require('electron') const remote = electron.remote -const Menu = remote.Menu -const MenuItem = remote.MenuItem const ipc = electron.ipcRenderer -const OSX = process.platform === 'darwin' - -var menu = new Menu() -var lastIndex = -1 -menu.append(new MenuItem({ - label: 'Show only unsaved', - click: function () { - store.dispatch(setSearchFilter('--unsaved')) - } -})) -menu.append(new MenuItem({ - label: 'Go to an unsaved article', - click: function () { - lastIndex++ - let state = store.getState() - let modified = state.articles.modified - if (modified.length === 0) return - if (modified.length <= lastIndex) { - lastIndex = 0 - } - store.dispatch(switchArticle(modified[lastIndex].key)) - } -})) +const OSX = global.process.platform === 'darwin' const BRAND_COLOR = '#18AF90' @@ -74,6 +50,10 @@ export default class ArticleTopBar extends React.Component { if (isModalOpen()) return true this.focusInput(e) } + this.newPostHandler = e => { + if (isModalOpen()) return true + this.handleNewPostButtonClick(e) + } this.state = { isTooltipHidden: true, @@ -101,6 +81,7 @@ export default class ArticleTopBar extends React.Component { ipc.on('top-save-all', this.saveAllHandler) ipc.on('top-focus-search', this.focusSearchHandler) + ipc.on('top-new-post', this.newPostHandler) } componentWillUnmount () { @@ -109,6 +90,7 @@ export default class ArticleTopBar extends React.Component { ipc.removeListener('top-save-all', this.saveAllHandler) ipc.removeListener('top-focus-search', this.focusSearchHandler) + ipc.removeListener('top-new-post', this.newPostHandler) } handleTooltipRequest (e) { @@ -152,21 +134,29 @@ export default class ArticleTopBar extends React.Component { this.focusInput() } - handleOnlyUnsavedChange (e) { - let { dispatch } = this.props + handleNewPostButtonClick (e) { + let { dispatch, folders, status } = this.props + let { targetFolders } = status - dispatch(toggleOnlyUnsavedFilter()) - } + let isFolderFilterApplied = targetFolders.length > 0 + let FolderKey = isFolderFilterApplied + ? targetFolders[0].key + : folders[0].key - handleSaveAllButtonClick (e) { - let { dispatch } = this.props + let newArticle = { + key: keygen(), + title: '', + content: '', + mode: 'markdown', + tags: [], + FolderKey: FolderKey, + craetedAt: new Date(), + updatedAt: new Date() + } - dispatch(saveAllArticles()) - remote.getCurrentWebContents().send('list-focus') - } - - handleSaveMenuButtonClick (e) { - menu.popup(590, 45) + dispatch(saveArticle(newArticle.key, newArticle, true)) + if (isFolderFilterApplied) dispatch(switchFolder(targetFolders[0].name)) + remote.getCurrentWebContents().send('detail-edit') } handleTutorialButtonClick (e) { @@ -176,7 +166,7 @@ export default class ArticleTopBar extends React.Component { } render () { - let { status, modified } = this.props + let { status } = this.props return (
@@ -207,13 +197,11 @@ export default class ArticleTopBar extends React.Component { {status.isTutorialOpen ? searchTutorialElement : null} -
- -
@@ -260,10 +248,9 @@ export default class ArticleTopBar extends React.Component { } ArticleTopBar.propTypes = { - search: PropTypes.string, dispatch: PropTypes.func, status: PropTypes.shape({ search: PropTypes.string }), - modified: PropTypes.array + folders: PropTypes.array } diff --git a/browser/main/HomePage/index.js b/browser/main/HomePage/index.js index f2f5de5d..9d9e0769 100644 --- a/browser/main/HomePage/index.js +++ b/browser/main/HomePage/index.js @@ -64,23 +64,26 @@ class HomePage extends React.Component { } render () { - let { dispatch, status, user, articles, allArticles, modified, activeArticle, folders, tags, filters } = this.props + let { dispatch, status, user, articles, allArticles, modified, activeArticle, folders, tags } = this.props return (
) @@ -208,12 +209,7 @@ function remap (state) { allArticles, modified, activeArticle, - tags, - filters: { - folder: folderFilters, - tag: tagFilters, - text: textFilters - } + tags } } @@ -228,11 +224,6 @@ HomePage.propTypes = { activeArticle: PropTypes.shape(), dispatch: PropTypes.func, folders: PropTypes.array, - filters: PropTypes.shape({ - folder: PropTypes.array, - tag: PropTypes.array, - text: PropTypes.array - }), tags: PropTypes.array } diff --git a/browser/styles/main/ArticleNavigator.styl b/browser/styles/main/ArticleNavigator.styl index ca7e7336..563a3d41 100644 --- a/browser/styles/main/ArticleNavigator.styl +++ b/browser/styles/main/ArticleNavigator.styl @@ -56,42 +56,91 @@ articleCount = #999 &:active background-color brandColor border-color brandColor - .controlSection - height 88px - padding 22px 15px - margin-bottom 44px - .tutorial - fixed top left - z-index 35 - pointer-event none - font-style italic - transition 0.1s - &.hide - opacity 0 - .newPostBtn - position relative - border none - background-color brandColor - color white - height 44px - width 170px - border-radius 5px - font-size 20px - transition 0.1s - z-index 30 - .tooltip - tooltip() - margin-left 48px - margin-top -3px - &:hover - background-color lighten(brandColor, 7%) - .tooltip - opacity 1 - .folders, .members - .header - border-bottom 1px solid borderColor + .ArticleNavigator-unsaved + position absolute + top 100px + width 100% + height 225px + .ArticleNavigator-unsaved-header + border-bottom 1px solid alpha(borderColor, 0.5) + padding-bottom 5px + clearfix() + position relative + z-index 30 + padding-left 10px + font-size 18px + line-height 22px + margin-bottom 5px + .ArticleNavigator-unsaved-list + .ArticleNavigator-unsaved-list-item + height 33px + padding-left 15px + clearfix() + transition 0.1s + cursor pointer + overflow hidden + &:hover + background-color alpha(white, 0.05) + &.active, &:active + background-color alpha(lighten(brandColor, 25%), 70%) + .ArticleNavigator-unsaved-list-item-label + float left + width 151px + line-height 33px + overflow ellipsis + .ArticleNavigator-unsaved-list-item-discard-button + float right + width 33px + line-height 30px + height 33px + border none + background-color transparent + color white + font-size 18px + opacity 0.5 + &:hover + opacity 1 + .ArticleNavigator-unsaved-list-empty + height 33px + padding-left 15px + color alpha(white, 0.4) + transition 0.1s + line-height 33px + &:hover + color alpha(white, 0.6) + .ArticleNavigator-unsaved-control + absolute bottom + height 33px + border-top 1px solid alpha(borderColor, 0.5) + width 100% + .ArticleNavigator-unsaved-control-save-all-button + border none + background-color transparent + font-size 14px + color brandColor + padding-left 15px + width 100% + height 33px + text-align left + &:hover + color lighten(brandColor, 15%) + background-color alpha(white, 0.05) + &:active + color white + &:disabled + color alpha(brandColor, 0.5) + &:hover + color alpha(lighten(brandColor, 25%), 0.5) + background-color transparent + + + .ArticleNavigator-folders + absolute bottom + top 365px + width 100% + .ArticleNavigator-folders-header + border-bottom 1px solid alpha(borderColor, 0.5) padding-bottom 5px - margin-bottom 10px clearfix() position relative z-index 30 @@ -124,11 +173,6 @@ articleCount = #999 &:active background-color brandColor border-color brandColor - .folders - absolute bottom - top 200px - width 100% - .header .tutorial position fixed z-index 35px @@ -136,7 +180,7 @@ articleCount = #999 font-style italic .folderList absolute bottom - top 38px + top 33px overflow-y auto .folderList button height 33px @@ -149,23 +193,9 @@ articleCount = #999 padding-left 15px overflow ellipsis &:hover - background-color transparentify(white, 5%) + background-color alpha(white, 0.05) &.active, &:active - background-color transparentify(lighten(brandColor, 25%), 70%) + background-color alpha(lighten(brandColor, 25%), 70%) .articleCount color articleCount font-size 12px - .members - .memberList>div - height 33px - width 200px - margin-bottom 5px - padding-left 15px - .memberImage - float left - margin-top 5.5px - border-radius 11px - .memberProfileName - float left - line-height 33px - margin-left 7px diff --git a/browser/styles/main/ArticleTopBar.styl b/browser/styles/main/ArticleTopBar.styl index 8f2daa76..8193a778 100644 --- a/browser/styles/main/ArticleTopBar.styl +++ b/browser/styles/main/ArticleTopBar.styl @@ -1,6 +1,5 @@ bgColor = #E6E6E6 inputBgColor = white -iptFocusBorderColor = #369DCD topBarBtnColor = #B3B3B3 topBarBtnBgColor = #B3B3B3 @@ -87,7 +86,7 @@ infoBtnActiveBgColor = #3A3A3A line-height 33px z-index 0 &:focus - border-color iptFocusBorderColor + border-color focusBorderColor i.fa.fa-search position absolute display block @@ -110,13 +109,15 @@ infoBtnActiveBgColor = #3A3A3A line-height 20px text-align center padding 0 + &:focus + color textColor &:hover color white background-color topBarBtnBgColor &:active color white background-color darken(topBarBtnBgColor, 35%) - .ArticleTopBar-left-unsaved + .ArticleTopBar-left-control line-height 33px float left height 33px @@ -132,40 +133,22 @@ infoBtnActiveBgColor = #3A3A3A height 33px border-radius 16.5px transition 0.1s + border 1px solid transparent &:hover - color inherit + color textColor &:active - color inherit + color textColor background-color lighten(topBarBtnBgColor, 15%) &:disabled color inactiveTextColor background transparent &:focus - color focusBorderColor - &.ArticleTopBar-left-unsaved-save-button - position relative - .ArticleTopBar-left-unsaved-save-button-count - position absolute - font-size 10px - background-color brandColor - color white - height 14px - width 14px - line-height 14px - border-radius 7px - top 16px - right -3px - transition 0.15s - &.hide - transform scale(0) - .ArticleTopBar-left-unsaved-save-button-tooltip - tooltip() - margin-top 30px - margin-left -100px - &:hover - .ArticleTopBar-left-unsaved-save-button-tooltip - opacity 1 - + color textColor + .tooltip + tooltip() + &:hover + .tooltip + opacity 1 &>.ArticleTopBar-right float right &>button @@ -183,7 +166,7 @@ infoBtnActiveBgColor = #3A3A3A border 1px solid bgColor transition 0.1s &:focus - border-color focusBorderColor + background-color lighten(infoBtnActiveBgColor, 15%) .tooltip tooltip() margin-left -50px