mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 01:36:22 +00:00
add unsaved list & move new post button to top bar
This commit is contained in:
@@ -138,22 +138,15 @@ export default class ArticleDetail extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
if (nextProps.activeArticle == null || this.props.activeArticle == null || nextProps.activeArticle.key !== this.props.activeArticle.key) {
|
let nextArticle = nextProps.activeArticle
|
||||||
let nextArticle = nextProps.activeArticle
|
let nextModified = nextArticle != null ? _.findWhere(nextProps.modified, {key: nextArticle.key}) : null
|
||||||
let nextModified = nextArticle != null ? _.findWhere(nextProps.modified, {key: nextArticle.key}) : null
|
|
||||||
|
|
||||||
let article = Object.assign({content: ''}, nextProps.activeArticle, nextModified)
|
let article = Object.assign({content: ''}, nextProps.activeArticle, nextModified)
|
||||||
let nextState = {
|
let nextState = {
|
||||||
article,
|
article
|
||||||
previewMode: false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (article.content.trim().length > 0 && article.mode === 'markdown') {
|
|
||||||
nextState.previewMode = true
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState(nextState)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setState(nextState)
|
||||||
}
|
}
|
||||||
|
|
||||||
editArticle () {
|
editArticle () {
|
||||||
@@ -404,12 +397,12 @@ export default class ArticleDetail extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ArticleDetail.propTypes = {
|
ArticleDetail.propTypes = {
|
||||||
|
dispatch: PropTypes.func,
|
||||||
status: PropTypes.shape(),
|
status: PropTypes.shape(),
|
||||||
activeArticle: PropTypes.shape(),
|
tags: PropTypes.array,
|
||||||
modified: PropTypes.array,
|
|
||||||
user: PropTypes.shape(),
|
user: PropTypes.shape(),
|
||||||
folders: PropTypes.array,
|
folders: PropTypes.array,
|
||||||
tags: PropTypes.array,
|
modified: PropTypes.array,
|
||||||
dispatch: PropTypes.func
|
activeArticle: PropTypes.shape()
|
||||||
}
|
}
|
||||||
ArticleDetail.prototype.linkState = linkState
|
ArticleDetail.prototype.linkState = linkState
|
||||||
|
|||||||
@@ -85,12 +85,16 @@ export default class ArticleList extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleArticleListKeyDown (e) {
|
handleArticleListKeyDown (e) {
|
||||||
console.log(e.keyCode)
|
|
||||||
if (e.metaKey || e.ctrlKey) return true
|
if (e.metaKey || e.ctrlKey) return true
|
||||||
|
|
||||||
if (e.keyCode === 65) {
|
if (e.keyCode === 65 && !e.shiftKey) {
|
||||||
e.preventDefault()
|
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) {
|
if (e.keyCode === 68) {
|
||||||
@@ -129,7 +133,7 @@ export default class ArticleList extends React.Component {
|
|||||||
article = Object.assign({}, article, modifiedArticle)
|
article = Object.assign({}, article, modifiedArticle)
|
||||||
}
|
}
|
||||||
let tagElements = Array.isArray(article.tags) && article.tags.length > 0
|
let tagElements = Array.isArray(article.tags) && article.tags.length > 0
|
||||||
? article.tags.map(tag => {
|
? article.tags.slice().map(tag => {
|
||||||
return (<TagLink key={tag} tag={tag}/>)
|
return (<TagLink key={tag} tag={tag}/>)
|
||||||
})
|
})
|
||||||
: (<span>Not tagged yet</span>)
|
: (<span>Not tagged yet</span>)
|
||||||
@@ -189,9 +193,9 @@ export default class ArticleList extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ArticleList.propTypes = {
|
ArticleList.propTypes = {
|
||||||
|
dispatch: PropTypes.func,
|
||||||
folders: PropTypes.array,
|
folders: PropTypes.array,
|
||||||
articles: PropTypes.array,
|
articles: PropTypes.array,
|
||||||
modified: PropTypes.array,
|
modified: PropTypes.array,
|
||||||
activeArticle: PropTypes.shape(),
|
activeArticle: PropTypes.shape()
|
||||||
dispatch: PropTypes.func
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import React, { PropTypes } from 'react'
|
import React, { PropTypes } from 'react'
|
||||||
import { findWhere } from 'lodash'
|
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 { openModal, isModalOpen } from 'browser/lib/modal'
|
||||||
import FolderMark from 'browser/components/FolderMark'
|
import FolderMark from 'browser/components/FolderMark'
|
||||||
import Preferences from '../modal/Preferences'
|
import Preferences from '../modal/Preferences'
|
||||||
import CreateNewFolder from '../modal/CreateNewFolder'
|
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 ipc = require('electron').ipcRenderer
|
||||||
|
|
||||||
const BRAND_COLOR = '#18AF90'
|
const BRAND_COLOR = '#18AF90'
|
||||||
|
const OSX = global.process.platform === 'darwin'
|
||||||
|
|
||||||
const preferenceTutorialElement = (
|
const preferenceTutorialElement = (
|
||||||
<svg width='300' height='300' className='tutorial'>
|
<svg width='300' height='300' className='tutorial'>
|
||||||
@@ -29,7 +31,7 @@ c-4,0-7.9,0-11.9-0.1C164,294,164,297,165.9,297L165.9,297z'/>
|
|||||||
const newPostTutorialElement = (
|
const newPostTutorialElement = (
|
||||||
<svg width='900' height='900' className='tutorial'>
|
<svg width='900' height='900' className='tutorial'>
|
||||||
<text x='290' y='155' fill={BRAND_COLOR} fontSize='24'>Create a new post!!</text>
|
<text x='290' y='155' fill={BRAND_COLOR} fontSize='24'>Create a new post!!</text>
|
||||||
<text x='300' y='180' fill={BRAND_COLOR} fontSize='16' children={`press \`${process.platform === 'darwin' ? '⌘' : '^'} + Enter\` or \`a\``}/>
|
<text x='300' y='180' fill={BRAND_COLOR} fontSize='16' children={`press \`${OSX === 'darwin' ? '⌘' : '^'} + Enter\` or \`a\``}/>
|
||||||
<svg x='130' y='-20' width='400' height='400'>
|
<svg x='130' y='-20' width='400' height='400'>
|
||||||
<path fill='white' d='M56.2,132.5c11.7-2.9,23.9-6,36.1-4.1c8.7,1.4,16.6,5.5,23.7,10.5c13.3,9.4,24.5,21.5,40.2,27
|
<path fill='white' d='M56.2,132.5c11.7-2.9,23.9-6,36.1-4.1c8.7,1.4,16.6,5.5,23.7,10.5c13.3,9.4,24.5,21.5,40.2,27
|
||||||
c1.8,0.6,2.6-2.3,0.8-2.9c-17.1-6-28.9-20.3-44-29.7c-7-4.4-14.8-7.4-23-8.2c-11.7-1.1-23.3,1.7-34.5,4.5
|
c1.8,0.6,2.6-2.3,0.8-2.9c-17.1-6-28.9-20.3-44-29.7c-7-4.4-14.8-7.4-23-8.2c-11.7-1.1-23.3,1.7-34.5,4.5
|
||||||
@@ -62,10 +64,6 @@ c-3.4-6.1-8.2-11.3-13.8-15.4C50.2,11.6,31,10.9,15.3,19C13.6,19.8,15.1,22.4,16.8,
|
|||||||
export default class ArticleNavigator extends React.Component {
|
export default class ArticleNavigator extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.newPostHandler = e => {
|
|
||||||
if (isModalOpen()) return true
|
|
||||||
this.handleNewPostButtonClick(e)
|
|
||||||
}
|
|
||||||
this.newFolderHandler = e => {
|
this.newFolderHandler = e => {
|
||||||
if (isModalOpen()) return true
|
if (isModalOpen()) return true
|
||||||
this.handleNewFolderButton(e)
|
this.handleNewFolderButton(e)
|
||||||
@@ -73,12 +71,10 @@ export default class ArticleNavigator extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
ipc.on('nav-new-post', this.newPostHandler)
|
|
||||||
ipc.on('nav-new-folder', this.newFolderHandler)
|
ipc.on('nav-new-folder', this.newFolderHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
ipc.removeListener('nav-new-post', this.newPostHandler)
|
|
||||||
ipc.removeListener('nav-new-folder', this.newFolderHandler)
|
ipc.removeListener('nav-new-folder', this.newFolderHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,30 +82,6 @@ export default class ArticleNavigator extends React.Component {
|
|||||||
openModal(Preferences)
|
openModal(Preferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNewPostButtonClick (e) {
|
|
||||||
let { dispatch, folders, status } = this.props
|
|
||||||
let { targetFolders } = status
|
|
||||||
|
|
||||||
let isFolderFilterApplied = targetFolders.length > 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) {
|
handleNewFolderButton (e) {
|
||||||
let { user } = this.props
|
let { user } = this.props
|
||||||
openModal(CreateNewFolder, {user: user})
|
openModal(CreateNewFolder, {user: user})
|
||||||
@@ -127,11 +99,52 @@ export default class ArticleNavigator extends React.Component {
|
|||||||
dispatch(setSearchFilter(''))
|
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 () {
|
render () {
|
||||||
let { status, user, folders, allArticles } = this.props
|
let { status, user, folders, allArticles, modified, activeArticle } = this.props
|
||||||
let { targetFolders } = status
|
let { targetFolders } = status
|
||||||
if (targetFolders == null) targetFolders = []
|
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 (
|
||||||
|
<div key={modifiedArticle.key} onClick={e => this.handleUnsavedItemClick(combinedArticle)(e)} className={className}>
|
||||||
|
<div className='ArticleNavigator-unsaved-list-item-label'>
|
||||||
|
<ModeIcon mode={combinedArticle.mode}/>
|
||||||
|
{combinedArticle.title}
|
||||||
|
</div>
|
||||||
|
<button onClick={e => this.handleUncacheButtonClick(combinedArticle)(e)} className='ArticleNavigator-unsaved-list-item-discard-button'><i className='fa fa-times'/></button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}).filter(modifiedArticle => modifiedArticle).sort((a, b) => a.updatedAt - b.updatedAt)
|
||||||
|
|
||||||
let folderElememts = folders.map((folder, index) => {
|
let folderElememts = folders.map((folder, index) => {
|
||||||
let isActive = findWhere(targetFolders, {key: folder.key})
|
let isActive = findWhere(targetFolders, {key: folder.key})
|
||||||
let articleCount = allArticles.filter(article => article.FolderKey === folder.key && article.status !== 'NEW').length
|
let articleCount = allArticles.filter(article => article.FolderKey === folder.key && article.status !== 'NEW').length
|
||||||
@@ -157,22 +170,27 @@ export default class ArticleNavigator extends React.Component {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='controlSection'>
|
<div className='ArticleNavigator-unsaved'>
|
||||||
<button onClick={e => this.handleNewPostButtonClick(e)} className='newPostBtn'>
|
<div className='ArticleNavigator-unsaved-header'>Work in progress</div>
|
||||||
New Post
|
<div className='ArticleNavigator-unsaved-list'>
|
||||||
<span className='tooltip'>Create a new Post ({process.platform === 'darwin' ? '⌘' : '^'} + n)</span>
|
{modifiedElements.length > 0
|
||||||
</button>
|
? modifiedElements
|
||||||
|
: (
|
||||||
{status.isTutorialOpen ? newPostTutorialElement : null}
|
<div className='ArticleNavigator-unsaved-list-empty'>Empty list</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className='ArticleNavigator-unsaved-control'>
|
||||||
|
<button onClick={e => this.handleSaveAllClick()} className='ArticleNavigator-unsaved-control-save-all-button' disabled={modifiedElements.length === 0}>Save all</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='folders'>
|
<div className='ArticleNavigator-folders'>
|
||||||
<div className='header'>
|
<div className='ArticleNavigator-folders-header'>
|
||||||
<div className='title'>Folders</div>
|
<div className='title'>Folders</div>
|
||||||
<button onClick={e => this.handleNewFolderButton(e)} className='addBtn'>
|
<button onClick={e => this.handleNewFolderButton(e)} className='addBtn'>
|
||||||
<i className='fa fa-fw fa-plus'/>
|
<i className='fa fa-fw fa-plus'/>
|
||||||
<span className='tooltip'>Create a new folder ({process.platform === 'darwin' ? '⌘' : '^'} + Shift + n)</span>
|
<span className='tooltip'>Create a new folder ({OSX === 'darwin' ? '⌘' : '^'} + Shift + n)</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{status.isTutorialOpen ? newFolderTutorialElement : null}
|
{status.isTutorialOpen ? newFolderTutorialElement : null}
|
||||||
@@ -189,12 +207,17 @@ export default class ArticleNavigator extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ArticleNavigator.propTypes = {
|
ArticleNavigator.propTypes = {
|
||||||
user: PropTypes.object,
|
dispatch: PropTypes.func,
|
||||||
folders: PropTypes.array,
|
|
||||||
allArticles: PropTypes.array,
|
|
||||||
status: PropTypes.shape({
|
status: PropTypes.shape({
|
||||||
folderId: PropTypes.number
|
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
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +1,15 @@
|
|||||||
import React, { PropTypes } from 'react'
|
import React, { PropTypes } from 'react'
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import ExternalLink from 'browser/components/ExternalLink'
|
import ExternalLink from 'browser/components/ExternalLink'
|
||||||
import { setSearchFilter, clearSearch, toggleOnlyUnsavedFilter, toggleTutorial, saveAllArticles, switchArticle } from '../actions'
|
import { setSearchFilter, clearSearch, toggleTutorial, saveArticle, switchFolder } from '../actions'
|
||||||
import store from '../store'
|
|
||||||
import { isModalOpen } from 'browser/lib/modal'
|
import { isModalOpen } from 'browser/lib/modal'
|
||||||
|
import keygen from 'browser/lib/keygen'
|
||||||
|
|
||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const remote = electron.remote
|
const remote = electron.remote
|
||||||
const Menu = remote.Menu
|
|
||||||
const MenuItem = remote.MenuItem
|
|
||||||
const ipc = electron.ipcRenderer
|
const ipc = electron.ipcRenderer
|
||||||
|
|
||||||
const OSX = process.platform === 'darwin'
|
const OSX = global.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 BRAND_COLOR = '#18AF90'
|
const BRAND_COLOR = '#18AF90'
|
||||||
|
|
||||||
@@ -74,6 +50,10 @@ export default class ArticleTopBar extends React.Component {
|
|||||||
if (isModalOpen()) return true
|
if (isModalOpen()) return true
|
||||||
this.focusInput(e)
|
this.focusInput(e)
|
||||||
}
|
}
|
||||||
|
this.newPostHandler = e => {
|
||||||
|
if (isModalOpen()) return true
|
||||||
|
this.handleNewPostButtonClick(e)
|
||||||
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isTooltipHidden: true,
|
isTooltipHidden: true,
|
||||||
@@ -101,6 +81,7 @@ export default class ArticleTopBar extends React.Component {
|
|||||||
|
|
||||||
ipc.on('top-save-all', this.saveAllHandler)
|
ipc.on('top-save-all', this.saveAllHandler)
|
||||||
ipc.on('top-focus-search', this.focusSearchHandler)
|
ipc.on('top-focus-search', this.focusSearchHandler)
|
||||||
|
ipc.on('top-new-post', this.newPostHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
@@ -109,6 +90,7 @@ export default class ArticleTopBar extends React.Component {
|
|||||||
|
|
||||||
ipc.removeListener('top-save-all', this.saveAllHandler)
|
ipc.removeListener('top-save-all', this.saveAllHandler)
|
||||||
ipc.removeListener('top-focus-search', this.focusSearchHandler)
|
ipc.removeListener('top-focus-search', this.focusSearchHandler)
|
||||||
|
ipc.removeListener('top-new-post', this.newPostHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTooltipRequest (e) {
|
handleTooltipRequest (e) {
|
||||||
@@ -152,21 +134,29 @@ export default class ArticleTopBar extends React.Component {
|
|||||||
this.focusInput()
|
this.focusInput()
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOnlyUnsavedChange (e) {
|
handleNewPostButtonClick (e) {
|
||||||
let { dispatch } = this.props
|
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 newArticle = {
|
||||||
let { dispatch } = this.props
|
key: keygen(),
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
mode: 'markdown',
|
||||||
|
tags: [],
|
||||||
|
FolderKey: FolderKey,
|
||||||
|
craetedAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
}
|
||||||
|
|
||||||
dispatch(saveAllArticles())
|
dispatch(saveArticle(newArticle.key, newArticle, true))
|
||||||
remote.getCurrentWebContents().send('list-focus')
|
if (isFolderFilterApplied) dispatch(switchFolder(targetFolders[0].name))
|
||||||
}
|
remote.getCurrentWebContents().send('detail-edit')
|
||||||
|
|
||||||
handleSaveMenuButtonClick (e) {
|
|
||||||
menu.popup(590, 45)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTutorialButtonClick (e) {
|
handleTutorialButtonClick (e) {
|
||||||
@@ -176,7 +166,7 @@ export default class ArticleTopBar extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { status, modified } = this.props
|
let { status } = this.props
|
||||||
return (
|
return (
|
||||||
<div className='ArticleTopBar'>
|
<div className='ArticleTopBar'>
|
||||||
<div className='ArticleTopBar-left'>
|
<div className='ArticleTopBar-left'>
|
||||||
@@ -207,13 +197,11 @@ export default class ArticleTopBar extends React.Component {
|
|||||||
|
|
||||||
{status.isTutorialOpen ? searchTutorialElement : null}
|
{status.isTutorialOpen ? searchTutorialElement : null}
|
||||||
|
|
||||||
<div className={'ArticleTopBar-left-unsaved'}>
|
<div className={'ArticleTopBar-left-control'}>
|
||||||
<button onClick={e => this.handleSaveAllButtonClick(e)} className='ArticleTopBar-left-unsaved-save-button' disabled={modified.length === 0}>
|
<button onClick={e => this.handleNewPostButtonClick(e)}>
|
||||||
<i className='fa fa-save'/>
|
<i className='fa fa-plus'/>
|
||||||
<span className={'ArticleTopBar-left-unsaved-save-button-count' + (modified.length === 0 ? ' hide' : '')} children={modified.length}/>
|
<span className='tooltip'>New Post ({OSX ? '⌘' : '^'} + n)</span>
|
||||||
<span className='ArticleTopBar-left-unsaved-save-button-tooltip' children={`Save all ${modified.length} articles (${OSX ? '⌘ + Shift + s' : '^ + Shift + s'})`}></span>
|
|
||||||
</button>
|
</button>
|
||||||
<button onClick={e => this.handleSaveMenuButtonClick(e)} className='ArticleTopBar-left-unsaved-menu-button'><i className='fa fa-angle-down'/></button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -260,10 +248,9 @@ export default class ArticleTopBar extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ArticleTopBar.propTypes = {
|
ArticleTopBar.propTypes = {
|
||||||
search: PropTypes.string,
|
|
||||||
dispatch: PropTypes.func,
|
dispatch: PropTypes.func,
|
||||||
status: PropTypes.shape({
|
status: PropTypes.shape({
|
||||||
search: PropTypes.string
|
search: PropTypes.string
|
||||||
}),
|
}),
|
||||||
modified: PropTypes.array
|
folders: PropTypes.array
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,23 +64,26 @@ class HomePage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
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 (
|
return (
|
||||||
<div className='HomePage'>
|
<div className='HomePage'>
|
||||||
<ArticleNavigator
|
<ArticleNavigator
|
||||||
ref='nav'
|
ref='nav'
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
|
status={status}
|
||||||
user={user}
|
user={user}
|
||||||
folders={folders}
|
folders={folders}
|
||||||
status={status}
|
|
||||||
allArticles={allArticles}
|
allArticles={allArticles}
|
||||||
|
articles={articles}
|
||||||
|
modified={modified}
|
||||||
|
activeArticle={activeArticle}
|
||||||
/>
|
/>
|
||||||
<ArticleTopBar
|
<ArticleTopBar
|
||||||
ref='top'
|
ref='top'
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
status={status}
|
status={status}
|
||||||
modified={modified}
|
folders={folders}
|
||||||
/>
|
/>
|
||||||
<ArticleList
|
<ArticleList
|
||||||
ref='list'
|
ref='list'
|
||||||
@@ -88,19 +91,17 @@ class HomePage extends React.Component {
|
|||||||
folders={folders}
|
folders={folders}
|
||||||
articles={articles}
|
articles={articles}
|
||||||
modified={modified}
|
modified={modified}
|
||||||
status={status}
|
|
||||||
activeArticle={activeArticle}
|
activeArticle={activeArticle}
|
||||||
/>
|
/>
|
||||||
<ArticleDetail
|
<ArticleDetail
|
||||||
ref='detail'
|
ref='detail'
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
user={user}
|
|
||||||
activeArticle={activeArticle}
|
|
||||||
modified={modified}
|
|
||||||
folders={folders}
|
|
||||||
status={status}
|
status={status}
|
||||||
tags={tags}
|
tags={tags}
|
||||||
filters={filters}
|
user={user}
|
||||||
|
folders={folders}
|
||||||
|
modified={modified}
|
||||||
|
activeArticle={activeArticle}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -208,12 +209,7 @@ function remap (state) {
|
|||||||
allArticles,
|
allArticles,
|
||||||
modified,
|
modified,
|
||||||
activeArticle,
|
activeArticle,
|
||||||
tags,
|
tags
|
||||||
filters: {
|
|
||||||
folder: folderFilters,
|
|
||||||
tag: tagFilters,
|
|
||||||
text: textFilters
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,11 +224,6 @@ HomePage.propTypes = {
|
|||||||
activeArticle: PropTypes.shape(),
|
activeArticle: PropTypes.shape(),
|
||||||
dispatch: PropTypes.func,
|
dispatch: PropTypes.func,
|
||||||
folders: PropTypes.array,
|
folders: PropTypes.array,
|
||||||
filters: PropTypes.shape({
|
|
||||||
folder: PropTypes.array,
|
|
||||||
tag: PropTypes.array,
|
|
||||||
text: PropTypes.array
|
|
||||||
}),
|
|
||||||
tags: PropTypes.array
|
tags: PropTypes.array
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,42 +56,91 @@ articleCount = #999
|
|||||||
&:active
|
&:active
|
||||||
background-color brandColor
|
background-color brandColor
|
||||||
border-color brandColor
|
border-color brandColor
|
||||||
.controlSection
|
.ArticleNavigator-unsaved
|
||||||
height 88px
|
position absolute
|
||||||
padding 22px 15px
|
top 100px
|
||||||
margin-bottom 44px
|
width 100%
|
||||||
.tutorial
|
height 225px
|
||||||
fixed top left
|
.ArticleNavigator-unsaved-header
|
||||||
z-index 35
|
border-bottom 1px solid alpha(borderColor, 0.5)
|
||||||
pointer-event none
|
padding-bottom 5px
|
||||||
font-style italic
|
clearfix()
|
||||||
transition 0.1s
|
position relative
|
||||||
&.hide
|
z-index 30
|
||||||
opacity 0
|
padding-left 10px
|
||||||
.newPostBtn
|
font-size 18px
|
||||||
position relative
|
line-height 22px
|
||||||
border none
|
margin-bottom 5px
|
||||||
background-color brandColor
|
.ArticleNavigator-unsaved-list
|
||||||
color white
|
.ArticleNavigator-unsaved-list-item
|
||||||
height 44px
|
height 33px
|
||||||
width 170px
|
padding-left 15px
|
||||||
border-radius 5px
|
clearfix()
|
||||||
font-size 20px
|
transition 0.1s
|
||||||
transition 0.1s
|
cursor pointer
|
||||||
z-index 30
|
overflow hidden
|
||||||
.tooltip
|
&:hover
|
||||||
tooltip()
|
background-color alpha(white, 0.05)
|
||||||
margin-left 48px
|
&.active, &:active
|
||||||
margin-top -3px
|
background-color alpha(lighten(brandColor, 25%), 70%)
|
||||||
&:hover
|
.ArticleNavigator-unsaved-list-item-label
|
||||||
background-color lighten(brandColor, 7%)
|
float left
|
||||||
.tooltip
|
width 151px
|
||||||
opacity 1
|
line-height 33px
|
||||||
.folders, .members
|
overflow ellipsis
|
||||||
.header
|
.ArticleNavigator-unsaved-list-item-discard-button
|
||||||
border-bottom 1px solid borderColor
|
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
|
padding-bottom 5px
|
||||||
margin-bottom 10px
|
|
||||||
clearfix()
|
clearfix()
|
||||||
position relative
|
position relative
|
||||||
z-index 30
|
z-index 30
|
||||||
@@ -124,11 +173,6 @@ articleCount = #999
|
|||||||
&:active
|
&:active
|
||||||
background-color brandColor
|
background-color brandColor
|
||||||
border-color brandColor
|
border-color brandColor
|
||||||
.folders
|
|
||||||
absolute bottom
|
|
||||||
top 200px
|
|
||||||
width 100%
|
|
||||||
.header
|
|
||||||
.tutorial
|
.tutorial
|
||||||
position fixed
|
position fixed
|
||||||
z-index 35px
|
z-index 35px
|
||||||
@@ -136,7 +180,7 @@ articleCount = #999
|
|||||||
font-style italic
|
font-style italic
|
||||||
.folderList
|
.folderList
|
||||||
absolute bottom
|
absolute bottom
|
||||||
top 38px
|
top 33px
|
||||||
overflow-y auto
|
overflow-y auto
|
||||||
.folderList button
|
.folderList button
|
||||||
height 33px
|
height 33px
|
||||||
@@ -149,23 +193,9 @@ articleCount = #999
|
|||||||
padding-left 15px
|
padding-left 15px
|
||||||
overflow ellipsis
|
overflow ellipsis
|
||||||
&:hover
|
&:hover
|
||||||
background-color transparentify(white, 5%)
|
background-color alpha(white, 0.05)
|
||||||
&.active, &:active
|
&.active, &:active
|
||||||
background-color transparentify(lighten(brandColor, 25%), 70%)
|
background-color alpha(lighten(brandColor, 25%), 70%)
|
||||||
.articleCount
|
.articleCount
|
||||||
color articleCount
|
color articleCount
|
||||||
font-size 12px
|
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
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
bgColor = #E6E6E6
|
bgColor = #E6E6E6
|
||||||
inputBgColor = white
|
inputBgColor = white
|
||||||
iptFocusBorderColor = #369DCD
|
|
||||||
|
|
||||||
topBarBtnColor = #B3B3B3
|
topBarBtnColor = #B3B3B3
|
||||||
topBarBtnBgColor = #B3B3B3
|
topBarBtnBgColor = #B3B3B3
|
||||||
@@ -87,7 +86,7 @@ infoBtnActiveBgColor = #3A3A3A
|
|||||||
line-height 33px
|
line-height 33px
|
||||||
z-index 0
|
z-index 0
|
||||||
&:focus
|
&:focus
|
||||||
border-color iptFocusBorderColor
|
border-color focusBorderColor
|
||||||
i.fa.fa-search
|
i.fa.fa-search
|
||||||
position absolute
|
position absolute
|
||||||
display block
|
display block
|
||||||
@@ -110,13 +109,15 @@ infoBtnActiveBgColor = #3A3A3A
|
|||||||
line-height 20px
|
line-height 20px
|
||||||
text-align center
|
text-align center
|
||||||
padding 0
|
padding 0
|
||||||
|
&:focus
|
||||||
|
color textColor
|
||||||
&:hover
|
&:hover
|
||||||
color white
|
color white
|
||||||
background-color topBarBtnBgColor
|
background-color topBarBtnBgColor
|
||||||
&:active
|
&:active
|
||||||
color white
|
color white
|
||||||
background-color darken(topBarBtnBgColor, 35%)
|
background-color darken(topBarBtnBgColor, 35%)
|
||||||
.ArticleTopBar-left-unsaved
|
.ArticleTopBar-left-control
|
||||||
line-height 33px
|
line-height 33px
|
||||||
float left
|
float left
|
||||||
height 33px
|
height 33px
|
||||||
@@ -132,40 +133,22 @@ infoBtnActiveBgColor = #3A3A3A
|
|||||||
height 33px
|
height 33px
|
||||||
border-radius 16.5px
|
border-radius 16.5px
|
||||||
transition 0.1s
|
transition 0.1s
|
||||||
|
border 1px solid transparent
|
||||||
&:hover
|
&:hover
|
||||||
color inherit
|
color textColor
|
||||||
&:active
|
&:active
|
||||||
color inherit
|
color textColor
|
||||||
background-color lighten(topBarBtnBgColor, 15%)
|
background-color lighten(topBarBtnBgColor, 15%)
|
||||||
&:disabled
|
&:disabled
|
||||||
color inactiveTextColor
|
color inactiveTextColor
|
||||||
background transparent
|
background transparent
|
||||||
&:focus
|
&:focus
|
||||||
color focusBorderColor
|
color textColor
|
||||||
&.ArticleTopBar-left-unsaved-save-button
|
.tooltip
|
||||||
position relative
|
tooltip()
|
||||||
.ArticleTopBar-left-unsaved-save-button-count
|
&:hover
|
||||||
position absolute
|
.tooltip
|
||||||
font-size 10px
|
opacity 1
|
||||||
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
|
|
||||||
|
|
||||||
&>.ArticleTopBar-right
|
&>.ArticleTopBar-right
|
||||||
float right
|
float right
|
||||||
&>button
|
&>button
|
||||||
@@ -183,7 +166,7 @@ infoBtnActiveBgColor = #3A3A3A
|
|||||||
border 1px solid bgColor
|
border 1px solid bgColor
|
||||||
transition 0.1s
|
transition 0.1s
|
||||||
&:focus
|
&:focus
|
||||||
border-color focusBorderColor
|
background-color lighten(infoBtnActiveBgColor, 15%)
|
||||||
.tooltip
|
.tooltip
|
||||||
tooltip()
|
tooltip()
|
||||||
margin-left -50px
|
margin-left -50px
|
||||||
|
|||||||
Reference in New Issue
Block a user