diff --git a/atom-lib/updater.js b/atom-lib/updater.js index d506b643..38a112fb 100644 --- a/atom-lib/updater.js +++ b/atom-lib/updater.js @@ -5,33 +5,38 @@ var path = require('path') var version = app.getVersion() var versionText = (version == null || version.length === 0) ? 'DEV version' : 'v' + version - +var versionNotified = false autoUpdater .on('error', function (err, message) { console.error(err) console.error(message) + console.log(path.resolve(__dirname, '../resources/favicon-230x230.png')) nn.notify({ title: 'Error! ' + versionText, - icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'), + icon: path.resolve(__dirname, '../resources/favicon-230x230.png'), message: message }) }) // .on('checking-for-update', function () { // // Connecting + // console.log('checking...') // }) .on('update-available', function () { nn.notify({ title: 'Update is available!! ' + versionText, - icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'), + icon: path.resolve(__dirname, '../resources/favicon-230x230.png'), message: 'Download started.. wait for the update ready.' }) }) .on('update-not-available', function () { - nn.notify({ - title: 'Latest Build!! ' + versionText, - icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'), - message: 'Hope you to enjoy our app :D' - }) + if (!versionNotified) { + nn.notify({ + title: 'Latest Build!! ' + versionText, + icon: path.resolve(__dirname, '../resources/favicon-230x230.png'), + message: 'Hope you to enjoy our app :D' + }) + versionNotified = true + } }) module.exports = autoUpdater diff --git a/browser/finder/FinderDetail.js b/browser/finder/FinderDetail.js index b50392fa..7d3ad12e 100644 --- a/browser/finder/FinderDetail.js +++ b/browser/finder/FinderDetail.js @@ -11,7 +11,16 @@ export default class FinderDetail extends React.Component { return (
- {activeArticle.title}
+
+ {activeArticle.title} +
+
+ +
+
{activeArticle.mode === 'markdown' ? @@ -30,5 +39,6 @@ export default class FinderDetail extends React.Component { } FinderDetail.propTypes = { - activeArticle: PropTypes.shape() + activeArticle: PropTypes.shape(), + saveToClipboard: PropTypes.func } diff --git a/browser/finder/index.html b/browser/finder/index.html index 0a058ce7..8e3928ac 100644 --- a/browser/finder/index.html +++ b/browser/finder/index.html @@ -2,7 +2,7 @@ - CodeXen Popup + Boost Finder diff --git a/browser/finder/index.js b/browser/finder/index.js index 7fef8c7d..8ef40501 100644 --- a/browser/finder/index.js +++ b/browser/finder/index.js @@ -14,9 +14,16 @@ import remote from 'remote' var hideFinder = remote.getGlobal('hideFinder') import clipboard from 'clipboard' +var notifier = require('node-notifier') +var path = require('path') +function getIconPath () { + return path.resolve(global.__dirname, '../../resources/favicon-230x230.png') +} + require('../styles/finder/index.styl') const FOLDER_FILTER = 'FOLDER_FILTER' +const FOLDER_EXACT_FILTER = 'FOLDER_EXACT_FILTER' const TEXT_FILTER = 'TEXT_FILTER' const TAG_FILTER = 'TAG_FILTER' @@ -45,10 +52,7 @@ class FinderMain extends React.Component { } if (e.keyCode === 13) { - let { activeArticle } = this.props - clipboard.writeText(activeArticle.content) - activityRecord.emit('FINDER_COPY') - hideFinder() + this.saveToClipboard() e.preventDefault() } if (e.keyCode === 27) { @@ -57,6 +61,19 @@ class FinderMain extends React.Component { } } + saveToClipboard () { + let { activeArticle } = this.props + clipboard.writeText(activeArticle.content) + activityRecord.emit('FINDER_COPY') + + notifier.notify({ + icon: getIconPath(), + 'title': 'Saved to Clipboard!', + 'message': 'Paste it wherever you want!' + }) + hideFinder() + } + handleSearchChange (e) { let { dispatch } = this.props @@ -83,6 +100,7 @@ class FinderMain extends React.Component { render () { let { articles, activeArticle, status, dispatch } = this.props + let saveToClipboard = () => this.saveToClipboard() return (
this.handleClick(e)} onKeyDown={e => this.handleKeyDown(e)} className='Finder'> this.selectArticle(article)} /> - +
) } @@ -116,27 +137,47 @@ FinderMain.propTypes = { dispatch: PropTypes.func } +// Ignore invalid key +function ignoreInvalidKey (key) { + return key.length > 0 && !key.match(/^\/\/$/) && !key.match(/^\/$/) && !key.match(/^#$/) +} + +// Build filter object by key +function buildFilter (key) { + if (key.match(/^\/\/.+/)) { + return {type: FOLDER_EXACT_FILTER, value: key.match(/^\/\/(.+)$/)[1]} + } + if (key.match(/^\/.+/)) { + return {type: FOLDER_FILTER, value: key.match(/^\/(.+)$/)[1]} + } + if (key.match(/^#(.+)/)) { + return {type: TAG_FILTER, value: key.match(/^#(.+)$/)[1]} + } + return {type: TEXT_FILTER, value: key} +} + 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 filters = status.search.split(' ') + .map(key => key.trim()) + .filter(ignoreInvalidKey) + .map(buildFilter) + + let folderExactFilters = filters.filter(filter => filter.type === FOLDER_EXACT_FILTER) 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) + let targetFolders if (folders != null) { - let targetFolders = folders.filter(folder => { - return _.findWhere(folderFilters, {value: folder.name}) + let exactTargetFolders = folders.filter(folder => { + return _.find(folderExactFilters, filter => folder.name.match(new RegExp(`^${filter.value}$`))) }) - status.targetFolders = targetFolders + let fuzzyTargetFolders = folders.filter(folder => { + return _.find(folderFilters, filter => folder.name.match(new RegExp(`^${filter.value}`))) + }) + targetFolders = status.targetFolders = exactTargetFolders.concat(fuzzyTargetFolders) if (targetFolders.length > 0) { articles = articles.filter(article => { @@ -164,6 +205,7 @@ function remap (state) { let activeArticle = _.findWhere(articles, {key: status.articleKey}) if (activeArticle == null) activeArticle = articles[0] + console.log(status.search) return { articles, activeArticle, diff --git a/browser/finder/reducer.js b/browser/finder/reducer.js index 72b087c3..76ddde7f 100644 --- a/browser/finder/reducer.js +++ b/browser/finder/reducer.js @@ -14,10 +14,10 @@ function status (state = initialStatus, action) { switch (action.type) { case SELECT_ARTICLE: state.articleKey = action.data.key - return state + return Object.assign({}, state) case SEARCH_ARTICLE: state.search = action.data.input - return state + return Object.assign({}, state) default: return state } diff --git a/browser/main/HomePage.js b/browser/main/HomePage.js index 57094068..c40b4dfb 100644 --- a/browser/main/HomePage.js +++ b/browser/main/HomePage.js @@ -12,6 +12,7 @@ import { isModalOpen, closeModal } from 'boost/modal' const TEXT_FILTER = 'TEXT_FILTER' const FOLDER_FILTER = 'FOLDER_FILTER' +const FOLDER_EXACT_FILTER = 'FOLDER_EXACT_FILTER' const TAG_FILTER = 'TAG_FILTER' class HomePage extends React.Component { @@ -98,7 +99,7 @@ class HomePage extends React.Component { } render () { - let { dispatch, status, articles, activeArticle, folders, filters } = this.props + let { dispatch, status, articles, allArticles, activeArticle, folders, tags, filters } = this.props return (
@@ -107,6 +108,7 @@ class HomePage extends React.Component { dispatch={dispatch} folders={folders} status={status} + allArticles={allArticles} />
@@ -134,6 +137,25 @@ class HomePage extends React.Component { } } +// Ignore invalid key +function ignoreInvalidKey (key) { + return key.length > 0 && !key.match(/^\/\/$/) && !key.match(/^\/$/) && !key.match(/^#$/) +} + +// Build filter object by key +function buildFilter (key) { + if (key.match(/^\/\/.+/)) { + return {type: FOLDER_EXACT_FILTER, value: key.match(/^\/\/(.+)$/)[1]} + } + if (key.match(/^\/.+/)) { + return {type: FOLDER_FILTER, value: key.match(/^\/(.+)$/)[1]} + } + if (key.match(/^#(.+)/)) { + return {type: TAG_FILTER, value: key.match(/^#(.+)$/)[1]} + } + return {type: TEXT_FILTER, value: key} +} + function remap (state) { let { folders, articles, status } = state @@ -141,26 +163,33 @@ function remap (state) { articles.sort((a, b) => { return new Date(b.updatedAt) - new Date(a.updatedAt) }) + let allArticles = articles.slice() + + let tags = _.uniq(allArticles.reduce((sum, article) => { + if (!_.isArray(article.tags)) return sum + return sum.concat(article.tags) + }, [])) // Filter articles - 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 filters = status.search.split(' ') + .map(key => key.trim()) + .filter(ignoreInvalidKey) + .map(buildFilter) + + let folderExactFilters = filters.filter(filter => filter.type === FOLDER_EXACT_FILTER) 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) + let targetFolders if (folders != null) { - let targetFolders = folders.filter(folder => { - return _.findWhere(folderFilters, {value: folder.name}) + let exactTargetFolders = folders.filter(folder => { + return _.find(folderExactFilters, filter => folder.name.match(new RegExp(`^${filter.value}$`))) }) - status.targetFolders = targetFolders + let fuzzyTargetFolders = folders.filter(folder => { + return _.find(folderFilters, filter => folder.name.match(new RegExp(`^${filter.value}`))) + }) + targetFolders = status.targetFolders = exactTargetFolders.concat(fuzzyTargetFolders) if (targetFolders.length > 0) { articles = articles.filter(article => { @@ -202,11 +231,10 @@ function remap (state) { // or Change IDLE MODE if (status.mode === CREATE_MODE) { let newArticle = _.findWhere(articles, {status: 'NEW'}) - let FolderKey = folders[0].key - if (folderFilters.length > 0) { - let targetFolder = _.findWhere(folders, {name: folderFilters[0].value}) - if (targetFolder != null) FolderKey = targetFolder.key - } + console.log('targetFolders') + let FolderKey = targetFolders.length > 0 + ? targetFolders[0].key + : folders[0].key if (newArticle == null) { newArticle = { @@ -229,8 +257,10 @@ function remap (state) { return { folders, status, + allArticles, articles, activeArticle, + tags, filters: { folder: folderFilters, tag: tagFilters, @@ -247,6 +277,7 @@ HomePage.propTypes = { userId: PropTypes.string }), articles: PropTypes.array, + allArticles: PropTypes.array, activeArticle: PropTypes.shape(), dispatch: PropTypes.func, folders: PropTypes.array, diff --git a/browser/main/HomePage/ArticleDetail.js b/browser/main/HomePage/ArticleDetail.js index 10c702c5..12fbe9a2 100644 --- a/browser/main/HomePage/ArticleDetail.js +++ b/browser/main/HomePage/ArticleDetail.js @@ -5,7 +5,20 @@ import _ from 'lodash' import ModeIcon from 'boost/components/ModeIcon' import MarkdownPreview from 'boost/components/MarkdownPreview' import CodeEditor from 'boost/components/CodeEditor' -import { IDLE_MODE, CREATE_MODE, EDIT_MODE, switchMode, switchArticle, switchFolder, clearSearch, updateArticle, destroyArticle, NEW } from 'boost/actions' +import { + IDLE_MODE, + CREATE_MODE, + EDIT_MODE, + switchMode, + switchArticle, + switchFolder, + clearSearch, + lockStatus, + unlockStatus, + updateArticle, + destroyArticle, + NEW +} from 'boost/actions' import linkState from 'boost/linkState' import FolderMark from 'boost/components/FolderMark' import TagLink from 'boost/components/TagLink' @@ -82,7 +95,12 @@ export default class ArticleDetail extends React.Component { this.state = { article: makeInstantArticle(props.activeArticle), - previewMode: false + previewMode: false, + isArticleEdited: false, + isTagChanged: false, + isTitleChanged: false, + isContentChanged: false, + isModeChanged: false } } @@ -117,7 +135,11 @@ export default class ArticleDetail extends React.Component { if (isModeChanged) { Object.assign(nextState, { openDeleteConfirmMenu: false, - previewMode: false + previewMode: false, + isArticleEdited: false, + isTagChanged: false, + isTitleChanged: false, + isContentChanged: false }) } @@ -224,6 +246,8 @@ export default class ArticleDetail extends React.Component { handleCancelButtonClick (e) { let { activeArticle, dispatch } = this.props + + dispatch(unlockStatus()) if (activeArticle.status === NEW) dispatch(switchArticle(null)) dispatch(switchMode(IDLE_MODE)) } @@ -236,6 +260,8 @@ export default class ArticleDetail extends React.Component { let folder = _.findWhere(folders, {key: article.FolderKey}) if (folder == null) return false + dispatch(unlockStatus()) + delete newArticle.status newArticle.updatedAt = new Date() if (newArticle.createdAt == null) { @@ -263,19 +289,85 @@ export default class ArticleDetail extends React.Component { this.setState({article: article}) } + handleTitleChange (e) { + let { article } = this.state + article.title = e.target.value + let _isTitleChanged = article.title !== this.props.activeArticle.title + + let { isTagChanged, isContentChanged, isArticleEdited, isModeChanged } = this.state + let _isArticleEdited = _isTitleChanged || isTagChanged || isContentChanged || isModeChanged + + this.setState({ + article, + isTitleChanged: _isTitleChanged, + isArticleEdited: _isArticleEdited + }, () => { + if (isArticleEdited !== _isArticleEdited) { + let { dispatch } = this.props + if (_isArticleEdited) { + console.log('lockit') + dispatch(lockStatus()) + } else { + console.log('unlockit') + dispatch(unlockStatus()) + } + } + }) + } + handleTagsChange (newTag, tags) { let article = this.state.article article.tags = tags this.setState({article: article}) + + let _isTagChanged = _.difference(article.tags, this.props.activeArticle.tags).length > 0 || _.difference(this.props.activeArticle.tags, article.tags).length > 0 + + let { isTitleChanged, isContentChanged, isArticleEdited, isModeChanged } = this.state + let _isArticleEdited = _isTagChanged || isTitleChanged || isContentChanged || isModeChanged + + this.setState({ + article, + isTagChanged: _isTagChanged, + isArticleEdited: _isArticleEdited + }, () => { + if (isArticleEdited !== _isArticleEdited) { + let { dispatch } = this.props + if (_isArticleEdited) { + console.log('lockit') + dispatch(lockStatus()) + } else { + console.log('unlockit') + dispatch(unlockStatus()) + } + } + }) } handleModeChange (value) { - let article = this.state.article + let { article } = this.state article.mode = value + let _isModeChanged = article.mode !== this.props.activeArticle.mode + + let { isTagChanged, isContentChanged, isArticleEdited, isTitleChanged } = this.state + let _isArticleEdited = _isModeChanged || isTagChanged || isContentChanged || isTitleChanged + this.setState({ - article: article, - previewMode: false + article, + previewMode: false, + isModeChanged: _isModeChanged, + isArticleEdited: _isArticleEdited + }, () => { + if (isArticleEdited !== _isArticleEdited) { + let { dispatch } = this.props + if (_isArticleEdited) { + console.log('lockit') + dispatch(lockStatus()) + } else { + console.log('unlockit') + dispatch(unlockStatus()) + } + } }) } @@ -286,9 +378,29 @@ export default class ArticleDetail extends React.Component { } handleContentChange (e, value) { - let article = this.state.article + let { article } = this.state article.content = value - this.setState({article: article}) + let _isContentChanged = article.content !== this.props.activeArticle.content + + let { isTagChanged, isModeChanged, isArticleEdited, isTitleChanged } = this.state + let _isArticleEdited = _isContentChanged || isTagChanged || isModeChanged || isTitleChanged + + this.setState({ + article, + isContentChanged: _isContentChanged, + isArticleEdited: _isArticleEdited + }, () => { + if (isArticleEdited !== _isArticleEdited) { + let { dispatch } = this.props + if (_isArticleEdited) { + console.log('lockit') + dispatch(lockStatus()) + } else { + console.log('unlockit') + dispatch(unlockStatus()) + } + } + }) } handleTogglePreviewButtonClick (e) { @@ -303,7 +415,7 @@ export default class ArticleDetail extends React.Component { } renderEdit () { - let { folders, status } = this.props + let { folders, status, tags } = this.props let folderOptions = folders.map(folder => { return ( @@ -322,10 +434,12 @@ export default class ArticleDetail extends React.Component { > {folderOptions} + {this.state.isArticleEdited ? ' (edited)' : ''} this.handleTagsChange(tags, tag)} + suggestTags={tags} /> {status.isTutorialOpen ? tagSelectTutorialElement : null} @@ -346,7 +460,7 @@ export default class ArticleDetail extends React.Component {
- this.handleTitleKeyDown(e)} placeholder='Title' ref='title' valueLink={this.linkState('article.title')}/> + this.handleTitleKeyDown(e)} placeholder='Title' ref='title' value={this.state.article.title} onChange={e => this.handleTitleChange(e)}/>
{ let isActive = findWhere(targetFolders, {key: folder.key}) + let articleCount = allArticles.filter(article => article.FolderKey === folder.key).length return ( ) }) @@ -150,6 +151,7 @@ export default class ArticleNavigator extends React.Component { ArticleNavigator.propTypes = { activeUser: PropTypes.object, folders: PropTypes.array, + allArticles: PropTypes.array, status: PropTypes.shape({ folderId: PropTypes.number }), diff --git a/browser/main/HomePage/ArticleTopBar.js b/browser/main/HomePage/ArticleTopBar.js index bfa38cae..62880bc7 100644 --- a/browser/main/HomePage/ArticleTopBar.js +++ b/browser/main/HomePage/ArticleTopBar.js @@ -10,7 +10,9 @@ const searchTutorialElement = ( Search some posts!! {'- Search by tag : #{string}'} - {'- Search by folder : in:{folder_name}\n'} + {'- Search by folder : /{folder_name}\n'} + + {'exact match : //{folder_name}'} - Search by tag : #{'{string}'}
- - Search by folder : in:{'{folder_name}'} + - Search by folder : /{'{folder_name}'}
diff --git a/browser/main/index.js b/browser/main/index.js index a749bdbe..028a2180 100644 --- a/browser/main/index.js +++ b/browser/main/index.js @@ -11,8 +11,12 @@ require('../styles/main/index.styl') import { openModal } from 'boost/modal' import Tutorial from 'boost/components/modal/Tutorial' import activityRecord from 'boost/activityRecord' +import ipc from 'ipc' activityRecord.init() +window.addEventListener('online', function () { + ipc.send('check-update', 'check-update') +}) let routes = ( diff --git a/browser/styles/finder/index.styl b/browser/styles/finder/index.styl index ca2a9111..d91383c5 100644 --- a/browser/styles/finder/index.styl +++ b/browser/styles/finder/index.styl @@ -79,6 +79,31 @@ body white-space nowrap text-overflow ellipsis overflow-x hidden + clearfix() + .left + float left + .right + float right + button + border-radius 16.5px + cursor pointer + height 33px + width 33px + border none + margin-right 5px + font-size 18px + color inactiveTextColor + background-color transparent + padding 0 + .tooltip + tooltip() + &.clipboardBtn .tooltip + margin-left -160px + margin-top 25px + &:hover + color textColor + .tooltip + opacity 1 .content position absolute top 55px diff --git a/browser/styles/main/HomeContainer/components/ArticleDetail.styl b/browser/styles/main/HomeContainer/components/ArticleDetail.styl index 0a3182f4..2f4ae10c 100644 --- a/browser/styles/main/HomeContainer/components/ArticleDetail.styl +++ b/browser/styles/main/HomeContainer/components/ArticleDetail.styl @@ -98,44 +98,66 @@ iptFocusBorderColor = #369DCD &:hover background-color white .TagSelect - white-space nowrap - overflow-x auto - position relative - margin-top 5px - noSelect() - z-index 30 - background-color #E6E6E6 - .tagItem - background-color brandColor - border-radius 2px - color white - margin 0 2px - padding 0 - border 1px solid darken(brandColor, 10%) - button.tagRemoveBtn + .tags + white-space nowrap + overflow-x auto + position relative + max-width 350px + margin-top 5px + noSelect() + z-index 30 + background-color #E6E6E6 + .tagItem + background-color brandColor + border-radius 2px color white + margin 0 2px + padding 0 + border 1px solid darken(brandColor, 10%) + button.tagRemoveBtn + color white + border-radius 2px + border none + background-color transparent + padding 4px 2px + border-right 1px solid #E6E6E6 + font-size 8px + line-height 12px + transition 0.1s + &:hover + background-color lighten(brandColor, 10%) + .tagLabel + padding 4px 4px + font-size 12px + line-height 12px + input.tagInput + background-color transparent + outline none + margin 0 2px border-radius 2px border none - background-color transparent - padding 4px 2px - border-right 1px solid #E6E6E6 - font-size 8px - line-height 12px transition 0.1s + height 18px + .suggestTags + position fixed + width 150px + max-height 150px + background-color white + z-index 5 + border 1px solid borderColor + border-radius 5px + button + width 100% + display block + padding 0 15px + height 33px + line-height 33px + background-color transparent + border none + text-align left + font-size 14px &:hover - background-color lighten(brandColor, 10%) - .tagLabel - padding 4px 4px - font-size 12px - line-height 12px - input.tagInput - background-color transparent - outline none - margin 0 2px - border-radius 2px - border none - transition 0.1s - height 18px + background-color darken(white, 10%) .right button cursor pointer @@ -222,9 +244,6 @@ iptFocusBorderColor = #369DCD display inline-block &:hover background-color darken(white, 10%) - - - .title absolute left top bottom right 150px diff --git a/browser/styles/main/HomeContainer/components/ArticleNavigator.styl b/browser/styles/main/HomeContainer/components/ArticleNavigator.styl index 77b19b03..af61cf3d 100644 --- a/browser/styles/main/HomeContainer/components/ArticleNavigator.styl +++ b/browser/styles/main/HomeContainer/components/ArticleNavigator.styl @@ -1,4 +1,5 @@ articleNavBgColor = #353535 +articleCount = #999 .ArticleNavigator background-color articleNavBgColor @@ -149,7 +150,10 @@ articleNavBgColor = #353535 &:hover background-color transparentify(white, 5%) &.active, &:active - background-color brandColor + background-color transparentify(lighten(brandColor, 25%), 70%) + .articleCount + color articleCount + font-size 12px .members .memberList>div height 33px diff --git a/browser/styles/main/HomeContainer/index.styl b/browser/styles/main/HomeContainer/index.styl index bb74ee2d..aed33a3d 100644 --- a/browser/styles/main/HomeContainer/index.styl +++ b/browser/styles/main/HomeContainer/index.styl @@ -9,3 +9,4 @@ @require './lib/CreateNewFolder' @require './lib/Preferences' @require './lib/Tutorial' +@require './lib/EditedAlert' diff --git a/browser/styles/main/HomeContainer/lib/CreateNewFolder.styl b/browser/styles/main/HomeContainer/lib/CreateNewFolder.styl index 2e1c6507..af3ec002 100644 --- a/browser/styles/main/HomeContainer/lib/CreateNewFolder.styl +++ b/browser/styles/main/HomeContainer/lib/CreateNewFolder.styl @@ -34,9 +34,30 @@ iptFocusBorderColor = #369DCD border-radius 5px border solid 1px borderColor outline none - margin 100px auto 25px + margin 75px auto 20px &:focus border-color iptFocusBorderColor + .colorSelect + text-align center + .option + cursor pointer + font-size 22px + height 48px + width 48px + margin 0 2px + border 1px solid transparent + border-radius 5px + overflow hidden + line-height 45px + text-align center + transition 0.1s + display inline-block + &:hover + border-color borderColor + font-size 28px + &.active + font-size 28px + border-color iptFocusBorderColor .alert color infoTextColor background-color infoBackgroundColor @@ -44,7 +65,7 @@ iptFocusBorderColor = #369DCD padding 15px 15px width 330px border-radius 5px - margin 0 auto + margin 15px auto 0 &.error color errorTextColor background-color errorBackgroundColor diff --git a/browser/styles/main/HomeContainer/lib/EditedAlert.styl b/browser/styles/main/HomeContainer/lib/EditedAlert.styl new file mode 100644 index 00000000..e7ce8d11 --- /dev/null +++ b/browser/styles/main/HomeContainer/lib/EditedAlert.styl @@ -0,0 +1,28 @@ +.EditedAlert.modal + width 350px + top 100px + .title + font-size 24px + margin-bottom 15px + .message + font-size 14px + margin-bottom 15px + .control + text-align right + button + border-radius 5px + height 33px + padding 0 15px + font-size 14px + background-color white + border 1px solid borderColor + border-radius 5px + margin-left 5px + &:hover + background-color darken(white, 10%) + &.primary + border-color brandColor + background-color brandColor + color white + &:hover + background-color lighten(brandColor, 10%) diff --git a/browser/styles/main/HomeContainer/lib/Preferences.styl b/browser/styles/main/HomeContainer/lib/Preferences.styl index 79fe0e56..cb74597a 100644 --- a/browser/styles/main/HomeContainer/lib/Preferences.styl +++ b/browser/styles/main/HomeContainer/lib/Preferences.styl @@ -440,19 +440,22 @@ iptFocusBorderColor = #369DCD padding 5px 0 &:last-child border-color transparent + .folderColor + float left + margin-left 10px + text-align center + width 44px .folderName float left width 175px overflow ellipsis - padding-left 15px - .folderPublic - float left - text-align center - width 100px .folderControl float right - width 145px + width 125px text-align center + &.folderHeader + .folderName + padding-left 25px &.newFolder .alert display block @@ -502,6 +505,30 @@ iptFocusBorderColor = #369DCD &:hover color lighten(brandColor, 10%) &.FolderRow + .sortBtns + float left + display block + height 30px + width 30px + margin-top 1.5px + position absolute + button + absolute left + background-color transparent + border none + height 15px + padding 0 + margin 0 + color stripBtnColor + &:first-child + top 0 + &:last-child + top 15px + &:hover + color stripHoverBtnColor + &:disabled + color lighten(stripBtnColor, 10%) + cursor not-allowed .folderName input height 33px border 1px solid borderColor @@ -512,16 +539,52 @@ iptFocusBorderColor = #369DCD width 150px &:focus border-color iptFocusBorderColor - .folderPublic select - height 33px - border 1px solid borderColor - background-color white - outline none - display block - margin 0 auto - font-size 14px - &:focus - border-color iptFocusBorderColor + .folderColor + .select + height 33px + width 33px + border 1px solid borderColor + background-color white + outline none + display block + margin 0 auto + font-size 14px + border-radius 5px + &:focus + border-color iptFocusBorderColor + .options + position absolute + background-color white + text-align left + border 1px solid borderColor + border-radius 5px + padding 0 5px 5px + margin-left 5px + margin-top -34px + clearfix() + .label + margin-left 5px + line-height 22px + font-size 12px + button + float left + border none + width 33px + height 33px + margin-right 5px + border 1px solid transparent + line-height 29px + overflow hidden + border-radius 5px + background-color transparent + outline none + transition 0.1s + &:hover + border-color borderColor + &.active + border-color iptFocusBorderColor + .FolderMark + transform scale(1.4) .folderControl button border none diff --git a/browser/styles/main/HomeContainer/lib/Tutorial.styl b/browser/styles/main/HomeContainer/lib/Tutorial.styl index 913bfbe5..309c785a 100644 --- a/browser/styles/main/HomeContainer/lib/Tutorial.styl +++ b/browser/styles/main/HomeContainer/lib/Tutorial.styl @@ -108,6 +108,8 @@ slideBgColor4 = #00B493 height 140px .slide3 background-color slideBgColor3 + .title + margin-bottom 15px .content font-size 18px &>img diff --git a/lib/actions.js b/lib/actions.js index d99aa2e4..21f4749c 100644 --- a/lib/actions.js +++ b/lib/actions.js @@ -4,6 +4,7 @@ export const ARTICLE_DESTROY = 'ARTICLE_DESTROY' export const FOLDER_CREATE = 'FOLDER_CREATE' export const FOLDER_UPDATE = 'FOLDER_UPDATE' export const FOLDER_DESTROY = 'FOLDER_DESTROY' +export const FOLDER_REPLACE = 'FOLDER_REPLACE' export const SWITCH_FOLDER = 'SWITCH_FOLDER' export const SWITCH_MODE = 'SWITCH_MODE' @@ -11,6 +12,8 @@ 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 LOCK_STATUS = 'LOCK_STATUS' +export const UNLOCK_STATUS = 'UNLOCK_STATUS' export const TOGGLE_TUTORIAL = 'TOGGLE_TUTORIAL' // Status - mode @@ -57,6 +60,16 @@ export function destroyFolder (key) { } } +export function replaceFolder (a, b) { + return { + type: FOLDER_REPLACE, + data: { + a, + b + } + } +} + export function switchFolder (folderName) { return { type: SWITCH_FOLDER, @@ -98,7 +111,19 @@ export function clearSearch () { } } -export function toggleTutorial() { +export function lockStatus () { + return { + type: LOCK_STATUS + } +} + +export function unlockStatus () { + return { + type: UNLOCK_STATUS + } +} + +export function toggleTutorial () { return { type: TOGGLE_TUTORIAL } diff --git a/lib/components/FolderMark.js b/lib/components/FolderMark.js index ef258bb1..dd6708b9 100644 --- a/lib/components/FolderMark.js +++ b/lib/components/FolderMark.js @@ -3,45 +3,50 @@ import React, { PropTypes } from 'react' const BLUE = '#3460C7' const LIGHTBLUE = '#2BA5F7' const ORANGE = '#FF8E00' -const YELLOW = '#EAEF31' -const GREEN = '#02FF26' -const DARKGREEN = '#008A59' +const YELLOW = '#E8D252' +const GREEN = '#3FD941' +const DARKGREEN = '#1FAD85' const RED = '#E10051' const PURPLE = '#B013A4' -const BRAND_COLOR = '#2BAC8F' function getColorByIndex (index) { switch (index % 8) { case 0: - return LIGHTBLUE + return RED case 1: return ORANGE case 2: - return RED + return YELLOW case 3: return GREEN case 4: return DARKGREEN case 5: - return YELLOW + return LIGHTBLUE case 6: return BLUE case 7: return PURPLE default: - return BRAND_COLOR + return DARKGREEN } } export default class FolderMark extends React.Component { render () { let color = getColorByIndex(this.props.color) + let className = 'FolderMark fa fa-square fa-fw' + if (this.props.className != null) { + className += ' active' + } + return ( - + ) } } FolderMark.propTypes = { - color: PropTypes.number + color: PropTypes.number, + className: PropTypes.string } diff --git a/lib/components/ModeSelect.js b/lib/components/ModeSelect.js index 554e361c..28404d20 100644 --- a/lib/components/ModeSelect.js +++ b/lib/components/ModeSelect.js @@ -18,7 +18,7 @@ export default class ModeSelect extends React.Component { } } - componentDidMount (e) { + componentDidMount () { this.blurHandler = e => { let searchElement = ReactDOM.findDOMNode(this.refs.search) if (this.state.mode === EDIT_MODE && document.activeElement !== searchElement) { @@ -28,7 +28,7 @@ export default class ModeSelect extends React.Component { window.addEventListener('click', this.blurHandler) } - componentWillUnmount (e) { + componentWillUnmount () { window.removeEventListener('click', this.blurHandler) let searchElement = ReactDOM.findDOMNode(this.refs.search) if (searchElement != null && this.searchKeyDownListener != null) { diff --git a/lib/components/TagSelect.js b/lib/components/TagSelect.js index b7fbead8..f000d32d 100644 --- a/lib/components/TagSelect.js +++ b/lib/components/TagSelect.js @@ -3,23 +3,54 @@ import ReactDOM from 'react-dom' import _ from 'lodash' import linkState from 'boost/linkState' +function isNotEmptyString (str) { + return _.isString(str) && str.length > 0 +} + export default class TagSelect extends React.Component { constructor (props) { super(props) this.state = { - input: '' + input: '', + isInputFocused: false } } - handleKeyDown (e) { - if (e.keyCode !== 13) return false - e.preventDefault() + componentDidMount () { + this.blurInputBlurHandler = e => { + if (ReactDOM.findDOMNode(this.refs.tagInput) !== document.activeElement) { + this.setState({isInputFocused: false}) + } + } + window.addEventListener('click', this.blurInputBlurHandler) + } + componentWillUnmount (e) { + window.removeEventListener('click', this.blurInputBlurHandler) + } + + // Suggestは必ずInputの下に位置するようにする + componentDidUpdate () { + if (this.shouldShowSuggest()) { + let inputRect = ReactDOM.findDOMNode(this.refs.tagInput).getBoundingClientRect() + let suggestElement = ReactDOM.findDOMNode(this.refs.suggestTags) + if (suggestElement != null) { + suggestElement.style.top = inputRect.top + 20 + 'px' + suggestElement.style.left = inputRect.left + 'px' + } + } + } + + shouldShowSuggest () { + return this.state.isInputFocused && isNotEmptyString(this.state.input) + } + + addTag (tag, clearInput = true) { let tags = this.props.tags.slice(0) - let newTag = this.state.input.trim() + let newTag = tag.trim() - if (newTag.length === 0) { + if (newTag.length === 0 && clearInput) { this.setState({input: ''}) return } @@ -30,13 +61,38 @@ export default class TagSelect extends React.Component { if (_.isFunction(this.props.onChange)) { this.props.onChange(newTag, tags) } - this.setState({input: ''}) + if (clearInput) this.setState({input: ''}) + } + + handleKeyDown (e) { + switch (e.keyCode) { + case 8: + { + if (this.state.input.length > 0) break + e.preventDefault() + + let tags = this.props.tags.slice(0) + tags.pop() + + this.props.onChange(null, tags) + } + break + case 13: + { + e.preventDefault() + this.addTag(this.state.input) + } + } } handleThisClick (e) { ReactDOM.findDOMNode(this.refs.tagInput).focus() } + handleInputFocus (e) { + this.setState({isInputFocused: true}) + } + handleItemRemoveButton (tag) { return e => { e.stopPropagation() @@ -50,8 +106,16 @@ export default class TagSelect extends React.Component { } } + handleSuggestClick (tag) { + return e => { + this.addTag(tag) + } + } + render () { - var tagElements = _.isArray(this.props.tags) + let { tags, suggestTags } = this.props + + let tagElements = _.isArray(tags) ? this.props.tags.map(tag => ( @@ -59,16 +123,37 @@ export default class TagSelect extends React.Component { )) : null + let suggestElements = this.shouldShowSuggest() ? suggestTags + .filter(tag => { + return tag.match(this.state.input) + }) + .map(tag => { + return + }) + : null + return (
this.handleThisClick(e)}> - {tagElements} - this.handleKeyDown(e)} - ref='tagInput' - valueLink={this.linkState('input')} - placeholder='Click here to add tags' - className='tagInput'/> +
+ {tagElements} + this.handleKeyDown(e)} + ref='tagInput' + valueLink={this.linkState('input')} + placeholder='Click here to add tags' + className='tagInput' + onFocus={e => this.handleInputFocus(e)} + /> +
+ {suggestElements != null && suggestElements.length > 0 + ? ( +
+ {suggestElements} +
+ ) + : null + }
) } @@ -76,7 +161,8 @@ export default class TagSelect extends React.Component { TagSelect.propTypes = { tags: PropTypes.arrayOf(PropTypes.string), - onChange: PropTypes.func + onChange: PropTypes.func, + suggestTags: PropTypes.array } TagSelect.prototype.linkState = linkState diff --git a/lib/components/modal/CreateNewFolder.js b/lib/components/modal/CreateNewFolder.js index 0934e219..2686dafe 100644 --- a/lib/components/modal/CreateNewFolder.js +++ b/lib/components/modal/CreateNewFolder.js @@ -2,6 +2,7 @@ import React, { PropTypes } from 'react' import linkState from 'boost/linkState' import { createFolder } from 'boost/actions' import store from 'boost/store' +import FolderMark from 'boost/components/FolderMark' export default class CreateNewFolder extends React.Component { constructor (props) { @@ -9,6 +10,7 @@ export default class CreateNewFolder extends React.Component { this.state = { name: '', + color: Math.round(Math.random() * 7), alert: null } } @@ -20,9 +22,11 @@ export default class CreateNewFolder extends React.Component { handleConfirmButton (e) { this.setState({alert: null}, () => { let { close } = this.props - let name = this.state.name + let { name, color } = this.state + let input = { - name + name, + color } try { @@ -38,6 +42,20 @@ export default class CreateNewFolder extends React.Component { }) } + handleColorClick (colorIndex) { + return e => { + this.setState({ + color: colorIndex + }) + } + } + + handleKeyDown (e) { + if (e.keyCode === 13) { + this.handleConfirmButton() + } + } + render () { let alert = this.state.alert let alertElement = alert != null ? ( @@ -45,6 +63,20 @@ export default class CreateNewFolder extends React.Component { {alert.message}

) : null + let colorIndexes = [] + for (let i = 0; i < 8; i++) { + colorIndexes.push(i) + } + let colorElements = colorIndexes.map(index => { + let className = 'option' + if (index === this.state.color) className += ' active' + + return ( + this.handleColorClick(index)(e)}> + + + ) + }) return (
@@ -52,7 +84,10 @@ export default class CreateNewFolder extends React.Component {
Create new folder
- + this.handleKeyDown(e)} className='ipt' type='text' valueLink={this.linkState('name')} placeholder='Enter folder name'/> +
+ {colorElements} +
{alertElement} diff --git a/lib/components/modal/EditedAlert.js b/lib/components/modal/EditedAlert.js new file mode 100644 index 00000000..514e7d33 --- /dev/null +++ b/lib/components/modal/EditedAlert.js @@ -0,0 +1,35 @@ +import React, { PropTypes } from 'react' +import store from 'boost/store' +import { unlockStatus } from 'boost/actions' + +export default class EditedAlert extends React.Component { + handleNoButtonClick (e) { + this.props.close() + } + + handleYesButtonClick (e) { + store.dispatch(unlockStatus()) + store.dispatch(this.props.action) + this.props.close() + } + + render () { + return ( +
+
Your article is still editing!
+ +
Do you really want to leave without finishing?
+ +
+ + +
+
+ ) + } +} + +EditedAlert.propTypes = { + action: PropTypes.object, + close: PropTypes.func +} diff --git a/lib/components/modal/Preference/AppSettingTab.js b/lib/components/modal/Preference/AppSettingTab.js index 77bec059..fbf5cadc 100644 --- a/lib/components/modal/Preference/AppSettingTab.js +++ b/lib/components/modal/Preference/AppSettingTab.js @@ -36,12 +36,20 @@ export default class AppSettingTab extends React.Component { ipc.removeListener('APP_SETTING_ERROR', this.handleSettingError) } - handleSaveButtonClick (e) { + submitHotKey () { ipc.send('hotkeyUpdated', { toggleFinder: this.state.toggleFinder }) } + handleSaveButtonClick (e) { + this.submitHotKey() + } + + handleKeyDown (e) { + this.submitHotKey() + } + render () { let alert = this.state.alert let alertElement = alert != null ? ( @@ -56,7 +64,7 @@ export default class AppSettingTab extends React.Component {
Hotkey
- + this.handleKeyDown(e)} valueLink={this.linkState('toggleFinder')} type='text'/>
diff --git a/lib/components/modal/Preference/FolderRow.js b/lib/components/modal/Preference/FolderRow.js index 336a0d6c..44e56b82 100644 --- a/lib/components/modal/Preference/FolderRow.js +++ b/lib/components/modal/Preference/FolderRow.js @@ -2,7 +2,7 @@ import React, { PropTypes } from 'react' import linkState from 'boost/linkState' import FolderMark from 'boost/components/FolderMark' import store from 'boost/store' -import { updateFolder, destroyFolder } from 'boost/actions' +import { updateFolder, destroyFolder, replaceFolder } from 'boost/actions' const IDLE = 'IDLE' const EDIT = 'EDIT' @@ -17,6 +17,20 @@ export default class FolderRow extends React.Component { } } + handleUpClick (e) { + let { index } = this.props + if (index > 0) { + store.dispatch(replaceFolder(index, index - 1)) + } + } + + handleDownClick (e) { + let { index, count } = this.props + if (index < count - 1) { + store.dispatch(replaceFolder(index, index + 1)) + } + } + handleCancelButtonClick (e) { this.setState({ mode: IDLE @@ -26,7 +40,9 @@ export default class FolderRow extends React.Component { handleEditButtonClick (e) { this.setState({ mode: EDIT, - name: this.props.folder.name + name: this.props.folder.name, + color: this.props.folder.color, + isColorEditing: false }) } @@ -34,12 +50,34 @@ export default class FolderRow extends React.Component { this.setState({mode: DELETE}) } + handleNameInputKeyDown (e) { + if (e.keyCode === 13) { + this.handleSaveButtonClick() + } + } + + handleColorSelectClick (e) { + this.setState({ + isColorEditing: true + }) + } + + handleColorButtonClick (index) { + return e => { + this.setState({ + color: index, + isColorEditing: false + }) + } + } + handleSaveButtonClick (e) { let { folder, setAlert } = this.props setAlert(null, () => { let input = { - name: this.state.name + name: this.state.name, + color: this.state.color } folder = Object.assign({}, folder, input) @@ -68,10 +106,40 @@ export default class FolderRow extends React.Component { switch (this.state.mode) { case EDIT: + let colorIndexes = [] + for (let i = 0; i < 8; i++) { + colorIndexes.push(i) + } + + let colorOptions = colorIndexes.map(index => { + let className = this.state.color === index + ? 'active' + : null + return ( + + ) + }) + return (
+
+ + {this.state.isColorEditing + ? ( +
+
Color select
+ {colorOptions} +
+ ) + : null + } +
- + this.handleNameInputKeyDown(e)} valueLink={this.linkState('name')} type='text'/>
@@ -93,7 +161,12 @@ export default class FolderRow extends React.Component { default: return (
-
{folder.name}
+
+ + +
+
+
{folder.name}
@@ -106,6 +179,8 @@ export default class FolderRow extends React.Component { FolderRow.propTypes = { folder: PropTypes.shape(), + index: PropTypes.number, + count: PropTypes.number, setAlert: PropTypes.func } diff --git a/lib/components/modal/Preference/FolderSettingTab.js b/lib/components/modal/Preference/FolderSettingTab.js index ff1e8229..09a8ba7d 100644 --- a/lib/components/modal/Preference/FolderSettingTab.js +++ b/lib/components/modal/Preference/FolderSettingTab.js @@ -12,6 +12,12 @@ export default class FolderSettingTab extends React.Component { } } + handleNewFolderNameKeyDown (e) { + if (e.keyCode === 13) { + this.handleSaveButtonClick() + } + } + handleSaveButtonClick (e) { this.setState({alert: null}, () => { if (this.state.name.trim().length === 0) return false @@ -40,10 +46,16 @@ export default class FolderSettingTab extends React.Component { render () { let { folders } = this.props - let folderElements = folders.map(folder => { + let folderElements = folders.map((folder, index) => { return ( - this.setAlert(alert, cb)}/> - ) + this.setAlert(alert, cb)} + /> + ) }) let alert = this.state.alert @@ -59,13 +71,13 @@ export default class FolderSettingTab extends React.Component {
Manage folder
-
Folder name
+
Folder
Edit/Delete
{folderElements}
- + this.handleNewFolderNameKeyDown(e)} valueLink={this.linkState('name')} type='text' placeholder='New Folder'/>
diff --git a/lib/components/modal/Tutorial.js b/lib/components/modal/Tutorial.js index cb2f2ed4..cc927d92 100644 --- a/lib/components/modal/Tutorial.js +++ b/lib/components/modal/Tutorial.js @@ -88,10 +88,11 @@ export default class Tutorial extends React.Component { return (
Easy to access with Finder
- With Finder, You can search your articles faster.
- You can open Finder by pressing Control + shift + tab
- To put the content of an article in the clipboard, press Enter.
- So you can paste it with Cmd(⌘) + V + The Finder helps you organize all of the files and documents.
+ There is a short-cut key [control + shift + tab] to open the Finder.
+ It is available to save your articles on the Clipboard
+ by selecting your file with pressing Enter key,
+ and to paste the contents of the Clipboard with [Command-V]
diff --git a/lib/reducer.js b/lib/reducer.js index 52cd5916..baff2d55 100644 --- a/lib/reducer.js +++ b/lib/reducer.js @@ -1,14 +1,42 @@ import { combineReducers } from 'redux' import _ from 'lodash' -import { SWITCH_FOLDER, SWITCH_MODE, SWITCH_ARTICLE, SET_SEARCH_FILTER, SET_TAG_FILTER, CLEAR_SEARCH, TOGGLE_TUTORIAL, ARTICLE_UPDATE, ARTICLE_DESTROY, FOLDER_CREATE, FOLDER_UPDATE, FOLDER_DESTROY, IDLE_MODE, CREATE_MODE } from './actions' +import { + // Status action type + SWITCH_FOLDER, + SWITCH_MODE, + SWITCH_ARTICLE, + SET_SEARCH_FILTER, + SET_TAG_FILTER, + CLEAR_SEARCH, + LOCK_STATUS, + UNLOCK_STATUS, + TOGGLE_TUTORIAL, + + // Article action type + ARTICLE_UPDATE, + ARTICLE_DESTROY, + + // Folder action type + FOLDER_CREATE, + FOLDER_UPDATE, + FOLDER_DESTROY, + FOLDER_REPLACE, + + // view mode + IDLE_MODE, + CREATE_MODE +} from './actions' import dataStore from 'boost/dataStore' import keygen from 'boost/keygen' import activityRecord from 'boost/activityRecord' +import { openModal } from 'boost/modal' +import EditedAlert from 'boost/components/modal/EditedAlert' const initialStatus = { mode: IDLE_MODE, search: '', - isTutorialOpen: false + isTutorialOpen: false, + isStatusLocked: false } let data = dataStore.getData() @@ -26,12 +54,11 @@ function folders (state = initialFolders, action) { Object.assign(newFolder, { key: keygen(), createdAt: new Date(), - updatedAt: new Date(), - // random number (0-7) - color: Math.round(Math.random() * 7) + updatedAt: new Date() }) - if (newFolder.length === 0) throw new Error('Folder name is required') + if (newFolder.name == null && newFolder.name.length === 0) throw new Error('Folder name is required') + if (newFolder.name.match(/\//)) throw new Error('`/` is not available for folder name') let conflictFolder = _.findWhere(state, {name: newFolder.name}) if (conflictFolder != null) throw new Error(`${newFolder.name} already exists!`) @@ -48,7 +75,8 @@ function folders (state = initialFolders, action) { if (!_.isString(folder.name)) throw new Error('Folder name must be a string') folder.name = folder.name.trim().replace(/\s/, '_') - if (folder.length === 0) throw new Error('Folder name is required') + if (folder.name.length === 0) throw new Error('Folder name is required') + if (folder.name.match(/\//)) throw new Error('`/` is not available for folder name') // Folder existence check if (targetFolder == null) throw new Error('Folder doesnt exist') @@ -80,6 +108,15 @@ function folders (state = initialFolders, action) { activityRecord.emit('FOLDER_DESTROY') return state } + case FOLDER_REPLACE: + { + let { a, b } = action.data + let folderA = state[a] + let folderB = state[b] + state.splice(a, 1, folderB) + state.splice(b, 1, folderA) + } + return state default: return state } @@ -125,13 +162,28 @@ function articles (state = initialArticles, action) { function status (state = initialStatus, action) { state = Object.assign({}, state) + switch (action.type) { case TOGGLE_TUTORIAL: state.isTutorialOpen = !state.isTutorialOpen return state + case LOCK_STATUS: + state.isStatusLocked = true + return state + case UNLOCK_STATUS: + state.isStatusLocked = false + return state + } + + // if status locked, status become unmutable + if (state.isStatusLocked) { + openModal(EditedAlert, {action}) + return state + } + switch (action.type) { case SWITCH_FOLDER: state.mode = IDLE_MODE - state.search = `in:${action.data} ` + state.search = `//${action.data} ` return state case SWITCH_MODE: diff --git a/main.js b/main.js index f96acb1a..032ac349 100644 --- a/main.js +++ b/main.js @@ -30,8 +30,8 @@ updater .on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) { nn.notify({ title: 'Ready to Update!! ' + versionText, - icon: path.join(__dirname, 'browser/main/resources/favicon-230x230.png'), - message: 'Click tray icon to update app: ' + releaseName + icon: path.join(__dirname, '/resources/favicon-230x230.png'), + message: 'Click update button on Main window: ' + releaseName }) update = quitAndUpdate @@ -50,6 +50,14 @@ app.on('ready', function () { // menu start var template = require('./atom-lib/menu-template') + setInterval(function () { + if (update == null) updater.checkForUpdates() + }, 1000 * 60 * 60 * 24) + + ipc.on('check-update', function (event, msg) { + if (update == null) updater.checkForUpdates() + }) + ipc.on('update-app', function (event, msg) { if (update != null) { appQuit = true diff --git a/package.json b/package.json index f3007e40..32ec18ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "boost", - "version": "0.4.0-beta.2", + "version": "0.4.1-beta", "description": "Boost App", "main": "main.js", "scripts": { diff --git a/webpack.config.js b/webpack.config.js index b9492ffb..ce7cdf5d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -68,7 +68,8 @@ var config = { 'superagent-promise', 'lodash', 'markdown-it', - 'moment' + 'moment', + 'node-notifier' ] } diff --git a/webpack.config.production.js b/webpack.config.production.js index 737e0d13..8eda4cc5 100644 --- a/webpack.config.production.js +++ b/webpack.config.production.js @@ -45,7 +45,8 @@ module.exports = { 'superagent-promise', 'lodash', 'markdown-it', - 'moment' + 'moment', + 'node-notifier' ], resolve: { extensions: ['', '.js', '.jsx', 'styl']