1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 01:36:22 +00:00

move statusbar

This commit is contained in:
Dick Choi
2016-10-15 18:20:13 +09:00
parent 7107777df3
commit 7729ed4f72
17 changed files with 312 additions and 151 deletions

View File

@@ -47,7 +47,23 @@ export default class CodeEditor extends React.Component {
theme: this.props.theme,
indentUnit: this.props.indentSize,
tabSize: this.props.indentSize,
indentWithTabs: this.props.indentType !== 'space'
indentWithTabs: this.props.indentType !== 'space',
keyMap: 'sublime',
extraKeys: {
Tab: function (cm) {
if (cm.somethingSelected()) cm.indentSelection('add')
else {
if (cm.getOption('indentWithTabs')) {
cm.execCommand('insertTab')
} else {
cm.execCommand('insertSoftTab')
}
}
},
'Cmd-T': function (cm) {
// Do nothing
}
}
})
this.setMode(this.props.mode)

View File

@@ -9,6 +9,8 @@ import dataApi from 'browser/main/lib/dataApi'
import { hashHistory } from 'react-router'
import ee from 'browser/main/lib/eventEmitter'
import markdown from 'browser/lib/markdown'
import StatusBar from '../StatusBar'
import _ from 'lodash'
const electron = require('electron')
const { remote } = electron
@@ -279,6 +281,10 @@ class MarkdownNoteDetail extends React.Component {
ignorePreviewPointerEvents={this.props.ignorePreviewPointerEvents}
/>
</div>
<StatusBar
{..._.pick(this.props, ['config', 'location', 'dispatch'])}
/>
</div>
)
}

View File

@@ -1,7 +1,7 @@
$info-height = 75px
.root
absolute top bottom right
absolute top right bottom
border-width 0 0 1px
border-style solid
border-color $ui-borderColor
@@ -61,8 +61,9 @@ $info-height = 75px
border-radius 2px
.body
absolute bottom left right
absolute left right
top $info-height
bottom $statusBar-height
.body-noteEditor
absolute top bottom left right

View File

@@ -11,6 +11,9 @@ import { hashHistory } from 'react-router'
import ee from 'browser/main/lib/eventEmitter'
import CodeMirror from 'codemirror'
import SnippetTab from './SnippetTab'
import StatusBar from '../StatusBar'
import context from 'browser/lib/context'
import ConfigManager from 'browser/main/lib/ConfigManager'
function pass (name) {
switch (name) {
@@ -46,10 +49,6 @@ class SnippetNoteDetail extends React.Component {
}
}
focus () {
this.refs.description.focus()
}
componentWillReceiveProps (nextProps) {
if (nextProps.note.key !== this.props.note.key) {
if (this.saveQueue != null) this.saveNow()
@@ -205,12 +204,10 @@ class SnippetNoteDetail extends React.Component {
}
handleContextButtonClick (e) {
let menu = new Menu()
menu.append(new MenuItem({
context.popup([{
label: 'Delete',
click: (e) => this.handleDeleteMenuClick(e)
}))
menu.popup(remote.getCurrentWindow())
}])
}
handleDeleteMenuClick (e) {
@@ -242,24 +239,6 @@ class SnippetNoteDetail extends React.Component {
this.addSnippet()
}
addSnippet () {
let { note } = this.state
note.snippets = note.snippets.concat([{
name: '',
mode: 'text',
content: ''
}])
let snippetIndex = note.snippets.length - 1
this.setState({
note,
snippetIndex
}, () => {
this.refs['tab-' + snippetIndex].startRenaming()
})
}
handleTabButtonClick (e, index) {
this.setState({
snippetIndex: index
@@ -312,19 +291,6 @@ class SnippetNoteDetail extends React.Component {
})
}
handleModeButtonClick (index) {
return (e) => {
let menu = new Menu()
CodeMirror.modeInfo.forEach((mode) => {
menu.append(new MenuItem({
label: mode.name,
click: (e) => this.handleModeOptionClick(index, mode.name)(e)
}))
})
menu.popup(remote.getCurrentWindow())
}
}
handleModeOptionClick (index, name) {
return (e) => {
let snippets = this.state.note.snippets.slice()
@@ -367,26 +333,123 @@ class SnippetNoteDetail extends React.Component {
}
break
case 76:
let shouldFocus = global.process.platform === 'darwin'
{
let isSuper = global.process.platform === 'darwin'
? e.metaKey
: e.ctrlKey
if (shouldFocus) {
if (isSuper) {
e.preventDefault()
this.focus()
}
}
break
case 84:
{
let shouldFocus = global.process.platform === 'darwin'
let isSuper = global.process.platform === 'darwin'
? e.metaKey
: e.ctrlKey
if (e.shouldFocus) {
if (isSuper) {
e.preventDefault()
this.addSnippet()
}
}
break
}
}
handleModeButtonClick (e, index) {
let menu = new Menu()
CodeMirror.modeInfo.forEach((mode) => {
menu.append(new MenuItem({
label: mode.name,
click: (e) => this.handleModeOptionClick(index, mode.name)(e)
}))
})
menu.popup(remote.getCurrentWindow())
}
handleIndentTypeButtonClick (e) {
context.popup([
{
label: 'tab',
click: (e) => this.handleIndentTypeItemClick(e, 'tab')
},
{
label: 'space',
click: (e) => this.handleIndentTypeItemClick(e, 'space')
}
])
}
handleIndentSizeButtonClick (e) {
context.popup([
{
label: '2',
click: (e) => this.handleIndentSizeItemClick(e, 2)
},
{
label: '4',
click: (e) => this.handleIndentSizeItemClick(e, 4)
},
{
label: '8',
click: (e) => this.handleIndentSizeItemClick(e, 8)
}
])
}
handleIndentSizeItemClick (e, indentSize) {
let { config, dispatch } = this.props
let editor = Object.assign({}, config.editor, {
indentSize
})
ConfigManager.set({
editor
})
dispatch({
type: 'SET_CONFIG',
config: {
editor
}
})
}
handleIndentTypeItemClick (e, indentType) {
let { config, dispatch } = this.props
let editor = Object.assign({}, config.editor, {
indentType
})
ConfigManager.set({
editor
})
dispatch({
type: 'SET_CONFIG',
config: {
editor
}
})
}
focus () {
this.refs.description.focus()
}
addSnippet () {
let { note } = this.state
note.snippets = note.snippets.concat([{
name: '',
mode: 'Plain Text',
content: ''
}])
let snippetIndex = note.snippets.length - 1
this.setState({
note,
snippetIndex
}, () => {
this.refs['tab-' + snippetIndex].startRenaming()
})
}
jumpNextTab () {
@@ -542,6 +605,34 @@ class SnippetNoteDetail extends React.Component {
</div>
{viewList}
</div>
<div styleName='override'>
<button
onClick={(e) => this.handleModeButtonClick(e, this.state.snippetIndex)}
>
{this.state.note.snippets[this.state.snippetIndex].mode == null
? 'Select Syntax...'
: this.state.note.snippets[this.state.snippetIndex].mode
}&nbsp;
<i className='fa fa-caret-down'/>
</button>
<button
onClick={(e) => this.handleIndentTypeButtonClick(e)}
>
Indent: {config.editor.indentType}&nbsp;
<i className='fa fa-caret-down'/>
</button>
<button
onClick={(e) => this.handleIndentSizeButtonClick(e)}
>
size: {config.editor.indentSize}&nbsp;
<i className='fa fa-caret-down'/>
</button>
</div>
<StatusBar
{..._.pick(this.props, ['config', 'location', 'dispatch'])}
/>
</div>
)
}

View File

@@ -59,8 +59,9 @@ $info-height = 75px
opacity 0
.body
absolute bottom left right
absolute left right
top $info-height
bottom $statusBar-height
.body .description
absolute top left right
@@ -101,6 +102,19 @@ $info-height = 75px
.tabView-content
absolute top left right bottom
.override
absolute bottom left
height 23px
z-index 1
button
navButtonColor()
height 24px
border-width 0 1px 0 0
border-style solid
border-color $ui-borderColor
&:active .update-icon
color white
body[data-theme="dark"]
.root
border-color $ui-dark-borderColor
@@ -151,15 +165,3 @@ body[data-theme="dark"]
.tabList .plusButton
navDarkButtonColor()
.tabView-top-mode
border-color $ui-dark-borderColor
background-color $dark-default-button-background
color $ui-dark-inactive-text-color
&:hover
color $ui-dark-text-color
background-color $ui-dark-button--hover-backgroundColor
&:active
background-color $ui-dark-button--active-backgroundColor
&:active:hover
background-color $ui-dark-button--active-backgroundColor

View File

@@ -5,6 +5,7 @@ import _ from 'lodash'
import MarkdownNoteDetail from './MarkdownNoteDetail'
import SnippetNoteDetail from './SnippetNoteDetail'
import ee from 'browser/main/lib/eventEmitter'
import StatusBar from '../StatusBar'
const OSX = global.process.platform === 'darwin'
@@ -50,6 +51,9 @@ class Detail extends React.Component {
<div styleName='empty'>
<div styleName='empty-message'>{OSX ? 'Command(⌘)' : 'Ctrl(^)'} + N<br />to create a new post</div>
</div>
<StatusBar
{..._.pick(this.props, ['config', 'location', 'dispatch'])}
/>
</div>
)
}

View File

@@ -7,7 +7,6 @@ import TopBar from './TopBar'
import NoteList from './NoteList'
import Detail from './Detail'
import dataApi from 'browser/main/lib/dataApi'
import StatusBar from './StatusBar'
import _ from 'lodash'
import ConfigManager from 'browser/main/lib/ConfigManager'
import modal from 'browser/main/lib/modal'
@@ -32,6 +31,15 @@ class Main extends React.Component {
}
}
getChildContext () {
let { status, config } = this.props
return {
status,
config
}
}
componentDidMount () {
let { dispatch, config } = this.props
@@ -202,14 +210,18 @@ class Main extends React.Component {
ignorePreviewPointerEvents={this.state.isRightSliderFocused}
/>
</div>
<StatusBar
{..._.pick(this.props, ['config', 'location', 'dispatch'])}
/>
</div>
)
}
}
Main.childContextTypes = {
status: PropTypes.shape({
updateReady: PropTypes.bool.isRequired
}).isRequired,
config: PropTypes.shape({}).isRequired
}
Main.propTypes = {
dispatch: PropTypes.func,
data: PropTypes.shape({}).isRequired

View File

@@ -2,8 +2,7 @@
absolute top left bottom right
.body
absolute right top
bottom $statusBar-height - 1
absolute right top bottom
left $sideNav-width
.body--expanded

View File

@@ -1,7 +1,6 @@
.root
absolute left bottom
border-top $ui-border
border-bottom $ui-border
top $topBar-height - 1
.control

View File

@@ -45,7 +45,7 @@ class NoteList extends React.Component {
this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000)
ee.on('list:next', this.selectNextNoteHandler)
ee.on('list:prior', this.selectPriorNoteHandler)
ee.on('lost:focus', this.focusHandler)
ee.on('list:focus', this.focusHandler)
}
componentWillReceiveProps (nextProps) {
@@ -63,7 +63,7 @@ class NoteList extends React.Component {
ee.off('list:next', this.selectNextNoteHandler)
ee.off('list:prior', this.selectPriorNoteHandler)
ee.off('lost:focus', this.focusHandler)
ee.off('list:focus', this.focusHandler)
}
componentDidUpdate (prevProps) {

View File

@@ -1,9 +1,7 @@
.root
absolute top left
bottom $statusBar-height - 1
absolute top left bottom
width $sideNav-width
border-right $ui-border
border-bottom $ui-border
background-color $ui-backgroundColor
user-select none
color $ui-text-color

View File

@@ -2,30 +2,31 @@
absolute bottom left right
height $statusBar-height - 1
background-color $ui-backgroundColor
border-top $ui-border
display flex
.pathname
absolute left
.blank
flex 1
.help
navButtonColor()
height 24px
overflow ellipsis
right 185px
line-height 24px
font-size 12px
padding 0 15px
color $ui-inactive-text-color
width 24px
border-width 0 0 0 1px
border-style solid
border-color $ui-borderColor
&:active .update-icon
color white
.zoom
navButtonColor()
absolute right
height 24px
width 60px
border-width 0 1px
border-style solid
border-color $ui-borderColor
.update
navButtonColor()
position absolute
right 60px
height 24px
border-width 0 0 0 1px
border-style solid
@@ -40,12 +41,14 @@ body[data-theme="dark"]
.root
background-color $ui-dark-backgroundColor
.pathname
color $ui-dark-inactive-text-color
.zoom
border-color $ui-dark-borderColor
.help
navButtonColor()
border-color $ui-dark-borderColor
border-left 1px solid $ui-dark-borderColor
.update
navDarkButtonColor()
border-color $ui-dark-borderColor

View File

@@ -4,50 +4,12 @@ import styles from './StatusBar.styl'
import ZoomManager from 'browser/main/lib/ZoomManager'
const electron = require('electron')
const ipc = electron.ipcRenderer
const { remote } = electron
const { remote, ipcRenderer } = electron
const { Menu, MenuItem, dialog } = remote
const zoomOptions = [0.8, 0.9, 1, 1.1, 1.2, 1.3]
function notify (...args) {
return new window.Notification(...args)
}
class StatusBar extends React.Component {
constructor (props) {
super(props)
this.state = {
updateReady: false
}
this.updateReadyHandler = (message) => {
this.setState({
updateReady: true
}, () => {
notify('Update ready!', {
body: 'New Boostnote is ready to be installed.'
})
this.updateApp()
})
}
this.updateFoundHandler = (message) => {
notify('Update found!', {
body: 'Preparing to update...'
})
}
}
componentDidMount () {
ipc.on('update-ready', this.updateReadyHandler)
ipc.on('update-found', this.updateFoundHandler)
}
componentWillUnmount () {
ipc.removeListener('update-ready', this.updateReadyHandler)
ipc.removeListener('update-found', this.updateFoundHandler)
}
updateApp () {
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
@@ -57,7 +19,7 @@ class StatusBar extends React.Component {
})
if (index === 0) {
ipc.send('update-app-confirm')
ipcRenderer.send('update-app-confirm')
}
}
@@ -84,19 +46,22 @@ class StatusBar extends React.Component {
}
render () {
let { config, location } = this.props
let { config, status } = this.context
return (
<div className='StatusBar'
styleName='root'
>
<div styleName='pathname'>{location.pathname + location.search}</div>
{this.state.updateReady
<div styleName='blank' />
{status.updateReady
? <button onClick={this.updateApp} styleName='update'>
<i styleName='update-icon' className='fa fa-cloud-download' /> Ready to Update!
</button>
: null
}
<button styleName='help'>
<i className='fa fa-info-circle' />
</button>
<button styleName='zoom'
onClick={(e) => this.handleZoomButtonClick(e)}
>
@@ -108,6 +73,13 @@ class StatusBar extends React.Component {
}
}
StatusBar.contextTypes = {
status: PropTypes.shape({
updateReady: PropTypes.bool.isRequired
}).isRequired,
config: PropTypes.shape({}).isRequired
}
StatusBar.propTypes = {
config: PropTypes.shape({
zoom: PropTypes.number

View File

@@ -9,12 +9,9 @@ import { syncHistoryWithStore } from 'react-router-redux'
require('./lib/ipcClient')
const electron = require('electron')
const ipc = electron.ipcRenderer
ipc.send('check-update', 'check-update')
window.addEventListener('online', function () {
ipc.send('check-update', 'check-update')
})
const { remote, ipcRenderer } = electron
const { dialog } = remote
document.addEventListener('drop', function (e) {
e.preventDefault()
@@ -32,6 +29,23 @@ if (process.env !== 'production') {
let el = document.getElementById('content')
const history = syncHistoryWithStore(hashHistory, store)
function notify (...args) {
return new window.Notification(...args)
}
function updateApp () {
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Update Boostnote',
detail: 'New Boostnote is ready to be installed.',
buttons: ['Restart & Install', 'Not Now']
})
if (index === 0) {
ipcRenderer.send('update-app-confirm')
}
}
ReactDOM.render((
<Provider store={store}>
<Router history={history}>
@@ -52,4 +66,25 @@ ReactDOM.render((
), el, function () {
let loadingCover = document.getElementById('loadingCover')
loadingCover.parentNode.removeChild(loadingCover)
ipcRenderer.on('update-ready', function () {
store.dispatch({
type: 'UPDATE_AVAILABLE'
})
notify('Update ready!', {
body: 'New Boostnote is ready to be installed.'
})
updateApp()
})
ipcRenderer.on('update-found', function () {
notify('Update found!', {
body: 'Preparing to update...'
})
})
ipcRenderer.send('update-check', 'check-update')
window.addEventListener('online', function () {
ipcRenderer.send('update-check', 'check-update')
})
})

View File

@@ -475,9 +475,24 @@ function config (state = defaultConfig, action) {
return state
}
const defaultStatus = {
updateReady: false
}
function status (state = defaultStatus, action) {
switch (action.type) {
case 'UPDATE_AVAILABLE':
return Object.assign({}, defaultStatus, {
updateReady: true
})
}
return state
}
let reducer = combineReducers({
data,
config,
status,
routing: routerReducer
})

View File

@@ -67,8 +67,15 @@ updater.on('update-downloaded', (info) => {
}
})
ipc.on('update-check', function (event, msg) {
if (isUpdateReady) {
mainWindow.webContents.send('update-ready', 'Update available!')
} else {
checkUpdate()
}
})
ipc.on('update-app-confirm', function (event, msg) {
console.log('confirmed')
if (isUpdateReady) {
mainWindow.removeAllListeners()
updater.install()

View File

@@ -56,6 +56,7 @@
<script src="../node_modules/codemirror/lib/codemirror.js"></script>
<script src="../node_modules/codemirror/mode/meta.js"></script>
<script src="../node_modules/codemirror/addon/mode/loadmode.js"></script>
<script src="../node_modules/codemirror/keymap/sublime.js"></script>
<script src="../compiled/katex.js"></script>
<script src="../compiled/react.js"></script>
<script src="../compiled/react-dom.js"></script>