mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 17:56:25 +00:00
save ALL
This commit is contained in:
@@ -1,7 +1,35 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import ExternalLink from 'browser/components/ExternalLink'
|
||||
import { setSearchFilter, clearSearch, toggleOnlyUnsavedFilter, toggleTutorial } from '../actions'
|
||||
import { setSearchFilter, clearSearch, toggleOnlyUnsavedFilter, toggleTutorial, saveAllArticles, switchArticle } from '../actions'
|
||||
import store from '../store'
|
||||
|
||||
const electron = require('electron')
|
||||
const remote = electron.remote
|
||||
const Menu = remote.Menu
|
||||
const MenuItem = remote.MenuItem
|
||||
|
||||
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'
|
||||
|
||||
@@ -111,19 +139,28 @@ export default class ArticleTopBar extends React.Component {
|
||||
dispatch(toggleOnlyUnsavedFilter())
|
||||
}
|
||||
|
||||
handleSaveAllButtonClick (e) {
|
||||
let { dispatch } = this.props
|
||||
|
||||
dispatch(saveAllArticles())
|
||||
}
|
||||
|
||||
handleSaveMenuButtonClick (e) {
|
||||
menu.popup(590, 45)
|
||||
}
|
||||
|
||||
handleTutorialButtonClick (e) {
|
||||
let { dispatch } = this.props
|
||||
console.log(e.target.value)
|
||||
|
||||
dispatch(toggleTutorial())
|
||||
}
|
||||
|
||||
render () {
|
||||
let { status } = this.props
|
||||
let { status, modified } = this.props
|
||||
return (
|
||||
<div className='ArticleTopBar'>
|
||||
<div className='left'>
|
||||
<div className='search'>
|
||||
<div className='ArticleTopBar-left'>
|
||||
<div className='ArticleTopBar-left-search'>
|
||||
<i className='fa fa-search fa-fw' />
|
||||
<input
|
||||
ref='searchInput'
|
||||
@@ -142,16 +179,25 @@ export default class ArticleTopBar extends React.Component {
|
||||
<div className={'tooltip' + (this.state.isTooltipHidden ? ' hide' : '')}>
|
||||
<ul>
|
||||
<li>- Search by tag : #{'{string}'}</li>
|
||||
<li>- Search by folder : /{'{folder_name}'}</li>
|
||||
<li><small>exact match : //{'{folder_name}'}</small></li>
|
||||
<li>- Search by folder : /{'{folder_name}'}<br/><small>exact match : //{'{folder_name}'}</small></li>
|
||||
<li>- Only unsaved : --unsaved</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{status.isTutorialOpen ? searchTutorialElement : null}
|
||||
<label className='only-unsaved'><input value={status.onlyUnsaved} onChange={e => this.handleOnlyUnsavedChange(e)} type='checkbox'/> only unsaved</label>
|
||||
|
||||
<div className={'ArticleTopBar-left-unsaved'}>
|
||||
<button onClick={e => this.handleSaveAllButtonClick(e)} className='ArticleTopBar-left-unsaved-save-button' disabled={modified.length === 0}>
|
||||
<i className='fa fa-save'/>
|
||||
<span className={'ArticleTopBar-left-unsaved-save-button-count' + (modified.length === 0 ? ' hide' : '')} children={modified.length}/>
|
||||
<span className='ArticleTopBar-left-unsaved-save-button-tooltip' children={`Save all ${modified.length} articles (⌘ + Shift + s)`}></span>
|
||||
</button>
|
||||
<button onClick={e => this.handleSaveMenuButtonClick(e)} className='ArticleTopBar-left-unsaved-menu-button'><i className='fa fa-angle-down'/></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='right'>
|
||||
|
||||
<div className='ArticleTopBar-right'>
|
||||
<button onClick={e => this.handleTutorialButtonClick(e)}>?<span className='tooltip'>How to use</span>
|
||||
</button>
|
||||
<a ref='links' className='linksBtn' href>
|
||||
@@ -198,5 +244,6 @@ ArticleTopBar.propTypes = {
|
||||
dispatch: PropTypes.func,
|
||||
status: PropTypes.shape({
|
||||
search: PropTypes.string
|
||||
})
|
||||
}),
|
||||
modified: PropTypes.array
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ class HomePage extends React.Component {
|
||||
ref='top'
|
||||
dispatch={dispatch}
|
||||
status={status}
|
||||
modified={modified}
|
||||
/>
|
||||
<ArticleList
|
||||
ref='list'
|
||||
@@ -112,7 +113,7 @@ class HomePage extends React.Component {
|
||||
|
||||
// Ignore invalid key
|
||||
function ignoreInvalidKey (key) {
|
||||
return key.length > 0 && !key.match(/^\/\/$/) && !key.match(/^\/$/) && !key.match(/^#$/)
|
||||
return key.length > 0 && !key.match(/^\/\/$/) && !key.match(/^\/$/) && !key.match(/^#$/) && !key.match(/^--/)
|
||||
}
|
||||
|
||||
// Build filter object by key
|
||||
@@ -144,10 +145,6 @@ function remap (state) {
|
||||
let articles = _articles != null ? _articles.data : []
|
||||
let modified = _articles != null ? _articles.modified : []
|
||||
|
||||
if (state.status.onlyUnsaved) {
|
||||
articles = modified.map(modifiedArticle => _.findWhere(articles, {key: modifiedArticle.key}))
|
||||
}
|
||||
|
||||
articles.sort((a, b) => {
|
||||
return new Date(b.updatedAt) - new Date(a.updatedAt)
|
||||
})
|
||||
@@ -158,6 +155,7 @@ function remap (state) {
|
||||
return sum.concat(article.tags)
|
||||
}, []))
|
||||
|
||||
if (status.search.split(' ').some(key => key === '--unsaved')) articles = articles.filter(article => _.findWhere(modified, {key: article.key}))
|
||||
// Filter articles
|
||||
let filters = status.search.split(' ')
|
||||
.map(key => key.trim())
|
||||
|
||||
@@ -5,6 +5,7 @@ export const CLEAR_NEW_ARTICLE = 'CLEAR_NEW_ARTICLE'
|
||||
export const ARTICLE_UPDATE = 'ARTICLE_UPDATE'
|
||||
export const ARTICLE_DESTROY = 'ARTICLE_DESTROY'
|
||||
export const ARTICLE_SAVE = 'ARTICLE_SAVE'
|
||||
export const ARTICLE_SAVE_ALL = 'ARTICLE_SAVE_ALL'
|
||||
export const ARTICLE_CACHE = 'ARTICLE_CACHE'
|
||||
export const FOLDER_CREATE = 'FOLDER_CREATE'
|
||||
export const FOLDER_UPDATE = 'FOLDER_UPDATE'
|
||||
@@ -16,7 +17,6 @@ export const SWITCH_ARTICLE = 'SWITCH_ARTICLE'
|
||||
export const SET_SEARCH_FILTER = 'SET_SEARCH_FILTER'
|
||||
export const SET_TAG_FILTER = 'SET_TAG_FILTER'
|
||||
export const CLEAR_SEARCH = 'CLEAR_SEARCH'
|
||||
export const TOGGLE_ONLY_UNSAVED_FILTER = 'TOGGLE_ONLY_UNSAVED_FILTER'
|
||||
|
||||
export const TOGGLE_TUTORIAL = 'TOGGLE_TUTORIAL'
|
||||
|
||||
@@ -51,6 +51,12 @@ export function saveArticle (key, article, forceSwitch) {
|
||||
}
|
||||
}
|
||||
|
||||
export function saveAllArticles () {
|
||||
return {
|
||||
type: ARTICLE_SAVE_ALL
|
||||
}
|
||||
}
|
||||
|
||||
export function updateArticle (article) {
|
||||
return {
|
||||
type: ARTICLE_UPDATE,
|
||||
@@ -132,12 +138,6 @@ export function clearSearch () {
|
||||
}
|
||||
}
|
||||
|
||||
export function toggleOnlyUnsavedFilter () {
|
||||
return {
|
||||
type: TOGGLE_ONLY_UNSAVED_FILTER
|
||||
}
|
||||
}
|
||||
|
||||
export function toggleTutorial () {
|
||||
return {
|
||||
type: TOGGLE_TUTORIAL
|
||||
@@ -146,20 +146,23 @@ export function toggleTutorial () {
|
||||
|
||||
export default {
|
||||
updateUser,
|
||||
|
||||
clearNewArticle,
|
||||
updateArticle,
|
||||
destroyArticle,
|
||||
cacheArticle,
|
||||
saveArticle,
|
||||
saveAllArticles,
|
||||
|
||||
createFolder,
|
||||
updateFolder,
|
||||
destroyFolder,
|
||||
replaceFolder,
|
||||
|
||||
switchFolder,
|
||||
switchArticle,
|
||||
setSearchFilter,
|
||||
setTagFilter,
|
||||
clearSearch,
|
||||
toggleOnlyUnsavedFilter,
|
||||
toggleTutorial
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
SET_SEARCH_FILTER,
|
||||
SET_TAG_FILTER,
|
||||
CLEAR_SEARCH,
|
||||
TOGGLE_ONLY_UNSAVED_FILTER,
|
||||
TOGGLE_TUTORIAL,
|
||||
|
||||
// user
|
||||
@@ -18,6 +17,7 @@ import {
|
||||
ARTICLE_DESTROY,
|
||||
ARTICLE_CACHE,
|
||||
ARTICLE_SAVE,
|
||||
ARTICLE_SAVE_ALL,
|
||||
|
||||
// Folder action type
|
||||
FOLDER_CREATE,
|
||||
@@ -31,8 +31,7 @@ import activityRecord from 'browser/lib/activityRecord'
|
||||
|
||||
const initialStatus = {
|
||||
search: '',
|
||||
isTutorialOpen: false,
|
||||
onlyUnsaved: false
|
||||
isTutorialOpen: false
|
||||
}
|
||||
|
||||
let data = dataStore.getData()
|
||||
@@ -195,6 +194,18 @@ function articles (state = initialArticles, action) {
|
||||
dataStore.setArticles(state.data)
|
||||
return state
|
||||
}
|
||||
case ARTICLE_SAVE_ALL:
|
||||
if (state.modified.length > 0) {
|
||||
state.modified.forEach(modifiedArticle => {
|
||||
let targetIndex = _.findIndex(state.data, _article => modifiedArticle.key === _article.key)
|
||||
Object.assign(state.data[targetIndex], modifiedArticle, {key: modifiedArticle.key, updatedAt: new Date()})
|
||||
})
|
||||
}
|
||||
|
||||
state.modified = []
|
||||
dataStore.setArticles(state.data)
|
||||
|
||||
return state
|
||||
case ARTICLE_UPDATE:
|
||||
{
|
||||
let article = action.data.article
|
||||
@@ -265,10 +276,6 @@ function status (state = initialStatus, action) {
|
||||
case CLEAR_SEARCH:
|
||||
state.search = ''
|
||||
|
||||
return state
|
||||
case TOGGLE_ONLY_UNSAVED_FILTER:
|
||||
state.onlyUnsaved = !state.onlyUnsaved
|
||||
|
||||
return state
|
||||
default:
|
||||
return state
|
||||
|
||||
@@ -36,14 +36,14 @@ infoBtnActiveBgColor = #3A3A3A
|
||||
fixed top left bottom right
|
||||
z-index 20
|
||||
background-color transparentify(black, 80%)
|
||||
&>.left
|
||||
&>.ArticleTopBar-left
|
||||
float left
|
||||
&>.tutorial
|
||||
fixed top
|
||||
left 200px
|
||||
z-index 36
|
||||
font-style italic
|
||||
&>.search
|
||||
&>.ArticleTopBar-left-search
|
||||
position relative
|
||||
float left
|
||||
height 33px
|
||||
@@ -63,11 +63,15 @@ infoBtnActiveBgColor = #3A3A3A
|
||||
&.hide
|
||||
opacity 0
|
||||
ul
|
||||
li
|
||||
line-height 18px
|
||||
li:last-child
|
||||
line-height 10px
|
||||
margin-bottom 3px
|
||||
small
|
||||
font-size 10px
|
||||
position relative
|
||||
top -2px
|
||||
margin-left 15px
|
||||
input
|
||||
absolute top left
|
||||
@@ -122,13 +126,45 @@ infoBtnActiveBgColor = #3A3A3A
|
||||
transition 0.1s
|
||||
&:hover
|
||||
color refreshBtnActiveColor
|
||||
.only-unsaved
|
||||
.ArticleTopBar-left-unsaved
|
||||
line-height 33px
|
||||
float left
|
||||
height 33px
|
||||
margin-top 13.5px
|
||||
margin-left 10px
|
||||
&>.right
|
||||
button
|
||||
background transparent
|
||||
font-size 20px
|
||||
border none
|
||||
outline none
|
||||
color inactiveTextColor
|
||||
&:hover
|
||||
color inherit
|
||||
&.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
|
||||
|
||||
&>.ArticleTopBar-right
|
||||
float right
|
||||
&>button
|
||||
display block
|
||||
|
||||
Reference in New Issue
Block a user