1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-15 02:36:36 +00:00

Merge branch 'master' into export-yfm

This commit is contained in:
Baptiste Augrain
2020-06-12 15:17:02 +02:00
327 changed files with 21961 additions and 12973 deletions

View File

@@ -2,7 +2,7 @@ import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './ConfigTab.styl'
import ConfigManager from 'browser/main/lib/ConfigManager'
import store from 'browser/main/store'
import { store } from 'browser/main/store'
import PropTypes from 'prop-types'
import _ from 'lodash'
import i18n from 'browser/lib/i18n'
@@ -11,7 +11,7 @@ const electron = require('electron')
const { shell } = electron
const ipc = electron.ipcRenderer
class Blog extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
@@ -20,12 +20,12 @@ class Blog extends React.Component {
}
}
handleLinkClick (e) {
handleLinkClick(e) {
shell.openExternal(e.currentTarget.href)
e.preventDefault()
}
clearMessage () {
clearMessage() {
_.debounce(() => {
this.setState({
BlogAlert: null
@@ -33,30 +33,41 @@ class Blog extends React.Component {
}, 2000)()
}
componentDidMount () {
componentDidMount() {
this.handleSettingDone = () => {
this.setState({BlogAlert: {
type: 'success',
message: i18n.__('Successfully applied!')
}})
this.setState({
BlogAlert: {
type: 'success',
message: i18n.__('Successfully applied!')
}
})
}
this.handleSettingError = (err) => {
this.setState({BlogAlert: {
type: 'error',
message: err.message != null ? err.message : i18n.__('An error occurred!')
}})
this.handleSettingError = err => {
this.setState({
BlogAlert: {
type: 'error',
message:
err.message != null ? err.message : i18n.__('An error occurred!')
}
})
}
this.oldBlog = this.state.config.blog
ipc.addListener('APP_SETTING_DONE', this.handleSettingDone)
ipc.addListener('APP_SETTING_ERROR', this.handleSettingError)
}
handleBlogChange (e) {
handleBlogChange(e) {
const { config } = this.state
config.blog = {
password: !_.isNil(this.refs.passwordInput) ? this.refs.passwordInput.value : config.blog.password,
username: !_.isNil(this.refs.usernameInput) ? this.refs.usernameInput.value : config.blog.username,
token: !_.isNil(this.refs.tokenInput) ? this.refs.tokenInput.value : config.blog.token,
password: !_.isNil(this.refs.passwordInput)
? this.refs.passwordInput.value
: config.blog.password,
username: !_.isNil(this.refs.usernameInput)
? this.refs.usernameInput.value
: config.blog.username,
token: !_.isNil(this.refs.tokenInput)
? this.refs.tokenInput.value
: config.blog.token,
authMethod: this.refs.authMethodDropdown.value,
address: this.refs.addressInput.value,
type: this.refs.typeDropdown.value
@@ -75,7 +86,7 @@ class Blog extends React.Component {
}
}
handleSaveButtonClick (e) {
handleSaveButtonClick(e) {
const newConfig = {
blog: this.state.config.blog
}
@@ -90,36 +101,36 @@ class Blog extends React.Component {
this.props.haveToSave()
}
render () {
const {config, BlogAlert} = this.state
const blogAlertElement = BlogAlert != null
? <p className={`alert ${BlogAlert.type}`}>
{BlogAlert.message}
</p>
: null
render() {
const { config, BlogAlert } = this.state
const blogAlertElement =
BlogAlert != null ? (
<p className={`alert ${BlogAlert.type}`}>{BlogAlert.message}</p>
) : null
return (
<div styleName='root'>
<div styleName='group'>
<div styleName='group-header'>{i18n.__('Blog')}</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
{i18n.__('Blog Type')}
</div>
<div styleName='group-section-label'>{i18n.__('Blog Type')}</div>
<div styleName='group-section-control'>
<select
value={config.blog.type}
ref='typeDropdown'
onChange={(e) => this.handleBlogChange(e)}
onChange={e => this.handleBlogChange(e)}
>
<option value='wordpress' key='wordpress'>{i18n.__('wordpress')}</option>
<option value='wordpress' key='wordpress'>
{i18n.__('wordpress')}
</option>
</select>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Blog Address')}</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleBlogChange(e)}
<input
styleName='group-section-control-input'
onChange={e => this.handleBlogChange(e)}
ref='addressInput'
value={config.blog.address}
type='text'
@@ -127,8 +138,11 @@ class Blog extends React.Component {
</div>
</div>
<div styleName='group-control'>
<button styleName='group-control-rightButton'
onClick={(e) => this.handleSaveButtonClick(e)}>{i18n.__('Save')}
<button
styleName='group-control-rightButton'
onClick={e => this.handleSaveButtonClick(e)}
>
{i18n.__('Save')}
</button>
{blogAlertElement}
</div>
@@ -143,49 +157,59 @@ class Blog extends React.Component {
<select
value={config.blog.authMethod}
ref='authMethodDropdown'
onChange={(e) => this.handleBlogChange(e)}
onChange={e => this.handleBlogChange(e)}
>
<option value='JWT' key='JWT'>{i18n.__('JWT')}</option>
<option value='USER' key='USER'>{i18n.__('USER')}</option>
<option value='JWT' key='JWT'>
{i18n.__('JWT')}
</option>
<option value='USER' key='USER'>
{i18n.__('USER')}
</option>
</select>
</div>
</div>
{ config.blog.authMethod === 'JWT' &&
{config.blog.authMethod === 'JWT' && (
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Token')}</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleBlogChange(e)}
<input
styleName='group-section-control-input'
onChange={e => this.handleBlogChange(e)}
ref='tokenInput'
value={config.blog.token}
type='text' />
type='text'
/>
</div>
</div>
}
{ config.blog.authMethod === 'USER' &&
)}
{config.blog.authMethod === 'USER' && (
<div>
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('UserName')}</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleBlogChange(e)}
<input
styleName='group-section-control-input'
onChange={e => this.handleBlogChange(e)}
ref='usernameInput'
value={config.blog.username}
type='text' />
type='text'
/>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Password')}</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleBlogChange(e)}
<input
styleName='group-section-control-input'
onChange={e => this.handleBlogChange(e)}
ref='passwordInput'
value={config.blog.password}
type='password' />
type='password'
/>
</div>
</div>
</div>
}
)}
</div>
)
}

View File

@@ -1,8 +1,111 @@
@import('./Tab')
.container
display flex
flex-direction column
align-items center
justify-content center
position relative
margin-bottom 2em
margin-left 2em
.box-minmax
width 608px
height 45px
display flex
justify-content space-between
font-size $tab--button-font-size
color $ui-text-color
span first-child
margin-top 18px
padding-right 10px
padding-left 10px
padding-top 8px
position relative
border $ui-borderColor
border-radius 5px
background $ui-backgroundColor
div[id^="secondRow"]
position absolute
z-index 2
left 0
top 0
margin-bottom -42px
.rs-label
margin-left -20px
div[id^="firstRow"]
position absolute
z-index 2
left 0
top 0
margin-bottom -25px
.rs-range
&::-webkit-slider-thumb
margin-top 0px
transform rotate(180deg)
.rs-label
margin-bottom -85px
margin-top 85px
.rs-range
margin-top 29px
width 600px
-webkit-appearance none
&:focus
outline black
&::-webkit-slider-runnable-track
width 100%
height 0.1px
cursor pointer
box-shadow none
background $ui-backgroundColor
border-radius 0px
border 0px solid #010101
cursor none
&::-webkit-slider-thumb
box-shadow none
border 1px solid $ui-borderColor
box-shadow 0px 10px 10px rgba(0, 0, 0, 0.25)
height 32px
width 32px
border-radius 22px
background white
cursor pointer
-webkit-appearance none
margin-top -20px
border-color $ui-default-button-backgroundColor
height 32px
border-top-left-radius 10%
border-top-right-radius 10%
.rs-label
position relative
transform-origin center center
display block
background transparent
border-radius none
line-height 30px
font-weight normal
box-sizing border-box
border none
margin-bottom -5px
margin-top -10px
clear both
float left
height 17px
margin-left -25px
left attr(value)
color $ui-text-color
font-style normal
font-weight normal
line-height normal
font-size $tab--button-font-size
.root
padding 15px
color $ui-text-color
margin-bottom 30px
.group
@@ -14,10 +117,17 @@
.group-header2
font-size 20px
color $ui-text-color
margin-bottom 15px
margin-top 30px
.group-header--sub
@extend .group-header
margin-bottom 10px
.group-header2--sub
@extend .group-header2
margin-bottom 10px
.group-section
margin-bottom 20px
display flex
@@ -128,30 +238,30 @@ colorDarkControl()
background-color $ui-dark-backgroundColor
color $ui-dark-text-color
colorSolarizedDarkControl()
colorThemedControl(theme)
border none
background-color $ui-solarized-dark-button-backgroundColor
color $ui-solarized-dark-text-color
background-color get-theme-var(theme, 'button-backgroundColor')
color get-theme-var(theme, 'text-color')
colorMonokaiControl()
border none
background-color $ui-monokai-button-backgroundColor
color $ui-monokai-text-color
body[data-theme="default"],
body[data-theme="white"]
.root
color $ui-text-color
colorDraculaControl()
border none
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
.group-header2
color $ui-text-color
body[data-theme="dark"]
.root
color $ui-dark-text-color
.group-header
.group-header--sub
color $ui-dark-text-color
border-color $ui-dark-borderColor
.group-header2
.group-header2--sub
color $ui-dark-text-color
.group-section-control-input
@@ -169,85 +279,44 @@ body[data-theme="dark"]
.group-section-control
select, .group-section-control-input
colorDarkControl()
.rs-label
color $ui-dark-text-color
body[data-theme="solarized-dark"]
.root
color $ui-solarized-dark-text-color
apply-theme(theme)
body[data-theme={theme}]
.root
color get-theme-var(theme, 'text-color')
.group-header
color $ui-solarized-dark-text-color
border-color $ui-solarized-dark-borderColor
.group-header
.group-header--sub
color get-theme-var(theme, 'text-color')
border-color get-theme-var(theme, 'borderColor')
.group-header2
color $ui-solarized-dark-text-color
.group-header2
.group-header2--sub
color get-theme-var(theme, 'text-color')
.group-section-control-input
border-color $ui-solarized-dark-borderColor
.group-section-control-input
border-color get-theme-var(theme, 'borderColor')
.group-control
border-color $ui-solarized-dark-borderColor
.group-control-leftButton
colorDarkDefaultButton()
border-color $ui-solarized-dark-borderColor
.group-control-rightButton
colorSolarizedDarkPrimaryButton()
.group-hint
colorSolarizedDarkControl()
.group-section-control
select, .group-section-control-input
colorSolarizedDarkControl()
.group-control
border-color get-theme-var(theme, 'borderColor')
.group-control-leftButton
colorDarkDefaultButton()
border-color get-theme-var(theme, 'borderColor')
.group-control-rightButton
colorThemedPrimaryButton(theme)
.group-hint
colorThemedControl(theme)
.group-section-control
select, .group-section-control-input
colorThemedControl(theme)
.rs-label
color get-theme-var(theme, 'text-color')
body[data-theme="monokai"]
.root
color $ui-monokai-text-color
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
.group-header
color $ui-monokai-text-color
border-color $ui-monokai-borderColor
.group-header2
color $ui-monokai-text-color
.group-section-control-input
border-color $ui-monokai-borderColor
.group-control
border-color $ui-monokai-borderColor
.group-control-leftButton
colorDarkDefaultButton()
border-color $ui-monokai-borderColor
.group-control-rightButton
colorMonokaiPrimaryButton()
.group-hint
colorMonokaiControl()
.group-section-control
select, .group-section-control-input
colorMonokaiControl()
body[data-theme="dracula"]
.root
color $ui-dracula-text-color
.group-header
color $ui-dracula-text-color
border-color $ui-dracula-borderColor
.group-header2
color $ui-dracula-text-color
.group-section-control-input
border-color $ui-dracula-borderColor
.group-control
border-color $ui-dracula-borderColor
.group-control-leftButton
colorDarkDefaultButton()
border-color $ui-dracula-borderColor
.group-control-rightButton
colorDraculaPrimaryButton()
.group-hint
colorDraculaControl()
.group-section-control
select, .group-section-control-input
colorDraculaControl()
for theme in $themes
apply-theme(theme)

View File

@@ -7,52 +7,93 @@ const electron = require('electron')
const { shell } = electron
class Crowdfunding extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
}
this.state = {}
}
handleLinkClick (e) {
handleLinkClick(e) {
shell.openExternal(e.currentTarget.href)
e.preventDefault()
}
render () {
render() {
return (
<div styleName='root'>
<div styleName='header'>{i18n.__('Crowdfunding')}</div>
<div styleName='group-header'>{i18n.__('Crowdfunding')}</div>
<p>{i18n.__('Thank you for using Boostnote!')}</p>
<br />
<p>{i18n.__('We launched IssueHunt which is an issue-based crowdfunding / sourcing platform for open source projects.')}</p>
<p>{i18n.__('Anyone can put a bounty on not only a bug but also on OSS feature requests listed on IssueHunt. Collected funds will be distributed to project owners and contributors.')}</p>
<br />
<p>{i18n.__('### Sustainable Open Source Ecosystem')}</p>
<p>{i18n.__('We discussed about open-source ecosystem and IssueHunt concept with the Boostnote team repeatedly. We actually also discussed with Matz who father of Ruby.')}</p>
<p>{i18n.__('The original reason why we made IssueHunt was to reward our contributors of Boostnote project. Weve got tons of Github stars and hundred of contributors in two years.')}</p>
<p>{i18n.__('We thought that it will be nice if we can pay reward for our contributors.')}</p>
<br />
<p>{i18n.__('### We believe Meritocracy')}</p>
<p>{i18n.__('We think developers who has skill and did great things must be rewarded properly.')}</p>
<p>{i18n.__('OSS projects are used in everywhere on the internet, but no matter how they great, most of owners of those projects need to have another job to sustain their living.')}</p>
<p>
{i18n.__(
'We launched IssueHunt which is an issue-based crowdfunding / sourcing platform for open source projects.'
)}
</p>
<p>
{i18n.__(
'Anyone can put a bounty on not only a bug but also on OSS feature requests listed on IssueHunt. Collected funds will be distributed to project owners and contributors.'
)}
</p>
<div styleName='group-header2--sub'>
{i18n.__('Sustainable Open Source Ecosystem')}
</div>
<p>
{i18n.__(
'We discussed about open-source ecosystem and IssueHunt concept with the Boostnote team repeatedly. We actually also discussed with Matz who father of Ruby.'
)}
</p>
<p>
{i18n.__(
'The original reason why we made IssueHunt was to reward our contributors of Boostnote project. Weve got tons of Github stars and hundred of contributors in two years.'
)}
</p>
<p>
{i18n.__(
'We thought that it will be nice if we can pay reward for our contributors.'
)}
</p>
<div styleName='group-header2--sub'>
{i18n.__('We believe Meritocracy')}
</div>
<p>
{i18n.__(
'We think developers who have skills and do great things must be rewarded properly.'
)}
</p>
<p>
{i18n.__(
'OSS projects are used in everywhere on the internet, but no matter how they great, most of owners of those projects need to have another job to sustain their living.'
)}
</p>
<p>{i18n.__('It sometimes looks like exploitation.')}</p>
<p>{i18n.__('Weve realized IssueHunt could enhance sustainability of open-source ecosystem.')}</p>
<p>
{i18n.__(
'Weve realized IssueHunt could enhance sustainability of open-source ecosystem.'
)}
</p>
<br />
<p>{i18n.__('As same as issues of Boostnote are already funded on IssueHunt, your open-source projects can be also started funding from now.')}</p>
<p>
{i18n.__(
'As same as issues of Boostnote are already funded on IssueHunt, your open-source projects can be also started funding from now.'
)}
</p>
<br />
<p>{i18n.__('Thank you,')}</p>
<p>{i18n.__('The Boostnote Team')}</p>
<br />
<button styleName='cf-link'>
<a href='http://bit.ly/issuehunt-from-boostnote-app' onClick={(e) => this.handleLinkClick(e)}>{i18n.__('See IssueHunt')}</a>
<a
href='http://bit.ly/issuehunt-from-boostnote-app'
onClick={e => this.handleLinkClick(e)}
>
{i18n.__('See IssueHunt')}
</a>
</button>
</div>
)
}
}
Crowdfunding.propTypes = {
}
Crowdfunding.propTypes = {}
export default CSSModules(Crowdfunding, styles)

View File

@@ -1,14 +1,8 @@
@import('./Tab')
@import('./ConfigTab')
.root
padding 15px
white-space pre
line-height 1.4
color alpha($ui-text-color, 90%)
width 100%
font-size 14px
p
font-size 16px
line-height 1.4
.cf-link
height 35px
@@ -30,20 +24,15 @@ body[data-theme="dark"]
p
color $ui-dark-text-color
body[data-theme="solarized-dark"]
.root
color $ui-solarized-dark-text-color
p
color $ui-solarized-dark-text-color
apply-theme(theme)
body[data-theme={theme}]
.root
color get-theme-var(theme, 'text-color')
p
color get-theme-var(theme, 'text-color')
body[data-theme="monokai"]
.root
color $ui-monokai-text-color
p
color $ui-monokai-text-color
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
body[data-theme="dracula"]
.root
color $ui-dracula-text-color
p
color $ui-dracula-text-color
for theme in $themes
apply-theme(theme)

View File

@@ -11,7 +11,7 @@ const electron = require('electron')
const ipc = electron.ipcRenderer
class ExportTab extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
@@ -19,7 +19,7 @@ class ExportTab extends React.Component {
}
}
clearMessage () {
clearMessage() {
_.debounce(() => {
this.setState({
ExportAlert: null
@@ -27,7 +27,7 @@ class ExportTab extends React.Component {
}, 2000)()
}
componentDidMount () {
componentDidMount() {
this.handleSettingDone = () => {
this.setState({
ExportAlert: {
@@ -36,11 +36,12 @@ class ExportTab extends React.Component {
}
})
}
this.handleSettingError = (err) => {
this.handleSettingError = err => {
this.setState({
ExportAlert: {
type: 'error',
message: err.message != null ? err.message : i18n.__('An error occurred!')
message:
err.message != null ? err.message : i18n.__('An error occurred!')
}
})
}
@@ -51,12 +52,12 @@ class ExportTab extends React.Component {
ipc.addListener('APP_SETTING_ERROR', this.handleSettingError)
}
componentWillUnmount () {
componentWillUnmount() {
ipc.removeListener('APP_SETTING_DONE', this.handleSettingDone)
ipc.removeListener('APP_SETTING_ERROR', this.handleSettingError)
}
handleSaveButtonClick (e) {
handleSaveButtonClick(e) {
const newConfig = {
export: this.state.config.export
}
@@ -72,12 +73,14 @@ class ExportTab extends React.Component {
this.props.haveToSave()
}
handleExportChange (e) {
handleExportChange(e) {
const { config } = this.state
config.export = {
metadata: this.refs.metadata.value,
variable: !_.isNil(this.refs.variable) ? this.refs.variable.value : config.export.variable,
variable: !_.isNil(this.refs.variable)
? this.refs.variable.value
: config.export.variable,
prefixAttachmentFolder: this.refs.prefixAttachmentFolder.checked
}
@@ -96,14 +99,13 @@ class ExportTab extends React.Component {
}
}
render () {
render() {
const { config, ExportAlert } = this.state
const ExportAlertElement = ExportAlert != null
? <p className={`alert ${ExportAlert.type}`}>
{ExportAlert.message}
</p>
: null
const ExportAlertElement =
ExportAlert != null ? (
<p className={`alert ${ExportAlert.type}`}>{ExportAlert.message}</p>
) : null
return (
<div styleName='root'>
@@ -111,48 +113,60 @@ class ExportTab extends React.Component {
<div styleName='group-header'>{i18n.__('Export')}</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
{i18n.__('Metadata')}
</div>
<div styleName='group-section-label'>{i18n.__('Metadata')}</div>
<div styleName='group-section-control'>
<select value={config.export.metadata}
onChange={(e) => this.handleExportChange(e)}
<select
value={config.export.metadata}
onChange={e => this.handleExportChange(e)}
ref='metadata'
>
<option value='DONT_EXPORT'>{i18n.__(`Don't export`)}</option>
<option value='MERGE_HEADER'>{i18n.__('Merge with the header')}</option>
<option value='MERGE_VARIABLE'>{i18n.__('Merge with a variable')}</option>
<option value='MERGE_HEADER'>
{i18n.__('Merge with the header')}
</option>
<option value='MERGE_VARIABLE'>
{i18n.__('Merge with a variable')}
</option>
</select>
</div>
</div>
{ config.export.metadata === 'MERGE_VARIABLE' &&
{config.export.metadata === 'MERGE_VARIABLE' && (
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Variable Name')}</div>
<div styleName='group-section-label'>
{i18n.__('Variable Name')}
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleExportChange(e)}
<input
styleName='group-section-control-input'
onChange={e => this.handleExportChange(e)}
ref='variable'
value={config.export.variable}
type='text' />
type='text'
/>
</div>
</div>
}
)}
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleExportChange(e)}
<input
onChange={e => this.handleExportChange(e)}
checked={config.export.prefixAttachmentFolder}
ref='prefixAttachmentFolder'
type='checkbox'
/>&nbsp;
/>
&nbsp;
{i18n.__('Prefix attachment folder')}
</label>
</div>
<div styleName='group-control'>
<button styleName='group-control-rightButton'
onClick={(e) => this.handleSaveButtonClick(e)}>{i18n.__('Save')}
<button
styleName='group-control-rightButton'
onClick={e => this.handleSaveButtonClick(e)}
>
{i18n.__('Save')}
</button>
{ExportAlertElement}
</div>

View File

@@ -4,13 +4,13 @@ import CSSModules from 'browser/lib/CSSModules'
import ReactDOM from 'react-dom'
import styles from './FolderItem.styl'
import dataApi from 'browser/main/lib/dataApi'
import store from 'browser/main/store'
import { store } from 'browser/main/store'
import { SketchPicker } from 'react-color'
import { SortableElement, SortableHandle } from 'react-sortable-hoc'
import i18n from 'browser/lib/i18n'
class FolderItem extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
@@ -24,7 +24,7 @@ class FolderItem extends React.Component {
}
}
handleEditChange (e) {
handleEditChange(e) {
const { folder } = this.state
folder.name = this.refs.nameInput.value
@@ -33,18 +33,18 @@ class FolderItem extends React.Component {
})
}
handleConfirmButtonClick (e) {
handleConfirmButtonClick(e) {
this.confirm()
}
confirm () {
confirm() {
const { storage, folder } = this.props
dataApi
.updateFolder(storage.key, folder.key, {
color: this.state.folder.color,
name: this.state.folder.name
})
.then((data) => {
.then(data => {
store.dispatch({
type: 'UPDATE_FOLDER',
storage: data.storage
@@ -55,9 +55,12 @@ class FolderItem extends React.Component {
})
}
handleColorButtonClick (e) {
const folder = Object.assign({}, this.state.folder, { showColumnPicker: true, colorPickerPos: { left: 0, top: 0 } })
this.setState({ folder }, function () {
handleColorButtonClick(e) {
const folder = Object.assign({}, this.state.folder, {
showColumnPicker: true,
colorPickerPos: { left: 0, top: 0 }
})
this.setState({ folder }, function() {
// After the color picker has been painted, re-calculate its position
// by comparing its dimensions to the host dimensions.
const { hostBoundingBox } = this.props
@@ -67,30 +70,32 @@ class FolderItem extends React.Component {
const folder = Object.assign({}, this.state.folder, {
colorPickerPos: {
left: 25,
top: offsetTop < 0 ? offsetTop - 5 : 0 // subtract 5px for aestetics
top: offsetTop < 0 ? offsetTop - 5 : 0 // subtract 5px for aestetics
}
})
this.setState({ folder })
})
}
handleColorChange (color) {
handleColorChange(color) {
const folder = Object.assign({}, this.state.folder, { color: color.hex })
this.setState({ folder })
}
handleColorPickerClose (event) {
const folder = Object.assign({}, this.state.folder, { showColumnPicker: false })
handleColorPickerClose(event) {
const folder = Object.assign({}, this.state.folder, {
showColumnPicker: false
})
this.setState({ folder })
}
handleCancelButtonClick (e) {
handleCancelButtonClick(e) {
this.setState({
status: 'IDLE'
})
}
handleFolderItemBlur (e) {
handleFolderItemBlur(e) {
let el = e.relatedTarget
while (el != null) {
if (el === this.refs.root) {
@@ -101,7 +106,7 @@ class FolderItem extends React.Component {
this.confirm()
}
renderEdit (e) {
renderEdit(e) {
const popover = { position: 'absolute', zIndex: 2 }
const cover = {
position: 'fixed',
@@ -110,51 +115,64 @@ class FolderItem extends React.Component {
bottom: 0,
left: 0
}
const pickerStyle = Object.assign({}, {
position: 'absolute'
}, this.state.folder.colorPickerPos)
const pickerStyle = Object.assign(
{},
{
position: 'absolute'
},
this.state.folder.colorPickerPos
)
return (
<div styleName='folderItem'
onBlur={(e) => this.handleFolderItemBlur(e)}
<div
styleName='folderItem'
onBlur={e => this.handleFolderItemBlur(e)}
tabIndex='-1'
ref='root'
>
<div styleName='folderItem-left'>
<button styleName='folderItem-left-colorButton' style={{color: this.state.folder.color}}
onClick={(e) => !this.state.folder.showColumnPicker && this.handleColorButtonClick(e)}
<button
styleName='folderItem-left-colorButton'
style={{ color: this.state.folder.color }}
onClick={e =>
!this.state.folder.showColumnPicker &&
this.handleColorButtonClick(e)
}
>
{this.state.folder.showColumnPicker
? <div style={popover}>
<div style={cover}
{this.state.folder.showColumnPicker ? (
<div style={popover}>
<div
style={cover}
onClick={() => this.handleColorPickerClose()}
/>
<div style={pickerStyle}>
<SketchPicker
ref='colorPicker'
color={this.state.folder.color}
onChange={(color) => this.handleColorChange(color)}
onChangeComplete={(color) => this.handleColorChange(color)}
onChange={color => this.handleColorChange(color)}
onChangeComplete={color => this.handleColorChange(color)}
/>
</div>
</div>
: null
}
) : null}
<i className='fa fa-square' />
</button>
<input styleName='folderItem-left-nameInput'
<input
styleName='folderItem-left-nameInput'
value={this.state.folder.name}
ref='nameInput'
onChange={(e) => this.handleEditChange(e)}
onChange={e => this.handleEditChange(e)}
/>
</div>
<div styleName='folderItem-right'>
<button styleName='folderItem-right-confirmButton'
onClick={(e) => this.handleConfirmButtonClick(e)}
<button
styleName='folderItem-right-confirmButton'
onClick={e => this.handleConfirmButtonClick(e)}
>
{i18n.__('Confirm')}
</button>
<button styleName='folderItem-right-button'
onClick={(e) => this.handleCancelButtonClick(e)}
<button
styleName='folderItem-right-button'
onClick={e => this.handleCancelButtonClick(e)}
>
{i18n.__('Cancel')}
</button>
@@ -163,79 +181,85 @@ class FolderItem extends React.Component {
)
}
handleDeleteConfirmButtonClick (e) {
handleDeleteConfirmButtonClick(e) {
const { storage, folder } = this.props
dataApi
.deleteFolder(storage.key, folder.key)
.then((data) => {
store.dispatch({
type: 'DELETE_FOLDER',
storage: data.storage,
folderKey: data.folderKey
})
dataApi.deleteFolder(storage.key, folder.key).then(data => {
store.dispatch({
type: 'DELETE_FOLDER',
storage: data.storage,
folderKey: data.folderKey
})
}
renderDelete () {
return (
<div styleName='folderItem'>
<div styleName='folderItem-left'>
{i18n.__('Are you sure to ')} <span styleName='folderItem-left-danger'>{i18n.__(' delete')}</span> {i18n.__('this folder?')}
</div>
<div styleName='folderItem-right'>
<button styleName='folderItem-right-dangerButton'
onClick={(e) => this.handleDeleteConfirmButtonClick(e)}
>
{i18n.__('Confirm')}
</button>
<button styleName='folderItem-right-button'
onClick={(e) => this.handleCancelButtonClick(e)}
>
{i18n.__('Cancel')}
</button>
</div>
</div>
)
}
handleEditButtonClick (e) {
const { folder: propsFolder } = this.props
const { folder: stateFolder } = this.state
const folder = Object.assign({}, stateFolder, propsFolder)
this.setState({
status: 'EDIT',
folder
}, () => {
this.refs.nameInput.select()
})
}
handleDeleteButtonClick (e) {
renderDelete() {
return (
<div styleName='folderItem'>
<div styleName='folderItem-left'>
{i18n.__('Are you sure to ')}{' '}
<span styleName='folderItem-left-danger'>{i18n.__(' delete')}</span>{' '}
{i18n.__('this folder?')}
</div>
<div styleName='folderItem-right'>
<button
styleName='folderItem-right-dangerButton'
onClick={e => this.handleDeleteConfirmButtonClick(e)}
>
{i18n.__('Confirm')}
</button>
<button
styleName='folderItem-right-button'
onClick={e => this.handleCancelButtonClick(e)}
>
{i18n.__('Cancel')}
</button>
</div>
</div>
)
}
handleEditButtonClick(e) {
const { folder: propsFolder } = this.props
const { folder: stateFolder } = this.state
const folder = Object.assign({}, stateFolder, propsFolder)
this.setState(
{
status: 'EDIT',
folder
},
() => {
this.refs.nameInput.select()
}
)
}
handleDeleteButtonClick(e) {
this.setState({
status: 'DELETE'
})
}
renderIdle () {
renderIdle() {
const { folder } = this.props
return (
<div styleName='folderItem'
onDoubleClick={(e) => this.handleEditButtonClick(e)}
<div
styleName='folderItem'
onDoubleClick={e => this.handleEditButtonClick(e)}
>
<div styleName='folderItem-left'
style={{borderColor: folder.color}}
>
<span styleName='folderItem-left-name'>{folder.name}</span>
<div styleName='folderItem-left' style={{ borderColor: folder.color }}>
<span>{folder.name}</span>
<span styleName='folderItem-left-key'>({folder.key})</span>
</div>
<div styleName='folderItem-right'>
<button styleName='folderItem-right-button'
onClick={(e) => this.handleEditButtonClick(e)}
<button
styleName='folderItem-right-button'
onClick={e => this.handleEditButtonClick(e)}
>
{i18n.__('Edit')}
</button>
<button styleName='folderItem-right-button'
onClick={(e) => this.handleDeleteButtonClick(e)}
<button
styleName='folderItem-right-button'
onClick={e => this.handleDeleteButtonClick(e)}
>
{i18n.__('Delete')}
</button>
@@ -244,7 +268,7 @@ class FolderItem extends React.Component {
)
}
render () {
render() {
switch (this.state.status) {
case 'DELETE':
return this.renderDelete()
@@ -277,7 +301,7 @@ FolderItem.propTypes = {
}
class Handle extends React.Component {
render () {
render() {
return (
<div styleName='folderItem-drag-handle'>
<i className='fa fa-reorder' />
@@ -287,11 +311,11 @@ class Handle extends React.Component {
}
class SortableFolderItemComponent extends React.Component {
render () {
const StyledHandle = CSSModules(Handle, this.props.styles)
render() {
const StyledHandle = CSSModules(Handle, styles)
const DragHandle = SortableHandle(StyledHandle)
const StyledFolderItem = CSSModules(FolderItem, this.props.styles)
const StyledFolderItem = CSSModules(FolderItem, styles)
return (
<div>

View File

@@ -62,7 +62,7 @@
.folderItem-right-button
vertical-align middle
height 25px
margin-top 2.5px
margin-top 2px
colorDefaultButton()
border-radius 2px
border $ui-border
@@ -107,73 +107,32 @@ body[data-theme="dark"]
.folderItem-right-dangerButton
colorDarkDangerButton()
apply-theme(theme)
body[data-theme={theme}]
.folderItem
&:hover
background-color get-theme-var(theme, 'button-backgroundColor')
.folderItem-left-danger
color $danger-color
body[data-theme="solarized-dark"]
.folderItem
&:hover
background-color $ui-solarized-dark-button-backgroundColor
.folderItem-left-key
color $ui-dark-inactive-text-color
.folderItem-left-danger
color $danger-color
.folderItem-left-colorButton
colorThemedPrimaryButton(theme)
.folderItem-left-key
color $ui-dark-inactive-text-color
.folderItem-right-button
colorThemedPrimaryButton(theme)
.folderItem-left-colorButton
colorSolarizedDarkPrimaryButton()
.folderItem-right-confirmButton
colorThemedPrimaryButton(theme)
.folderItem-right-button
colorSolarizedDarkPrimaryButton()
.folderItem-right-dangerButton
colorThemedPrimaryButton(theme)
.folderItem-right-confirmButton
colorSolarizedDarkPrimaryButton()
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
.folderItem-right-dangerButton
colorSolarizedDarkPrimaryButton()
body[data-theme="monokai"]
.folderItem
&:hover
background-color $ui-monokai-button-backgroundColor
.folderItem-left-danger
color $danger-color
.folderItem-left-key
color $ui-dark-inactive-text-color
.folderItem-left-colorButton
colorMonokaiPrimaryButton()
.folderItem-right-button
colorMonokaiPrimaryButton()
.folderItem-right-confirmButton
colorMonokaiPrimaryButton()
.folderItem-right-dangerButton
colorMonokaiPrimaryButton()
body[data-theme="dracula"]
.folderItem
&:hover
background-color $ui-dracula-button-backgroundColor
.folderItem-left-danger
color $danger-color
.folderItem-left-key
color $ui-dark-inactive-text-color
.folderItem-left-colorButton
colorDraculaPrimaryButton()
.folderItem-right-button
colorDraculaPrimaryButton()
.folderItem-right-confirmButton
colorDraculaPrimaryButton()
.folderItem-right-dangerButton
colorDraculaPrimaryButton()
for theme in $themes
apply-theme(theme)

View File

@@ -3,30 +3,34 @@ import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import dataApi from 'browser/main/lib/dataApi'
import styles from './FolderList.styl'
import store from 'browser/main/store'
import { store } from 'browser/main/store'
import FolderItem from './FolderItem'
import { SortableContainer } from 'react-sortable-hoc'
import i18n from 'browser/lib/i18n'
class FolderList extends React.Component {
render () {
render() {
const { storage, hostBoundingBox } = this.props
const folderList = storage.folders.map((folder, index) => {
return <FolderItem key={folder.key}
folder={folder}
storage={storage}
index={index}
hostBoundingBox={hostBoundingBox}
/>
return (
<FolderItem
key={folder.key}
folder={folder}
storage={storage}
index={index}
hostBoundingBox={hostBoundingBox}
/>
)
})
return (
<div styleName='folderList'>
{folderList.length > 0
? folderList
: <div styleName='folderList-empty'>{i18n.__('No Folders')}</div>
}
<div>
{folderList.length > 0 ? (
folderList
) : (
<div styleName='folderList-empty'>{i18n.__('No Folders')}</div>
)}
</div>
)
}
@@ -52,23 +56,21 @@ FolderList.propTypes = {
}
class SortableFolderListComponent extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.onSortEnd = ({oldIndex, newIndex}) => {
this.onSortEnd = ({ oldIndex, newIndex }) => {
const { storage } = this.props
dataApi
.reorderFolder(storage.key, oldIndex, newIndex)
.then((data) => {
store.dispatch({
type: 'REORDER_FOLDER',
storage: data.storage
})
this.setState()
dataApi.reorderFolder(storage.key, oldIndex, newIndex).then(data => {
store.dispatch({
type: 'REORDER_FOLDER',
storage: data.storage
})
this.setState()
})
}
}
render () {
render() {
const StyledFolderList = CSSModules(FolderList, this.props.styles)
const SortableFolderList = SortableContainer(StyledFolderList)

View File

@@ -3,7 +3,7 @@ import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './ConfigTab.styl'
import ConfigManager from 'browser/main/lib/ConfigManager'
import store from 'browser/main/store'
import { store } from 'browser/main/store'
import _ from 'lodash'
import i18n from 'browser/lib/i18n'
@@ -11,7 +11,7 @@ const electron = require('electron')
const ipc = electron.ipcRenderer
class HotkeyTab extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
@@ -20,27 +20,35 @@ class HotkeyTab extends React.Component {
}
}
componentDidMount () {
componentDidMount() {
this.handleSettingDone = () => {
this.setState({keymapAlert: {
type: 'success',
message: i18n.__('Successfully applied!')
}})
}
this.handleSettingError = (err) => {
if (
this.state.config.hotkey.toggleMain === '' ||
this.state.config.hotkey.toggleMode === ''
) {
this.setState({keymapAlert: {
this.setState({
keymapAlert: {
type: 'success',
message: i18n.__('Successfully applied!')
}})
}
})
}
this.handleSettingError = err => {
if (
this.state.config.hotkey.toggleMain === '' ||
this.state.config.hotkey.toggleMode === '' ||
this.state.config.hotkey.toggleDirection === ''
) {
this.setState({
keymapAlert: {
type: 'success',
message: i18n.__('Successfully applied!')
}
})
} else {
this.setState({keymapAlert: {
type: 'error',
message: err.message != null ? err.message : i18n.__('An error occurred!')
}})
this.setState({
keymapAlert: {
type: 'error',
message:
err.message != null ? err.message : i18n.__('An error occurred!')
}
})
}
}
this.oldHotkey = this.state.config.hotkey
@@ -48,12 +56,12 @@ class HotkeyTab extends React.Component {
ipc.addListener('APP_SETTING_ERROR', this.handleSettingError)
}
componentWillUnmount () {
componentWillUnmount() {
ipc.removeListener('APP_SETTING_DONE', this.handleSettingDone)
ipc.removeListener('APP_SETTING_ERROR', this.handleSettingError)
}
handleSaveButtonClick (e) {
handleSaveButtonClick(e) {
const newConfig = {
hotkey: this.state.config.hotkey
}
@@ -68,20 +76,25 @@ class HotkeyTab extends React.Component {
this.props.haveToSave()
}
handleHintToggleButtonClick (e) {
handleHintToggleButtonClick(e) {
this.setState({
isHotkeyHintOpen: !this.state.isHotkeyHintOpen
})
}
handleHotkeyChange (e) {
handleHotkeyChange(e) {
const { config } = this.state
config.hotkey = {
config.hotkey = Object.assign({}, config.hotkey, {
toggleMain: this.refs.toggleMain.value,
toggleMode: this.refs.toggleMode.value,
toggleDirection: this.refs.toggleDirection.value,
deleteNote: this.refs.deleteNote.value,
pasteSmartly: this.refs.pasteSmartly.value
}
pasteSmartly: this.refs.pasteSmartly.value,
prettifyMarkdown: this.refs.prettifyMarkdown.value,
toggleMenuBar: this.refs.toggleMenuBar.value,
insertDate: this.refs.insertDate.value,
insertDateTime: this.refs.insertDateTime.value
})
this.setState({
config
})
@@ -96,7 +109,7 @@ class HotkeyTab extends React.Component {
}
}
clearMessage () {
clearMessage() {
_.debounce(() => {
this.setState({
keymapAlert: null
@@ -104,13 +117,12 @@ class HotkeyTab extends React.Component {
}, 2000)()
}
render () {
render() {
const keymapAlert = this.state.keymapAlert
const keymapAlertElement = keymapAlert != null
? <p className={`alert ${keymapAlert.type}`}>
{keymapAlert.message}
</p>
: null
const keymapAlertElement =
keymapAlert != null ? (
<p className={`alert ${keymapAlert.type}`}>{keymapAlert.message}</p>
) : null
const { config } = this.state
return (
@@ -118,10 +130,13 @@ class HotkeyTab extends React.Component {
<div styleName='group'>
<div styleName='group-header'>{i18n.__('Hotkeys')}</div>
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Show/Hide Boostnote')}</div>
<div styleName='group-section-label'>
{i18n.__('Show/Hide Boostnote')}
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleHotkeyChange(e)}
<input
styleName='group-section-control-input'
onChange={e => this.handleHotkeyChange(e)}
ref='toggleMain'
value={config.hotkey.toggleMain}
type='text'
@@ -129,21 +144,53 @@ class HotkeyTab extends React.Component {
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Toggle Editor Mode')}</div>
<div styleName='group-section-label'>
{i18n.__('Show/Hide Menu Bar')}
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleHotkeyChange(e)}
<input
styleName='group-section-control-input'
onChange={e => this.handleHotkeyChange(e)}
ref='toggleMenuBar'
value={config.hotkey.toggleMenuBar}
type='text'
/>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
{i18n.__('Toggle Editor Mode')}
</div>
<div styleName='group-section-control'>
<input
styleName='group-section-control-input'
onChange={e => this.handleHotkeyChange(e)}
ref='toggleMode'
value={config.hotkey.toggleMode}
type='text'
/>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
{i18n.__('Toggle Direction')}
</div>
<div styleName='group-section-control'>
<input
styleName='group-section-control-input'
onChange={e => this.handleHotkeyChange(e)}
ref='toggleDirection'
value={config.hotkey.toggleDirection}
type='text'
/>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Delete Note')}</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleHotkeyChange(e)}
<input
styleName='group-section-control-input'
onChange={e => this.handleHotkeyChange(e)}
ref='deleteNote'
value={config.hotkey.deleteNote}
type='text'
@@ -153,53 +200,139 @@ class HotkeyTab extends React.Component {
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Paste HTML')}</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleHotkeyChange(e)}
<input
styleName='group-section-control-input'
onChange={e => this.handleHotkeyChange(e)}
ref='pasteSmartly'
value={config.hotkey.pasteSmartly}
type='text'
/>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
{i18n.__('Prettify Markdown')}
</div>
<div styleName='group-section-control'>
<input
styleName='group-section-control-input'
onChange={e => this.handleHotkeyChange(e)}
ref='prettifyMarkdown'
value={config.hotkey.prettifyMarkdown}
type='text'
/>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
{i18n.__('Insert Current Date')}
</div>
<div styleName='group-section-control'>
<input
styleName='group-section-control-input'
ref='insertDate'
value={config.hotkey.insertDate}
type='text'
disabled='true'
/>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
{i18n.__('Insert Current Date and Time')}
</div>
<div styleName='group-section-control'>
<input
styleName='group-section-control-input'
ref='insertDateTime'
value={config.hotkey.insertDateTime}
type='text'
disabled='true'
/>
</div>
</div>
<div styleName='group-control'>
<button styleName='group-control-leftButton'
onClick={(e) => this.handleHintToggleButtonClick(e)}
<button
styleName='group-control-leftButton'
onClick={e => this.handleHintToggleButtonClick(e)}
>
{this.state.isHotkeyHintOpen
? i18n.__('Hide Help')
: i18n.__('Help')
}
: i18n.__('Help')}
</button>
<button styleName='group-control-rightButton'
onClick={(e) => this.handleSaveButtonClick(e)}>{i18n.__('Save')}
<button
styleName='group-control-rightButton'
onClick={e => this.handleSaveButtonClick(e)}
>
{i18n.__('Save')}
</button>
{keymapAlertElement}
</div>
{this.state.isHotkeyHintOpen &&
{this.state.isHotkeyHintOpen && (
<div styleName='group-hint'>
<p>{i18n.__('Available Keys')}</p>
<ul>
<li><code>0</code> to <code>9</code></li>
<li><code>A</code> to <code>Z</code></li>
<li><code>F1</code> to <code>F24</code></li>
<li>Punctuations like <code>~</code>, <code>!</code>, <code>@</code>, <code>#</code>, <code>$</code>, etc.</li>
<li><code>Plus</code></li>
<li><code>Space</code></li>
<li><code>Backspace</code></li>
<li><code>Delete</code></li>
<li><code>Insert</code></li>
<li><code>Return</code> (or <code>Enter</code> as alias)</li>
<li><code>Up</code>, <code>Down</code>, <code>Left</code> and <code>Right</code></li>
<li><code>Home</code> and <code>End</code></li>
<li><code>PageUp</code> and <code>PageDown</code></li>
<li><code>Escape</code> (or <code>Esc</code> for short)</li>
<li><code>VolumeUp</code>, <code>VolumeDown</code> and <code>VolumeMute</code></li>
<li><code>MediaNextTrack</code>, <code>MediaPreviousTrack</code>, <code>MediaStop</code> and <code>MediaPlayPause</code></li>
<li><code>Control</code> (or <code>Ctrl</code> for short)</li>
<li><code>Shift</code></li>
<li>
<code>0</code> to <code>9</code>
</li>
<li>
<code>A</code> to <code>Z</code>
</li>
<li>
<code>F1</code> to <code>F24</code>
</li>
<li>
Punctuations like <code>~</code>, <code>!</code>,{' '}
<code>@</code>, <code>#</code>, <code>$</code>, etc.
</li>
<li>
<code>Plus</code>
</li>
<li>
<code>Space</code>
</li>
<li>
<code>Backspace</code>
</li>
<li>
<code>Delete</code>
</li>
<li>
<code>Insert</code>
</li>
<li>
<code>Return</code> (or <code>Enter</code> as alias)
</li>
<li>
<code>Up</code>, <code>Down</code>, <code>Left</code> and{' '}
<code>Right</code>
</li>
<li>
<code>Home</code> and <code>End</code>
</li>
<li>
<code>PageUp</code> and <code>PageDown</code>
</li>
<li>
<code>Escape</code> (or <code>Esc</code> for short)
</li>
<li>
<code>VolumeUp</code>, <code>VolumeDown</code> and{' '}
<code>VolumeMute</code>
</li>
<li>
<code>MediaNextTrack</code>, <code>MediaPreviousTrack</code>,{' '}
<code>MediaStop</code> and <code>MediaPlayPause</code>
</li>
<li>
<code>Control</code> (or <code>Ctrl</code> for short)
</li>
<li>
<code>Shift</code>
</li>
</ul>
</div>
}
)}
</div>
</div>
)

View File

@@ -2,7 +2,7 @@ import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './InfoTab.styl'
import ConfigManager from 'browser/main/lib/ConfigManager'
import store from 'browser/main/store'
import { store } from 'browser/main/store'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import _ from 'lodash'
import i18n from 'browser/lib/i18n'
@@ -12,7 +12,7 @@ const { shell, remote } = electron
const appVersion = remote.app.getVersion()
class InfoTab extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
@@ -20,18 +20,18 @@ class InfoTab extends React.Component {
}
}
handleLinkClick (e) {
handleLinkClick(e) {
shell.openExternal(e.currentTarget.href)
e.preventDefault()
}
handleConfigChange (e) {
handleConfigChange(e) {
const newConfig = { amaEnabled: this.refs.amaEnabled.checked }
this.setState({ config: newConfig })
}
handleSaveButtonClick (e) {
handleSaveButtonClick(e) {
const newConfig = {
amaEnabled: this.state.config.amaEnabled
}
@@ -43,7 +43,7 @@ class InfoTab extends React.Component {
})
} else {
this.setState({
amaMessage: i18n.__('Thank\'s for trusting us')
amaMessage: i18n.__("Thank's for trusting us")
})
}
@@ -61,62 +61,95 @@ class InfoTab extends React.Component {
})
}
infoMessage () {
toggleAutoUpdate() {
const newConfig = {
autoUpdateEnabled: !this.state.config.autoUpdateEnabled
}
this.setState({ config: newConfig })
ConfigManager.set(newConfig)
}
infoMessage() {
const { amaMessage } = this.state
return amaMessage ? <p styleName='policy-confirm'>{amaMessage}</p> : null
}
render () {
render() {
return (
<div styleName='root'>
<div styleName='header--sub'>{i18n.__('Community')}</div>
<div styleName='group-header'>{i18n.__('Community')}</div>
<div styleName='top'>
<ul styleName='list'>
<li>
<a href='https://issuehunt.io/repos/53266139'
onClick={(e) => this.handleLinkClick(e)}
>{i18n.__('Bounty on IssueHunt')}</a>
<a
href='https://issuehunt.io/repos/53266139'
onClick={e => this.handleLinkClick(e)}
>
{i18n.__('Bounty on IssueHunt')}
</a>
</li>
<li>
<a href='https://boostnote.io/#subscribe'
onClick={(e) => this.handleLinkClick(e)}
>{i18n.__('Subscribe to Newsletter')}</a>
<a
href='https://boostnote.io/#subscribe'
onClick={e => this.handleLinkClick(e)}
>
{i18n.__('Subscribe to Newsletter')}
</a>
</li>
<li>
<a href='https://github.com/BoostIO/Boostnote/issues'
onClick={(e) => this.handleLinkClick(e)}
>{i18n.__('GitHub')}</a>
<a
href='https://github.com/BoostIO/Boostnote/issues'
onClick={e => this.handleLinkClick(e)}
>
{i18n.__('GitHub')}
</a>
</li>
<li>
<a href='https://medium.com/boostnote'
onClick={(e) => this.handleLinkClick(e)}
>{i18n.__('Blog')}</a>
<a
href='https://medium.com/boostnote'
onClick={e => this.handleLinkClick(e)}
>
{i18n.__('Blog')}
</a>
</li>
<li>
<a href='https://www.facebook.com/groups/boostnote'
onClick={(e) => this.handleLinkClick(e)}
>{i18n.__('Facebook Group')}</a>
<a
href='https://www.facebook.com/groups/boostnote'
onClick={e => this.handleLinkClick(e)}
>
{i18n.__('Facebook Group')}
</a>
</li>
<li>
<a href='https://twitter.com/boostnoteapp'
onClick={(e) => this.handleLinkClick(e)}
>{i18n.__('Twitter')}</a>
<a
href='https://twitter.com/boostnoteapp'
onClick={e => this.handleLinkClick(e)}
>
{i18n.__('Twitter')}
</a>
</li>
</ul>
</div>
<hr />
<div styleName='header--sub'>{i18n.__('About')}</div>
<div styleName='group-header--sub'>{i18n.__('About')}</div>
<div styleName='top'>
<div styleName='icon-space'>
<img styleName='icon' src='../resources/app.png' width='92' height='92' />
<img
styleName='icon'
src='../resources/app.png'
width='92'
height='92'
/>
<div styleName='icon-right'>
<div styleName='appId'>{i18n.__('Boostnote')} {appVersion}</div>
<div styleName='appId'>Boostnote Legacy {appVersion}</div>
<div styleName='description'>
{i18n.__('An open source note-taking app made for programmers just like you.')}
{i18n.__(
'An open source note-taking app made for programmers just like you.'
)}
</div>
</div>
</div>
@@ -124,37 +157,71 @@ class InfoTab extends React.Component {
<ul styleName='list'>
<li>
<a href='https://boostnote.io'
onClick={(e) => this.handleLinkClick(e)}
>{i18n.__('Website')}</a>
<a
href='https://boostnote.io'
onClick={e => this.handleLinkClick(e)}
>
{i18n.__('Website')}
</a>
</li>
<li>
<a href='https://github.com/BoostIO/Boostnote/blob/master/docs/build.md'
onClick={(e) => this.handleLinkClick(e)}
>{i18n.__('Development')}</a>{i18n.__(' : Development configurations for Boostnote.')}
</li>
<li styleName='cc'>
{i18n.__('Copyright (C) 2017 - 2018 BoostIO')}
</li>
<li styleName='cc'>
{i18n.__('License: GPL v3')}
<a
href='https://github.com/BoostIO/Boostnote/blob/master/docs/build.md'
onClick={e => this.handleLinkClick(e)}
>
{i18n.__('Development')}
</a>
{i18n.__(' : Development configurations for Boostnote.')}
</li>
<li styleName='cc'>{i18n.__('Copyright (C) 2017 - 2020 BoostIO')}</li>
<li styleName='cc'>{i18n.__('License: GPL v3')}</li>
</ul>
<div>
<label>
<input
type='checkbox'
onChange={this.toggleAutoUpdate.bind(this)}
checked={this.state.config.autoUpdateEnabled}
/>
{i18n.__('Enable Auto Update')}
</label>
</div>
<hr styleName='separate-line' />
<div styleName='policy'>{i18n.__('Analytics')}</div>
<div>{i18n.__('Boostnote collects anonymous data for the sole purpose of improving the application, and strictly does not collect any personal information such the contents of your notes.')}</div>
<div>{i18n.__('You can see how it works on ')}<a href='https://github.com/BoostIO/Boostnote' onClick={(e) => this.handleLinkClick(e)}>GitHub</a>.</div>
<div styleName='group-header2--sub'>{i18n.__('Analytics')}</div>
<div>
{i18n.__(
'Boostnote collects anonymous data for the sole purpose of improving the application, and strictly does not collect any personal information such the contents of your notes.'
)}
</div>
<div>
{i18n.__('You can see how it works on ')}
<a
href='https://github.com/BoostIO/Boostnote'
onClick={e => this.handleLinkClick(e)}
>
GitHub
</a>
.
</div>
<br />
<div>{i18n.__('You can choose to enable or disable this option.')}</div>
<input onChange={(e) => this.handleConfigChange(e)}
<input
onChange={e => this.handleConfigChange(e)}
checked={this.state.config.amaEnabled}
ref='amaEnabled'
type='checkbox'
/>
{i18n.__('Enable analytics to help improve Boostnote')}<br />
<button styleName='policy-submit' onClick={(e) => this.handleSaveButtonClick(e)}>{i18n.__('Save')}</button>
{i18n.__('Enable analytics to help improve Boostnote')}
<br />
<button
styleName='policy-submit'
onClick={e => this.handleSaveButtonClick(e)}
>
{i18n.__('Save')}
</button>
<br />
{this.infoMessage()}
</div>
@@ -162,7 +229,6 @@ class InfoTab extends React.Component {
}
}
InfoTab.propTypes = {
}
InfoTab.propTypes = {}
export default CSSModules(InfoTab, styles)

View File

@@ -1,16 +1,4 @@
@import('./Tab')
.root
padding 15px
white-space pre
line-height 1.4
color alpha($ui-text-color, 90%)
width 100%
font-size 14px
.top
text-align left
margin-bottom 20px
@import('./ConfigTab.styl')
.icon-space
margin 20px 0
@@ -45,13 +33,21 @@
.separate-line
margin 40px 0
.policy
width 100%
font-size 20px
margin-bottom 10px
.policy-submit
margin-top 10px
height 35px
border-radius 2px
border none
background-color alpha(#1EC38B, 90%)
padding-left 20px
padding-right 20px
text-decoration none
color white
font-weight 600
font-size 16px
&:hover
background-color #1EC38B
transition 0.2s
.policy-confirm
margin-top 10px
@@ -60,25 +56,22 @@
body[data-theme="dark"]
.root
color alpha($tab--dark-text-color, 80%)
.appId
color $ui-dark-text-color
body[data-theme="solarized-dark"]
.root
color $ui-solarized-dark-text-color
.list
a
color $ui-solarized-dark-active-color
apply-theme(theme)
body[data-theme={theme}]
.root
color get-theme-var(theme, 'text-color')
.appId
color get-theme-var(theme, 'text-color')
.list
a
color get-theme-var(theme, 'active-color')
body[data-theme="monokai"]
.root
color $ui-monokai-text-color
.list
a
color $ui-monokai-active-color
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
body[data-theme="dracula"]
.root
color $ui-dracula-text-color
.list
a
color $ui-dracula-active-color
for theme in $themes
apply-theme(theme)

View File

@@ -64,102 +64,31 @@ top-bar--height = 50px
margin-top 10px
overflow-y auto
body[data-theme="dark"]
.root
modalDark()
apply-theme(theme)
body[data-theme={theme}]
.root
background-color transparent
.top-bar
background-color transparent
border-color get-theme-var(theme, 'borderColor')
p
color get-theme-var(theme, 'text-color')
.nav
background-color transparent
border-color get-theme-var(theme, 'borderColor')
.nav-button
background-color transparent
color get-theme-var(theme, 'text-color')
&:hover
color get-theme-var(theme, 'text-color')
.top-bar
background-color transparent
border-color #4A4D52
p
color $tab--dark-text-color
.nav-button--active
@extend .nav-button
color get-theme-var(theme, 'button--active-color')
background-color get-theme-var(theme, 'button--active-backgroundColor')
.nav
background-color transparent
border-color $ui-dark-borderColor
for theme in 'dark' 'solarized-dark' 'dracula'
apply-theme(theme)
.nav-button
background-color transparent
color $tab--dark-text-color
&:hover
color $ui-dark-text-color
.nav-button--active
@extend .nav-button
color white
background-color $dark-primary-button-background--active
&:hover
color white
body[data-theme="solarized-dark"]
.root
background-color transparent
.top-bar
background-color transparent
border-color $ui-solarized-dark-borderColor
p
color $ui-solarized-dark-text-color
.nav
background-color transparent
border-color $ui-solarized-dark-borderColor
.nav-button
background-color transparent
color $ui-solarized-dark-text-color
&:hover
color $ui-solarized-dark-text-color
.nav-button--active
@extend .nav-button
color $ui-solarized-dark-button--active-color
background-color $ui-solarized-dark-button--active-backgroundColor
&:hover
color white
body[data-theme="monokai"]
.root
background-color transparent
.top-bar
background-color transparent
border-color $ui-monokai-borderColor
p
color $ui-monokai-text-color
.nav
background-color transparent
border-color $ui-monokai-borderColor
.nav-button
background-color transparent
color $ui-monokai-text-color
&:hover
color $ui-monokai-text-color
.nav-button--active
@extend .nav-button
color $ui-monokai-button--active-color
background-color $ui-monokai-button--active-backgroundColor
&:hover
color white
body[data-theme="dracula"]
.root
background-color transparent
.top-bar
background-color transparent
border-color $ui-dracula-borderColor
p
color $ui-dracula-text-color
.nav
background-color transparent
border-color $ui-dracula-borderColor
.nav-button
background-color transparent
color $ui-dracula-text-color
&:hover
color $ui-dracula-text-color
.nav-button--active
@extend .nav-button
color $ui-dracula-button--active-color
background-color $ui-dracula-button--active-backgroundColor
&:hover
color #f8f8f2
for theme in $themes
apply-theme(theme)

View File

@@ -4,14 +4,21 @@ import _ from 'lodash'
import styles from './SnippetTab.styl'
import CSSModules from 'browser/lib/CSSModules'
import dataApi from 'browser/main/lib/dataApi'
import snippetManager from '../../../lib/SnippetManager'
const defaultEditorFontFamily = ['Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', 'monospace']
const defaultEditorFontFamily = [
'Monaco',
'Menlo',
'Ubuntu Mono',
'Consolas',
'source-code-pro',
'monospace'
]
const buildCMRulers = (rulers, enableRulers) =>
enableRulers ? rulers.map(ruler => ({ column: ruler })) : []
class SnippetEditor extends React.Component {
componentDidMount () {
componentDidMount() {
this.props.onRef(this)
const { rulers, enableRulers } = this.props
this.cm = CodeMirror(this.refs.root, {
@@ -48,36 +55,50 @@ class SnippetEditor extends React.Component {
})
}
componentWillUnmount () {
componentWillUnmount() {
this.props.onRef(undefined)
}
onSnippetChanged (newSnippet) {
onSnippetChanged(newSnippet) {
this.snippet = newSnippet
this.cm.setValue(this.snippet.content)
}
onSnippetNameOrPrefixChanged (newSnippet) {
onSnippetNameOrPrefixChanged(newSnippet) {
this.snippet.name = newSnippet.name
this.snippet.prefix = newSnippet.prefix.toString().replace(/\s/g, '').split(',')
this.snippet.prefix = newSnippet.prefix
.toString()
.replace(/\s/g, '')
.split(',')
this.saveSnippet()
}
saveSnippet () {
dataApi.updateSnippet(this.snippet).catch((err) => { throw err })
saveSnippet() {
dataApi
.updateSnippet(this.snippet)
.then(snippets => snippetManager.assignSnippets(snippets))
.catch(err => {
throw err
})
}
render () {
render() {
const { fontSize } = this.props
let fontFamily = this.props.fontFamily
fontFamily = _.isString(fontFamily) && fontFamily.length > 0
? [fontFamily].concat(defaultEditorFontFamily)
: defaultEditorFontFamily
fontFamily =
_.isString(fontFamily) && fontFamily.length > 0
? [fontFamily].concat(defaultEditorFontFamily)
: defaultEditorFontFamily
return (
<div styleName='SnippetEditor' ref='root' tabIndex='-1' style={{
fontFamily: fontFamily.join(', '),
fontSize: fontSize
}} />
<div
styleName='SnippetEditor'
ref='root'
tabIndex='-1'
style={{
fontFamily: fontFamily.join(', '),
fontSize: fontSize
}}
/>
)
}
}

View File

@@ -7,53 +7,65 @@ import eventEmitter from 'browser/main/lib/eventEmitter'
import context from 'browser/lib/context'
class SnippetList extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
snippets: []
}
}
componentDidMount () {
componentDidMount() {
this.reloadSnippetList()
eventEmitter.on('snippetList:reload', this.reloadSnippetList.bind(this))
}
reloadSnippetList () {
reloadSnippetList() {
dataApi.fetchSnippet().then(snippets => {
this.setState({snippets})
this.setState({ snippets })
this.props.onSnippetSelect(this.props.currentSnippet)
})
}
handleSnippetContextMenu (snippet) {
context.popup([{
label: i18n.__('Delete snippet'),
click: () => this.deleteSnippet(snippet)
}])
handleSnippetContextMenu(snippet) {
context.popup([
{
label: i18n.__('Delete snippet'),
click: () => this.deleteSnippet(snippet)
}
])
}
deleteSnippet (snippet) {
dataApi.deleteSnippet(snippet).then(() => {
this.reloadSnippetList()
this.props.onSnippetDeleted(snippet)
}).catch(err => { throw err })
deleteSnippet(snippet) {
dataApi
.deleteSnippet(snippet)
.then(() => {
this.reloadSnippetList()
this.props.onSnippetDeleted(snippet)
})
.catch(err => {
throw err
})
}
handleSnippetClick (snippet) {
handleSnippetClick(snippet) {
this.props.onSnippetSelect(snippet)
}
createSnippet () {
dataApi.createSnippet().then(() => {
this.reloadSnippetList()
// scroll to end of list when added new snippet
const snippetList = document.getElementById('snippets')
snippetList.scrollTop = snippetList.scrollHeight
}).catch(err => { throw err })
createSnippet() {
dataApi
.createSnippet()
.then(() => {
this.reloadSnippetList()
// scroll to end of list when added new snippet
const snippetList = document.getElementById('snippets')
snippetList.scrollTop = snippetList.scrollHeight
})
.catch(err => {
throw err
})
}
defineSnippetStyleName (snippet) {
defineSnippetStyleName(snippet) {
const { currentSnippet } = this.props
if (currentSnippet == null) {
@@ -67,29 +79,31 @@ class SnippetList extends React.Component {
}
}
render () {
render() {
const { snippets } = this.state
return (
<div styleName='snippet-list'>
<div styleName='group-section'>
<div styleName='group-section-control'>
<button styleName='group-control-button' onClick={() => this.createSnippet()}>
<button
styleName='group-control-button'
onClick={() => this.createSnippet()}
>
<i className='fa fa-plus' /> {i18n.__('New Snippet')}
</button>
</div>
</div>
<ul id='snippets' styleName='snippets'>
{
snippets.map((snippet) => (
<li
styleName={this.defineSnippetStyleName(snippet)}
key={snippet.id}
onContextMenu={() => this.handleSnippetContextMenu(snippet)}
onClick={() => this.handleSnippetClick(snippet)}>
{snippet.name}
</li>
))
}
{snippets.map(snippet => (
<li
styleName={this.defineSnippetStyleName(snippet)}
key={snippet.id}
onContextMenu={() => this.handleSnippetContextMenu(snippet)}
onClick={() => this.handleSnippetClick(snippet)}
>
{snippet.name}
</li>
))}
</ul>
</div>
)

View File

@@ -11,7 +11,7 @@ import copy from 'copy-to-clipboard'
const path = require('path')
class SnippetTab extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
currentSnippet: null
@@ -19,7 +19,7 @@ class SnippetTab extends React.Component {
this.changeDelay = null
}
notify (title, options) {
notify(title, options) {
if (global.process.platform === 'win32') {
options.icon = path.join(
'file://',
@@ -30,7 +30,7 @@ class SnippetTab extends React.Component {
return new window.Notification(title, options)
}
handleSnippetNameOrPrefixChange () {
handleSnippetNameOrPrefixChange() {
clearTimeout(this.changeDelay)
this.changeDelay = setTimeout(() => {
// notify the snippet editor that the name or prefix of snippet has been changed
@@ -39,20 +39,20 @@ class SnippetTab extends React.Component {
}, 500)
}
handleSnippetSelect (snippet) {
handleSnippetSelect(snippet) {
const { currentSnippet } = this.state
if (snippet !== null) {
if (currentSnippet === null || currentSnippet.id !== snippet.id) {
dataApi.fetchSnippet(snippet.id).then(changedSnippet => {
// notify the snippet editor to load the content of the new snippet
this.snippetEditor.onSnippetChanged(changedSnippet)
this.setState({currentSnippet: changedSnippet})
this.setState({ currentSnippet: changedSnippet })
})
}
}
}
onSnippetNameOrPrefixChanged (e, type) {
onSnippetNameOrPrefixChanged(e, type) {
const newSnippet = Object.assign({}, this.state.currentSnippet)
if (type === 'name') {
newSnippet.name = e.target.value
@@ -63,14 +63,14 @@ class SnippetTab extends React.Component {
this.handleSnippetNameOrPrefixChange()
}
handleDeleteSnippet (snippet) {
handleDeleteSnippet(snippet) {
// prevent old snippet still display when deleted
if (snippet.id === this.state.currentSnippet.id) {
this.setState({currentSnippet: null})
this.setState({ currentSnippet: null })
}
}
handleCopySnippet (e) {
handleCopySnippet(e) {
const showCopyNotification = this.props.config.ui.showCopyNotification
copy(this.state.currentSnippet.content)
if (showCopyNotification) {
@@ -81,7 +81,7 @@ class SnippetTab extends React.Component {
}
}
render () {
render() {
const { config, storageKey } = this.props
const { currentSnippet } = this.state
@@ -91,16 +91,23 @@ class SnippetTab extends React.Component {
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
return (
<div styleName='root'>
<div styleName='header'>{i18n.__('Snippets')}</div>
<div styleName='group-header'>{i18n.__('Snippets')}</div>
<SnippetList
onSnippetSelect={this.handleSnippetSelect.bind(this)}
onSnippetDeleted={this.handleDeleteSnippet.bind(this)}
currentSnippet={currentSnippet} />
<div styleName='snippet-detail' style={{visibility: currentSnippet ? 'visible' : 'hidden'}}>
currentSnippet={currentSnippet}
/>
<div
styleName='snippet-detail'
style={{ visibility: currentSnippet ? 'visible' : 'hidden' }}
>
<div styleName='group-section'>
<div styleName='group-section-control'>
<button styleName='group-control-rightButton'
onClick={e => this.handleCopySnippet(e)}>{i18n.__('Copy')}
<button
styleName='group-control-rightButton'
onClick={e => this.handleCopySnippet(e)}
>
{i18n.__('Copy')}
</button>
</div>
</div>
@@ -110,18 +117,26 @@ class SnippetTab extends React.Component {
<input
styleName='group-section-control-input'
value={currentSnippet ? currentSnippet.name : ''}
onChange={e => { this.onSnippetNameOrPrefixChanged(e, 'name') }}
type='text' />
onChange={e => {
this.onSnippetNameOrPrefixChanged(e, 'name')
}}
type='text'
/>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Snippet prefix')}</div>
<div styleName='group-section-label'>
{i18n.__('Snippet prefix')}
</div>
<div styleName='group-section-control'>
<input
styleName='group-section-control-input'
value={currentSnippet ? currentSnippet.prefix : ''}
onChange={e => { this.onSnippetNameOrPrefixChanged(e, 'prefix') }}
type='text' />
onChange={e => {
this.onSnippetNameOrPrefixChanged(e, 'prefix')
}}
type='text'
/>
</div>
</div>
<div styleName='snippet-editor-section'>
@@ -140,7 +155,10 @@ class SnippetTab extends React.Component {
matchingTriples={config.editor.matchingTriples}
explodingPairs={config.editor.explodingPairs}
scrollPastEnd={config.editor.scrollPastEnd}
onRef={ref => { this.snippetEditor = ref }} />
onRef={ref => {
this.snippetEditor = ref
}}
/>
</div>
</div>
</div>
@@ -148,7 +166,6 @@ class SnippetTab extends React.Component {
}
}
SnippetTab.PropTypes = {
}
SnippetTab.PropTypes = {}
export default CSSModules(SnippetTab, styles)

View File

@@ -1,14 +1,5 @@
@import('./Tab')
@import('./ConfigTab')
.root
padding 15px
white-space pre
line-height 1.4
color alpha($ui-text-color, 90%)
width 100%
font-size 14px
.group
margin-bottom 45px
@@ -127,7 +118,7 @@
background darken(#f5f5f5, 5)
.snippet-detail
width 70%
width 67%
height calc(100% - 200px)
position absolute
left 33%
@@ -149,66 +140,25 @@ body[data-theme="default"], body[data-theme="white"]
.snippet-item-selected
background darken($ui-backgroundColor, 5)
body[data-theme="dark"]
.snippets
background $ui-dark-backgroundColor
.snippet-item
color white
&::after
background $ui-dark-borderColor
&:hover
background darken($ui-dark-backgroundColor, 5)
.snippet-item-selected
background darken($ui-dark-backgroundColor, 5)
.snippet-detail
color white
.group-control-button
colorDarkPrimaryButton()
apply-theme(theme)
body[data-theme={theme}]
.snippets
background get-theme-var(theme, 'backgroundColor')
.snippet-item
color get-theme-var(theme, 'text-color')
&::after
background get-theme-var(theme, 'borderColor')
&:hover
background darken(get-theme-var(theme, 'backgroundColor'), 5)
.snippet-item-selected
background darken(get-theme-var(theme, 'backgroundColor'), 5)
.snippet-detail
color get-theme-var(theme, 'text-color')
.group-control-button
colorThemedPrimaryButton(theme)
body[data-theme="solarized-dark"]
.snippets
background $ui-solarized-dark-backgroundColor
.snippet-item
color white
&::after
background $ui-solarized-dark-borderColor
&:hover
background darken($ui-solarized-dark-backgroundColor, 5)
.snippet-item-selected
background darken($ui-solarized-dark-backgroundColor, 5)
.snippet-detail
color white
.group-control-button
colorSolarizedDarkPrimaryButton()
for theme in 'dark' 'solarized-dark' 'dracula'
apply-theme(theme)
body[data-theme="monokai"]
.snippets
background $ui-monokai-backgroundColor
.snippet-item
color White
&::after
background $ui-monokai-borderColor
&:hover
background darken($ui-monokai-backgroundColor, 5)
.snippet-item-selected
background darken($ui-monokai-backgroundColor, 5)
.snippet-detail
color white
.group-control-button
colorMonokaiPrimaryButton()
body[data-theme="dracula"]
.snippets
background $ui-dracula-backgroundColor
.snippet-item
color #f8f8f2
&::after
background $ui-dracula-borderColor
&:hover
background darken($ui-dracula-backgroundColor, 5)
.snippet-item-selected
background darken($ui-dracula-backgroundColor, 5)
.snippet-detail
color #f8f8f2
.group-control-button
colorDraculaPrimaryButton()
for theme in $themes
apply-theme(theme)

View File

@@ -4,7 +4,7 @@ import CSSModules from 'browser/lib/CSSModules'
import styles from './StorageItem.styl'
import consts from 'browser/lib/consts'
import dataApi from 'browser/main/lib/dataApi'
import store from 'browser/main/store'
import { store } from 'browser/main/store'
import FolderList from './FolderList'
import i18n from 'browser/lib/i18n'
@@ -12,7 +12,7 @@ const { shell, remote } = require('electron')
const { dialog } = remote
class StorageItem extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
@@ -20,137 +20,156 @@ class StorageItem extends React.Component {
}
}
handleNewFolderButtonClick (e) {
handleNewFolderButtonClick(e) {
const { storage } = this.props
const input = {
name: i18n.__('New Folder'),
color: consts.FOLDER_COLORS[Math.floor(Math.random() * 7) % 7]
}
dataApi.createFolder(storage.key, input)
.then((data) => {
dataApi
.createFolder(storage.key, input)
.then(data => {
store.dispatch({
type: 'UPDATE_FOLDER',
storage: data.storage
})
})
.catch((err) => {
.catch(err => {
console.error(err)
})
}
handleExternalButtonClick () {
handleExternalButtonClick() {
const { storage } = this.props
shell.showItemInFolder(storage.path)
}
handleUnlinkButtonClick (e) {
handleUnlinkButtonClick(e) {
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: i18n.__('Unlink Storage'),
detail: i18n.__('Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.'),
detail: i18n.__(
'Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.'
),
buttons: [i18n.__('Unlink'), i18n.__('Cancel')]
})
if (index === 0) {
const { storage } = this.props
dataApi.removeStorage(storage.key)
dataApi
.removeStorage(storage.key)
.then(() => {
store.dispatch({
type: 'REMOVE_STORAGE',
storageKey: storage.key
})
})
.catch((err) => {
.catch(err => {
throw err
})
}
}
handleLabelClick (e) {
handleLabelClick(e) {
const { storage } = this.props
this.setState({
isLabelEditing: true,
name: storage.name
}, () => {
this.refs.label.focus()
})
this.setState(
{
isLabelEditing: true,
name: storage.name
},
() => {
this.refs.label.focus()
}
)
}
handleLabelChange (e) {
handleLabelChange(e) {
this.setState({
name: this.refs.label.value
})
}
handleLabelBlur (e) {
handleLabelBlur(e) {
const { storage } = this.props
dataApi
.renameStorage(storage.key, this.state.name)
.then((_storage) => {
store.dispatch({
type: 'RENAME_STORAGE',
storage: _storage
})
this.setState({
isLabelEditing: false
})
dataApi.renameStorage(storage.key, this.state.name).then(_storage => {
store.dispatch({
type: 'RENAME_STORAGE',
storage: _storage
})
this.setState({
isLabelEditing: false
})
})
}
render () {
render() {
const { storage, hostBoundingBox } = this.props
return (
<div styleName='root' key={storage.key}>
<div styleName='header'>
{this.state.isLabelEditing
? <div styleName='header-label--edit'>
<input styleName='header-label-input'
{this.state.isLabelEditing ? (
<div styleName='header-label--edit'>
<input
styleName='header-label-input'
value={this.state.name}
ref='label'
onChange={(e) => this.handleLabelChange(e)}
onBlur={(e) => this.handleLabelBlur(e)}
onChange={e => this.handleLabelChange(e)}
onBlur={e => this.handleLabelBlur(e)}
/>
</div>
: <div styleName='header-label'
onClick={(e) => this.handleLabelClick(e)}
) : (
<div
styleName='header-label'
onClick={e => this.handleLabelClick(e)}
>
<i className='fa fa-folder-open' />&nbsp;
<i className='fa fa-folder-open' />
&nbsp;
{storage.name}&nbsp;
<span styleName='header-label-path'>({storage.path})</span>&nbsp;
<i styleName='header-label-editButton' className='fa fa-pencil' />
</div>
}
)}
<div styleName='header-control'>
<button styleName='header-control-button'
onClick={(e) => this.handleNewFolderButtonClick(e)}
<button
styleName='header-control-button'
onClick={e => this.handleNewFolderButtonClick(e)}
>
<i className='fa fa-plus' />
<span styleName='header-control-button-tooltip'
style={{left: -20}}
>{i18n.__('Add Folder')}</span>
<span
styleName='header-control-button-tooltip'
style={{ left: -20 }}
>
{i18n.__('Add Folder')}
</span>
</button>
<button styleName='header-control-button'
onClick={(e) => this.handleExternalButtonClick(e)}
<button
styleName='header-control-button'
onClick={e => this.handleExternalButtonClick(e)}
>
<i className='fa fa-external-link' />
<span styleName='header-control-button-tooltip'
style={{left: -50}}
>{i18n.__('Open Storage folder')}</span>
<span
styleName='header-control-button-tooltip'
style={{ left: -50 }}
>
{i18n.__('Open Storage folder')}
</span>
</button>
<button styleName='header-control-button'
onClick={(e) => this.handleUnlinkButtonClick(e)}
<button
styleName='header-control-button'
onClick={e => this.handleUnlinkButtonClick(e)}
>
<i className='fa fa-unlink' />
<span styleName='header-control-button-tooltip'
style={{left: -10}}
>{i18n.__('Unlink')}</span>
<span
styleName='header-control-button-tooltip'
style={{ left: -10 }}
>
{i18n.__('Unlink')}
</span>
</button>
</div>
</div>
<FolderList storage={storage}
hostBoundingBox={hostBoundingBox}
/>
<FolderList storage={storage} hostBoundingBox={hostBoundingBox} />
</div>
)
}

View File

@@ -101,4 +101,19 @@ body[data-theme="solarized-dark"]
.header-control-button
border-color $ui-solarized-dark-button-backgroundColor
background-color $ui-solarized-dark-button-backgroundColor
color $ui-solarized-dark-text-color
color $ui-solarized-dark-text-color
apply-theme(theme)
body[data-theme={theme}]
.header
border-color get-theme-var(theme, 'borderColor')
.header-control-button
colorThemedPrimaryButton(theme)
border-color get-theme-var(theme, 'borderColor')
for theme in 'dracula'
apply-theme(theme)
for theme in $themes
apply-theme(theme)

View File

@@ -3,30 +3,36 @@ import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './StoragesTab.styl'
import dataApi from 'browser/main/lib/dataApi'
import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
import StorageItem from './StorageItem'
import i18n from 'browser/lib/i18n'
import { humanFileSize } from 'browser/lib/utils'
import fs from 'fs'
const electron = require('electron')
const { shell, remote } = electron
function browseFolder () {
function browseFolder() {
const dialog = remote.dialog
const defaultPath = remote.app.getPath('home')
return new Promise((resolve, reject) => {
dialog.showOpenDialog({
title: i18n.__('Select Directory'),
defaultPath,
properties: ['openDirectory', 'createDirectory']
}, function (targetPaths) {
if (targetPaths == null) return resolve('')
resolve(targetPaths[0])
})
dialog.showOpenDialog(
{
title: i18n.__('Select Directory'),
defaultPath,
properties: ['openDirectory', 'createDirectory']
},
function(targetPaths) {
if (targetPaths == null) return resolve('')
resolve(targetPaths[0])
}
)
})
}
class StoragesTab extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
@@ -35,60 +41,148 @@ class StoragesTab extends React.Component {
name: 'Unnamed',
type: 'FILESYSTEM',
path: ''
}
},
attachments: []
}
this.loadAttachmentStorage()
}
handleAddStorageButton (e) {
this.setState({
page: 'ADD_STORAGE',
newStorage: {
name: 'Unnamed',
type: 'FILESYSTEM',
path: ''
}
}, () => {
this.refs.addStorageName.select()
loadAttachmentStorage() {
const promises = []
this.props.data.noteMap.map(note => {
const promise = attachmentManagement.getAttachmentsPathAndStatus(
note.content,
note.storage,
note.key
)
if (promise) promises.push(promise)
})
Promise.all(promises)
.then(data => {
const result = data.reduce((acc, curr) => acc.concat(curr), [])
this.setState({ attachments: result })
})
.catch(console.error)
}
handleLinkClick (e) {
handleAddStorageButton(e) {
this.setState(
{
page: 'ADD_STORAGE',
newStorage: {
name: 'Unnamed',
type: 'FILESYSTEM',
path: ''
}
},
() => {
this.refs.addStorageName.select()
}
)
}
handleLinkClick(e) {
shell.openExternal(e.currentTarget.href)
e.preventDefault()
}
renderList () {
const { data, boundingBox } = this.props
handleRemoveUnusedAttachments(attachments) {
attachmentManagement
.removeAttachmentsByPaths(attachments)
.then(() => this.loadAttachmentStorage())
.catch(console.error)
}
if (!boundingBox) { return null }
const storageList = data.storageMap.map((storage) => {
return <StorageItem
key={storage.key}
storage={storage}
hostBoundingBox={boundingBox}
/>
renderList() {
const { data, boundingBox } = this.props
const { attachments } = this.state
const unusedAttachments = attachments.filter(
attachment => !attachment.isInUse
)
const inUseAttachments = attachments.filter(
attachment => attachment.isInUse
)
const totalUnusedAttachments = unusedAttachments.length
const totalInuseAttachments = inUseAttachments.length
const totalAttachments = totalUnusedAttachments + totalInuseAttachments
const totalUnusedAttachmentsSize = unusedAttachments.reduce((acc, curr) => {
const stats = fs.statSync(curr.path)
const fileSizeInBytes = stats.size
return acc + fileSizeInBytes
}, 0)
const totalInuseAttachmentsSize = inUseAttachments.reduce((acc, curr) => {
const stats = fs.statSync(curr.path)
const fileSizeInBytes = stats.size
return acc + fileSizeInBytes
}, 0)
const totalAttachmentsSize =
totalUnusedAttachmentsSize + totalInuseAttachmentsSize
const unusedAttachmentPaths = unusedAttachments.reduce(
(acc, curr) => acc.concat(curr.path),
[]
)
if (!boundingBox) {
return null
}
const storageList = data.storageMap.map(storage => {
return (
<StorageItem
key={storage.key}
storage={storage}
hostBoundingBox={boundingBox}
/>
)
})
return (
<div styleName='list'>
<div styleName='header'>{i18n.__('Storage Locations')}</div>
{storageList.length > 0
? storageList
: <div styleName='list-empty'>{i18n.__('No storage found.')}</div>
}
{storageList.length > 0 ? (
storageList
) : (
<div styleName='list-empty'>{i18n.__('No storage found.')}</div>
)}
<div styleName='list-control'>
<button styleName='list-control-addStorageButton'
onClick={(e) => this.handleAddStorageButton(e)}
<button
styleName='list-control-addStorageButton'
onClick={e => this.handleAddStorageButton(e)}
>
<i className='fa fa-plus' /> {i18n.__('Add Storage Location')}
</button>
</div>
<div styleName='header'>{i18n.__('Attachment storage')}</div>
<p styleName='list-attachment-label'>
Unused attachments size: {humanFileSize(totalUnusedAttachmentsSize)} (
{totalUnusedAttachments} items)
</p>
<p styleName='list-attachment-label'>
In use attachments size: {humanFileSize(totalInuseAttachmentsSize)} (
{totalInuseAttachments} items)
</p>
<p styleName='list-attachment-label'>
Total attachments size: {humanFileSize(totalAttachmentsSize)} (
{totalAttachments} items)
</p>
<button
styleName='list-attachement-clear-button'
onClick={() =>
this.handleRemoveUnusedAttachments(unusedAttachmentPaths)
}
>
{i18n.__('Clear unused attachments')}
</button>
</div>
)
}
handleAddStorageBrowseButtonClick (e) {
handleAddStorageBrowseButtonClick(e) {
browseFolder()
.then((targetPath) => {
.then(targetPath => {
if (targetPath.length > 0) {
const { newStorage } = this.state
newStorage.path = targetPath
@@ -97,13 +191,13 @@ class StoragesTab extends React.Component {
})
}
})
.catch((err) => {
.catch(err => {
console.error('BrowseFAILED')
console.error(err)
})
}
handleAddStorageChange (e) {
handleAddStorageChange(e) {
const { newStorage } = this.state
newStorage.name = this.refs.addStorageName.value
newStorage.path = this.refs.addStoragePath.value
@@ -112,13 +206,13 @@ class StoragesTab extends React.Component {
})
}
handleAddStorageCreateButton (e) {
handleAddStorageCreateButton(e) {
dataApi
.addStorage({
name: this.state.newStorage.name,
path: this.state.newStorage.path
})
.then((data) => {
.then(data => {
const { dispatch } = this.props
dispatch({
type: 'ADD_STORAGE',
@@ -131,37 +225,39 @@ class StoragesTab extends React.Component {
})
}
handleAddStorageCancelButton (e) {
handleAddStorageCancelButton(e) {
this.setState({
page: 'LIST'
})
}
renderAddStorage () {
renderAddStorage() {
return (
<div styleName='addStorage'>
<div styleName='addStorage-header'>{i18n.__('Add Storage')}</div>
<div styleName='addStorage-body'>
<div styleName='addStorage-body-section'>
<div styleName='addStorage-body-section-label'>
{i18n.__('Name')}
</div>
<div styleName='addStorage-body-section-name'>
<input styleName='addStorage-body-section-name-input'
<input
styleName='addStorage-body-section-name-input'
ref='addStorageName'
value={this.state.newStorage.name}
onChange={(e) => this.handleAddStorageChange(e)}
onChange={e => this.handleAddStorageChange(e)}
/>
</div>
</div>
<div styleName='addStorage-body-section'>
<div styleName='addStorage-body-section-label'>{i18n.__('Type')}</div>
<div styleName='addStorage-body-section-label'>
{i18n.__('Type')}
</div>
<div styleName='addStorage-body-section-type'>
<select styleName='addStorage-body-section-type-select'
<select
styleName='addStorage-body-section-type-select'
value={this.state.newStorage.type}
readOnly
>
@@ -169,25 +265,31 @@ class StoragesTab extends React.Component {
</select>
<div styleName='addStorage-body-section-type-description'>
{i18n.__('Setting up 3rd-party cloud storage integration:')}{' '}
<a href='https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup'
onClick={(e) => this.handleLinkClick(e)}
>{i18n.__('Cloud-Syncing-and-Backup')}</a>
<a
href='https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup'
onClick={e => this.handleLinkClick(e)}
>
{i18n.__('Cloud-Syncing-and-Backup')}
</a>
</div>
</div>
</div>
<div styleName='addStorage-body-section'>
<div styleName='addStorage-body-section-label'>{i18n.__('Location')}
<div styleName='addStorage-body-section-label'>
{i18n.__('Location')}
</div>
<div styleName='addStorage-body-section-path'>
<input styleName='addStorage-body-section-path-input'
<input
styleName='addStorage-body-section-path-input'
ref='addStoragePath'
placeholder={i18n.__('Select Folder')}
value={this.state.newStorage.path}
onChange={(e) => this.handleAddStorageChange(e)}
onChange={e => this.handleAddStorageChange(e)}
/>
<button styleName='addStorage-body-section-path-button'
onClick={(e) => this.handleAddStorageBrowseButtonClick(e)}
<button
styleName='addStorage-body-section-path-button'
onClick={e => this.handleAddStorageBrowseButtonClick(e)}
>
...
</button>
@@ -195,21 +297,25 @@ class StoragesTab extends React.Component {
</div>
<div styleName='addStorage-body-control'>
<button styleName='addStorage-body-control-createButton'
onClick={(e) => this.handleAddStorageCreateButton(e)}
>{i18n.__('Add')}</button>
<button styleName='addStorage-body-control-cancelButton'
onClick={(e) => this.handleAddStorageCancelButton(e)}
>{i18n.__('Cancel')}</button>
<button
styleName='addStorage-body-control-createButton'
onClick={e => this.handleAddStorageCreateButton(e)}
>
{i18n.__('Add')}
</button>
<button
styleName='addStorage-body-control-cancelButton'
onClick={e => this.handleAddStorageCancelButton(e)}
>
{i18n.__('Cancel')}
</button>
</div>
</div>
</div>
)
}
renderContent () {
renderContent() {
switch (this.state.page) {
case 'ADD_STORAGE':
case 'ADD_FOLDER':
@@ -220,12 +326,8 @@ class StoragesTab extends React.Component {
}
}
render () {
return (
<div styleName='root'>
{this.renderContent()}
</div>
)
render() {
return <div styleName='root'>{this.renderContent()}</div>
}
}

View File

@@ -1,8 +1,4 @@
@import('./Tab')
.root
padding 15px
color $ui-text-color
@import('./ConfigTab')
.list
margin-bottom 15px
@@ -37,6 +33,17 @@
colorDefaultButton()
font-size $tab--button-font-size
border-radius 2px
.list-attachment-label
margin-bottom 10px
color $ui-text-color
.list-attachement-clear-button
height 30px
border none
border-top-right-radius 2px
border-bottom-right-radius 2px
colorPrimaryButton()
vertical-align middle
padding 0 20px
.addStorage
margin-bottom 15px
@@ -158,119 +165,52 @@ body[data-theme="dark"]
.addStorage-body-control-cancelButton
colorDarkDefaultButton()
border-color $ui-dark-borderColor
body[data-theme="solarized-dark"]
.root
color $ui-solarized-dark-text-color
.folderList-item
border-bottom $ui-solarized-dark-borderColor
.folderList-empty
color $ui-solarized-dark-text-color
.list-empty
color $ui-solarized-dark-text-color
.list-control-addStorageButton
border-color $ui-solarized-dark-button-backgroundColor
background-color $ui-solarized-dark-button-backgroundColor
color $ui-solarized-dark-text-color
.addStorage-header
color $ui-solarized-dark-text-color
border-color $ui-solarized-dark-borderColor
.addStorage-body-section-name-input
border-color $$ui-solarized-dark-borderColor
.addStorage-body-section-type-description
color $ui-solarized-dark-text-color
.addStorage-body-section-path-button
colorPrimaryButton()
.addStorage-body-control
border-color $ui-solarized-dark-borderColor
.addStorage-body-control-createButton
.list-attachement-clear-button
colorDarkPrimaryButton()
.addStorage-body-control-cancelButton
colorDarkDefaultButton()
border-color $ui-solarized-dark-borderColor
body[data-theme="monokai"]
.root
color $ui-monokai-text-color
apply-theme(theme)
body[data-theme={theme}]
.root
color get-theme-var(theme, 'text-color')
.folderList-item
border-bottom $ui-monokai-borderColor
.folderList-item
border-bottom get-theme-var(theme, 'borderColor')
.folderList-empty
color $ui-monokai-text-color
.folderList-empty
color get-theme-var(theme, 'text-color')
.list-empty
color $ui-monokai-text-color
.list-control-addStorageButton
border-color $ui-monokai-button-backgroundColor
background-color $ui-monokai-button-backgroundColor
color $ui-monokai-text-color
.list-empty
color get-theme-var(theme, 'text-color')
.list-control-addStorageButton
border-color get-theme-var(theme, 'button-backgroundColor')
background-color get-theme-var(theme, 'button-backgroundColor')
color get-theme-var(theme, 'text-color')
.addStorage-header
color $ui-monokai-text-color
border-color $ui-monokai-borderColor
.addStorage-header
color get-theme-var(theme, 'text-color')
border-color get-theme-var(theme, 'borderColor')
.addStorage-body-section-name-input
border-color $$ui-monokai-borderColor
.addStorage-body-section-name-input
border-color $get-theme-var(theme, 'borderColor')
.addStorage-body-section-type-description
color $ui-monokai-text-color
.addStorage-body-section-type-description
color get-theme-var(theme, 'text-color')
.addStorage-body-section-path-button
colorPrimaryButton()
.addStorage-body-control
border-color $ui-monokai-borderColor
.addStorage-body-section-path-button
colorPrimaryButton()
.addStorage-body-control
border-color get-theme-var(theme, 'borderColor')
.addStorage-body-control-createButton
colorDarkPrimaryButton()
.addStorage-body-control-cancelButton
colorDarkDefaultButton()
border-color $ui-monokai-borderColor
.addStorage-body-control-createButton
colorDarkPrimaryButton()
.addStorage-body-control-cancelButton
colorDarkDefaultButton()
border-color get-theme-var(theme, 'borderColor')
.list-attachement-clear-button
colorThemedPrimaryButton(theme)
body[data-theme="dracula"]
.root
color $ui-dracula-text-color
for theme in 'solarized-dark' 'dracula'
apply-theme(theme)
.folderList-item
border-bottom $ui-dracula-borderColor
.folderList-empty
color $ui-dracula-text-color
.list-empty
color $ui-dracula-text-color
.list-control-addStorageButton
border-color $ui-dracula-button-backgroundColor
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
.addStorage-header
color $ui-dracula-text-color
border-color $ui-dracula-borderColor
.addStorage-body-section-name-input
border-color $$ui-dracula-borderColor
.addStorage-body-section-type-description
color $ui-dracula-text-color
.addStorage-body-section-path-button
colorPrimaryButton()
.addStorage-body-control
border-color $ui-dracula-borderColor
.addStorage-body-control-createButton
colorDarkPrimaryButton()
.addStorage-body-control-cancelButton
colorDarkDefaultButton()
border-color $ui-dracula-borderColor
for theme in $themes
apply-theme(theme)

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@ import _ from 'lodash'
import i18n from 'browser/lib/i18n'
class Preferences extends React.Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
@@ -29,44 +29,39 @@ class Preferences extends React.Component {
}
}
componentDidMount () {
componentDidMount() {
this.refs.root.focus()
const boundingBox = this.getContentBoundingBox()
this.setState({ boundingBox })
}
switchTeam (teamId) {
this.setState({currentTeamId: teamId})
switchTeam(teamId) {
this.setState({ currentTeamId: teamId })
}
handleNavButtonClick (tab) {
return (e) => {
this.setState({currentTab: tab})
handleNavButtonClick(tab) {
return e => {
this.setState({ currentTab: tab })
}
}
handleEscButtonClick () {
handleEscButtonClick() {
this.props.close()
}
renderContent () {
renderContent() {
const { boundingBox } = this.state
const { dispatch, config, data } = this.props
switch (this.state.currentTab) {
case 'INFO':
return (
<InfoTab
dispatch={dispatch}
config={config}
/>
)
return <InfoTab dispatch={dispatch} config={config} />
case 'HOTKEY':
return (
<HotkeyTab
dispatch={dispatch}
config={config}
haveToSave={alert => this.setState({HotkeyAlert: alert})}
haveToSave={alert => this.setState({ HotkeyAlert: alert })}
/>
)
case 'UI':
@@ -74,19 +69,17 @@ class Preferences extends React.Component {
<UiTab
dispatch={dispatch}
config={config}
haveToSave={alert => this.setState({UIAlert: alert})}
haveToSave={alert => this.setState({ UIAlert: alert })}
/>
)
case 'CROWDFUNDING':
return (
<Crowdfunding />
)
return <Crowdfunding />
case 'BLOG':
return (
<Blog
dispatch={dispatch}
config={config}
haveToSave={alert => this.setState({BlogAlert: alert})}
haveToSave={alert => this.setState({ BlogAlert: alert })}
/>
)
case 'EXPORT':
@@ -95,17 +88,11 @@ class Preferences extends React.Component {
dispatch={dispatch}
config={config}
data={data}
haveToSave={alert => this.setState({ExportAlert: alert})}
haveToSave={alert => this.setState({ ExportAlert: alert })}
/>
)
case 'SNIPPET':
return (
<SnippetTab
dispatch={dispatch}
config={config}
data={data}
/>
)
return <SnippetTab dispatch={dispatch} config={config} data={data} />
case 'STORAGES':
default:
return (
@@ -118,68 +105,74 @@ class Preferences extends React.Component {
}
}
handleKeyDown (e) {
handleKeyDown(e) {
if (e.keyCode === 27) {
this.props.close()
}
}
getContentBoundingBox () {
getContentBoundingBox() {
return this.refs.content.getBoundingClientRect()
}
haveToSaveNotif (type, message) {
return (
<p styleName={`saving--${type}`}>{message}</p>
)
haveToSaveNotif(type, message) {
return <p styleName={`saving--${type}`}>{message}</p>
}
render () {
render() {
const content = this.renderContent()
const tabs = [
{target: 'STORAGES', label: i18n.__('Storage')},
{target: 'HOTKEY', label: i18n.__('Hotkeys'), Hotkey: this.state.HotkeyAlert},
{target: 'UI', label: i18n.__('Interface'), UI: this.state.UIAlert},
{target: 'INFO', label: i18n.__('About')},
{target: 'CROWDFUNDING', label: i18n.__('Crowdfunding')},
{target: 'BLOG', label: i18n.__('Blog'), Blog: this.state.BlogAlert},
{target: 'EXPORT', label: i18n.__('Export'), Export: this.state.ExportAlert},
{target: 'SNIPPET', label: i18n.__('Snippets')}
{ target: 'STORAGES', label: i18n.__('Storage') },
{
target: 'HOTKEY',
label: i18n.__('Hotkeys'),
Hotkey: this.state.HotkeyAlert
},
{ target: 'UI', label: i18n.__('Interface'), UI: this.state.UIAlert },
{ target: 'INFO', label: i18n.__('About') },
{ target: 'CROWDFUNDING', label: i18n.__('Crowdfunding') },
{ target: 'BLOG', label: i18n.__('Blog'), Blog: this.state.BlogAlert },
{
target: 'EXPORT',
label: i18n.__('Export'),
Export: this.state.ExportAlert
},
{ target: 'SNIPPET', label: i18n.__('Snippets') }
]
const navButtons = tabs.map((tab) => {
const navButtons = tabs.map(tab => {
const isActive = this.state.currentTab === tab.target
const isUiHotkeyTab = _.isObject(tab[tab.label]) && tab.label === tab[tab.label].tab
const isUiHotkeyTab =
_.isObject(tab[tab.label]) && tab.label === tab[tab.label].tab
return (
<button styleName={isActive
? 'nav-button--active'
: 'nav-button'
}
<button
styleName={isActive ? 'nav-button--active' : 'nav-button'}
key={tab.target}
onClick={(e) => this.handleNavButtonClick(tab.target)(e)}
onClick={e => this.handleNavButtonClick(tab.target)(e)}
>
<span styleName='nav-button-label'>
{tab.label}
</span>
{isUiHotkeyTab ? this.haveToSaveNotif(tab[tab.label].type, tab[tab.label].message) : null}
<span>{tab.label}</span>
{isUiHotkeyTab
? this.haveToSaveNotif(tab[tab.label].type, tab[tab.label].message)
: null}
</button>
)
})
return (
<div styleName='root'
<div
styleName='root'
ref='root'
tabIndex='-1'
onKeyDown={(e) => this.handleKeyDown(e)}
onKeyDown={e => this.handleKeyDown(e)}
>
<div styleName='top-bar'>
<p>{i18n.__('Your preferences for Boostnote')}</p>
</div>
<ModalEscButton handleEscButtonClick={(e) => this.handleEscButtonClick(e)} />
<div styleName='nav'>
{navButtons}
</div>
<ModalEscButton
handleEscButtonClick={e => this.handleEscButtonClick(e)}
/>
<div styleName='nav'>{navButtons}</div>
<div ref='content' styleName='content'>
{content}
</div>
@@ -193,4 +186,4 @@ Preferences.propTypes = {
dispatch: PropTypes.func
}
export default connect((x) => x)(CSSModules(Preferences, styles))
export default connect(x => x)(CSSModules(Preferences, styles))