You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-08-13 22:12:50 +02:00
Started applying config to Electron app
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1,18 +1,14 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
const { reg } = require('lib/registry.js');
|
|
||||||
const Setting = require('lib/models/Setting.js');
|
const Setting = require('lib/models/Setting.js');
|
||||||
const { bridge } = require('electron').remote.require('./bridge');
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
const { Header } = require('./Header.min.js');
|
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
const pathUtils = require('lib/path-utils.js');
|
const pathUtils = require('lib/path-utils.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { commandArgumentsToString } = require('lib/string-utils');
|
|
||||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
|
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
|
||||||
const shared = require('lib/components/shared/config-shared.js');
|
const shared = require('lib/components/shared/config-shared.js');
|
||||||
|
|
||||||
class ConfigScreenComponent extends React.Component {
|
class ConfigScreenComponent extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -20,7 +16,7 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
|
|
||||||
this.checkSyncConfig_ = async () => {
|
this.checkSyncConfig_ = async () => {
|
||||||
await shared.checkSyncConfig(this, this.state.settings);
|
await shared.checkSyncConfig(this, this.state.settings);
|
||||||
}
|
};
|
||||||
|
|
||||||
this.rowStyle_ = {
|
this.rowStyle_ = {
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
@@ -70,11 +66,7 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
sectionStyle.borderTopWidth = 0;
|
sectionStyle.borderTopWidth = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const noteComp = section.name !== 'general' ? null : (
|
const noteComp = section.name !== 'general' ? null : <div style={Object.assign({}, theme.textStyle, { marginBottom: 10 })}>{_('Notes and settings are stored in: %s', pathUtils.toSystemSlashes(Setting.value('profileDir'), process.platform))}</div>;
|
||||||
<div style={Object.assign({}, theme.textStyle, {marginBottom: 10})}>
|
|
||||||
{_('Notes and settings are stored in: %s', pathUtils.toSystemSlashes(Setting.value('profileDir'), process.platform))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (section.name === 'sync') {
|
if (section.name === 'sync') {
|
||||||
const syncTargetMd = SyncTargetRegistry.idToMetadata(settings['sync.target']);
|
const syncTargetMd = SyncTargetRegistry.idToMetadata(settings['sync.target']);
|
||||||
@@ -85,14 +77,18 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
const statusComp = !messages.length ? null : (
|
const statusComp = !messages.length ? null : (
|
||||||
<div style={statusStyle}>
|
<div style={statusStyle}>
|
||||||
{messages[0]}
|
{messages[0]}
|
||||||
{messages.length >= 1 ? (<p>{messages[1]}</p>) : null}
|
{messages.length >= 1 ? <p>{messages[1]}</p> : null}
|
||||||
</div>);
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
settingComps.push(
|
settingComps.push(
|
||||||
<div key="check_sync_config_button" style={this.rowStyle_}>
|
<div key="check_sync_config_button" style={this.rowStyle_}>
|
||||||
<button disabled={this.state.checkSyncConfigResult === 'checking'} style={theme.buttonStyle} onClick={this.checkSyncConfig_}>{_('Check synchronisation configuration')}</button>
|
<button disabled={this.state.checkSyncConfigResult === 'checking'} style={theme.buttonStyle} onClick={this.checkSyncConfig_}>
|
||||||
{ statusComp }
|
{_('Check synchronisation configuration')}
|
||||||
</div>);
|
</button>
|
||||||
|
{statusComp}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,9 +96,7 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
<div key={key} style={sectionStyle}>
|
<div key={key} style={sectionStyle}>
|
||||||
<h2 style={headerStyle}>{Setting.sectionNameToLabel(section.name)}</h2>
|
<h2 style={headerStyle}>{Setting.sectionNameToLabel(section.name)}</h2>
|
||||||
{noteComp}
|
{noteComp}
|
||||||
<div>
|
<div>{settingComps}</div>
|
||||||
{settingComps}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -145,18 +139,14 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
const updateSettingValue = (key, value) => {
|
const updateSettingValue = (key, value) => {
|
||||||
// console.info(key + ' = ' + value);
|
// console.info(key + ' = ' + value);
|
||||||
return shared.updateSettingValue(this, key, value);
|
return shared.updateSettingValue(this, key, value);
|
||||||
}
|
};
|
||||||
|
|
||||||
// Component key needs to be key+value otherwise it doesn't update when the settings change.
|
// Component key needs to be key+value otherwise it doesn't update when the settings change.
|
||||||
|
|
||||||
const md = Setting.settingMetadata(key);
|
const md = Setting.settingMetadata(key);
|
||||||
|
|
||||||
const descriptionText = Setting.keyDescription(key, 'desktop');
|
const descriptionText = Setting.keyDescription(key, 'desktop');
|
||||||
const descriptionComp = descriptionText ? (
|
const descriptionComp = descriptionText ? <div style={descriptionStyle}>{descriptionText}</div> : null;
|
||||||
<div style={descriptionStyle}>
|
|
||||||
{descriptionText}
|
|
||||||
</div>
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
if (md.isEnum) {
|
if (md.isEnum) {
|
||||||
let items = [];
|
let items = [];
|
||||||
@@ -164,31 +154,59 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
let array = this.keyValueToArray(settingOptions);
|
let array = this.keyValueToArray(settingOptions);
|
||||||
for (let i = 0; i < array.length; i++) {
|
for (let i = 0; i < array.length; i++) {
|
||||||
const e = array[i];
|
const e = array[i];
|
||||||
items.push(<option value={e.key.toString()} key={e.key}>{settingOptions[e.key]}</option>);
|
items.push(
|
||||||
|
<option value={e.key.toString()} key={e.key}>
|
||||||
|
{settingOptions[e.key]}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={key} style={rowStyle}>
|
<div key={key} style={rowStyle}>
|
||||||
<div style={labelStyle}><label>{md.label()}</label></div>
|
<div style={labelStyle}>
|
||||||
<select value={value} style={controlStyle} onChange={(event) => { updateSettingValue(key, event.target.value) }}>
|
<label>{md.label()}</label>
|
||||||
|
</div>
|
||||||
|
<select
|
||||||
|
value={value}
|
||||||
|
style={controlStyle}
|
||||||
|
onChange={event => {
|
||||||
|
updateSettingValue(key, event.target.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
{items}
|
{items}
|
||||||
</select>
|
</select>
|
||||||
{ descriptionComp }
|
{descriptionComp}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (md.type === Setting.TYPE_BOOL) {
|
} else if (md.type === Setting.TYPE_BOOL) {
|
||||||
const onCheckboxClick = (event) => {
|
const onCheckboxClick = event => {
|
||||||
updateSettingValue(key, !value)
|
updateSettingValue(key, !value);
|
||||||
}
|
};
|
||||||
|
|
||||||
// Hack: The {key+value.toString()} is needed as otherwise the checkbox doesn't update when the state changes.
|
// Hack: The {key+value.toString()} is needed as otherwise the checkbox doesn't update when the state changes.
|
||||||
// There's probably a better way to do this but can't figure it out.
|
// There's probably a better way to do this but can't figure it out.
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={key+value.toString()} style={rowStyle}>
|
<div key={key + value.toString()} style={rowStyle}>
|
||||||
<div style={controlStyle}>
|
<div style={controlStyle}>
|
||||||
<input id={'setting_checkbox_' + key} type="checkbox" checked={!!value} onChange={(event) => { onCheckboxClick(event) }}/><label onClick={(event) => { onCheckboxClick(event) }} style={labelStyle} htmlFor={'setting_checkbox_' + key}>{md.label()}</label>
|
<input
|
||||||
{ descriptionComp }
|
id={'setting_checkbox_' + key}
|
||||||
|
type="checkbox"
|
||||||
|
checked={!!value}
|
||||||
|
onChange={event => {
|
||||||
|
onCheckboxClick(event);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
onClick={event => {
|
||||||
|
onCheckboxClick(event);
|
||||||
|
}}
|
||||||
|
style={labelStyle}
|
||||||
|
htmlFor={'setting_checkbox_' + key}
|
||||||
|
>
|
||||||
|
{md.label()}
|
||||||
|
</label>
|
||||||
|
{descriptionComp}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -196,7 +214,8 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
const inputStyle = Object.assign({}, controlStyle, {
|
const inputStyle = Object.assign({}, controlStyle, {
|
||||||
width: '50%',
|
width: '50%',
|
||||||
minWidth: '20em',
|
minWidth: '20em',
|
||||||
border: '1px solid' });
|
border: '1px solid',
|
||||||
|
});
|
||||||
const inputType = md.secure === true ? 'password' : 'text';
|
const inputType = md.secure === true ? 'password' : 'text';
|
||||||
|
|
||||||
if (md.subType === 'file_path_and_args') {
|
if (md.subType === 'file_path_and_args') {
|
||||||
@@ -206,7 +225,7 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
const path = pathUtils.extractExecutablePath(cmdString);
|
const path = pathUtils.extractExecutablePath(cmdString);
|
||||||
const args = cmdString.substr(path.length + 1);
|
const args = cmdString.substr(path.length + 1);
|
||||||
return [pathUtils.unquotePath(path), args];
|
return [pathUtils.unquotePath(path), args];
|
||||||
}
|
};
|
||||||
|
|
||||||
const joinCmd = cmdArray => {
|
const joinCmd = cmdArray => {
|
||||||
if (!cmdArray[0] && !cmdArray[1]) return '';
|
if (!cmdArray[0] && !cmdArray[1]) return '';
|
||||||
@@ -214,74 +233,101 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
if (!cmdString) cmdString = '""';
|
if (!cmdString) cmdString = '""';
|
||||||
if (cmdArray[1]) cmdString += ' ' + cmdArray[1];
|
if (cmdArray[1]) cmdString += ' ' + cmdArray[1];
|
||||||
return cmdString;
|
return cmdString;
|
||||||
}
|
};
|
||||||
|
|
||||||
const onPathChange = event => {
|
const onPathChange = event => {
|
||||||
const cmd = splitCmd(this.state.settings[key]);
|
const cmd = splitCmd(this.state.settings[key]);
|
||||||
cmd[0] = event.target.value;
|
cmd[0] = event.target.value;
|
||||||
updateSettingValue(key, joinCmd(cmd));
|
updateSettingValue(key, joinCmd(cmd));
|
||||||
}
|
};
|
||||||
|
|
||||||
const onArgsChange = event => {
|
const onArgsChange = event => {
|
||||||
const cmd = splitCmd(this.state.settings[key]);
|
const cmd = splitCmd(this.state.settings[key]);
|
||||||
cmd[1] = event.target.value;
|
cmd[1] = event.target.value;
|
||||||
updateSettingValue(key, joinCmd(cmd));
|
updateSettingValue(key, joinCmd(cmd));
|
||||||
}
|
};
|
||||||
|
|
||||||
const browseButtonClick = () => {
|
const browseButtonClick = () => {
|
||||||
const paths = bridge().showOpenDialog();
|
const paths = bridge().showOpenDialog();
|
||||||
if (!paths || !paths.length) return;
|
if (!paths || !paths.length) return;
|
||||||
const cmd = splitCmd(this.state.settings[key]);
|
const cmd = splitCmd(this.state.settings[key]);
|
||||||
cmd[0] = paths[0]
|
cmd[0] = paths[0];
|
||||||
updateSettingValue(key, joinCmd(cmd));
|
updateSettingValue(key, joinCmd(cmd));
|
||||||
}
|
};
|
||||||
|
|
||||||
const cmd = splitCmd(this.state.settings[key]);
|
const cmd = splitCmd(this.state.settings[key]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={key} style={rowStyle}>
|
<div key={key} style={rowStyle}>
|
||||||
<div style={{display:'flex'}}>
|
<div style={{ display: 'flex' }}>
|
||||||
<div style={{flex:0, whiteSpace: 'nowrap'}}>
|
<div style={{ flex: 0, whiteSpace: 'nowrap' }}>
|
||||||
<div style={labelStyle}><label>{md.label()}</label></div>
|
<div style={labelStyle}>
|
||||||
|
<label>{md.label()}</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{flex:0}}>
|
<div style={{ flex: 0 }}>
|
||||||
<div style={subLabel}>Path:</div>
|
<div style={subLabel}>Path:</div>
|
||||||
<div style={subLabel}>Arguments:</div>
|
<div style={subLabel}>Arguments:</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{flex:1}}>
|
<div style={{ flex: 1 }}>
|
||||||
<div style={{display: 'flex', flexDirection: 'row', alignItems: 'center', marginBottom: inputStyle.marginBottom}}>
|
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', marginBottom: inputStyle.marginBottom }}>
|
||||||
<input type={inputType} style={Object.assign({}, inputStyle, {marginBottom:0})} onChange={(event) => {onPathChange(event)}} value={cmd[0]} />
|
<input
|
||||||
<button onClick={browseButtonClick} style={Object.assign({}, theme.buttonStyle, { marginLeft: 5, minHeight: 20, height: 20 })}>{_('Browse...')}</button>
|
type={inputType}
|
||||||
|
style={Object.assign({}, inputStyle, { marginBottom: 0 })}
|
||||||
|
onChange={event => {
|
||||||
|
onPathChange(event);
|
||||||
|
}}
|
||||||
|
value={cmd[0]}
|
||||||
|
/>
|
||||||
|
<button onClick={browseButtonClick} style={Object.assign({}, theme.buttonStyle, { marginLeft: 5, minHeight: 20, height: 20 })}>
|
||||||
|
{_('Browse...')}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<input type={inputType} style={inputStyle} onChange={(event) => {onArgsChange(event)}} value={cmd[1]}/>
|
<input
|
||||||
|
type={inputType}
|
||||||
|
style={inputStyle}
|
||||||
|
onChange={event => {
|
||||||
|
onArgsChange(event);
|
||||||
|
}}
|
||||||
|
value={cmd[1]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{display:'flex'}}>
|
<div style={{ display: 'flex' }}>
|
||||||
<div style={{flex:0, whiteSpace: 'nowrap'}}>
|
<div style={{ flex: 0, whiteSpace: 'nowrap' }}>
|
||||||
<div style={invisibleLabel}><label>{md.label()}</label></div>
|
<div style={invisibleLabel}>
|
||||||
</div>
|
<label>{md.label()}</label>
|
||||||
<div style={{flex:1}}>
|
</div>
|
||||||
{ descriptionComp }
|
|
||||||
</div>
|
</div>
|
||||||
|
<div style={{ flex: 1 }}>{descriptionComp}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const onTextChange = (event) => {
|
const onTextChange = event => {
|
||||||
updateSettingValue(key, event.target.value);
|
updateSettingValue(key, event.target.value);
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={key} style={rowStyle}>
|
<div key={key} style={rowStyle}>
|
||||||
<div style={labelStyle}><label>{md.label()}</label></div>
|
<div style={labelStyle}>
|
||||||
<input type={inputType} style={inputStyle} value={this.state.settings[key]} onChange={(event) => {onTextChange(event)}} />
|
<label>{md.label()}</label>
|
||||||
{ descriptionComp }
|
</div>
|
||||||
|
<input
|
||||||
|
type={inputType}
|
||||||
|
style={inputStyle}
|
||||||
|
value={this.state.settings[key]}
|
||||||
|
onChange={event => {
|
||||||
|
onTextChange(event);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{descriptionComp}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (md.type === Setting.TYPE_INT) {
|
} else if (md.type === Setting.TYPE_INT) {
|
||||||
const onNumChange = (event) => {
|
const onNumChange = event => {
|
||||||
updateSettingValue(key, event.target.value);
|
updateSettingValue(key, event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -290,9 +336,21 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={key} style={rowStyle}>
|
<div key={key} style={rowStyle}>
|
||||||
<div style={labelStyle}><label>{label.join(' ')}</label></div>
|
<div style={labelStyle}>
|
||||||
<input type="number" style={controlStyle} value={this.state.settings[key]} onChange={(event) => {onNumChange(event)}} min={md.minimum} max={md.maximum} step={md.step}/>
|
<label>{label.join(' ')}</label>
|
||||||
{ descriptionComp }
|
</div>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
style={controlStyle}
|
||||||
|
value={this.state.settings[key]}
|
||||||
|
onChange={event => {
|
||||||
|
onNumChange(event);
|
||||||
|
}}
|
||||||
|
min={md.minimum}
|
||||||
|
max={md.maximum}
|
||||||
|
step={md.step}
|
||||||
|
/>
|
||||||
|
{descriptionComp}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -318,13 +376,17 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
const theme = themeStyle(this.props.theme);
|
const theme = themeStyle(this.props.theme);
|
||||||
|
|
||||||
const style = Object.assign({
|
const style = Object.assign(
|
||||||
backgroundColor: theme.backgroundColor
|
{
|
||||||
}, this.props.style, {
|
backgroundColor: theme.backgroundColor,
|
||||||
overflow: 'hidden',
|
},
|
||||||
display: 'flex',
|
this.props.style,
|
||||||
flexDirection: 'column',
|
{
|
||||||
});
|
overflow: 'hidden',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
let settings = this.state.settings;
|
let settings = this.state.settings;
|
||||||
|
|
||||||
@@ -355,20 +417,41 @@ class ConfigScreenComponent extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div style={style}>
|
<div style={style}>
|
||||||
<div style={buttonBarStyle}>
|
<div style={buttonBarStyle}>
|
||||||
<button onClick={() => {this.onCancelClick()}} style={buttonStyle}><i style={theme.buttonIconStyle} className={"fa fa-chevron-left"}></i>{_('Cancel')}</button>
|
<button
|
||||||
<button disabled={!hasChanges} onClick={() => {this.onSaveClick()}} style={buttonStyleApprove}>{_('OK')}</button>
|
onClick={() => {
|
||||||
<button disabled={!hasChanges} onClick={() => {this.onApplyClick()}} style={buttonStyleApprove}>{_('Apply')}</button>
|
this.onCancelClick();
|
||||||
</div>
|
}}
|
||||||
<div style={containerStyle}>
|
style={buttonStyle}
|
||||||
{ settingComps }
|
>
|
||||||
|
<i style={theme.buttonIconStyle} className={'fa fa-chevron-left'}></i>
|
||||||
|
{_('Cancel')}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
disabled={!hasChanges}
|
||||||
|
onClick={() => {
|
||||||
|
this.onSaveClick();
|
||||||
|
}}
|
||||||
|
style={buttonStyleApprove}
|
||||||
|
>
|
||||||
|
{_('OK')}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
disabled={!hasChanges}
|
||||||
|
onClick={() => {
|
||||||
|
this.onApplyClick();
|
||||||
|
}}
|
||||||
|
style={buttonStyleApprove}
|
||||||
|
>
|
||||||
|
{_('Apply')}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div style={containerStyle}>{settingComps}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
theme: state.settings.theme,
|
theme: state.settings.theme,
|
||||||
settings: state.settings,
|
settings: state.settings,
|
||||||
|
@@ -1,23 +1,16 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
const { reg } = require('lib/registry.js');
|
|
||||||
const { bridge } = require('electron').remote.require('./bridge');
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
const { Header } = require('./Header.min.js');
|
const { Header } = require('./Header.min.js');
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
|
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const Shared = require('lib/components/shared/dropbox-login-shared');
|
const Shared = require('lib/components/shared/dropbox-login-shared');
|
||||||
|
|
||||||
class DropboxLoginScreenComponent extends React.Component {
|
class DropboxLoginScreenComponent extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.shared_ = new Shared(
|
this.shared_ = new Shared(this, msg => bridge().showInfoMessageBox(msg), msg => bridge().showErrorMessageBox(msg));
|
||||||
this,
|
|
||||||
(msg) => bridge().showInfoMessageBox(msg),
|
|
||||||
(msg) => bridge().showErrorMessageBox(msg)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
@@ -42,18 +35,23 @@ class DropboxLoginScreenComponent extends React.Component {
|
|||||||
<div style={containerStyle}>
|
<div style={containerStyle}>
|
||||||
<p style={theme.textStyle}>{_('To allow Joplin to synchronise with Dropbox, please follow the steps below:')}</p>
|
<p style={theme.textStyle}>{_('To allow Joplin to synchronise with Dropbox, please follow the steps below:')}</p>
|
||||||
<p style={theme.textStyle}>{_('Step 1: Open this URL in your browser to authorise the application:')}</p>
|
<p style={theme.textStyle}>{_('Step 1: Open this URL in your browser to authorise the application:')}</p>
|
||||||
<a style={theme.textStyle} href="#" onClick={this.shared_.loginUrl_click}>{this.state.loginUrl}</a>
|
<a style={theme.textStyle} href="#" onClick={this.shared_.loginUrl_click}>
|
||||||
|
{this.state.loginUrl}
|
||||||
|
</a>
|
||||||
<p style={theme.textStyle}>{_('Step 2: Enter the code provided by Dropbox:')}</p>
|
<p style={theme.textStyle}>{_('Step 2: Enter the code provided by Dropbox:')}</p>
|
||||||
<p><input type="text" value={this.state.authCode} onChange={this.shared_.authCodeInput_change} style={inputStyle}/></p>
|
<p>
|
||||||
<button disabled={this.state.checkingAuthToken} onClick={this.shared_.submit_click}>{_('Submit')}</button>
|
<input type="text" value={this.state.authCode} onChange={this.shared_.authCodeInput_change} style={inputStyle} />
|
||||||
|
</p>
|
||||||
|
<button disabled={this.state.checkingAuthToken} onClick={this.shared_.submit_click}>
|
||||||
|
{_('Submit')}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
theme: state.settings.theme,
|
theme: state.settings.theme,
|
||||||
};
|
};
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
const Setting = require('lib/models/Setting');
|
const Setting = require('lib/models/Setting');
|
||||||
const BaseItem = require('lib/models/BaseItem');
|
|
||||||
const EncryptionService = require('lib/services/EncryptionService');
|
const EncryptionService = require('lib/services/EncryptionService');
|
||||||
const { Header } = require('./Header.min.js');
|
const { Header } = require('./Header.min.js');
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
@@ -9,11 +8,9 @@ const { _ } = require('lib/locale.js');
|
|||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
const dialogs = require('./dialogs');
|
const dialogs = require('./dialogs');
|
||||||
const shared = require('lib/components/shared/encryption-config-shared.js');
|
const shared = require('lib/components/shared/encryption-config-shared.js');
|
||||||
const pathUtils = require('lib/path-utils.js');
|
|
||||||
const { bridge } = require('electron').remote.require('./bridge');
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
|
|
||||||
class EncryptionConfigScreenComponent extends React.Component {
|
class EncryptionConfigScreenComponent extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
shared.constructor(this);
|
shared.constructor(this);
|
||||||
@@ -55,15 +52,15 @@ class EncryptionConfigScreenComponent extends React.Component {
|
|||||||
backgroundColor: theme.backgroundColor,
|
backgroundColor: theme.backgroundColor,
|
||||||
border: '1px solid',
|
border: '1px solid',
|
||||||
borderColor: theme.dividerColor,
|
borderColor: theme.dividerColor,
|
||||||
}
|
};
|
||||||
|
|
||||||
const onSaveClick = () => {
|
const onSaveClick = () => {
|
||||||
return shared.onSavePasswordClick(this, mk);
|
return shared.onSavePasswordClick(this, mk);
|
||||||
}
|
};
|
||||||
|
|
||||||
const onPasswordChange = (event) => {
|
const onPasswordChange = event => {
|
||||||
return shared.onPasswordChange(this, mk, event.target.value);
|
return shared.onPasswordChange(this, mk, event.target.value);
|
||||||
}
|
};
|
||||||
|
|
||||||
const password = this.state.passwords[mk.id] ? this.state.passwords[mk.id] : '';
|
const password = this.state.passwords[mk.id] ? this.state.passwords[mk.id] : '';
|
||||||
const active = this.props.activeMasterKeyId === mk.id ? '✔' : '';
|
const active = this.props.activeMasterKeyId === mk.id ? '✔' : '';
|
||||||
@@ -76,7 +73,12 @@ class EncryptionConfigScreenComponent extends React.Component {
|
|||||||
<td style={theme.textStyle}>{mk.source_application}</td>
|
<td style={theme.textStyle}>{mk.source_application}</td>
|
||||||
<td style={theme.textStyle}>{time.formatMsToLocal(mk.created_time)}</td>
|
<td style={theme.textStyle}>{time.formatMsToLocal(mk.created_time)}</td>
|
||||||
<td style={theme.textStyle}>{time.formatMsToLocal(mk.updated_time)}</td>
|
<td style={theme.textStyle}>{time.formatMsToLocal(mk.updated_time)}</td>
|
||||||
<td style={theme.textStyle}><input type="password" style={passwordStyle} value={password} onChange={(event) => onPasswordChange(event)}/> <button style={theme.buttonStyle} onClick={() => onSaveClick()}>{_('Save')}</button></td>
|
<td style={theme.textStyle}>
|
||||||
|
<input type="password" style={passwordStyle} value={password} onChange={event => onPasswordChange(event)} />{' '}
|
||||||
|
<button style={theme.buttonStyle} onClick={() => onSaveClick()}>
|
||||||
|
{_('Save')}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
<td style={theme.textStyle}>{passwordOk}</td>
|
<td style={theme.textStyle}>{passwordOk}</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
@@ -128,10 +130,19 @@ class EncryptionConfigScreenComponent extends React.Component {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
await dialogs.alert(error.message);
|
await dialogs.alert(error.message);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const decryptedItemsInfo = <p style={theme.textStyle}>{shared.decryptedStatText(this)}</p>;
|
const decryptedItemsInfo = <p style={theme.textStyle}>{shared.decryptedStatText(this)}</p>;
|
||||||
const toggleButton = <button style={theme.buttonStyle} onClick={() => { onToggleButtonClick() }}>{this.props.encryptionEnabled ? _('Disable encryption') : _('Enable encryption')}</button>
|
const toggleButton = (
|
||||||
|
<button
|
||||||
|
style={theme.buttonStyle}
|
||||||
|
onClick={() => {
|
||||||
|
onToggleButtonClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{this.props.encryptionEnabled ? _('Disable encryption') : _('Enable encryption')}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
let masterKeySection = null;
|
let masterKeySection = null;
|
||||||
|
|
||||||
@@ -164,7 +175,11 @@ class EncryptionConfigScreenComponent extends React.Component {
|
|||||||
const rows = [];
|
const rows = [];
|
||||||
for (let i = 0; i < nonExistingMasterKeyIds.length; i++) {
|
for (let i = 0; i < nonExistingMasterKeyIds.length; i++) {
|
||||||
const id = nonExistingMasterKeyIds[i];
|
const id = nonExistingMasterKeyIds[i];
|
||||||
rows.push(<tr key={id}><td style={theme.textStyle}>{id}</td></tr>);
|
rows.push(
|
||||||
|
<tr key={id}>
|
||||||
|
<td style={theme.textStyle}>{id}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
nonExistingMasterKeySection = (
|
nonExistingMasterKeySection = (
|
||||||
@@ -176,7 +191,7 @@ class EncryptionConfigScreenComponent extends React.Component {
|
|||||||
<tr>
|
<tr>
|
||||||
<th style={theme.textStyle}>{_('ID')}</th>
|
<th style={theme.textStyle}>{_('ID')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
{ rows }
|
{rows}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -187,13 +202,25 @@ class EncryptionConfigScreenComponent extends React.Component {
|
|||||||
<div>
|
<div>
|
||||||
<Header style={headerStyle} />
|
<Header style={headerStyle} />
|
||||||
<div style={containerStyle}>
|
<div style={containerStyle}>
|
||||||
{<div style={{backgroundColor: theme.warningBackgroundColor, paddingLeft: 10, paddingRight: 10, paddingTop: 2, paddingBottom: 2 }}>
|
{
|
||||||
<p style={theme.textStyle}>
|
<div style={{ backgroundColor: theme.warningBackgroundColor, paddingLeft: 10, paddingRight: 10, paddingTop: 2, paddingBottom: 2 }}>
|
||||||
<span>{_('For more information about End-To-End Encryption (E2EE) and advices on how to enable it please check the documentation:')}</span> <a onClick={() => {bridge().openExternal('https://joplinapp.org/e2ee/')}} href="#">https://joplinapp.org/e2ee/</a>
|
<p style={theme.textStyle}>
|
||||||
</p>
|
<span>{_('For more information about End-To-End Encryption (E2EE) and advices on how to enable it please check the documentation:')}</span>{' '}
|
||||||
</div>}
|
<a
|
||||||
|
onClick={() => {
|
||||||
|
bridge().openExternal('https://joplinapp.org/e2ee/');
|
||||||
|
}}
|
||||||
|
href="#"
|
||||||
|
>
|
||||||
|
https://joplinapp.org/e2ee/
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<h1 style={theme.h1Style}>{_('Status')}</h1>
|
<h1 style={theme.h1Style}>{_('Status')}</h1>
|
||||||
<p style={theme.textStyle}>{_('Encryption is:')} <strong>{this.props.encryptionEnabled ? _('Enabled') : _('Disabled')}</strong></p>
|
<p style={theme.textStyle}>
|
||||||
|
{_('Encryption is:')} <strong>{this.props.encryptionEnabled ? _('Enabled') : _('Disabled')}</strong>
|
||||||
|
</p>
|
||||||
{decryptedItemsInfo}
|
{decryptedItemsInfo}
|
||||||
{toggleButton}
|
{toggleButton}
|
||||||
{masterKeySection}
|
{masterKeySection}
|
||||||
@@ -202,10 +229,9 @@ class EncryptionConfigScreenComponent extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
theme: state.settings.theme,
|
theme: state.settings.theme,
|
||||||
masterKeys: state.masterKeys,
|
masterKeys: state.masterKeys,
|
||||||
|
@@ -1,12 +1,10 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
const { reg } = require('lib/registry.js');
|
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { bridge } = require('electron').remote.require('./bridge');
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
|
|
||||||
class HeaderComponent extends React.Component {
|
class HeaderComponent extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -18,13 +16,13 @@ class HeaderComponent extends React.Component {
|
|||||||
this.searchOnQuery_ = null;
|
this.searchOnQuery_ = null;
|
||||||
this.searchElement_ = null;
|
this.searchElement_ = null;
|
||||||
|
|
||||||
const triggerOnQuery = (query) => {
|
const triggerOnQuery = query => {
|
||||||
clearTimeout(this.scheduleSearchChangeEventIid_);
|
clearTimeout(this.scheduleSearchChangeEventIid_);
|
||||||
if (this.searchOnQuery_) this.searchOnQuery_(query);
|
if (this.searchOnQuery_) this.searchOnQuery_(query);
|
||||||
this.scheduleSearchChangeEventIid_ = null;
|
this.scheduleSearchChangeEventIid_ = null;
|
||||||
}
|
};
|
||||||
|
|
||||||
this.search_onChange = (event) => {
|
this.search_onChange = event => {
|
||||||
this.setState({ searchQuery: event.target.value });
|
this.setState({ searchQuery: event.target.value });
|
||||||
|
|
||||||
if (this.scheduleSearchChangeEventIid_) clearTimeout(this.scheduleSearchChangeEventIid_);
|
if (this.scheduleSearchChangeEventIid_) clearTimeout(this.scheduleSearchChangeEventIid_);
|
||||||
@@ -34,10 +32,10 @@ class HeaderComponent extends React.Component {
|
|||||||
}, 500);
|
}, 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.search_onClear = (event) => {
|
this.search_onClear = event => {
|
||||||
this.resetSearch();
|
this.resetSearch();
|
||||||
if (this.searchElement_) this.searchElement_.focus();
|
if (this.searchElement_) this.searchElement_.focus();
|
||||||
}
|
};
|
||||||
|
|
||||||
this.search_onFocus = event => {
|
this.search_onFocus = event => {
|
||||||
if (this.hideSearchUsageLinkIID_) {
|
if (this.hideSearchUsageLinkIID_) {
|
||||||
@@ -46,7 +44,7 @@ class HeaderComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ showSearchUsageLink: true });
|
this.setState({ showSearchUsageLink: true });
|
||||||
}
|
};
|
||||||
|
|
||||||
this.search_onBlur = event => {
|
this.search_onBlur = event => {
|
||||||
if (this.hideSearchUsageLinkIID_) return;
|
if (this.hideSearchUsageLinkIID_) return;
|
||||||
@@ -54,22 +52,23 @@ class HeaderComponent extends React.Component {
|
|||||||
this.hideSearchUsageLinkIID_ = setTimeout(() => {
|
this.hideSearchUsageLinkIID_ = setTimeout(() => {
|
||||||
this.setState({ showSearchUsageLink: false });
|
this.setState({ showSearchUsageLink: false });
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
};
|
||||||
|
|
||||||
this.search_keyDown = event => {
|
this.search_keyDown = event => {
|
||||||
if (event.keyCode === 27) { // ESCAPE
|
if (event.keyCode === 27) {
|
||||||
|
// ESCAPE
|
||||||
this.resetSearch();
|
this.resetSearch();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
this.resetSearch = () => {
|
this.resetSearch = () => {
|
||||||
this.setState({ searchQuery: '' });
|
this.setState({ searchQuery: '' });
|
||||||
triggerOnQuery('');
|
triggerOnQuery('');
|
||||||
}
|
};
|
||||||
|
|
||||||
this.searchUsageLink_click = event => {
|
this.searchUsageLink_click = event => {
|
||||||
bridge().openExternal('https://joplinapp.org/#searching');
|
bridge().openExternal('https://joplinapp.org/#searching');
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentWillReceiveProps(nextProps) {
|
async componentWillReceiveProps(nextProps) {
|
||||||
@@ -79,7 +78,7 @@ class HeaderComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if(prevProps.notesParentType !== this.props.notesParentType && this.props.notesParentType !== 'Search' && this.state.searchQuery) {
|
if (prevProps.notesParentType !== this.props.notesParentType && this.props.notesParentType !== 'Search' && this.state.searchQuery) {
|
||||||
this.resetSearch();
|
this.resetSearch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,14 +121,14 @@ class HeaderComponent extends React.Component {
|
|||||||
color: style.color,
|
color: style.color,
|
||||||
};
|
};
|
||||||
if (options.title) iconStyle.marginRight = 5;
|
if (options.title) iconStyle.marginRight = 5;
|
||||||
if("undefined" != typeof(options.iconRotation)) {
|
if ('undefined' != typeof options.iconRotation) {
|
||||||
iconStyle.transition = "transform 0.15s ease-in-out";
|
iconStyle.transition = 'transform 0.15s ease-in-out';
|
||||||
iconStyle.transform = 'rotate(' + options.iconRotation + 'deg)';
|
iconStyle.transform = 'rotate(' + options.iconRotation + 'deg)';
|
||||||
}
|
}
|
||||||
icon = <i style={iconStyle} className={"fa " + options.iconName}></i>
|
icon = <i style={iconStyle} className={'fa ' + options.iconName}></i>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isEnabled = (!('enabled' in options) || options.enabled);
|
const isEnabled = !('enabled' in options) || options.enabled;
|
||||||
let classes = ['button'];
|
let classes = ['button'];
|
||||||
if (!isEnabled) classes.push('disabled');
|
if (!isEnabled) classes.push('disabled');
|
||||||
|
|
||||||
@@ -139,16 +138,21 @@ class HeaderComponent extends React.Component {
|
|||||||
|
|
||||||
const title = options.title ? options.title : '';
|
const title = options.title ? options.title : '';
|
||||||
|
|
||||||
return <a
|
return (
|
||||||
className={classes.join(' ')}
|
<a
|
||||||
style={finalStyle}
|
className={classes.join(' ')}
|
||||||
key={key}
|
style={finalStyle}
|
||||||
href="#"
|
key={key}
|
||||||
title={title}
|
href="#"
|
||||||
onClick={() => { if (isEnabled) options.onClick() }}
|
title={title}
|
||||||
>
|
onClick={() => {
|
||||||
{icon}<span className="title">{title}</span>
|
if (isEnabled) options.onClick();
|
||||||
</a>
|
}}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
<span className="title">{title}</span>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
makeSearch(key, style, options, state) {
|
makeSearch(key, style, options, state) {
|
||||||
@@ -159,8 +163,8 @@ class HeaderComponent extends React.Component {
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
paddingLeft: 6,
|
paddingLeft: 6,
|
||||||
paddingRight: 6,
|
paddingRight: 6,
|
||||||
paddingTop: 1, // vertical alignment with buttons
|
paddingTop: 1, // vertical alignment with buttons
|
||||||
paddingBottom: 0, // vertical alignment with buttons
|
paddingBottom: 0, // vertical alignment with buttons
|
||||||
height: style.fontSize * 2,
|
height: style.fontSize * 2,
|
||||||
color: style.color,
|
color: style.color,
|
||||||
fontSize: style.fontSize,
|
fontSize: style.fontSize,
|
||||||
@@ -191,33 +195,24 @@ class HeaderComponent extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const iconName = state.searchQuery ? 'fa-times' : 'fa-search';
|
const iconName = state.searchQuery ? 'fa-times' : 'fa-search';
|
||||||
const icon = <i style={iconStyle} className={"fa " + iconName}></i>
|
const icon = <i style={iconStyle} className={'fa ' + iconName}></i>;
|
||||||
if (options.onQuery) this.searchOnQuery_ = options.onQuery;
|
if (options.onQuery) this.searchOnQuery_ = options.onQuery;
|
||||||
|
|
||||||
const usageLink = !this.state.showSearchUsageLink ? null : (
|
const usageLink = !this.state.showSearchUsageLink ? null : (
|
||||||
<a onClick={this.searchUsageLink_click} style={theme.urlStyle} href="#">{_('Usage')}</a>
|
<a onClick={this.searchUsageLink_click} style={theme.urlStyle} href="#">
|
||||||
|
{_('Usage')}
|
||||||
|
</a>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={key} style={containerStyle}>
|
<div key={key} style={containerStyle}>
|
||||||
<input
|
<input type="text" style={inputStyle} placeholder={options.title} value={state.searchQuery} onChange={this.search_onChange} ref={elem => (this.searchElement_ = elem)} onFocus={this.search_onFocus} onBlur={this.search_onBlur} onKeyDown={this.search_keyDown} />
|
||||||
type="text"
|
<a href="#" style={searchButton} onClick={this.search_onClear}>
|
||||||
style={inputStyle}
|
{icon}
|
||||||
placeholder={options.title}
|
</a>
|
||||||
value={state.searchQuery}
|
|
||||||
onChange={this.search_onChange}
|
|
||||||
ref={elem => this.searchElement_ = elem}
|
|
||||||
onFocus={this.search_onFocus}
|
|
||||||
onBlur={this.search_onBlur}
|
|
||||||
onKeyDown={this.search_keyDown}
|
|
||||||
/>
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
style={searchButton}
|
|
||||||
onClick={this.search_onClear}
|
|
||||||
>{icon}</a>
|
|
||||||
{usageLink}
|
{usageLink}
|
||||||
</div>);
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -226,7 +221,7 @@ class HeaderComponent extends React.Component {
|
|||||||
const showBackButton = this.props.showBackButton === undefined || this.props.showBackButton === true;
|
const showBackButton = this.props.showBackButton === undefined || this.props.showBackButton === true;
|
||||||
style.height = theme.headerHeight;
|
style.height = theme.headerHeight;
|
||||||
style.display = 'flex';
|
style.display = 'flex';
|
||||||
style.flexDirection = 'row';
|
style.flexDirection = 'row';
|
||||||
style.borderBottom = '1px solid ' + theme.dividerColor;
|
style.borderBottom = '1px solid ' + theme.dividerColor;
|
||||||
style.boxSizing = 'border-box';
|
style.boxSizing = 'border-box';
|
||||||
|
|
||||||
@@ -266,14 +261,13 @@ class HeaderComponent extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="header" style={style}>
|
<div className="header" style={style}>
|
||||||
{ items }
|
{items}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
theme: state.settings.theme,
|
theme: state.settings.theme,
|
||||||
windowCommand: state.windowCommand,
|
windowCommand: state.windowCommand,
|
||||||
|
@@ -1,12 +1,8 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
const { reg } = require('lib/registry.js');
|
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
const { _ } = require('lib/locale.js');
|
|
||||||
const { bridge } = require('electron').remote.require('./bridge');
|
|
||||||
|
|
||||||
class HelpButtonComponent extends React.Component {
|
class HelpButtonComponent extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -19,16 +15,19 @@ class HelpButtonComponent extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const theme = themeStyle(this.props.theme);
|
const theme = themeStyle(this.props.theme);
|
||||||
let style = Object.assign({}, this.props.style, {color: theme.color, textDecoration: 'none'});
|
let style = Object.assign({}, this.props.style, { color: theme.color, textDecoration: 'none' });
|
||||||
const helpIconStyle = {flex:0, width: 16, height: 16, marginLeft: 10};
|
const helpIconStyle = { flex: 0, width: 16, height: 16, marginLeft: 10 };
|
||||||
const extraProps = {};
|
const extraProps = {};
|
||||||
if (this.props.tip) extraProps['data-tip'] = this.props.tip;
|
if (this.props.tip) extraProps['data-tip'] = this.props.tip;
|
||||||
return <a href="#" style={style} onClick={this.onClick} {...extraProps}><i style={helpIconStyle} className={"fa fa-question-circle"}></i></a>
|
return (
|
||||||
|
<a href="#" style={style} onClick={this.onClick} {...extraProps}>
|
||||||
|
<i style={helpIconStyle} className={'fa fa-question-circle'}></i>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
theme: state.settings.theme,
|
theme: state.settings.theme,
|
||||||
};
|
};
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { connect } = require('react-redux');
|
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
|
|
||||||
class IconButton extends React.Component {
|
class IconButton extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const style = this.props.style;
|
const style = this.props.style;
|
||||||
const theme = themeStyle(this.props.theme);
|
const theme = themeStyle(this.props.theme);
|
||||||
@@ -11,28 +9,37 @@ class IconButton extends React.Component {
|
|||||||
color: theme.color,
|
color: theme.color,
|
||||||
fontSize: theme.fontSize * 1.4,
|
fontSize: theme.fontSize * 1.4,
|
||||||
};
|
};
|
||||||
const icon = <i style={iconStyle} className={"fa " + this.props.iconName}></i>
|
const icon = <i style={iconStyle} className={'fa ' + this.props.iconName}></i>;
|
||||||
|
|
||||||
const rootStyle = Object.assign({
|
const rootStyle = Object.assign(
|
||||||
display: 'flex',
|
{
|
||||||
textDecoration: 'none',
|
display: 'flex',
|
||||||
padding: 10,
|
textDecoration: 'none',
|
||||||
width: theme.buttonMinHeight,
|
padding: 10,
|
||||||
height: theme.buttonMinHeight,
|
width: theme.buttonMinHeight,
|
||||||
boxSizing: 'border-box',
|
height: theme.buttonMinHeight,
|
||||||
alignItems: 'center',
|
boxSizing: 'border-box',
|
||||||
justifyContent: 'center',
|
alignItems: 'center',
|
||||||
backgroundColor: theme.backgroundColor,
|
justifyContent: 'center',
|
||||||
cursor: 'default',
|
backgroundColor: theme.backgroundColor,
|
||||||
}, style);
|
cursor: 'default',
|
||||||
|
},
|
||||||
|
style
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a href="#" style={rootStyle} className="icon-button" onClick={() => { if (this.props.onClick) this.props.onClick() }}>
|
<a
|
||||||
{ icon }
|
href="#"
|
||||||
|
style={rootStyle}
|
||||||
|
className="icon-button"
|
||||||
|
onClick={() => {
|
||||||
|
if (this.props.onClick) this.props.onClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { IconButton };
|
module.exports = { IconButton };
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
const { reg } = require('lib/registry.js');
|
|
||||||
const Folder = require('lib/models/Folder.js');
|
const Folder = require('lib/models/Folder.js');
|
||||||
const { bridge } = require('electron').remote.require('./bridge');
|
|
||||||
const { Header } = require('./Header.min.js');
|
const { Header } = require('./Header.min.js');
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
@@ -10,7 +8,6 @@ const { filename, basename } = require('lib/path-utils.js');
|
|||||||
const { importEnex } = require('lib/import-enex');
|
const { importEnex } = require('lib/import-enex');
|
||||||
|
|
||||||
class ImportScreenComponent extends React.Component {
|
class ImportScreenComponent extends React.Component {
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.setState({
|
this.setState({
|
||||||
doImport: true,
|
doImport: true,
|
||||||
@@ -21,11 +18,16 @@ class ImportScreenComponent extends React.Component {
|
|||||||
|
|
||||||
componentWillReceiveProps(newProps) {
|
componentWillReceiveProps(newProps) {
|
||||||
if (newProps.filePath) {
|
if (newProps.filePath) {
|
||||||
this.setState({
|
this.setState(
|
||||||
doImport: true,
|
{
|
||||||
filePath: newProps.filePath,
|
doImport: true,
|
||||||
messages: [],
|
filePath: newProps.filePath,
|
||||||
}, () => { this.doImport() });
|
messages: [],
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.doImport();
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +39,6 @@ class ImportScreenComponent extends React.Component {
|
|||||||
|
|
||||||
addMessage(key, text) {
|
addMessage(key, text) {
|
||||||
const messages = this.state.messages.slice();
|
const messages = this.state.messages.slice();
|
||||||
let found = false;
|
|
||||||
|
|
||||||
messages.push({ key: key, text: text });
|
messages.push({ key: key, text: text });
|
||||||
|
|
||||||
@@ -60,15 +61,13 @@ class ImportScreenComponent extends React.Component {
|
|||||||
async doImport() {
|
async doImport() {
|
||||||
const filePath = this.props.filePath;
|
const filePath = this.props.filePath;
|
||||||
const folderTitle = await Folder.findUniqueItemTitle(filename(filePath));
|
const folderTitle = await Folder.findUniqueItemTitle(filename(filePath));
|
||||||
const messages = this.state.messages.slice();
|
|
||||||
|
|
||||||
this.addMessage('start', _('New notebook "%s" will be created and file "%s" will be imported into it', folderTitle, basename(filePath)));
|
this.addMessage('start', _('New notebook "%s" will be created and file "%s" will be imported into it', folderTitle, basename(filePath)));
|
||||||
|
|
||||||
let lastProgress = '';
|
let lastProgress = '';
|
||||||
let progressCount = 0;
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
onProgress: (progressState) => {
|
onProgress: progressState => {
|
||||||
let line = [];
|
let line = [];
|
||||||
line.push(_('Found: %d.', progressState.loaded));
|
line.push(_('Found: %d.', progressState.loaded));
|
||||||
line.push(_('Created: %d.', progressState.created));
|
line.push(_('Created: %d.', progressState.created));
|
||||||
@@ -79,12 +78,12 @@ class ImportScreenComponent extends React.Component {
|
|||||||
lastProgress = line.join(' ');
|
lastProgress = line.join(' ');
|
||||||
this.addMessage('progress', lastProgress);
|
this.addMessage('progress', lastProgress);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: error => {
|
||||||
// Don't display the error directly because most of the time it doesn't matter
|
// Don't display the error directly because most of the time it doesn't matter
|
||||||
// (eg. for weird broken HTML, but the note is still imported)
|
// (eg. for weird broken HTML, but the note is still imported)
|
||||||
console.warn('When importing ENEX file', error);
|
console.warn('When importing ENEX file', error);
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
const folder = await Folder.save({ title: folderTitle });
|
const folder = await Folder.save({ title: folderTitle });
|
||||||
|
|
||||||
@@ -118,16 +117,13 @@ class ImportScreenComponent extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div style={{}}>
|
<div style={{}}>
|
||||||
<Header style={headerStyle} />
|
<Header style={headerStyle} />
|
||||||
<div style={messagesStyle}>
|
<div style={messagesStyle}>{messageComps}</div>
|
||||||
{messageComps}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
theme: state.settings.theme,
|
theme: state.settings.theme,
|
||||||
};
|
};
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
|
||||||
class ItemList extends React.Component {
|
class ItemList extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -52,7 +51,7 @@ class ItemList extends React.Component {
|
|||||||
|
|
||||||
makeItemIndexVisible(itemIndex) {
|
makeItemIndexVisible(itemIndex) {
|
||||||
const top = Math.min(this.props.items.length - 1, this.state.topItemIndex + 1);
|
const top = Math.min(this.props.items.length - 1, this.state.topItemIndex + 1);
|
||||||
const bottom = Math.max(0, this.state.bottomItemIndex)
|
const bottom = Math.max(0, this.state.bottomItemIndex);
|
||||||
|
|
||||||
if (itemIndex >= top && itemIndex <= bottom) return;
|
if (itemIndex >= top && itemIndex <= bottom) return;
|
||||||
|
|
||||||
@@ -81,8 +80,8 @@ class ItemList extends React.Component {
|
|||||||
if (!this.props.itemHeight) throw new Error('itemHeight is required');
|
if (!this.props.itemHeight) throw new Error('itemHeight is required');
|
||||||
|
|
||||||
const blankItem = function(key, height) {
|
const blankItem = function(key, height) {
|
||||||
return <div key={key} style={{height:height}}></div>
|
return <div key={key} style={{ height: height }}></div>;
|
||||||
}
|
};
|
||||||
|
|
||||||
let itemComps = [blankItem('top', this.state.topItemIndex * this.props.itemHeight)];
|
let itemComps = [blankItem('top', this.state.topItemIndex * this.props.itemHeight)];
|
||||||
|
|
||||||
@@ -98,7 +97,7 @@ class ItemList extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={this.listRef} className={classes.join(' ')} style={style} onScroll={this.onScroll} onKeyDown={this.onKeyDown}>
|
<div ref={this.listRef} className={classes.join(' ')} style={style} onScroll={this.onScroll} onKeyDown={this.onKeyDown}>
|
||||||
{ itemComps }
|
{itemComps}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,6 @@ const VerticalResizer = require('./VerticalResizer.min');
|
|||||||
const PluginManager = require('lib/services/PluginManager');
|
const PluginManager = require('lib/services/PluginManager');
|
||||||
|
|
||||||
class MainScreenComponent extends React.Component {
|
class MainScreenComponent extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -87,7 +86,7 @@ class MainScreenComponent extends React.Component {
|
|||||||
type: 'NOTE_SET_NEW_ONE',
|
type: 'NOTE_SET_NEW_ONE',
|
||||||
item: newNote,
|
item: newNote,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
let commandProcessed = true;
|
let commandProcessed = true;
|
||||||
|
|
||||||
@@ -107,7 +106,7 @@ class MainScreenComponent extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
promptOptions: {
|
promptOptions: {
|
||||||
label: _('Notebook title:'),
|
label: _('Notebook title:'),
|
||||||
onClose: async (answer) => {
|
onClose: async answer => {
|
||||||
if (answer) {
|
if (answer) {
|
||||||
let folder = null;
|
let folder = null;
|
||||||
try {
|
try {
|
||||||
@@ -125,14 +124,22 @@ class MainScreenComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ promptOptions: null });
|
this.setState({ promptOptions: null });
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if (command.name === 'setTags') {
|
} else if (command.name === 'setTags') {
|
||||||
const tags = await Tag.tagsByNoteId(command.noteId);
|
const tags = await Tag.tagsByNoteId(command.noteId);
|
||||||
const noteTags = tags.map((a) => { return {value: a.id, label: a.title } }).sort((a, b) => { return a.label.localeCompare(b.label); });
|
const noteTags = tags
|
||||||
|
.map(a => {
|
||||||
|
return { value: a.id, label: a.title };
|
||||||
|
})
|
||||||
|
.sort((a, b) => {
|
||||||
|
return a.label.localeCompare(b.label);
|
||||||
|
});
|
||||||
const allTags = await Tag.allWithNotes();
|
const allTags = await Tag.allWithNotes();
|
||||||
const tagSuggestions = allTags.map((a) => { return {value: a.id, label: a.title } });
|
const tagSuggestions = allTags.map(a => {
|
||||||
|
return { value: a.id, label: a.title };
|
||||||
|
});
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
promptOptions: {
|
promptOptions: {
|
||||||
@@ -140,13 +147,15 @@ class MainScreenComponent extends React.Component {
|
|||||||
inputType: 'tags',
|
inputType: 'tags',
|
||||||
value: noteTags,
|
value: noteTags,
|
||||||
autocomplete: tagSuggestions,
|
autocomplete: tagSuggestions,
|
||||||
onClose: async (answer) => {
|
onClose: async answer => {
|
||||||
if (answer !== null) {
|
if (answer !== null) {
|
||||||
const tagTitles = answer.map((a) => { return a.label.trim() });
|
const tagTitles = answer.map(a => {
|
||||||
|
return a.label.trim();
|
||||||
|
});
|
||||||
await Tag.setNoteTagsByTitles(command.noteId, tagTitles);
|
await Tag.setNoteTagsByTitles(command.noteId, tagTitles);
|
||||||
}
|
}
|
||||||
this.setState({ promptOptions: null });
|
this.setState({ promptOptions: null });
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if (command.name === 'renameFolder') {
|
} else if (command.name === 'renameFolder') {
|
||||||
@@ -157,7 +166,7 @@ class MainScreenComponent extends React.Component {
|
|||||||
promptOptions: {
|
promptOptions: {
|
||||||
label: _('Rename notebook:'),
|
label: _('Rename notebook:'),
|
||||||
value: folder.title,
|
value: folder.title,
|
||||||
onClose: async (answer) => {
|
onClose: async answer => {
|
||||||
if (answer !== null) {
|
if (answer !== null) {
|
||||||
try {
|
try {
|
||||||
folder.title = answer;
|
folder.title = answer;
|
||||||
@@ -167,7 +176,7 @@ class MainScreenComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({ promptOptions: null });
|
this.setState({ promptOptions: null });
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -178,7 +187,7 @@ class MainScreenComponent extends React.Component {
|
|||||||
promptOptions: {
|
promptOptions: {
|
||||||
label: _('Rename tag:'),
|
label: _('Rename tag:'),
|
||||||
value: tag.title,
|
value: tag.title,
|
||||||
onClose: async (answer) => {
|
onClose: async answer => {
|
||||||
if (answer !== null) {
|
if (answer !== null) {
|
||||||
try {
|
try {
|
||||||
tag.title = answer;
|
tag.title = answer;
|
||||||
@@ -187,13 +196,12 @@ class MainScreenComponent extends React.Component {
|
|||||||
bridge().showErrorMessageBox(error.message);
|
bridge().showErrorMessageBox(error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({promptOptions: null });
|
this.setState({ promptOptions: null });
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
} else if (command.name === 'search') {
|
} else if (command.name === 'search') {
|
||||||
|
|
||||||
if (!this.searchId_) this.searchId_ = uuid.create();
|
if (!this.searchId_) this.searchId_ = uuid.create();
|
||||||
|
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
@@ -222,7 +230,6 @@ class MainScreenComponent extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (command.name === 'commandNoteProperties') {
|
} else if (command.name === 'commandNoteProperties') {
|
||||||
this.setState({
|
this.setState({
|
||||||
notePropertiesDialogOptions: {
|
notePropertiesDialogOptions: {
|
||||||
@@ -273,7 +280,7 @@ class MainScreenComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ promptOptions: null });
|
this.setState({ promptOptions: null });
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if (command.name === 'selectTemplate') {
|
} else if (command.name === 'selectTemplate') {
|
||||||
@@ -283,7 +290,7 @@ class MainScreenComponent extends React.Component {
|
|||||||
inputType: 'dropdown',
|
inputType: 'dropdown',
|
||||||
value: this.props.templates[0], // Need to start with some value
|
value: this.props.templates[0], // Need to start with some value
|
||||||
autocomplete: this.props.templates,
|
autocomplete: this.props.templates,
|
||||||
onClose: async (answer) => {
|
onClose: async answer => {
|
||||||
if (answer) {
|
if (answer) {
|
||||||
if (command.noteType === 'note' || command.noteType === 'todo') {
|
if (command.noteType === 'note' || command.noteType === 'todo') {
|
||||||
createNewNote(answer.value, command.noteType === 'todo');
|
createNewNote(answer.value, command.noteType === 'todo');
|
||||||
@@ -297,7 +304,7 @@ class MainScreenComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ promptOptions: null });
|
this.setState({ promptOptions: null });
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -313,7 +320,7 @@ class MainScreenComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
styles(themeId, width, height, messageBoxVisible, isSidebarVisible, sidebarWidth, noteListWidth) {
|
styles(themeId, width, height, messageBoxVisible, isSidebarVisible, sidebarWidth, noteListWidth) {
|
||||||
const styleKey = [themeId, width, height, messageBoxVisible, (+isSidebarVisible), sidebarWidth, noteListWidth].join('_');
|
const styleKey = [themeId, width, height, messageBoxVisible, +isSidebarVisible, sidebarWidth, noteListWidth].join('_');
|
||||||
if (styleKey === this.styleKey_) return this.styles_;
|
if (styleKey === this.styleKey_) return this.styles_;
|
||||||
|
|
||||||
const theme = themeStyle(themeId);
|
const theme = themeStyle(themeId);
|
||||||
@@ -333,7 +340,7 @@ class MainScreenComponent extends React.Component {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingLeft: 10,
|
paddingLeft: 10,
|
||||||
backgroundColor: theme.warningBackgroundColor,
|
backgroundColor: theme.warningBackgroundColor,
|
||||||
}
|
};
|
||||||
|
|
||||||
this.styles_.verticalResizer = {
|
this.styles_.verticalResizer = {
|
||||||
width: 5,
|
width: 5,
|
||||||
@@ -390,10 +397,13 @@ class MainScreenComponent extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const theme = themeStyle(this.props.theme);
|
const theme = themeStyle(this.props.theme);
|
||||||
const style = Object.assign({
|
const style = Object.assign(
|
||||||
color: theme.color,
|
{
|
||||||
backgroundColor: theme.backgroundColor,
|
color: theme.color,
|
||||||
}, this.props.style);
|
backgroundColor: theme.backgroundColor,
|
||||||
|
},
|
||||||
|
this.props.style
|
||||||
|
);
|
||||||
const promptOptions = this.state.promptOptions;
|
const promptOptions = this.state.promptOptions;
|
||||||
const folders = this.props.folders;
|
const folders = this.props.folders;
|
||||||
const notes = this.props.notes;
|
const notes = this.props.notes;
|
||||||
@@ -408,47 +418,59 @@ class MainScreenComponent extends React.Component {
|
|||||||
title: _('Toggle sidebar'),
|
title: _('Toggle sidebar'),
|
||||||
iconName: 'fa-bars',
|
iconName: 'fa-bars',
|
||||||
iconRotation: this.props.sidebarVisibility ? 0 : 90,
|
iconRotation: this.props.sidebarVisibility ? 0 : 90,
|
||||||
onClick: () => { this.doCommand({ name: 'toggleSidebar'}) }
|
onClick: () => {
|
||||||
|
this.doCommand({ name: 'toggleSidebar' });
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
headerItems.push({
|
headerItems.push({
|
||||||
title: _('New note'),
|
title: _('New note'),
|
||||||
iconName: 'fa-file-o',
|
iconName: 'fa-file-o',
|
||||||
enabled: !!folders.length && !onConflictFolder,
|
enabled: !!folders.length && !onConflictFolder,
|
||||||
onClick: () => { this.doCommand({ name: 'newNote' }) },
|
onClick: () => {
|
||||||
|
this.doCommand({ name: 'newNote' });
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
headerItems.push({
|
headerItems.push({
|
||||||
title: _('New to-do'),
|
title: _('New to-do'),
|
||||||
iconName: 'fa-check-square-o',
|
iconName: 'fa-check-square-o',
|
||||||
enabled: !!folders.length && !onConflictFolder,
|
enabled: !!folders.length && !onConflictFolder,
|
||||||
onClick: () => { this.doCommand({ name: 'newTodo' }) },
|
onClick: () => {
|
||||||
|
this.doCommand({ name: 'newTodo' });
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
headerItems.push({
|
headerItems.push({
|
||||||
title: _('New notebook'),
|
title: _('New notebook'),
|
||||||
iconName: 'fa-book',
|
iconName: 'fa-book',
|
||||||
onClick: () => { this.doCommand({ name: 'newNotebook' }) },
|
onClick: () => {
|
||||||
|
this.doCommand({ name: 'newNotebook' });
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
headerItems.push({
|
headerItems.push({
|
||||||
title: _('Layout'),
|
title: _('Layout'),
|
||||||
iconName: 'fa-columns',
|
iconName: 'fa-columns',
|
||||||
enabled: !!notes.length,
|
enabled: !!notes.length,
|
||||||
onClick: () => { this.doCommand({ name: 'toggleVisiblePanes' }) },
|
onClick: () => {
|
||||||
|
this.doCommand({ name: 'toggleVisiblePanes' });
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
headerItems.push({
|
headerItems.push({
|
||||||
title: _('Search...'),
|
title: _('Search...'),
|
||||||
iconName: 'fa-search',
|
iconName: 'fa-search',
|
||||||
onQuery: (query) => { this.doCommand({ name: 'search', query: query }) },
|
onQuery: query => {
|
||||||
|
this.doCommand({ name: 'search', query: query });
|
||||||
|
},
|
||||||
type: 'search',
|
type: 'search',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.promptOnClose_) {
|
if (!this.promptOnClose_) {
|
||||||
this.promptOnClose_ = (answer, buttonType) => {
|
this.promptOnClose_ = (answer, buttonType) => {
|
||||||
return this.state.promptOptions.onClose(answer, buttonType);
|
return this.state.promptOptions.onClose(answer, buttonType);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const onViewDisabledItemsClick = () => {
|
const onViewDisabledItemsClick = () => {
|
||||||
@@ -456,36 +478,58 @@ class MainScreenComponent extends React.Component {
|
|||||||
type: 'NAV_GO',
|
type: 'NAV_GO',
|
||||||
routeName: 'Status',
|
routeName: 'Status',
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const onViewMasterKeysClick = () => {
|
const onViewMasterKeysClick = () => {
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: 'NAV_GO',
|
type: 'NAV_GO',
|
||||||
routeName: 'EncryptionConfig',
|
routeName: 'EncryptionConfig',
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
let messageComp = null;
|
let messageComp = null;
|
||||||
|
|
||||||
if (messageBoxVisible) {
|
if (messageBoxVisible) {
|
||||||
let msg = null;
|
let msg = null;
|
||||||
if (this.props.hasDisabledSyncItems) {
|
if (this.props.hasDisabledSyncItems) {
|
||||||
msg = <span>{_('Some items cannot be synchronised.')} <a href="#" onClick={() => { onViewDisabledItemsClick() }}>{_('View them now')}</a></span>
|
msg = (
|
||||||
|
<span>
|
||||||
|
{_('Some items cannot be synchronised.')}{' '}
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={() => {
|
||||||
|
onViewDisabledItemsClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{_('View them now')}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
} else if (this.props.showMissingMasterKeyMessage) {
|
} else if (this.props.showMissingMasterKeyMessage) {
|
||||||
msg = <span>{_('One or more master keys need a password.')} <a href="#" onClick={() => { onViewMasterKeysClick() }}>{_('Set the password')}</a></span>
|
msg = (
|
||||||
|
<span>
|
||||||
|
{_('One or more master keys need a password.')}{' '}
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={() => {
|
||||||
|
onViewMasterKeysClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{_('Set the password')}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
messageComp = (
|
messageComp = (
|
||||||
<div style={styles.messageBox}>
|
<div style={styles.messageBox}>
|
||||||
<span style={theme.textStyle}>
|
<span style={theme.textStyle}>{msg}</span>
|
||||||
{msg}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dialogInfo = PluginManager.instance().pluginDialogToShow(this.props.plugins);
|
const dialogInfo = PluginManager.instance().pluginDialogToShow(this.props.plugins);
|
||||||
const pluginDialog = !dialogInfo ? null : <dialogInfo.Dialog {...dialogInfo.props}/>;
|
const pluginDialog = !dialogInfo ? null : <dialogInfo.Dialog {...dialogInfo.props} />;
|
||||||
|
|
||||||
const modalLayerStyle = Object.assign({}, styles.modalLayer, { display: this.state.modalLayer.visible ? 'block' : 'none' });
|
const modalLayerStyle = Object.assign({}, styles.modalLayer, { display: this.state.modalLayer.visible ? 'block' : 'none' });
|
||||||
|
|
||||||
@@ -495,41 +539,25 @@ class MainScreenComponent extends React.Component {
|
|||||||
<div style={style}>
|
<div style={style}>
|
||||||
<div style={modalLayerStyle}>{this.state.modalLayer.message}</div>
|
<div style={modalLayerStyle}>{this.state.modalLayer.message}</div>
|
||||||
|
|
||||||
{ notePropertiesDialogOptions.visible && <NotePropertiesDialog
|
{notePropertiesDialogOptions.visible && <NotePropertiesDialog theme={this.props.theme} noteId={notePropertiesDialogOptions.noteId} onClose={this.notePropertiesDialog_close} onRevisionLinkClick={notePropertiesDialogOptions.onRevisionLinkClick} />}
|
||||||
theme={this.props.theme}
|
|
||||||
noteId={notePropertiesDialogOptions.noteId}
|
|
||||||
onClose={this.notePropertiesDialog_close}
|
|
||||||
onRevisionLinkClick={notePropertiesDialogOptions.onRevisionLinkClick}
|
|
||||||
/> }
|
|
||||||
|
|
||||||
<PromptDialog
|
<PromptDialog autocomplete={promptOptions && 'autocomplete' in promptOptions ? promptOptions.autocomplete : null} defaultValue={promptOptions && promptOptions.value ? promptOptions.value : ''} theme={this.props.theme} style={styles.prompt} onClose={this.promptOnClose_} label={promptOptions ? promptOptions.label : ''} description={promptOptions ? promptOptions.description : null} visible={!!this.state.promptOptions} buttons={promptOptions && 'buttons' in promptOptions ? promptOptions.buttons : null} inputType={promptOptions && 'inputType' in promptOptions ? promptOptions.inputType : null} />
|
||||||
autocomplete={promptOptions && ('autocomplete' in promptOptions) ? promptOptions.autocomplete : null}
|
|
||||||
defaultValue={promptOptions && promptOptions.value ? promptOptions.value : ''}
|
|
||||||
theme={this.props.theme}
|
|
||||||
style={styles.prompt}
|
|
||||||
onClose={this.promptOnClose_}
|
|
||||||
label={promptOptions ? promptOptions.label : ''}
|
|
||||||
description={promptOptions ? promptOptions.description : null}
|
|
||||||
visible={!!this.state.promptOptions}
|
|
||||||
buttons={promptOptions && ('buttons' in promptOptions) ? promptOptions.buttons : null}
|
|
||||||
inputType={promptOptions && ('inputType' in promptOptions) ? promptOptions.inputType : null} />
|
|
||||||
|
|
||||||
<Header style={styles.header} showBackButton={false} items={headerItems} />
|
<Header style={styles.header} showBackButton={false} items={headerItems} />
|
||||||
{messageComp}
|
{messageComp}
|
||||||
<SideBar style={styles.sideBar} />
|
<SideBar style={styles.sideBar} />
|
||||||
<VerticalResizer style={styles.verticalResizer} onDrag={this.sidebar_onDrag}/>
|
<VerticalResizer style={styles.verticalResizer} onDrag={this.sidebar_onDrag} />
|
||||||
<NoteList style={styles.noteList} />
|
<NoteList style={styles.noteList} />
|
||||||
<VerticalResizer style={styles.verticalResizer} onDrag={this.noteList_onDrag}/>
|
<VerticalResizer style={styles.verticalResizer} onDrag={this.noteList_onDrag} />
|
||||||
<NoteText style={styles.noteText} visiblePanes={this.props.noteVisiblePanes} noteDevToolsVisible={this.props.noteDevToolsVisible}/>
|
<NoteText style={styles.noteText} visiblePanes={this.props.noteVisiblePanes} noteDevToolsVisible={this.props.noteDevToolsVisible} />
|
||||||
|
|
||||||
{pluginDialog}
|
{pluginDialog}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
theme: state.settings.theme,
|
theme: state.settings.theme,
|
||||||
windowCommand: state.windowCommand,
|
windowCommand: state.windowCommand,
|
||||||
|
@@ -1,10 +1,9 @@
|
|||||||
const React = require('react'); const Component = React.Component;
|
const React = require('react');
|
||||||
|
const Component = React.Component;
|
||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
const { app } = require('../app.js');
|
|
||||||
const { bridge } = require('electron').remote.require('./bridge');
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
|
|
||||||
class NavigatorComponent extends Component {
|
class NavigatorComponent extends Component {
|
||||||
|
|
||||||
componentWillReceiveProps(newProps) {
|
componentWillReceiveProps(newProps) {
|
||||||
if (newProps.route) {
|
if (newProps.route) {
|
||||||
const screenInfo = this.props.screens[newProps.route.routeName];
|
const screenInfo = this.props.screens[newProps.route.routeName];
|
||||||
@@ -18,7 +17,10 @@ class NavigatorComponent extends Component {
|
|||||||
|
|
||||||
updateWindowTitle(title) {
|
updateWindowTitle(title) {
|
||||||
try {
|
try {
|
||||||
if (bridge().window()) bridge().window().setTitle(title);
|
if (bridge().window())
|
||||||
|
bridge()
|
||||||
|
.window()
|
||||||
|
.setTitle(title);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('updateWindowTitle', error);
|
console.warn('updateWindowTitle', error);
|
||||||
}
|
}
|
||||||
@@ -39,19 +41,16 @@ class NavigatorComponent extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={this.props.style}>
|
<div style={this.props.style}>
|
||||||
<Screen style={screenStyle} {...screenProps}/>
|
<Screen style={screenStyle} {...screenProps} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Navigator = connect(
|
const Navigator = connect(state => {
|
||||||
(state) => {
|
return {
|
||||||
return {
|
route: state.route,
|
||||||
route: state.route,
|
};
|
||||||
};
|
})(NavigatorComponent);
|
||||||
}
|
|
||||||
)(NavigatorComponent)
|
|
||||||
|
|
||||||
module.exports = { Navigator };
|
module.exports = { Navigator };
|
@@ -7,20 +7,14 @@ const BaseModel = require('lib/BaseModel');
|
|||||||
const markJsUtils = require('lib/markJsUtils');
|
const markJsUtils = require('lib/markJsUtils');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { bridge } = require('electron').remote.require('./bridge');
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
const Menu = bridge().Menu;
|
|
||||||
const MenuItem = bridge().MenuItem;
|
|
||||||
const eventManager = require('../eventManager');
|
const eventManager = require('../eventManager');
|
||||||
const InteropService = require('lib/services/InteropService');
|
|
||||||
const InteropServiceHelper = require('../InteropServiceHelper.js');
|
|
||||||
const Search = require('lib/models/Search');
|
|
||||||
const { stateUtils } = require('lib/reducer');
|
|
||||||
const Mark = require('mark.js/dist/mark.min.js');
|
const Mark = require('mark.js/dist/mark.min.js');
|
||||||
const SearchEngine = require('lib/services/SearchEngine');
|
const SearchEngine = require('lib/services/SearchEngine');
|
||||||
|
const Note = require('lib/models/Note');
|
||||||
const NoteListUtils = require('./utils/NoteListUtils');
|
const NoteListUtils = require('./utils/NoteListUtils');
|
||||||
const { replaceRegexDiacritics, pregQuote } = require('lib/string-utils');
|
const { replaceRegexDiacritics, pregQuote } = require('lib/string-utils');
|
||||||
|
|
||||||
class NoteListComponent extends React.Component {
|
class NoteListComponent extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -116,9 +110,9 @@ class NoteListComponent extends React.Component {
|
|||||||
id: item.id,
|
id: item.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const onDragStart = (event) => {
|
const onDragStart = event => {
|
||||||
let noteIds = [];
|
let noteIds = [];
|
||||||
|
|
||||||
// Here there is two cases:
|
// Here there is two cases:
|
||||||
@@ -136,17 +130,17 @@ class NoteListComponent extends React.Component {
|
|||||||
event.dataTransfer.setDragImage(new Image(), 1, 1);
|
event.dataTransfer.setDragImage(new Image(), 1, 1);
|
||||||
event.dataTransfer.clearData();
|
event.dataTransfer.clearData();
|
||||||
event.dataTransfer.setData('text/x-jop-note-ids', JSON.stringify(noteIds));
|
event.dataTransfer.setData('text/x-jop-note-ids', JSON.stringify(noteIds));
|
||||||
}
|
};
|
||||||
|
|
||||||
const onCheckboxClick = async (event) => {
|
const onCheckboxClick = async event => {
|
||||||
const checked = event.target.checked;
|
const checked = event.target.checked;
|
||||||
const newNote = {
|
const newNote = {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
todo_completed: checked ? time.unixMs() : 0,
|
todo_completed: checked ? time.unixMs() : 0,
|
||||||
}
|
};
|
||||||
await Note.save(newNote, { userSideValidation: true });
|
await Note.save(newNote, { userSideValidation: true });
|
||||||
eventManager.emit('todoToggle', { noteId: item.id });
|
eventManager.emit('todoToggle', { noteId: item.id });
|
||||||
}
|
};
|
||||||
|
|
||||||
const hPadding = 10;
|
const hPadding = 10;
|
||||||
|
|
||||||
@@ -167,11 +161,18 @@ class NoteListComponent extends React.Component {
|
|||||||
|
|
||||||
// Setting marginBottom = 1 because it makes the checkbox looks more centered, at least on Windows
|
// Setting marginBottom = 1 because it makes the checkbox looks more centered, at least on Windows
|
||||||
// but don't know how it will look in other OSes.
|
// but don't know how it will look in other OSes.
|
||||||
const checkbox = item.is_todo ?
|
const checkbox = item.is_todo ? (
|
||||||
<div style={{display: 'flex', height: style.height, alignItems: 'center', paddingLeft: hPadding}}>
|
<div style={{ display: 'flex', height: style.height, alignItems: 'center', paddingLeft: hPadding }}>
|
||||||
<input style={{margin:0, marginBottom:1}} type="checkbox" defaultChecked={!!item.todo_completed} onClick={(event) => { onCheckboxClick(event, item) }}/>
|
<input
|
||||||
|
style={{ margin: 0, marginBottom: 1 }}
|
||||||
|
type="checkbox"
|
||||||
|
defaultChecked={!!item.todo_completed}
|
||||||
|
onClick={event => {
|
||||||
|
onCheckboxClick(event, item);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
: null;
|
) : null;
|
||||||
|
|
||||||
let listItemTitleStyle = Object.assign({}, this.style().listItemTitle);
|
let listItemTitleStyle = Object.assign({}, this.style().listItemTitle);
|
||||||
listItemTitleStyle.paddingLeft = !checkbox ? hPadding : 4;
|
listItemTitleStyle.paddingLeft = !checkbox ? hPadding : 4;
|
||||||
@@ -204,41 +205,43 @@ class NoteListComponent extends React.Component {
|
|||||||
// with `textContent` so it cannot contain any XSS attacks. We use this feature because
|
// with `textContent` so it cannot contain any XSS attacks. We use this feature because
|
||||||
// mark.js can only deal with DOM elements.
|
// mark.js can only deal with DOM elements.
|
||||||
// https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
|
// https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
|
||||||
titleComp = <span dangerouslySetInnerHTML={{ __html: titleElement.outerHTML }}></span>
|
titleComp = <span dangerouslySetInnerHTML={{ __html: titleElement.outerHTML }}></span>;
|
||||||
} else {
|
} else {
|
||||||
titleComp = <span>{displayTitle}</span>
|
titleComp = <span>{displayTitle}</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const watchedIconStyle = {
|
const watchedIconStyle = {
|
||||||
paddingRight: 4,
|
paddingRight: 4,
|
||||||
color: theme.color,
|
color: theme.color,
|
||||||
};
|
};
|
||||||
const watchedIcon = this.props.watchedNoteFiles.indexOf(item.id) < 0 ? null : (
|
const watchedIcon = this.props.watchedNoteFiles.indexOf(item.id) < 0 ? null : <i style={watchedIconStyle} className={'fa fa-external-link'}></i>;
|
||||||
<i style={watchedIconStyle} className={"fa fa-external-link"}></i>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!this.itemAnchorRefs_[item.id]) this.itemAnchorRefs_[item.id] = React.createRef();
|
if (!this.itemAnchorRefs_[item.id]) this.itemAnchorRefs_[item.id] = React.createRef();
|
||||||
const ref = this.itemAnchorRefs_[item.id];
|
const ref = this.itemAnchorRefs_[item.id];
|
||||||
|
|
||||||
// Need to include "todo_completed" in key so that checkbox is updated when
|
// Need to include "todo_completed" in key so that checkbox is updated when
|
||||||
// item is changed via sync.
|
// item is changed via sync.
|
||||||
return <div key={item.id + '_' + item.todo_completed} style={style}>
|
return (
|
||||||
{checkbox}
|
<div key={item.id + '_' + item.todo_completed} style={style}>
|
||||||
<a
|
{checkbox}
|
||||||
ref={ref}
|
<a
|
||||||
className="list-item"
|
ref={ref}
|
||||||
onContextMenu={(event) => this.itemContextMenu(event)}
|
className="list-item"
|
||||||
href="#"
|
onContextMenu={event => this.itemContextMenu(event)}
|
||||||
draggable={true}
|
href="#"
|
||||||
style={listItemTitleStyle}
|
draggable={true}
|
||||||
onClick={(event) => { onTitleClick(event, item) }}
|
style={listItemTitleStyle}
|
||||||
onDragStart={(event) => onDragStart(event) }
|
onClick={event => {
|
||||||
data-id={item.id}
|
onTitleClick(event, item);
|
||||||
>
|
}}
|
||||||
{watchedIcon}
|
onDragStart={event => onDragStart(event)}
|
||||||
{titleComp}
|
data-id={item.id}
|
||||||
</a>
|
>
|
||||||
</div>
|
{watchedIcon}
|
||||||
|
{titleComp}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
itemAnchorRef(itemId) {
|
itemAnchorRef(itemId) {
|
||||||
@@ -288,7 +291,8 @@ class NoteListComponent extends React.Component {
|
|||||||
const keyCode = event.keyCode;
|
const keyCode = event.keyCode;
|
||||||
const noteIds = this.props.selectedNoteIds;
|
const noteIds = this.props.selectedNoteIds;
|
||||||
|
|
||||||
if (noteIds.length === 1 && (keyCode === 40 || keyCode === 38)) { // DOWN / UP
|
if (noteIds.length === 1 && (keyCode === 40 || keyCode === 38)) {
|
||||||
|
// DOWN / UP
|
||||||
const noteId = noteIds[0];
|
const noteId = noteIds[0];
|
||||||
let noteIndex = BaseModel.modelIndexById(this.props.notes, noteId);
|
let noteIndex = BaseModel.modelIndexById(this.props.notes, noteId);
|
||||||
const inc = keyCode === 38 ? -1 : +1;
|
const inc = keyCode === 38 ? -1 : +1;
|
||||||
@@ -312,12 +316,14 @@ class NoteListComponent extends React.Component {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noteIds.length && (keyCode === 46 || (keyCode === 8 && event.metaKey))) { // DELETE / CMD+Backspace
|
if (noteIds.length && (keyCode === 46 || (keyCode === 8 && event.metaKey))) {
|
||||||
|
// DELETE / CMD+Backspace
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
await NoteListUtils.confirmDeleteNotes(noteIds);
|
await NoteListUtils.confirmDeleteNotes(noteIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noteIds.length && keyCode === 32) { // SPACE
|
if (noteIds.length && keyCode === 32) {
|
||||||
|
// SPACE
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const notes = BaseModel.modelsByIds(this.props.notes, noteIds);
|
const notes = BaseModel.modelsByIds(this.props.notes, noteIds);
|
||||||
@@ -332,7 +338,8 @@ class NoteListComponent extends React.Component {
|
|||||||
this.focusNoteId_(todos[0].id);
|
this.focusNoteId_(todos[0].id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyCode === 9) { // TAB
|
if (keyCode === 9) {
|
||||||
|
// TAB
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (event.shiftKey) {
|
if (event.shiftKey) {
|
||||||
@@ -361,7 +368,7 @@ class NoteListComponent extends React.Component {
|
|||||||
this.focusItemIID_ = setInterval(() => {
|
this.focusItemIID_ = setInterval(() => {
|
||||||
if (this.itemAnchorRef(noteId)) {
|
if (this.itemAnchorRef(noteId)) {
|
||||||
this.itemAnchorRef(noteId).focus();
|
this.itemAnchorRef(noteId).focus();
|
||||||
clearInterval(this.focusItemIID_)
|
clearInterval(this.focusItemIID_);
|
||||||
this.focusItemIID_ = null;
|
this.focusItemIID_ = null;
|
||||||
}
|
}
|
||||||
}, 10);
|
}, 10);
|
||||||
@@ -384,34 +391,26 @@ class NoteListComponent extends React.Component {
|
|||||||
|
|
||||||
if (!notes.length) {
|
if (!notes.length) {
|
||||||
const padding = 10;
|
const padding = 10;
|
||||||
const emptyDivStyle = Object.assign({
|
const emptyDivStyle = Object.assign(
|
||||||
padding: padding + 'px',
|
{
|
||||||
fontSize: theme.fontSize,
|
padding: padding + 'px',
|
||||||
color: theme.color,
|
fontSize: theme.fontSize,
|
||||||
backgroundColor: theme.backgroundColor,
|
color: theme.color,
|
||||||
fontFamily: theme.fontFamily,
|
backgroundColor: theme.backgroundColor,
|
||||||
}, style);
|
fontFamily: theme.fontFamily,
|
||||||
|
},
|
||||||
|
style
|
||||||
|
);
|
||||||
emptyDivStyle.width = emptyDivStyle.width - padding * 2;
|
emptyDivStyle.width = emptyDivStyle.width - padding * 2;
|
||||||
emptyDivStyle.height = emptyDivStyle.height - padding * 2;
|
emptyDivStyle.height = emptyDivStyle.height - padding * 2;
|
||||||
return <div style={emptyDivStyle}>{ this.props.folders.length ? _('No notes in here. Create one by clicking on "New note".') : _('There is currently no notebook. Create one by clicking on "New notebook".')}</div>
|
return <div style={emptyDivStyle}>{this.props.folders.length ? _('No notes in here. Create one by clicking on "New note".') : _('There is currently no notebook. Create one by clicking on "New notebook".')}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <ItemList ref={this.itemListRef} itemHeight={this.style().listItem.height} className={'note-list'} items={notes} style={style} itemRenderer={this.itemRenderer} onKeyDown={this.onKeyDown} />;
|
||||||
<ItemList
|
|
||||||
ref={this.itemListRef}
|
|
||||||
itemHeight={this.style().listItem.height}
|
|
||||||
className={"note-list"}
|
|
||||||
items={notes}
|
|
||||||
style={style}
|
|
||||||
itemRenderer={this.itemRenderer}
|
|
||||||
onKeyDown={this.onKeyDown}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
notes: state.notes,
|
notes: state.notes,
|
||||||
folders: state.folders,
|
folders: state.folders,
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { connect } = require('react-redux');
|
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const moment = require('moment');
|
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
const Datetime = require('react-datetime');
|
const Datetime = require('react-datetime');
|
||||||
@@ -10,7 +8,6 @@ const formatcoords = require('formatcoords');
|
|||||||
const { bridge } = require('electron').remote.require('./bridge');
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
|
|
||||||
class NotePropertiesDialog extends React.Component {
|
class NotePropertiesDialog extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -131,7 +128,7 @@ class NotePropertiesDialog extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.styles_.input = {
|
this.styles_.input = {
|
||||||
display:'inline-block',
|
display: 'inline-block',
|
||||||
color: theme.color,
|
color: theme.color,
|
||||||
backgroundColor: theme.backgroundColor,
|
backgroundColor: theme.backgroundColor,
|
||||||
border: '1px solid',
|
border: '1px solid',
|
||||||
@@ -205,68 +202,86 @@ class NotePropertiesDialog extends React.Component {
|
|||||||
newFormNote[this.state.editedKey] = this.state.editedValue;
|
newFormNote[this.state.editedKey] = this.state.editedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState(
|
||||||
formNote: newFormNote,
|
{
|
||||||
editedKey: null,
|
formNote: newFormNote,
|
||||||
editedValue: null
|
editedKey: null,
|
||||||
}, () => { resolve() });
|
editedValue: null,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async cancelProperty() {
|
async cancelProperty() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.okButton.current.focus();
|
this.okButton.current.focus();
|
||||||
this.setState({
|
this.setState(
|
||||||
editedKey: null,
|
{
|
||||||
editedValue: null
|
editedKey: null,
|
||||||
}, () => { resolve() });
|
editedValue: null,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
createNoteField(key, value) {
|
createNoteField(key, value) {
|
||||||
const styles = this.styles(this.props.theme);
|
const styles = this.styles(this.props.theme);
|
||||||
const theme = themeStyle(this.props.theme);
|
const theme = themeStyle(this.props.theme);
|
||||||
const labelComp = <label style={Object.assign({}, theme.textStyle, {marginRight: '1em', width: '6em', display:'inline-block', fontWeight: 'bold'})}>{this.formatLabel(key)}</label>;
|
const labelComp = <label style={Object.assign({}, theme.textStyle, { marginRight: '1em', width: '6em', display: 'inline-block', fontWeight: 'bold' })}>{this.formatLabel(key)}</label>;
|
||||||
let controlComp = null;
|
let controlComp = null;
|
||||||
let editComp = null;
|
let editComp = null;
|
||||||
let editCompHandler = null;
|
let editCompHandler = null;
|
||||||
let editCompIcon = null;
|
let editCompIcon = null;
|
||||||
|
|
||||||
const onKeyDown = (event) => {
|
const onKeyDown = event => {
|
||||||
if (event.keyCode === 13) {
|
if (event.keyCode === 13) {
|
||||||
this.saveProperty();
|
this.saveProperty();
|
||||||
} else if (event.keyCode === 27) {
|
} else if (event.keyCode === 27) {
|
||||||
this.cancelProperty();
|
this.cancelProperty();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if (this.state.editedKey === key) {
|
if (this.state.editedKey === key) {
|
||||||
if (key.indexOf('_time') >= 0) {
|
if (key.indexOf('_time') >= 0) {
|
||||||
|
controlComp = (
|
||||||
|
<Datetime
|
||||||
|
ref="editField"
|
||||||
|
defaultValue={value}
|
||||||
|
dateFormat={time.dateFormat()}
|
||||||
|
timeFormat={time.timeFormat()}
|
||||||
|
inputProps={{
|
||||||
|
onKeyDown: event => onKeyDown(event, key),
|
||||||
|
style: styles.input,
|
||||||
|
}}
|
||||||
|
onChange={momentObject => {
|
||||||
|
this.setState({ editedValue: momentObject });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
controlComp = <Datetime
|
editCompHandler = () => {
|
||||||
ref="editField"
|
this.saveProperty();
|
||||||
defaultValue={value}
|
};
|
||||||
dateFormat={time.dateFormat()}
|
|
||||||
timeFormat={time.timeFormat()}
|
|
||||||
inputProps={{
|
|
||||||
onKeyDown: (event) => onKeyDown(event, key),
|
|
||||||
style: styles.input
|
|
||||||
}}
|
|
||||||
onChange={(momentObject) => {this.setState({ editedValue: momentObject })}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
editCompHandler = () => {this.saveProperty()};
|
|
||||||
editCompIcon = 'fa-save';
|
editCompIcon = 'fa-save';
|
||||||
} else {
|
} else {
|
||||||
|
controlComp = (
|
||||||
controlComp = <input
|
<input
|
||||||
defaultValue={value}
|
defaultValue={value}
|
||||||
type="text"
|
type="text"
|
||||||
ref="editField"
|
ref="editField"
|
||||||
onChange={(event) => {this.setState({ editedValue: event.target.value })}}
|
onChange={event => {
|
||||||
onKeyDown={(event) => onKeyDown(event)}
|
this.setState({ editedValue: event.target.value });
|
||||||
style={styles.input}
|
}}
|
||||||
/>
|
onKeyDown={event => onKeyDown(event)}
|
||||||
|
style={styles.input}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let displayedValue = value;
|
let displayedValue = value;
|
||||||
@@ -287,15 +302,25 @@ class NotePropertiesDialog extends React.Component {
|
|||||||
const ll = this.latLongFromLocation(value);
|
const ll = this.latLongFromLocation(value);
|
||||||
url = Note.geoLocationUrlFromLatLong(ll.latitude, ll.longitude);
|
url = Note.geoLocationUrlFromLatLong(ll.latitude, ll.longitude);
|
||||||
}
|
}
|
||||||
controlComp = <a href="#" onClick={() => bridge().openExternal(url)} style={theme.urlStyle}>{displayedValue}</a>
|
controlComp = (
|
||||||
|
<a href="#" onClick={() => bridge().openExternal(url)} style={theme.urlStyle}>
|
||||||
|
{displayedValue}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
} else if (key === 'revisionsLink') {
|
} else if (key === 'revisionsLink') {
|
||||||
controlComp = <a href="#" onClick={this.revisionsLink_click} style={theme.urlStyle}>{_('Previous versions of this note')}</a>
|
controlComp = (
|
||||||
|
<a href="#" onClick={this.revisionsLink_click} style={theme.urlStyle}>
|
||||||
|
{_('Previous versions of this note')}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
controlComp = <div style={Object.assign({}, theme.textStyle, {display: 'inline-block'})}>{displayedValue}</div>
|
controlComp = <div style={Object.assign({}, theme.textStyle, { display: 'inline-block' })}>{displayedValue}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['id', 'revisionsLink', 'markup_language'].indexOf(key) < 0) {
|
if (['id', 'revisionsLink', 'markup_language'].indexOf(key) < 0) {
|
||||||
editCompHandler = () => {this.editPropertyButtonClick(key, value)};
|
editCompHandler = () => {
|
||||||
|
this.editPropertyButtonClick(key, value);
|
||||||
|
};
|
||||||
editCompIcon = 'fa-edit';
|
editCompIcon = 'fa-edit';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,16 +328,16 @@ class NotePropertiesDialog extends React.Component {
|
|||||||
if (editCompHandler) {
|
if (editCompHandler) {
|
||||||
editComp = (
|
editComp = (
|
||||||
<a href="#" onClick={editCompHandler} style={styles.editPropertyButton}>
|
<a href="#" onClick={editCompHandler} style={styles.editPropertyButton}>
|
||||||
<i className={'fa ' + editCompIcon} aria-hidden="true" style={{ marginLeft: '.5em'}}></i>
|
<i className={'fa ' + editCompIcon} aria-hidden="true" style={{ marginLeft: '.5em' }}></i>
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={key} style={this.styles_.controlBox} className="note-property-box">
|
<div key={key} style={this.styles_.controlBox} className="note-property-box">
|
||||||
{ labelComp }
|
{labelComp}
|
||||||
{ controlComp }
|
{controlComp}
|
||||||
{ editComp }
|
{editComp}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -325,7 +350,7 @@ class NotePropertiesDialog extends React.Component {
|
|||||||
formatValue(key, note) {
|
formatValue(key, note) {
|
||||||
if (key === 'location') {
|
if (key === 'location') {
|
||||||
if (!Number(note.latitude) && !Number(note.longitude)) return null;
|
if (!Number(note.latitude) && !Number(note.longitude)) return null;
|
||||||
const dms = formatcoords(Number(note.latitude), Number(note.longitude))
|
const dms = formatcoords(Number(note.latitude), Number(note.longitude));
|
||||||
return dms.format('DDMMss', { decimalPlaces: 0 });
|
return dms.format('DDMMss', { decimalPlaces: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,24 +362,21 @@ class NotePropertiesDialog extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const style = this.props.style;
|
|
||||||
const theme = themeStyle(this.props.theme);
|
const theme = themeStyle(this.props.theme);
|
||||||
const styles = this.styles(this.props.theme);
|
const styles = this.styles(this.props.theme);
|
||||||
const formNote = this.state.formNote;
|
const formNote = this.state.formNote;
|
||||||
|
|
||||||
const buttonComps = [];
|
const buttonComps = [];
|
||||||
buttonComps.push(
|
buttonComps.push(
|
||||||
<button
|
<button key="ok" style={styles.button} onClick={this.okButton_click} ref={this.okButton} onKeyDown={this.onKeyDown}>
|
||||||
key="ok"
|
|
||||||
style={styles.button}
|
|
||||||
onClick={this.okButton_click}
|
|
||||||
ref={this.okButton}
|
|
||||||
onKeyDown={this.onKeyDown}
|
|
||||||
>
|
|
||||||
{_('Apply')}
|
{_('Apply')}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
buttonComps.push(<button key="cancel" style={styles.button} onClick={this.cancelButton_click}>{_('Cancel')}</button>);
|
buttonComps.push(
|
||||||
|
<button key="cancel" style={styles.button} onClick={this.cancelButton_click}>
|
||||||
|
{_('Cancel')}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
const noteComps = [];
|
const noteComps = [];
|
||||||
|
|
||||||
@@ -371,14 +393,11 @@ class NotePropertiesDialog extends React.Component {
|
|||||||
<div style={theme.dialogBox}>
|
<div style={theme.dialogBox}>
|
||||||
<div style={theme.dialogTitle}>{_('Note properties')}</div>
|
<div style={theme.dialogTitle}>{_('Note properties')}</div>
|
||||||
<div>{noteComps}</div>
|
<div>{noteComps}</div>
|
||||||
<div style={{ textAlign: 'right', marginTop: 10 }}>
|
<div style={{ textAlign: 'right', marginTop: 10 }}>{buttonComps}</div>
|
||||||
{buttonComps}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = NotePropertiesDialog;
|
module.exports = NotePropertiesDialog;
|
||||||
|
@@ -6,6 +6,7 @@ const NoteTextViewer = require('./NoteTextViewer.min');
|
|||||||
const HelpButton = require('./HelpButton.min');
|
const HelpButton = require('./HelpButton.min');
|
||||||
const BaseModel = require('lib/BaseModel');
|
const BaseModel = require('lib/BaseModel');
|
||||||
const Revision = require('lib/models/Revision');
|
const Revision = require('lib/models/Revision');
|
||||||
|
const Note = require('lib/models/Note');
|
||||||
const Setting = require('lib/models/Setting');
|
const Setting = require('lib/models/Setting');
|
||||||
const RevisionService = require('lib/services/RevisionService');
|
const RevisionService = require('lib/services/RevisionService');
|
||||||
const shared = require('lib/components/shared/note-screen-shared.js');
|
const shared = require('lib/components/shared/note-screen-shared.js');
|
||||||
@@ -15,7 +16,6 @@ const ReactTooltip = require('react-tooltip');
|
|||||||
const { substrWithEllipsis } = require('lib/string-utils');
|
const { substrWithEllipsis } = require('lib/string-utils');
|
||||||
|
|
||||||
class NoteRevisionViewerComponent extends React.PureComponent {
|
class NoteRevisionViewerComponent extends React.PureComponent {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -56,12 +56,15 @@ class NoteRevisionViewerComponent extends React.PureComponent {
|
|||||||
|
|
||||||
const revisions = await Revision.allByType(BaseModel.TYPE_NOTE, this.props.noteId);
|
const revisions = await Revision.allByType(BaseModel.TYPE_NOTE, this.props.noteId);
|
||||||
|
|
||||||
this.setState({
|
this.setState(
|
||||||
revisions: revisions,
|
{
|
||||||
currentRevId: revisions.length ? revisions[revisions.length - 1].id : '',
|
revisions: revisions,
|
||||||
}, () => {
|
currentRevId: revisions.length ? revisions[revisions.length - 1].id : '',
|
||||||
this.reloadNote();
|
},
|
||||||
});
|
() => {
|
||||||
|
this.reloadNote();
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async importButton_onClick() {
|
async importButton_onClick() {
|
||||||
@@ -82,11 +85,14 @@ class NoteRevisionViewerComponent extends React.PureComponent {
|
|||||||
if (!value) {
|
if (!value) {
|
||||||
if (this.props.onBack) this.props.onBack();
|
if (this.props.onBack) this.props.onBack();
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState(
|
||||||
currentRevId: value,
|
{
|
||||||
}, () => {
|
currentRevId: value,
|
||||||
this.reloadNote();
|
},
|
||||||
});
|
() => {
|
||||||
|
this.reloadNote();
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +123,7 @@ class NoteRevisionViewerComponent extends React.PureComponent {
|
|||||||
resources: await shared.attachedResources(noteBody),
|
resources: await shared.attachedResources(noteBody),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.viewerRef_.current.wrappedInstance.send('setHtml', result.html, { cssFiles: result.cssFiles });
|
this.viewerRef_.current.wrappedInstance.send('setHtml', result.html, { cssFiles: result.cssFiles });
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -130,45 +136,45 @@ class NoteRevisionViewerComponent extends React.PureComponent {
|
|||||||
const rev = revs[i];
|
const rev = revs[i];
|
||||||
const stats = Revision.revisionPatchStatsText(rev);
|
const stats = Revision.revisionPatchStatsText(rev);
|
||||||
|
|
||||||
revisionListItems.push(<option
|
revisionListItems.push(
|
||||||
key={rev.id}
|
<option key={rev.id} value={rev.id}>
|
||||||
value={rev.id}
|
{time.formatMsToLocal(rev.item_updated_time) + ' (' + stats + ')'}
|
||||||
>{time.formatMsToLocal(rev.item_updated_time) + ' (' + stats + ')'}</option>);
|
</option>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const restoreButtonTitle = _('Restore');
|
const restoreButtonTitle = _('Restore');
|
||||||
const helpMessage = _('Click "%s" to restore the note. It will be copied in the notebook named "%s". The current version of the note will not be replaced or modified.', restoreButtonTitle, RevisionService.instance().restoreFolderTitle());
|
const helpMessage = _('Click "%s" to restore the note. It will be copied in the notebook named "%s". The current version of the note will not be replaced or modified.', restoreButtonTitle, RevisionService.instance().restoreFolderTitle());
|
||||||
|
|
||||||
const titleInput = (
|
const titleInput = (
|
||||||
<div style={{display:'flex', flexDirection: 'row', alignItems:'center', marginBottom: 10, borderWidth: 1, borderBottomStyle: 'solid', borderColor: theme.dividerColor, paddingBottom:10}}>
|
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', marginBottom: 10, borderWidth: 1, borderBottomStyle: 'solid', borderColor: theme.dividerColor, paddingBottom: 10 }}>
|
||||||
<button onClick={this.backButton_click} style={Object.assign({}, theme.buttonStyle, { marginRight: 10, height: theme.inputStyle.height })}>{'⬅ ' + _('Back')}</button>
|
<button onClick={this.backButton_click} style={Object.assign({}, theme.buttonStyle, { marginRight: 10, height: theme.inputStyle.height })}>
|
||||||
<input readOnly type="text" style={style.titleInput} value={this.state.note ? this.state.note.title : ''}/>
|
{'⬅ ' + _('Back')}
|
||||||
|
</button>
|
||||||
|
<input readOnly type="text" style={style.titleInput} value={this.state.note ? this.state.note.title : ''} />
|
||||||
<select disabled={!this.state.revisions.length} value={this.state.currentRevId} style={style.revisionList} onChange={this.revisionList_onChange}>
|
<select disabled={!this.state.revisions.length} value={this.state.currentRevId} style={style.revisionList} onChange={this.revisionList_onChange}>
|
||||||
{revisionListItems}
|
{revisionListItems}
|
||||||
</select>
|
</select>
|
||||||
<button disabled={!this.state.revisions.length || this.state.restoring} onClick={this.importButton_onClick} style={Object.assign({}, theme.buttonStyle, { marginLeft: 10, height: theme.inputStyle.height })}>{restoreButtonTitle}</button>
|
<button disabled={!this.state.revisions.length || this.state.restoring} onClick={this.importButton_onClick} style={Object.assign({}, theme.buttonStyle, { marginLeft: 10, height: theme.inputStyle.height })}>
|
||||||
<HelpButton tip={helpMessage} id="noteRevisionHelpButton" onClick={this.helpButton_onClick}/>
|
{restoreButtonTitle}
|
||||||
|
</button>
|
||||||
|
<HelpButton tip={helpMessage} id="noteRevisionHelpButton" onClick={this.helpButton_onClick} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const viewer = <NoteTextViewer
|
const viewer = <NoteTextViewer viewerStyle={{ display: 'flex', flex: 1 }} ref={this.viewerRef_} onDomReady={this.viewer_domReady} />;
|
||||||
viewerStyle={{display:'flex', flex:1}}
|
|
||||||
ref={this.viewerRef_}
|
|
||||||
onDomReady={this.viewer_domReady}
|
|
||||||
/>
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={style.root}>
|
<div style={style.root}>
|
||||||
{titleInput}
|
{titleInput}
|
||||||
{viewer}
|
{viewer}
|
||||||
<ReactTooltip place="bottom" delayShow={300} className="help-tooltip"/>
|
<ReactTooltip place="bottom" delayShow={300} className="help-tooltip" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
theme: state.settings.theme,
|
theme: state.settings.theme,
|
||||||
};
|
};
|
||||||
|
@@ -4,7 +4,6 @@ const { themeStyle } = require('../theme.js');
|
|||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
|
|
||||||
class NoteSearchBarComponent extends React.Component {
|
class NoteSearchBarComponent extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -54,14 +53,12 @@ class NoteSearchBarComponent extends React.Component {
|
|||||||
color: theme.color,
|
color: theme.color,
|
||||||
};
|
};
|
||||||
|
|
||||||
const icon = <i style={iconStyle} className={"fa " + iconName}></i>
|
const icon = <i style={iconStyle} className={'fa ' + iconName}></i>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a href="#" style={searchButton} onClick={clickHandler}>
|
||||||
href="#"
|
{icon}
|
||||||
style={searchButton}
|
</a>
|
||||||
onClick={clickHandler}
|
|
||||||
>{icon}</a>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +69,8 @@ class NoteSearchBarComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
searchInput_keyDown(event) {
|
searchInput_keyDown(event) {
|
||||||
if (event.keyCode === 13) { // ENTER
|
if (event.keyCode === 13) {
|
||||||
|
// ENTER
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (!event.shiftKey) {
|
if (!event.shiftKey) {
|
||||||
@@ -82,7 +80,8 @@ class NoteSearchBarComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.keyCode === 27) { // ESCAPE
|
if (event.keyCode === 27) {
|
||||||
|
// ESCAPE
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (this.props.onClose) this.props.onClose();
|
if (this.props.onClose) this.props.onClose();
|
||||||
@@ -110,32 +109,34 @@ class NoteSearchBarComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const theme = themeStyle(this.props.theme);
|
|
||||||
|
|
||||||
const closeButton = this.buttonIconComponent('fa-times', this.closeButton_click);
|
const closeButton = this.buttonIconComponent('fa-times', this.closeButton_click);
|
||||||
const previousButton = this.buttonIconComponent('fa-chevron-up', this.previousButton_click);
|
const previousButton = this.buttonIconComponent('fa-chevron-up', this.previousButton_click);
|
||||||
const nextButton = this.buttonIconComponent('fa-chevron-down', this.nextButton_click);
|
const nextButton = this.buttonIconComponent('fa-chevron-down', this.nextButton_click);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={this.props.style}>
|
<div style={this.props.style}>
|
||||||
<div style={{display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||||
{ closeButton }
|
{closeButton}
|
||||||
<input placeholder={_('Search...')} value={this.state.query} onChange={this.searchInput_change} onKeyDown={this.searchInput_keyDown} ref="searchInput" type="text" style={{width: 200, marginRight: 5}}></input>
|
<input placeholder={_('Search...')} value={this.state.query} onChange={this.searchInput_change} onKeyDown={this.searchInput_keyDown} ref="searchInput" type="text" style={{ width: 200, marginRight: 5 }}></input>
|
||||||
{ nextButton }
|
{nextButton}
|
||||||
{ previousButton }
|
{previousButton}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
theme: state.settings.theme,
|
theme: state.settings.theme,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const NoteSearchBar = connect(mapStateToProps, null, null, { withRef: true })(NoteSearchBarComponent);
|
const NoteSearchBar = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
{ withRef: true }
|
||||||
|
)(NoteSearchBarComponent);
|
||||||
|
|
||||||
module.exports = NoteSearchBar;
|
module.exports = NoteSearchBar;
|
@@ -2,15 +2,11 @@ const React = require('react');
|
|||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
const { _ } = require('lib/locale.js');
|
|
||||||
|
|
||||||
class NoteStatusBarComponent extends React.Component {
|
class NoteStatusBarComponent extends React.Component {
|
||||||
|
|
||||||
style() {
|
style() {
|
||||||
const theme = themeStyle(this.props.theme);
|
const theme = themeStyle(this.props.theme);
|
||||||
|
|
||||||
const itemHeight = 34;
|
|
||||||
|
|
||||||
let style = {
|
let style = {
|
||||||
root: Object.assign({}, theme.textStyle, {
|
root: Object.assign({}, theme.textStyle, {
|
||||||
backgroundColor: theme.backgroundColor,
|
backgroundColor: theme.backgroundColor,
|
||||||
@@ -22,18 +18,12 @@ class NoteStatusBarComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const theme = themeStyle(this.props.theme);
|
|
||||||
const style = this.props.style;
|
|
||||||
const note = this.props.note;
|
const note = this.props.note;
|
||||||
|
return <div style={this.style().root}>{time.formatMsToLocal(note.user_updated_time)}</div>;
|
||||||
return (
|
|
||||||
<div style={this.style().root}>{time.formatMsToLocal(note.user_updated_time)}</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
// notes: state.notes,
|
// notes: state.notes,
|
||||||
// folders: state.folders,
|
// folders: state.folders,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,7 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
const { themeStyle } = require('../theme.js');
|
|
||||||
const { _ } = require('lib/locale.js');
|
|
||||||
|
|
||||||
class NoteTextViewerComponent extends React.Component {
|
class NoteTextViewerComponent extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -49,7 +46,7 @@ class NoteTextViewerComponent extends React.Component {
|
|||||||
|
|
||||||
let isAlreadyReady = false;
|
let isAlreadyReady = false;
|
||||||
try {
|
try {
|
||||||
isAlreadyReady = !this.webviewRef_.current.isLoading()
|
isAlreadyReady = !this.webviewRef_.current.isLoading();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ignore - it means the view has not started loading, and the DOM ready event has not been emitted yet
|
// Ignore - it means the view has not started loading, and the DOM ready event has not been emitted yet
|
||||||
// Error is "The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called."
|
// Error is "The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called."
|
||||||
@@ -111,14 +108,14 @@ class NoteTextViewerComponent extends React.Component {
|
|||||||
return this.webviewRef_.current.getWebContents().printToPDF(options, callback);
|
return this.webviewRef_.current.getWebContents().printToPDF(options, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
print(options = {}) {
|
print() {
|
||||||
// In Electron 4x, print is broken so need to use this hack:
|
// In Electron 4x, print is broken so need to use this hack:
|
||||||
// https://github.com/electron/electron/issues/16219#issuecomment-451454948
|
// https://github.com/electron/electron/issues/16219#issuecomment-451454948
|
||||||
// Note that this is not a perfect workaround since it means the options are ignored
|
// Note that this is not a perfect workaround since it means the options are ignored
|
||||||
// In particular it means that background images and colours won't be printed (printBackground property will be ignored)
|
// In particular it means that background images and colours won't be printed (printBackground property will be ignored)
|
||||||
|
|
||||||
// return this.webviewRef_.current.getWebContents().print({});
|
// return this.webviewRef_.current.getWebContents().print({});
|
||||||
return this.webviewRef_.current.getWebContents().executeJavaScript("window.print()")
|
return this.webviewRef_.current.getWebContents().executeJavaScript('window.print()');
|
||||||
}
|
}
|
||||||
|
|
||||||
openDevTools() {
|
openDevTools() {
|
||||||
@@ -138,23 +135,21 @@ class NoteTextViewerComponent extends React.Component {
|
|||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <webview
|
return <webview ref={this.webviewRef_} style={this.props.viewerStyle} preload="gui/note-viewer/preload.js" src="gui/note-viewer/index.html" webpreferences="contextIsolation" />;
|
||||||
ref={this.webviewRef_}
|
|
||||||
style={this.props.viewerStyle}
|
|
||||||
preload="gui/note-viewer/preload.js"
|
|
||||||
src="gui/note-viewer/index.html"
|
|
||||||
webpreferences="contextIsolation"
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
theme: state.settings.theme,
|
theme: state.settings.theme,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const NoteTextViewer = connect(mapStateToProps, null, null, { withRef: true })(NoteTextViewerComponent);
|
const NoteTextViewer = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
{ withRef: true }
|
||||||
|
)(NoteTextViewerComponent);
|
||||||
|
|
||||||
module.exports = NoteTextViewer;
|
module.exports = NoteTextViewer;
|
@@ -7,7 +7,6 @@ const { themeStyle } = require('../theme.js');
|
|||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
|
|
||||||
class OneDriveLoginScreenComponent extends React.Component {
|
class OneDriveLoginScreenComponent extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.webview_ = null;
|
this.webview_ = null;
|
||||||
@@ -37,7 +36,7 @@ class OneDriveLoginScreenComponent extends React.Component {
|
|||||||
webview_domReady() {
|
webview_domReady() {
|
||||||
this.setState({ webviewReady: true });
|
this.setState({ webviewReady: true });
|
||||||
|
|
||||||
this.webview_.addEventListener('did-navigate', async (event) => {
|
this.webview_.addEventListener('did-navigate', async event => {
|
||||||
const url = event.url;
|
const url = event.url;
|
||||||
|
|
||||||
if (this.authCode_) return;
|
if (this.authCode_) return;
|
||||||
@@ -50,11 +49,14 @@ class OneDriveLoginScreenComponent extends React.Component {
|
|||||||
this.authCode_ = parsedUrl.query.code;
|
this.authCode_ = parsedUrl.query.code;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await reg.syncTarget().api().execTokenRequest(this.authCode_, this.redirectUrl(), true);
|
await reg
|
||||||
|
.syncTarget()
|
||||||
|
.api()
|
||||||
|
.execTokenRequest(this.authCode_, this.redirectUrl(), true);
|
||||||
this.props.dispatch({ type: 'NAV_BACK' });
|
this.props.dispatch({ type: 'NAV_BACK' });
|
||||||
reg.scheduleSync(0);
|
reg.scheduleSync(0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
bridge().showErrorMessageBox('Could not login to OneDrive. Please try again.\n\n' + error.message + "\n\n" + url.match(/.{1,64}/g).join('\n'));
|
bridge().showErrorMessageBox('Could not login to OneDrive. Please try again.\n\n' + error.message + '\n\n' + url.match(/.{1,64}/g).join('\n'));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.authCode_ = null;
|
this.authCode_ = null;
|
||||||
@@ -62,11 +64,17 @@ class OneDriveLoginScreenComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
startUrl() {
|
startUrl() {
|
||||||
return reg.syncTarget().api().authCodeUrl(this.redirectUrl());
|
return reg
|
||||||
|
.syncTarget()
|
||||||
|
.api()
|
||||||
|
.authCodeUrl(this.redirectUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
redirectUrl() {
|
redirectUrl() {
|
||||||
return reg.syncTarget().api().nativeClientRedirectUrl();
|
return reg
|
||||||
|
.syncTarget()
|
||||||
|
.api()
|
||||||
|
.nativeClientRedirectUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -94,14 +102,13 @@ class OneDriveLoginScreenComponent extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Header style={headerStyle} buttons={headerButtons} />
|
<Header style={headerStyle} buttons={headerButtons} />
|
||||||
<webview src={this.startUrl()} style={webviewStyle} nodeintegration="1" ref={elem => this.webview_ = elem} />
|
<webview src={this.startUrl()} style={webviewStyle} nodeintegration="1" ref={elem => (this.webview_ = elem)} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
theme: state.settings.theme,
|
theme: state.settings.theme,
|
||||||
};
|
};
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { connect } = require('react-redux');
|
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const moment = require('moment');
|
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
const Datetime = require('react-datetime');
|
const Datetime = require('react-datetime');
|
||||||
@@ -10,7 +8,6 @@ const Select = require('react-select').default;
|
|||||||
const makeAnimated = require('react-select/lib/animated').default;
|
const makeAnimated = require('react-select/lib/animated').default;
|
||||||
|
|
||||||
class PromptDialog extends React.Component {
|
class PromptDialog extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -103,51 +100,58 @@ class PromptDialog extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.styles_.select = {
|
this.styles_.select = {
|
||||||
control: (provided) => (Object.assign(provided, {
|
control: provided =>
|
||||||
minWidth: width * 0.2,
|
Object.assign(provided, {
|
||||||
maxWidth: width * 0.5,
|
minWidth: width * 0.2,
|
||||||
})),
|
maxWidth: width * 0.5,
|
||||||
input: (provided) => (Object.assign(provided, {
|
}),
|
||||||
minWidth: '20px',
|
input: provided =>
|
||||||
color: theme.color,
|
Object.assign(provided, {
|
||||||
})),
|
minWidth: '20px',
|
||||||
menu: (provided) => (Object.assign(provided, {
|
color: theme.color,
|
||||||
color: theme.color,
|
}),
|
||||||
fontFamily: theme.fontFamily,
|
menu: provided =>
|
||||||
backgroundColor: theme.backgroundColor,
|
Object.assign(provided, {
|
||||||
})),
|
color: theme.color,
|
||||||
option: (provided) => (Object.assign(provided, {
|
fontFamily: theme.fontFamily,
|
||||||
color: theme.color,
|
backgroundColor: theme.backgroundColor,
|
||||||
fontFamily: theme.fontFamily,
|
}),
|
||||||
})),
|
option: provided =>
|
||||||
multiValueLabel: (provided) => (Object.assign(provided, {
|
Object.assign(provided, {
|
||||||
fontFamily: theme.fontFamily,
|
color: theme.color,
|
||||||
})),
|
fontFamily: theme.fontFamily,
|
||||||
multiValueRemove: (provided) => (Object.assign(provided, {
|
}),
|
||||||
color: theme.color,
|
multiValueLabel: provided =>
|
||||||
})),
|
Object.assign(provided, {
|
||||||
|
fontFamily: theme.fontFamily,
|
||||||
|
}),
|
||||||
|
multiValueRemove: provided =>
|
||||||
|
Object.assign(provided, {
|
||||||
|
color: theme.color,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.styles_.selectTheme = (tagTheme) => (Object.assign(tagTheme, {
|
this.styles_.selectTheme = tagTheme =>
|
||||||
borderRadius: 2,
|
Object.assign(tagTheme, {
|
||||||
colors: Object.assign(tagTheme.colors, {
|
borderRadius: 2,
|
||||||
primary: theme.raisedBackgroundColor,
|
colors: Object.assign(tagTheme.colors, {
|
||||||
primary25: theme.raisedBackgroundColor,
|
primary: theme.raisedBackgroundColor,
|
||||||
neutral0: theme.backgroundColor,
|
primary25: theme.raisedBackgroundColor,
|
||||||
neutral5: theme.backgroundColor,
|
neutral0: theme.backgroundColor,
|
||||||
neutral10: theme.raisedBackgroundColor,
|
neutral5: theme.backgroundColor,
|
||||||
neutral20: theme.raisedBackgroundColor,
|
neutral10: theme.raisedBackgroundColor,
|
||||||
neutral30: theme.raisedBackgroundColor,
|
neutral20: theme.raisedBackgroundColor,
|
||||||
neutral40: theme.color,
|
neutral30: theme.raisedBackgroundColor,
|
||||||
neutral50: theme.color,
|
neutral40: theme.color,
|
||||||
neutral60: theme.color,
|
neutral50: theme.color,
|
||||||
neutral70: theme.color,
|
neutral60: theme.color,
|
||||||
neutral80: theme.color,
|
neutral70: theme.color,
|
||||||
neutral90: theme.color,
|
neutral80: theme.color,
|
||||||
danger: theme.backgroundColor,
|
neutral90: theme.color,
|
||||||
dangerLight: theme.colorError2,
|
danger: theme.backgroundColor,
|
||||||
}),
|
dangerLight: theme.colorError2,
|
||||||
}));
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
this.styles_.desc = Object.assign({}, theme.textStyle, {
|
this.styles_.desc = Object.assign({}, theme.textStyle, {
|
||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
@@ -173,11 +177,11 @@ class PromptDialog extends React.Component {
|
|||||||
this.props.onClose(accept ? outputAnswer : null, buttonType);
|
this.props.onClose(accept ? outputAnswer : null, buttonType);
|
||||||
}
|
}
|
||||||
this.setState({ visible: false, answer: '' });
|
this.setState({ visible: false, answer: '' });
|
||||||
}
|
};
|
||||||
|
|
||||||
const onChange = (event) => {
|
const onChange = event => {
|
||||||
this.setState({ answer: event.target.value });
|
this.setState({ answer: event.target.value });
|
||||||
}
|
};
|
||||||
|
|
||||||
// const anythingToDate = (o) => {
|
// const anythingToDate = (o) => {
|
||||||
// if (o && o.toDate) return o.toDate();
|
// if (o && o.toDate) return o.toDate();
|
||||||
@@ -188,16 +192,16 @@ class PromptDialog extends React.Component {
|
|||||||
// return m.isValid() ? m.toDate() : null;
|
// return m.isValid() ? m.toDate() : null;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
const onDateTimeChange = (momentObject) => {
|
const onDateTimeChange = momentObject => {
|
||||||
this.setState({ answer: momentObject });
|
this.setState({ answer: momentObject });
|
||||||
}
|
};
|
||||||
|
|
||||||
const onSelectChange = (newValue) => {
|
const onSelectChange = newValue => {
|
||||||
this.setState({ answer: newValue });
|
this.setState({ answer: newValue });
|
||||||
this.focusInput_ = true;
|
this.focusInput_ = true;
|
||||||
}
|
};
|
||||||
|
|
||||||
const onKeyDown = (event) => {
|
const onKeyDown = event => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
if (this.props.inputType !== 'tags' && this.props.inputType !== 'dropdown') {
|
if (this.props.inputType !== 'tags' && this.props.inputType !== 'dropdown') {
|
||||||
onClose(true);
|
onClose(true);
|
||||||
@@ -208,80 +212,55 @@ class PromptDialog extends React.Component {
|
|||||||
} else if (event.key === 'Escape') {
|
} else if (event.key === 'Escape') {
|
||||||
onClose(false);
|
onClose(false);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const descComp = this.props.description ? <div style={styles.desc}>{this.props.description}</div> : null;
|
const descComp = this.props.description ? <div style={styles.desc}>{this.props.description}</div> : null;
|
||||||
|
|
||||||
let inputComp = null;
|
let inputComp = null;
|
||||||
|
|
||||||
if (this.props.inputType === 'datetime') {
|
if (this.props.inputType === 'datetime') {
|
||||||
inputComp = <Datetime
|
inputComp = <Datetime value={this.state.answer} inputProps={{ style: styles.input }} dateFormat={time.dateFormat()} timeFormat={time.timeFormat()} onChange={momentObject => onDateTimeChange(momentObject)} />;
|
||||||
value={this.state.answer}
|
|
||||||
inputProps={{style: styles.input}}
|
|
||||||
dateFormat={time.dateFormat()}
|
|
||||||
timeFormat={time.timeFormat()}
|
|
||||||
onChange={(momentObject) => onDateTimeChange(momentObject)}
|
|
||||||
/>
|
|
||||||
} else if (this.props.inputType === 'tags') {
|
} else if (this.props.inputType === 'tags') {
|
||||||
inputComp = <CreatableSelect
|
inputComp = <CreatableSelect styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} value={this.state.answer} placeholder="" components={makeAnimated()} isMulti={true} isClearable={false} backspaceRemovesValue={true} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={event => onKeyDown(event)} />;
|
||||||
styles={styles.select}
|
|
||||||
theme={styles.selectTheme}
|
|
||||||
ref={this.answerInput_}
|
|
||||||
value={this.state.answer}
|
|
||||||
placeholder=""
|
|
||||||
components={makeAnimated()}
|
|
||||||
isMulti={true}
|
|
||||||
isClearable={false}
|
|
||||||
backspaceRemovesValue={true}
|
|
||||||
options={this.props.autocomplete}
|
|
||||||
onChange={onSelectChange}
|
|
||||||
onKeyDown={(event) => onKeyDown(event)}
|
|
||||||
/>
|
|
||||||
} else if (this.props.inputType === 'dropdown') {
|
} else if (this.props.inputType === 'dropdown') {
|
||||||
inputComp = <Select
|
inputComp = <Select styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} components={makeAnimated()} value={this.props.answer} defaultValue={this.props.defaultValue} isClearable={false} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={event => onKeyDown(event)} />;
|
||||||
styles={styles.select}
|
|
||||||
theme={styles.selectTheme}
|
|
||||||
ref={this.answerInput_}
|
|
||||||
components={makeAnimated()}
|
|
||||||
value={this.props.answer}
|
|
||||||
defaultValue={this.props.defaultValue}
|
|
||||||
isClearable={false}
|
|
||||||
options={this.props.autocomplete}
|
|
||||||
onChange={onSelectChange}
|
|
||||||
onKeyDown={(event) => onKeyDown(event)}
|
|
||||||
/>
|
|
||||||
} else {
|
} else {
|
||||||
inputComp = <input
|
inputComp = <input style={styles.input} ref={this.answerInput_} value={this.state.answer} type="text" onChange={event => onChange(event)} onKeyDown={event => onKeyDown(event)} />;
|
||||||
style={styles.input}
|
|
||||||
ref={this.answerInput_}
|
|
||||||
value={this.state.answer}
|
|
||||||
type="text"
|
|
||||||
onChange={(event) => onChange(event)}
|
|
||||||
onKeyDown={(event) => onKeyDown(event)}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const buttonComps = [];
|
const buttonComps = [];
|
||||||
if (buttonTypes.indexOf('ok') >= 0) buttonComps.push(<button key="ok" style={styles.button} onClick={() => onClose(true, 'ok')}>{_('OK')}</button>);
|
if (buttonTypes.indexOf('ok') >= 0)
|
||||||
if (buttonTypes.indexOf('cancel') >= 0) buttonComps.push(<button key="cancel" style={styles.button} onClick={() => onClose(false, 'cancel')}>{_('Cancel')}</button>);
|
buttonComps.push(
|
||||||
if (buttonTypes.indexOf('clear') >= 0) buttonComps.push(<button key="clear" style={styles.button} onClick={() => onClose(false, 'clear')}>{_('Clear')}</button>);
|
<button key="ok" style={styles.button} onClick={() => onClose(true, 'ok')}>
|
||||||
|
{_('OK')}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
if (buttonTypes.indexOf('cancel') >= 0)
|
||||||
|
buttonComps.push(
|
||||||
|
<button key="cancel" style={styles.button} onClick={() => onClose(false, 'cancel')}>
|
||||||
|
{_('Cancel')}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
if (buttonTypes.indexOf('clear') >= 0)
|
||||||
|
buttonComps.push(
|
||||||
|
<button key="clear" style={styles.button} onClick={() => onClose(false, 'clear')}>
|
||||||
|
{_('Clear')}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={styles.modalLayer}>
|
<div style={styles.modalLayer}>
|
||||||
<div style={styles.promptDialog}>
|
<div style={styles.promptDialog}>
|
||||||
<label style={styles.label}>{this.props.label ? this.props.label : ''}</label>
|
<label style={styles.label}>{this.props.label ? this.props.label : ''}</label>
|
||||||
<div style={{display: 'inline-block', color: 'black', backgroundColor: theme.backgroundColor}}>
|
<div style={{ display: 'inline-block', color: 'black', backgroundColor: theme.backgroundColor }}>
|
||||||
{inputComp}
|
{inputComp}
|
||||||
{descComp}
|
{descComp}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ textAlign: 'right', marginTop: 10 }}>
|
<div style={{ textAlign: 'right', marginTop: 10 }}>{buttonComps}</div>
|
||||||
{buttonComps}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { PromptDialog };
|
module.exports = { PromptDialog };
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { render } = require('react-dom');
|
const { render } = require('react-dom');
|
||||||
const { createStore } = require('redux');
|
|
||||||
const { connect, Provider } = require('react-redux');
|
const { connect, Provider } = require('react-redux');
|
||||||
|
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
@@ -21,20 +20,22 @@ const { app } = require('../app');
|
|||||||
|
|
||||||
const { bridge } = require('electron').remote.require('./bridge');
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
|
|
||||||
async function initialize(dispatch) {
|
async function initialize() {
|
||||||
this.wcsTimeoutId_ = null;
|
this.wcsTimeoutId_ = null;
|
||||||
|
|
||||||
bridge().window().on('resize', function() {
|
bridge()
|
||||||
if (this.wcsTimeoutId_) clearTimeout(this.wcsTimeoutId_);
|
.window()
|
||||||
|
.on('resize', function() {
|
||||||
|
if (this.wcsTimeoutId_) clearTimeout(this.wcsTimeoutId_);
|
||||||
|
|
||||||
this.wcsTimeoutId_ = setTimeout(() => {
|
this.wcsTimeoutId_ = setTimeout(() => {
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
type: 'WINDOW_CONTENT_SIZE_SET',
|
type: 'WINDOW_CONTENT_SIZE_SET',
|
||||||
size: bridge().windowContentSize(),
|
size: bridge().windowContentSize(),
|
||||||
});
|
});
|
||||||
this.wcsTimeoutId_ = null;
|
this.wcsTimeoutId_ = null;
|
||||||
}, 10);
|
}, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Need to dispatch this to make sure the components are
|
// Need to dispatch this to make sure the components are
|
||||||
// displayed at the right size. The windowContentSize is
|
// displayed at the right size. The windowContentSize is
|
||||||
@@ -52,12 +53,11 @@ async function initialize(dispatch) {
|
|||||||
|
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
type: 'SIDEBAR_VISIBILITY_SET',
|
type: 'SIDEBAR_VISIBILITY_SET',
|
||||||
visibility: Setting.value('sidebarVisibility')
|
visibility: Setting.value('sidebarVisibility'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class RootComponent extends React.Component {
|
class RootComponent extends React.Component {
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
if (this.props.appState == 'starting') {
|
if (this.props.appState == 'starting') {
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
@@ -93,14 +93,11 @@ class RootComponent extends React.Component {
|
|||||||
ClipperConfig: { screen: ClipperConfigScreen, title: () => _('Clipper Options') },
|
ClipperConfig: { screen: ClipperConfigScreen, title: () => _('Clipper Options') },
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return <Navigator style={navigatorStyle} screens={screens} />;
|
||||||
<Navigator style={navigatorStyle} screens={screens} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
size: state.windowContentSize,
|
size: state.windowContentSize,
|
||||||
appState: state.appState,
|
appState: state.appState,
|
||||||
@@ -116,4 +113,4 @@ render(
|
|||||||
<Root />
|
<Root />
|
||||||
</Provider>,
|
</Provider>,
|
||||||
document.getElementById('react-root')
|
document.getElementById('react-root')
|
||||||
)
|
);
|
||||||
|
@@ -1,28 +1,25 @@
|
|||||||
const React = require("react");
|
const React = require('react');
|
||||||
const { connect } = require("react-redux");
|
const { connect } = require('react-redux');
|
||||||
const shared = require("lib/components/shared/side-menu-shared.js");
|
const shared = require('lib/components/shared/side-menu-shared.js');
|
||||||
const { Synchronizer } = require("lib/synchronizer.js");
|
const { Synchronizer } = require('lib/synchronizer.js');
|
||||||
const BaseModel = require("lib/BaseModel.js");
|
const BaseModel = require('lib/BaseModel.js');
|
||||||
const Setting = require('lib/models/Setting.js');
|
const Setting = require('lib/models/Setting.js');
|
||||||
const Folder = require("lib/models/Folder.js");
|
const Folder = require('lib/models/Folder.js');
|
||||||
const Note = require("lib/models/Note.js");
|
const Note = require('lib/models/Note.js');
|
||||||
const Tag = require("lib/models/Tag.js");
|
const Tag = require('lib/models/Tag.js');
|
||||||
const { _ } = require("lib/locale.js");
|
const { _ } = require('lib/locale.js');
|
||||||
const { themeStyle } = require("../theme.js");
|
const { themeStyle } = require('../theme.js');
|
||||||
const { bridge } = require("electron").remote.require("./bridge");
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
const Menu = bridge().Menu;
|
const Menu = bridge().Menu;
|
||||||
const MenuItem = bridge().MenuItem;
|
const MenuItem = bridge().MenuItem;
|
||||||
const InteropServiceHelper = require("../InteropServiceHelper.js");
|
const InteropServiceHelper = require('../InteropServiceHelper.js');
|
||||||
const { substrWithEllipsis } = require('lib/string-utils');
|
const { substrWithEllipsis } = require('lib/string-utils');
|
||||||
const { shim } = require('lib/shim');
|
|
||||||
|
|
||||||
class SideBarComponent extends React.Component {
|
class SideBarComponent extends React.Component {
|
||||||
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.onFolderDragStart_ = (event) => {
|
this.onFolderDragStart_ = event => {
|
||||||
const folderId = event.currentTarget.getAttribute('folderid');
|
const folderId = event.currentTarget.getAttribute('folderid');
|
||||||
if (!folderId) return;
|
if (!folderId) return;
|
||||||
|
|
||||||
@@ -31,12 +28,12 @@ class SideBarComponent extends React.Component {
|
|||||||
event.dataTransfer.setData('text/x-jop-folder-ids', JSON.stringify([folderId]));
|
event.dataTransfer.setData('text/x-jop-folder-ids', JSON.stringify([folderId]));
|
||||||
};
|
};
|
||||||
|
|
||||||
this.onFolderDragOver_ = (event) => {
|
this.onFolderDragOver_ = event => {
|
||||||
if (event.dataTransfer.types.indexOf("text/x-jop-note-ids") >= 0) event.preventDefault();
|
if (event.dataTransfer.types.indexOf('text/x-jop-note-ids') >= 0) event.preventDefault();
|
||||||
if (event.dataTransfer.types.indexOf("text/x-jop-folder-ids") >= 0) event.preventDefault();
|
if (event.dataTransfer.types.indexOf('text/x-jop-folder-ids') >= 0) event.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.onFolderDrop_ = async (event) => {
|
this.onFolderDrop_ = async event => {
|
||||||
const folderId = event.currentTarget.getAttribute('folderid');
|
const folderId = event.currentTarget.getAttribute('folderid');
|
||||||
const dt = event.dataTransfer;
|
const dt = event.dataTransfer;
|
||||||
if (!dt) return;
|
if (!dt) return;
|
||||||
@@ -45,41 +42,41 @@ class SideBarComponent extends React.Component {
|
|||||||
// to put the dropped folder at the root. But for notes, folderId needs to always be defined
|
// to put the dropped folder at the root. But for notes, folderId needs to always be defined
|
||||||
// since there's no such thing as a root note.
|
// since there's no such thing as a root note.
|
||||||
|
|
||||||
if (dt.types.indexOf("text/x-jop-note-ids") >= 0) {
|
if (dt.types.indexOf('text/x-jop-note-ids') >= 0) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (!folderId) return;
|
if (!folderId) return;
|
||||||
|
|
||||||
const noteIds = JSON.parse(dt.getData("text/x-jop-note-ids"));
|
const noteIds = JSON.parse(dt.getData('text/x-jop-note-ids'));
|
||||||
for (let i = 0; i < noteIds.length; i++) {
|
for (let i = 0; i < noteIds.length; i++) {
|
||||||
await Note.moveToFolder(noteIds[i], folderId);
|
await Note.moveToFolder(noteIds[i], folderId);
|
||||||
}
|
}
|
||||||
} else if (dt.types.indexOf("text/x-jop-folder-ids") >= 0) {
|
} else if (dt.types.indexOf('text/x-jop-folder-ids') >= 0) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const folderIds = JSON.parse(dt.getData("text/x-jop-folder-ids"));
|
const folderIds = JSON.parse(dt.getData('text/x-jop-folder-ids'));
|
||||||
for (let i = 0; i < folderIds.length; i++) {
|
for (let i = 0; i < folderIds.length; i++) {
|
||||||
await Folder.moveToFolder(folderIds[i], folderId);
|
await Folder.moveToFolder(folderIds[i], folderId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.onTagDrop_ = async (event) => {
|
this.onTagDrop_ = async event => {
|
||||||
const tagId = event.currentTarget.getAttribute('tagid');
|
const tagId = event.currentTarget.getAttribute('tagid');
|
||||||
const dt = event.dataTransfer;
|
const dt = event.dataTransfer;
|
||||||
if (!dt) return;
|
if (!dt) return;
|
||||||
|
|
||||||
if (dt.types.indexOf("text/x-jop-note-ids") >= 0) {
|
if (dt.types.indexOf('text/x-jop-note-ids') >= 0) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const noteIds = JSON.parse(dt.getData("text/x-jop-note-ids"));
|
const noteIds = JSON.parse(dt.getData('text/x-jop-note-ids'));
|
||||||
for (let i = 0; i < noteIds.length; i++) {
|
for (let i = 0; i < noteIds.length; i++) {
|
||||||
await Tag.addNote(tagId, noteIds[i]);
|
await Tag.addNote(tagId, noteIds[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
this.onFolderToggleClick_ = async (event) => {
|
this.onFolderToggleClick_ = async event => {
|
||||||
const folderId = event.currentTarget.getAttribute('folderid');
|
const folderId = event.currentTarget.getAttribute('folderid');
|
||||||
|
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
@@ -99,7 +96,7 @@ class SideBarComponent extends React.Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
tagHeaderIsExpanded: Setting.value('tagHeaderIsExpanded'),
|
tagHeaderIsExpanded: Setting.value('tagHeaderIsExpanded'),
|
||||||
folderHeaderIsExpanded: Setting.value('folderHeaderIsExpanded')
|
folderHeaderIsExpanded: Setting.value('folderHeaderIsExpanded'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,23 +110,23 @@ class SideBarComponent extends React.Component {
|
|||||||
backgroundColor: theme.backgroundColor2,
|
backgroundColor: theme.backgroundColor2,
|
||||||
},
|
},
|
||||||
listItemContainer: {
|
listItemContainer: {
|
||||||
boxSizing: "border-box",
|
boxSizing: 'border-box',
|
||||||
height: itemHeight,
|
height: itemHeight,
|
||||||
// paddingLeft: 14,
|
// paddingLeft: 14,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: "stretch",
|
alignItems: 'stretch',
|
||||||
// Allow 3 levels of color depth
|
// Allow 3 levels of color depth
|
||||||
backgroundColor: theme.depthColor.replace('OPACITY', Math.min(depth * 0.1, 0.3)),
|
backgroundColor: theme.depthColor.replace('OPACITY', Math.min(depth * 0.1, 0.3)),
|
||||||
},
|
},
|
||||||
listItem: {
|
listItem: {
|
||||||
fontFamily: theme.fontFamily,
|
fontFamily: theme.fontFamily,
|
||||||
fontSize: theme.fontSize,
|
fontSize: theme.fontSize,
|
||||||
textDecoration: "none",
|
textDecoration: 'none',
|
||||||
color: theme.color2,
|
color: theme.color2,
|
||||||
cursor: "default",
|
cursor: 'default',
|
||||||
opacity: 0.8,
|
opacity: 0.8,
|
||||||
whiteSpace: "nowrap",
|
whiteSpace: 'nowrap',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
@@ -138,60 +135,60 @@ class SideBarComponent extends React.Component {
|
|||||||
},
|
},
|
||||||
listItemExpandIcon: {
|
listItemExpandIcon: {
|
||||||
color: theme.color2,
|
color: theme.color2,
|
||||||
cursor: "default",
|
cursor: 'default',
|
||||||
opacity: 0.8,
|
opacity: 0.8,
|
||||||
// fontFamily: theme.fontFamily,
|
// fontFamily: theme.fontFamily,
|
||||||
fontSize: theme.fontSize,
|
fontSize: theme.fontSize,
|
||||||
textDecoration: "none",
|
textDecoration: 'none',
|
||||||
paddingRight: 5,
|
paddingRight: 5,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
conflictFolder: {
|
conflictFolder: {
|
||||||
color: theme.colorError2,
|
color: theme.colorError2,
|
||||||
fontWeight: "bold",
|
fontWeight: 'bold',
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
height: itemHeight * 1.8,
|
height: itemHeight * 1.8,
|
||||||
fontFamily: theme.fontFamily,
|
fontFamily: theme.fontFamily,
|
||||||
fontSize: theme.fontSize * 1.16,
|
fontSize: theme.fontSize * 1.16,
|
||||||
textDecoration: "none",
|
textDecoration: 'none',
|
||||||
boxSizing: "border-box",
|
boxSizing: 'border-box',
|
||||||
color: theme.color2,
|
color: theme.color2,
|
||||||
paddingLeft: 8,
|
paddingLeft: 8,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
padding: 6,
|
padding: 6,
|
||||||
fontFamily: theme.fontFamily,
|
fontFamily: theme.fontFamily,
|
||||||
fontSize: theme.fontSize,
|
fontSize: theme.fontSize,
|
||||||
textDecoration: "none",
|
textDecoration: 'none',
|
||||||
boxSizing: "border-box",
|
boxSizing: 'border-box',
|
||||||
color: theme.color2,
|
color: theme.color2,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
justifyContent: "center",
|
justifyContent: 'center',
|
||||||
border: "1px solid rgba(255,255,255,0.2)",
|
border: '1px solid rgba(255,255,255,0.2)',
|
||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
marginLeft: 5,
|
marginLeft: 5,
|
||||||
marginRight: 5,
|
marginRight: 5,
|
||||||
cursor: "default",
|
cursor: 'default',
|
||||||
},
|
},
|
||||||
syncReport: {
|
syncReport: {
|
||||||
fontFamily: theme.fontFamily,
|
fontFamily: theme.fontFamily,
|
||||||
fontSize: Math.round(theme.fontSize * 0.9),
|
fontSize: Math.round(theme.fontSize * 0.9),
|
||||||
color: theme.color2,
|
color: theme.color2,
|
||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: "left",
|
alignItems: 'left',
|
||||||
justifyContent: "top",
|
justifyContent: 'top',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
marginLeft: 5,
|
marginLeft: 5,
|
||||||
marginRight: 5,
|
marginRight: 5,
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
wordWrap: "break-word",
|
wordWrap: 'break-word',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -236,42 +233,40 @@ class SideBarComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
|
||||||
if (shim.isLinux()) {
|
|
||||||
// For some reason, the UI seems to sleep in some Linux distro during
|
|
||||||
// sync. Cannot find the reason for it and cannot replicate, so here
|
|
||||||
// as a test force the update at regular intervals.
|
|
||||||
// https://github.com/laurent22/joplin/issues/312#issuecomment-429472193
|
|
||||||
if (!prevProps.syncStarted && this.props.syncStarted) {
|
|
||||||
this.clearForceUpdateDuringSync();
|
|
||||||
|
|
||||||
this.forceUpdateDuringSyncIID_ = setInterval(() => {
|
|
||||||
this.forceUpdate();
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevProps.syncStarted && !this.props.syncStarted) this.clearForceUpdateDuringSync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.clearForceUpdateDuringSync();
|
this.clearForceUpdateDuringSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
componentDidUpdate(prevProps) {
|
||||||
if (prevProps.windowCommand !== this.props.windowCommand) {
|
if (prevProps.windowCommand !== this.props.windowCommand) {
|
||||||
this.doCommand(this.props.windowCommand);
|
this.doCommand(this.props.windowCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (shim.isLinux()) {
|
||||||
|
// // For some reason, the UI seems to sleep in some Linux distro during
|
||||||
|
// // sync. Cannot find the reason for it and cannot replicate, so here
|
||||||
|
// // as a test force the update at regular intervals.
|
||||||
|
// // https://github.com/laurent22/joplin/issues/312#issuecomment-429472193
|
||||||
|
// if (!prevProps.syncStarted && this.props.syncStarted) {
|
||||||
|
// this.clearForceUpdateDuringSync();
|
||||||
|
|
||||||
|
// this.forceUpdateDuringSyncIID_ = setInterval(() => {
|
||||||
|
// this.forceUpdate();
|
||||||
|
// }, 2000);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (prevProps.syncStarted && !this.props.syncStarted) this.clearForceUpdateDuringSync();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
async itemContextMenu(event) {
|
async itemContextMenu(event) {
|
||||||
const itemId = event.target.getAttribute("data-id");
|
const itemId = event.target.getAttribute('data-id');
|
||||||
if (itemId === Folder.conflictFolderId()) return;
|
if (itemId === Folder.conflictFolderId()) return;
|
||||||
|
|
||||||
const itemType = Number(event.target.getAttribute("data-type"));
|
const itemType = Number(event.target.getAttribute('data-type'));
|
||||||
if (!itemId || !itemType) throw new Error("No data on element");
|
if (!itemId || !itemType) throw new Error('No data on element');
|
||||||
|
|
||||||
let deleteMessage = "";
|
let deleteMessage = '';
|
||||||
if (itemType === BaseModel.TYPE_FOLDER) {
|
if (itemType === BaseModel.TYPE_FOLDER) {
|
||||||
const folder = await Folder.load(itemId);
|
const folder = await Folder.load(itemId);
|
||||||
deleteMessage = _('Delete notebook "%s"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.', substrWithEllipsis(folder.title, 0, 32));
|
deleteMessage = _('Delete notebook "%s"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.', substrWithEllipsis(folder.title, 0, 32));
|
||||||
@@ -279,7 +274,7 @@ class SideBarComponent extends React.Component {
|
|||||||
const tag = await Tag.load(itemId);
|
const tag = await Tag.load(itemId);
|
||||||
deleteMessage = _('Remove tag "%s" from all notes?', substrWithEllipsis(tag.title, 0, 32));
|
deleteMessage = _('Remove tag "%s" from all notes?', substrWithEllipsis(tag.title, 0, 32));
|
||||||
} else if (itemType === BaseModel.TYPE_SEARCH) {
|
} else if (itemType === BaseModel.TYPE_SEARCH) {
|
||||||
deleteMessage = _("Remove this search from the sidebar?");
|
deleteMessage = _('Remove this search from the sidebar?');
|
||||||
}
|
}
|
||||||
|
|
||||||
const menu = new Menu();
|
const menu = new Menu();
|
||||||
@@ -291,7 +286,7 @@ class SideBarComponent extends React.Component {
|
|||||||
|
|
||||||
menu.append(
|
menu.append(
|
||||||
new MenuItem({
|
new MenuItem({
|
||||||
label: _("Delete"),
|
label: _('Delete'),
|
||||||
click: async () => {
|
click: async () => {
|
||||||
const ok = bridge().showConfirmMessageBox(deleteMessage);
|
const ok = bridge().showConfirmMessageBox(deleteMessage);
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
@@ -302,7 +297,7 @@ class SideBarComponent extends React.Component {
|
|||||||
await Tag.untagAll(itemId);
|
await Tag.untagAll(itemId);
|
||||||
} else if (itemType === BaseModel.TYPE_SEARCH) {
|
} else if (itemType === BaseModel.TYPE_SEARCH) {
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: "SEARCH_DELETE",
|
type: 'SEARCH_DELETE',
|
||||||
id: itemId,
|
id: itemId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -313,11 +308,11 @@ class SideBarComponent extends React.Component {
|
|||||||
if (itemType === BaseModel.TYPE_FOLDER && !item.encryption_applied) {
|
if (itemType === BaseModel.TYPE_FOLDER && !item.encryption_applied) {
|
||||||
menu.append(
|
menu.append(
|
||||||
new MenuItem({
|
new MenuItem({
|
||||||
label: _("Rename"),
|
label: _('Rename'),
|
||||||
click: async () => {
|
click: async () => {
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: "WINDOW_COMMAND",
|
type: 'WINDOW_COMMAND',
|
||||||
name: "renameFolder",
|
name: 'renameFolder',
|
||||||
id: itemId,
|
id: itemId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -337,9 +332,9 @@ class SideBarComponent extends React.Component {
|
|||||||
// })
|
// })
|
||||||
// );
|
// );
|
||||||
|
|
||||||
menu.append(new MenuItem({ type: "separator" }));
|
menu.append(new MenuItem({ type: 'separator' }));
|
||||||
|
|
||||||
const InteropService = require("lib/services/InteropService.js");
|
const InteropService = require('lib/services/InteropService.js');
|
||||||
|
|
||||||
const exportMenu = new Menu();
|
const exportMenu = new Menu();
|
||||||
const ioService = new InteropService();
|
const ioService = new InteropService();
|
||||||
@@ -348,14 +343,19 @@ class SideBarComponent extends React.Component {
|
|||||||
const module = ioModules[i];
|
const module = ioModules[i];
|
||||||
if (module.type !== 'exporter') continue;
|
if (module.type !== 'exporter') continue;
|
||||||
|
|
||||||
exportMenu.append(new MenuItem({ label: module.fullLabel() , click: async () => {
|
exportMenu.append(
|
||||||
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceFolderIds: [itemId] });
|
new MenuItem({
|
||||||
}}));
|
label: module.fullLabel(),
|
||||||
|
click: async () => {
|
||||||
|
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceFolderIds: [itemId] });
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.append(
|
menu.append(
|
||||||
new MenuItem({
|
new MenuItem({
|
||||||
label: _("Export"),
|
label: _('Export'),
|
||||||
submenu: exportMenu,
|
submenu: exportMenu,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -367,9 +367,9 @@ class SideBarComponent extends React.Component {
|
|||||||
label: _('Rename'),
|
label: _('Rename'),
|
||||||
click: async () => {
|
click: async () => {
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: "WINDOW_COMMAND",
|
type: 'WINDOW_COMMAND',
|
||||||
name: "renameTag",
|
name: 'renameTag',
|
||||||
id: itemId
|
id: itemId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -381,14 +381,14 @@ class SideBarComponent extends React.Component {
|
|||||||
|
|
||||||
folderItem_click(folder) {
|
folderItem_click(folder) {
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: "FOLDER_SELECT",
|
type: 'FOLDER_SELECT',
|
||||||
id: folder ? folder.id : null,
|
id: folder ? folder.id : null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
tagItem_click(tag) {
|
tagItem_click(tag) {
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: "TAG_SELECT",
|
type: 'TAG_SELECT',
|
||||||
id: tag ? tag.id : null,
|
id: tag ? tag.id : null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -405,8 +405,6 @@ class SideBarComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
anchorItemRef(type, id) {
|
anchorItemRef(type, id) {
|
||||||
let refs = null;
|
|
||||||
|
|
||||||
if (!this.anchorItemRefs[type]) this.anchorItemRefs[type] = {};
|
if (!this.anchorItemRefs[type]) this.anchorItemRefs[type] = {};
|
||||||
if (this.anchorItemRefs[type][id]) return this.anchorItemRefs[type][id];
|
if (this.anchorItemRefs[type][id]) return this.anchorItemRefs[type][id];
|
||||||
this.anchorItemRefs[type][id] = React.createRef();
|
this.anchorItemRefs[type][id] = React.createRef();
|
||||||
@@ -426,17 +424,23 @@ class SideBarComponent extends React.Component {
|
|||||||
let expandIconStyle = {
|
let expandIconStyle = {
|
||||||
visibility: hasChildren ? 'visible' : 'hidden',
|
visibility: hasChildren ? 'visible' : 'hidden',
|
||||||
paddingLeft: 8 + depth * 10,
|
paddingLeft: 8 + depth * 10,
|
||||||
}
|
};
|
||||||
|
|
||||||
const iconName = this.props.collapsedFolderIds.indexOf(folder.id) >= 0 ? 'fa-plus-square' : 'fa-minus-square';
|
const iconName = this.props.collapsedFolderIds.indexOf(folder.id) >= 0 ? 'fa-plus-square' : 'fa-minus-square';
|
||||||
const expandIcon = <i style={expandIconStyle} className={"fa " + iconName}></i>
|
const expandIcon = <i style={expandIconStyle} className={'fa ' + iconName}></i>;
|
||||||
const expandLink = hasChildren ? <a style={expandLinkStyle} href="#" folderid={folder.id} onClick={this.onFolderToggleClick_}>{expandIcon}</a> : <span style={expandLinkStyle}>{expandIcon}</span>
|
const expandLink = hasChildren ? (
|
||||||
|
<a style={expandLinkStyle} href="#" folderid={folder.id} onClick={this.onFolderToggleClick_}>
|
||||||
|
{expandIcon}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<span style={expandLinkStyle}>{expandIcon}</span>
|
||||||
|
);
|
||||||
|
|
||||||
const anchorRef = this.anchorItemRef('folder', folder.id);
|
const anchorRef = this.anchorItemRef('folder', folder.id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="list-item-container" style={containerStyle} key={folder.id} onDragStart={this.onFolderDragStart_} onDragOver={this.onFolderDragOver_} onDrop={this.onFolderDrop_} draggable={true} folderid={folder.id}>
|
<div className="list-item-container" style={containerStyle} key={folder.id} onDragStart={this.onFolderDragStart_} onDragOver={this.onFolderDragOver_} onDrop={this.onFolderDrop_} draggable={true} folderid={folder.id}>
|
||||||
{ expandLink }
|
{expandLink}
|
||||||
<a
|
<a
|
||||||
ref={anchorRef}
|
ref={anchorRef}
|
||||||
className="list-item"
|
className="list-item"
|
||||||
@@ -506,15 +510,15 @@ class SideBarComponent extends React.Component {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
makeDivider(key) {
|
makeDivider(key) {
|
||||||
return <div style={{ height: 2, backgroundColor: "blue" }} key={key} />;
|
return <div style={{ height: 2, backgroundColor: 'blue' }} key={key} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
makeHeader(key, label, iconName, extraProps = {}) {
|
makeHeader(key, label, iconName, extraProps = {}) {
|
||||||
const style = this.style().header;
|
const style = this.style().header;
|
||||||
const icon = <i style={{ fontSize: style.fontSize, marginRight: 5 }} className={"fa " + iconName} />;
|
const icon = <i style={{ fontSize: style.fontSize, marginRight: 5 }} className={'fa ' + iconName} />;
|
||||||
|
|
||||||
if (extraProps.toggleblock || extraProps.onClick) {
|
if (extraProps.toggleblock || extraProps.onClick) {
|
||||||
style.cursor = "pointer";
|
style.cursor = 'pointer';
|
||||||
}
|
}
|
||||||
|
|
||||||
let headerClick = extraProps.onClick || null;
|
let headerClick = extraProps.onClick || null;
|
||||||
@@ -525,22 +529,27 @@ class SideBarComponent extends React.Component {
|
|||||||
const toggleKey = `${key}IsExpanded`;
|
const toggleKey = `${key}IsExpanded`;
|
||||||
if (extraProps.toggleblock) {
|
if (extraProps.toggleblock) {
|
||||||
let isExpanded = this.state[toggleKey];
|
let isExpanded = this.state[toggleKey];
|
||||||
toggleIcon = <i className={`fa ${isExpanded ? 'fa-chevron-down' : 'fa-chevron-left'}`} style={{ fontSize: style.fontSize * 0.75,
|
toggleIcon = <i className={`fa ${isExpanded ? 'fa-chevron-down' : 'fa-chevron-left'}`} style={{ fontSize: style.fontSize * 0.75, marginRight: 12, marginLeft: 5, marginTop: style.fontSize * 0.125 }}></i>;
|
||||||
marginRight: 12, marginLeft: 5, marginTop: style.fontSize * 0.125}}></i>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ref = this.anchorItemRef('headers', key);
|
const ref = this.anchorItemRef('headers', key);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={ref} style={style} key={key} {...extraProps} onClick={(event) => {
|
<div
|
||||||
|
ref={ref}
|
||||||
|
style={style}
|
||||||
|
key={key}
|
||||||
|
{...extraProps}
|
||||||
|
onClick={event => {
|
||||||
// if a custom click event is attached, trigger that.
|
// if a custom click event is attached, trigger that.
|
||||||
if (headerClick) {
|
if (headerClick) {
|
||||||
headerClick(key, event);
|
headerClick(key, event);
|
||||||
}
|
}
|
||||||
this.onHeaderClick_(key, event);
|
this.onHeaderClick_(key, event);
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{icon}
|
{icon}
|
||||||
<span style={{flex: 1 }}>{label}</span>
|
<span style={{ flex: 1 }}>{label}</span>
|
||||||
{toggleIcon}
|
{toggleIcon}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -562,7 +571,8 @@ class SideBarComponent extends React.Component {
|
|||||||
const keyCode = event.keyCode;
|
const keyCode = event.keyCode;
|
||||||
const selectedItem = this.selectedItem();
|
const selectedItem = this.selectedItem();
|
||||||
|
|
||||||
if (keyCode === 40 || keyCode === 38) { // DOWN / UP
|
if (keyCode === 40 || keyCode === 38) {
|
||||||
|
// DOWN / UP
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const focusItems = [];
|
const focusItems = [];
|
||||||
@@ -603,7 +613,8 @@ class SideBarComponent extends React.Component {
|
|||||||
focusItem.ref.current.focus();
|
focusItem.ref.current.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyCode === 9) { // TAB
|
if (keyCode === 9) {
|
||||||
|
// TAB
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (event.shiftKey) {
|
if (event.shiftKey) {
|
||||||
@@ -621,7 +632,8 @@ class SideBarComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedItem && selectedItem.type === 'folder' && keyCode === 32) { // SPACE
|
if (selectedItem && selectedItem.type === 'folder' && keyCode === 32) {
|
||||||
|
// SPACE
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
@@ -644,15 +656,15 @@ class SideBarComponent extends React.Component {
|
|||||||
|
|
||||||
synchronizeButton(type) {
|
synchronizeButton(type) {
|
||||||
const style = Object.assign({}, this.style().button, { marginBottom: 5 });
|
const style = Object.assign({}, this.style().button, { marginBottom: 5 });
|
||||||
const iconName = "fa-refresh";
|
const iconName = 'fa-refresh';
|
||||||
const label = type === "sync" ? _("Synchronise") : _("Cancel");
|
const label = type === 'sync' ? _('Synchronise') : _('Cancel');
|
||||||
let iconStyle = { fontSize: style.fontSize, marginRight: 5 };
|
let iconStyle = { fontSize: style.fontSize, marginRight: 5 };
|
||||||
|
|
||||||
if(type !== 'sync'){
|
if (type !== 'sync') {
|
||||||
iconStyle.animation = 'icon-infinite-rotation 1s linear infinite';
|
iconStyle.animation = 'icon-infinite-rotation 1s linear infinite';
|
||||||
}
|
}
|
||||||
|
|
||||||
const icon = <i style={iconStyle} className={"fa " + iconName} />;
|
const icon = <i style={iconStyle} className={'fa ' + iconName} />;
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
className="synchronize-button"
|
className="synchronize-button"
|
||||||
@@ -670,32 +682,38 @@ class SideBarComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const theme = themeStyle(this.props.theme);
|
|
||||||
const style = Object.assign({}, this.style().root, this.props.style, {
|
const style = Object.assign({}, this.style().root, this.props.style, {
|
||||||
overflowX: "hidden",
|
overflowX: 'hidden',
|
||||||
overflowY: "hidden",
|
overflowY: 'hidden',
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
});
|
});
|
||||||
|
|
||||||
let items = [];
|
let items = [];
|
||||||
items.push(this.makeHeader("folderHeader", _("Notebooks"), "fa-book", {
|
items.push(
|
||||||
onDrop: this.onFolderDrop_,
|
this.makeHeader('folderHeader', _('Notebooks'), 'fa-book', {
|
||||||
folderid: '',
|
onDrop: this.onFolderDrop_,
|
||||||
toggleblock: 1
|
folderid: '',
|
||||||
}));
|
toggleblock: 1,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
if (this.props.folders.length) {
|
if (this.props.folders.length) {
|
||||||
const result = shared.renderFolders(this.props, this.folderItem.bind(this));
|
const result = shared.renderFolders(this.props, this.folderItem.bind(this));
|
||||||
const folderItems = result.items;
|
const folderItems = result.items;
|
||||||
this.folderItemsOrder_ = result.order;
|
this.folderItemsOrder_ = result.order;
|
||||||
items.push(<div className="folders" key="folder_items" style={{display: this.state.folderHeaderIsExpanded ? 'block': 'none'}}>
|
items.push(
|
||||||
{folderItems}</div>);
|
<div className="folders" key="folder_items" style={{ display: this.state.folderHeaderIsExpanded ? 'block' : 'none' }}>
|
||||||
|
{folderItems}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push(this.makeHeader("tagHeader", _("Tags"), "fa-tags", {
|
items.push(
|
||||||
toggleblock: 1
|
this.makeHeader('tagHeader', _('Tags'), 'fa-tags', {
|
||||||
}));
|
toggleblock: 1,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
if (this.props.tags.length) {
|
if (this.props.tags.length) {
|
||||||
const result = shared.renderTags(this.props, this.tagItem.bind(this));
|
const result = shared.renderTags(this.props, this.tagItem.bind(this));
|
||||||
@@ -703,7 +721,7 @@ class SideBarComponent extends React.Component {
|
|||||||
this.tagItemsOrder_ = result.order;
|
this.tagItemsOrder_ = result.order;
|
||||||
|
|
||||||
items.push(
|
items.push(
|
||||||
<div className="tags" key="tag_items" style={{display: this.state.tagHeaderIsExpanded ? 'block': 'none'}}>
|
<div className="tags" key="tag_items" style={{ display: this.state.tagHeaderIsExpanded ? 'block' : 'none' }}>
|
||||||
{tagItems}
|
{tagItems}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -725,27 +743,24 @@ class SideBarComponent extends React.Component {
|
|||||||
const syncReportText = [];
|
const syncReportText = [];
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
syncReportText.push(
|
syncReportText.push(
|
||||||
<div key={i} style={{ wordWrap: "break-word", width: "100%" }}>
|
<div key={i} style={{ wordWrap: 'break-word', width: '100%' }}>
|
||||||
{lines[i]}
|
{lines[i]}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncButton = this.synchronizeButton(this.props.syncStarted ? "cancel" : "sync");
|
const syncButton = this.synchronizeButton(this.props.syncStarted ? 'cancel' : 'sync');
|
||||||
|
|
||||||
const syncReportComp = !syncReportText.length ? null : (
|
const syncReportComp = !syncReportText.length ? null : (
|
||||||
<div style={this.style().syncReport} key="sync_report">
|
<div style={this.style().syncReport} key="sync_report">
|
||||||
{syncReportText}
|
{syncReportText}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={this.rootRef} onKeyDown={this.onKeyDown} className="side-bar" style={style}>
|
<div ref={this.rootRef} onKeyDown={this.onKeyDown} className="side-bar" style={style}>
|
||||||
|
<div style={{ flex: 1, overflowX: 'hidden', overflowY: 'auto' }}>{items}</div>
|
||||||
<div style={{flex:1, overflowX: 'hidden', overflowY: 'auto'}}>
|
<div style={{ flex: 0 }}>
|
||||||
{items}
|
|
||||||
</div>
|
|
||||||
<div style={{flex:0}}>
|
|
||||||
{syncReportComp}
|
{syncReportComp}
|
||||||
{syncButton}
|
{syncButton}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
const { reg } = require('lib/registry.js');
|
|
||||||
const Setting = require('lib/models/Setting.js');
|
const Setting = require('lib/models/Setting.js');
|
||||||
const { bridge } = require('electron').remote.require('./bridge');
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
const { Header } = require('./Header.min.js');
|
const { Header } = require('./Header.min.js');
|
||||||
@@ -10,7 +9,6 @@ const { ReportService } = require('lib/services/report.js');
|
|||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
|
||||||
class StatusScreenComponent extends React.Component {
|
class StatusScreenComponent extends React.Component {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -29,7 +27,7 @@ class StatusScreenComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async exportDebugReportClick() {
|
async exportDebugReportClick() {
|
||||||
const filename = 'syncReport-' + (new Date()).getTime() + '.csv';
|
const filename = 'syncReport-' + new Date().getTime() + '.csv';
|
||||||
|
|
||||||
const filePath = bridge().showSaveDialog({
|
const filePath = bridge().showSaveDialog({
|
||||||
title: _('Please select where the sync status should be exported to'),
|
title: _('Please select where the sync status should be exported to'),
|
||||||
@@ -48,7 +46,7 @@ class StatusScreenComponent extends React.Component {
|
|||||||
const style = this.props.style;
|
const style = this.props.style;
|
||||||
|
|
||||||
const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width });
|
const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width });
|
||||||
const retryStyle = Object.assign({}, theme.urlStyle, {marginLeft: 5});
|
const retryStyle = Object.assign({}, theme.urlStyle, { marginLeft: 5 });
|
||||||
|
|
||||||
const containerPadding = 10;
|
const containerPadding = 10;
|
||||||
|
|
||||||
@@ -58,7 +56,11 @@ class StatusScreenComponent extends React.Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function renderSectionTitleHtml(key, title) {
|
function renderSectionTitleHtml(key, title) {
|
||||||
return <h2 key={'section_' + key} style={theme.h2Style}>{title}</h2>
|
return (
|
||||||
|
<h2 key={'section_' + key} style={theme.h2Style}>
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderSectionHtml = (key, section) => {
|
const renderSectionHtml = (key, section) => {
|
||||||
@@ -77,9 +79,13 @@ class StatusScreenComponent extends React.Component {
|
|||||||
const onClick = async () => {
|
const onClick = async () => {
|
||||||
await item.retryHandler();
|
await item.retryHandler();
|
||||||
this.resfreshScreen();
|
this.resfreshScreen();
|
||||||
}
|
};
|
||||||
|
|
||||||
retryLink = <a href="#" onClick={onClick} style={retryStyle}>{_('Retry')}</a>;
|
retryLink = (
|
||||||
|
<a href="#" onClick={onClick} style={retryStyle}>
|
||||||
|
{_('Retry')}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
text = item.text;
|
text = item.text;
|
||||||
} else {
|
} else {
|
||||||
@@ -88,28 +94,18 @@ class StatusScreenComponent extends React.Component {
|
|||||||
|
|
||||||
if (!text) text = '\xa0';
|
if (!text) text = '\xa0';
|
||||||
|
|
||||||
itemsHtml.push(<div style={theme.textStyle} key={'item_' + n}><span>{text}</span>{retryLink}</div>);
|
itemsHtml.push(
|
||||||
|
<div style={theme.textStyle} key={'item_' + n}>
|
||||||
|
<span>{text}</span>
|
||||||
|
{retryLink}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <div key={key}>{itemsHtml}</div>;
|
||||||
<div key={key}>
|
};
|
||||||
{itemsHtml}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderBodyHtml(report) {
|
function renderBodyHtml(report) {
|
||||||
let output = [];
|
|
||||||
let baseStyle = {
|
|
||||||
paddingLeft: 6,
|
|
||||||
paddingRight: 6,
|
|
||||||
paddingTop: 2,
|
|
||||||
paddingBottom: 2,
|
|
||||||
flex: 0,
|
|
||||||
color: theme.color,
|
|
||||||
fontSize: theme.fontSize,
|
|
||||||
};
|
|
||||||
|
|
||||||
let sectionsHtml = [];
|
let sectionsHtml = [];
|
||||||
|
|
||||||
for (let i = 0; i < report.length; i++) {
|
for (let i = 0; i < report.length; i++) {
|
||||||
@@ -118,11 +114,7 @@ class StatusScreenComponent extends React.Component {
|
|||||||
sectionsHtml.push(renderSectionHtml(i, section));
|
sectionsHtml.push(renderSectionHtml(i, section));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <div>{sectionsHtml}</div>;
|
||||||
<div>
|
|
||||||
{sectionsHtml}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let body = renderBodyHtml(this.state.report);
|
let body = renderBodyHtml(this.state.report);
|
||||||
@@ -131,16 +123,17 @@ class StatusScreenComponent extends React.Component {
|
|||||||
<div style={style}>
|
<div style={style}>
|
||||||
<Header style={headerStyle} />
|
<Header style={headerStyle} />
|
||||||
<div style={containerStyle}>
|
<div style={containerStyle}>
|
||||||
<a style={theme.textStyle} onClick={() => this.exportDebugReportClick()}href="#">Export debug report</a>
|
<a style={theme.textStyle} onClick={() => this.exportDebugReportClick()} href="#">
|
||||||
|
Export debug report
|
||||||
|
</a>
|
||||||
{body}
|
{body}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
theme: state.settings.theme,
|
theme: state.settings.theme,
|
||||||
settings: state.settings,
|
settings: state.settings,
|
||||||
|
@@ -12,7 +12,7 @@ class TagItemComponent extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return { theme: state.settings.theme };
|
return { theme: state.settings.theme };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ class TagListComponent extends React.Component {
|
|||||||
const tags = this.props.items;
|
const tags = this.props.items;
|
||||||
|
|
||||||
style.display = 'flex';
|
style.display = 'flex';
|
||||||
style.flexDirection = 'row';
|
style.flexDirection = 'row';
|
||||||
style.borderBottom = '1px solid ' + theme.dividerColor;
|
style.borderBottom = '1px solid ' + theme.dividerColor;
|
||||||
style.boxSizing = 'border-box';
|
style.boxSizing = 'border-box';
|
||||||
style.fontSize = theme.fontSize;
|
style.fontSize = theme.fontSize;
|
||||||
@@ -18,12 +18,14 @@ class TagListComponent extends React.Component {
|
|||||||
const tagItems = [];
|
const tagItems = [];
|
||||||
if (tags || tags.length > 0) {
|
if (tags || tags.length > 0) {
|
||||||
// Sort by id for now, but probably needs to be changed in the future.
|
// Sort by id for now, but probably needs to be changed in the future.
|
||||||
tags.sort((a, b) => { return a.title < b.title ? -1 : +1; });
|
tags.sort((a, b) => {
|
||||||
|
return a.title < b.title ? -1 : +1;
|
||||||
|
});
|
||||||
|
|
||||||
for (let i = 0; i < tags.length; i++) {
|
for (let i = 0; i < tags.length; i++) {
|
||||||
const props = {
|
const props = {
|
||||||
title: tags[i].title,
|
title: tags[i].title,
|
||||||
key: tags[i].id
|
key: tags[i].id,
|
||||||
};
|
};
|
||||||
tagItems.push(<TagItem {...props} />);
|
tagItems.push(<TagItem {...props} />);
|
||||||
}
|
}
|
||||||
@@ -35,13 +37,13 @@ class TagListComponent extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tag-list" style={style}>
|
<div className="tag-list" style={style}>
|
||||||
{ tagItems }
|
{tagItems}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return { theme: state.settings.theme };
|
return { theme: state.settings.theme };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,19 +1,16 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
const { reg } = require('lib/registry.js');
|
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
const { _ } = require('lib/locale.js');
|
|
||||||
const ToolbarButton = require('./ToolbarButton.min.js');
|
const ToolbarButton = require('./ToolbarButton.min.js');
|
||||||
const ToolbarSpace = require('./ToolbarSpace.min.js');
|
const ToolbarSpace = require('./ToolbarSpace.min.js');
|
||||||
|
|
||||||
class ToolbarComponent extends React.Component {
|
class ToolbarComponent extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const style = this.props.style;
|
const style = this.props.style;
|
||||||
const theme = themeStyle(this.props.theme);
|
const theme = themeStyle(this.props.theme);
|
||||||
style.height = theme.toolbarHeight;
|
style.height = theme.toolbarHeight;
|
||||||
style.display = 'flex';
|
style.display = 'flex';
|
||||||
style.flexDirection = 'row';
|
style.flexDirection = 'row';
|
||||||
style.borderBottom = '1px solid ' + theme.dividerColor;
|
style.borderBottom = '1px solid ' + theme.dividerColor;
|
||||||
style.boxSizing = 'border-box';
|
style.boxSizing = 'border-box';
|
||||||
|
|
||||||
@@ -28,10 +25,13 @@ class ToolbarComponent extends React.Component {
|
|||||||
|
|
||||||
if (!key) key = o.type + '_' + i;
|
if (!key) key = o.type + '_' + i;
|
||||||
|
|
||||||
const props = Object.assign({
|
const props = Object.assign(
|
||||||
key: key,
|
{
|
||||||
theme: this.props.theme,
|
key: key,
|
||||||
}, o);
|
theme: this.props.theme,
|
||||||
|
},
|
||||||
|
o
|
||||||
|
);
|
||||||
|
|
||||||
if (itemType === 'button') {
|
if (itemType === 'button') {
|
||||||
itemComps.push(<ToolbarButton {...props} />);
|
itemComps.push(<ToolbarButton {...props} />);
|
||||||
@@ -43,14 +43,13 @@ class ToolbarComponent extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="editor-toolbar" style={style}>
|
<div className="editor-toolbar" style={style}>
|
||||||
{ itemComps }
|
{itemComps}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = state => {
|
||||||
return { theme: state.settings.theme };
|
return { theme: state.settings.theme };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { connect } = require('react-redux');
|
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
|
|
||||||
class ToolbarButton extends React.Component {
|
class ToolbarButton extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const theme = themeStyle(this.props.theme);
|
const theme = themeStyle(this.props.theme);
|
||||||
|
|
||||||
@@ -16,13 +14,13 @@ class ToolbarButton extends React.Component {
|
|||||||
if (this.props.iconName) {
|
if (this.props.iconName) {
|
||||||
const iconStyle = {
|
const iconStyle = {
|
||||||
fontSize: Math.round(theme.fontSize * 1.5),
|
fontSize: Math.round(theme.fontSize * 1.5),
|
||||||
color: theme.color
|
color: theme.color,
|
||||||
};
|
};
|
||||||
if (title) iconStyle.marginRight = 5;
|
if (title) iconStyle.marginRight = 5;
|
||||||
icon = <i style={iconStyle} className={"fa " + this.props.iconName}></i>
|
icon = <i style={iconStyle} className={'fa ' + this.props.iconName}></i>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isEnabled = (!('enabled' in this.props) || this.props.enabled === true);
|
const isEnabled = !('enabled' in this.props) || this.props.enabled === true;
|
||||||
let classes = ['button'];
|
let classes = ['button'];
|
||||||
if (!isEnabled) classes.push('disabled');
|
if (!isEnabled) classes.push('disabled');
|
||||||
|
|
||||||
@@ -36,13 +34,15 @@ class ToolbarButton extends React.Component {
|
|||||||
style={finalStyle}
|
style={finalStyle}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
href="#"
|
href="#"
|
||||||
onClick={() => { if (isEnabled && this.props.onClick) this.props.onClick() }}
|
onClick={() => {
|
||||||
>
|
if (isEnabled && this.props.onClick) this.props.onClick();
|
||||||
{icon}{title}
|
}}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
{title}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ToolbarButton;
|
module.exports = ToolbarButton;
|
@@ -1,22 +1,14 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { connect } = require('react-redux');
|
|
||||||
const { themeStyle } = require('../theme.js');
|
const { themeStyle } = require('../theme.js');
|
||||||
|
|
||||||
class ToolbarSpace extends React.Component {
|
class ToolbarSpace extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const theme = themeStyle(this.props.theme);
|
const theme = themeStyle(this.props.theme);
|
||||||
const style = Object.assign({}, theme.toolbarStyle);
|
const style = Object.assign({}, theme.toolbarStyle);
|
||||||
style.minWidth = style.height / 2;
|
style.minWidth = style.height / 2;
|
||||||
|
|
||||||
return (
|
return <span style={style}></span>;
|
||||||
<span
|
|
||||||
style={style}
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ToolbarSpace;
|
module.exports = ToolbarSpace;
|
@@ -1,12 +1,11 @@
|
|||||||
const React = require("react");
|
const React = require('react');
|
||||||
const electron = require('electron');
|
const electron = require('electron');
|
||||||
|
|
||||||
class VerticalResizer extends React.PureComponent {
|
class VerticalResizer extends React.PureComponent {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
parentRight: 0,
|
parentRight: 0,
|
||||||
parentHeight: 0,
|
parentHeight: 0,
|
||||||
parentWidth: 0,
|
parentWidth: 0,
|
||||||
@@ -29,17 +28,17 @@ class VerticalResizer extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onDragStart(event) {
|
onDragStart(event) {
|
||||||
document.addEventListener('dragover', this.document_onDragOver)
|
document.addEventListener('dragover', this.document_onDragOver);
|
||||||
|
|
||||||
event.dataTransfer.dropEffect= 'none';
|
event.dataTransfer.dropEffect = 'none';
|
||||||
|
|
||||||
const cursor = electron.screen.getCursorScreenPoint();
|
const cursor = electron.screen.getCursorScreenPoint();
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
drag: {
|
drag: {
|
||||||
startX: cursor.x,
|
startX: cursor.x,
|
||||||
lastX: cursor.x,
|
lastX: cursor.x,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.props.onDragStart) this.props.onDragStart({});
|
if (this.props.onDragStart) this.props.onDragStart({});
|
||||||
@@ -58,11 +57,14 @@ class VerticalResizer extends React.PureComponent {
|
|||||||
const delta = newX - this.state.drag.lastX;
|
const delta = newX - this.state.drag.lastX;
|
||||||
if (!delta) return;
|
if (!delta) return;
|
||||||
|
|
||||||
this.setState({
|
this.setState(
|
||||||
drag: Object.assign({}, this.state.drag, { lastX: newX }),
|
{
|
||||||
}, () => {
|
drag: Object.assign({}, this.state.drag, { lastX: newX }),
|
||||||
this.props.onDrag({ deltaX: delta });
|
},
|
||||||
});
|
() => {
|
||||||
|
this.props.onDrag({ deltaX: delta });
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onDragEnd(event) {
|
onDragEnd(event) {
|
||||||
@@ -76,26 +78,22 @@ class VerticalResizer extends React.PureComponent {
|
|||||||
render() {
|
render() {
|
||||||
const debug = false;
|
const debug = false;
|
||||||
|
|
||||||
const rootStyle = Object.assign({}, {
|
const rootStyle = Object.assign(
|
||||||
height: '100%',
|
{},
|
||||||
width:5,
|
{
|
||||||
borderColor:'red',
|
height: '100%',
|
||||||
borderWidth: debug ? 1 : 0,
|
width: 5,
|
||||||
borderStyle:'solid',
|
borderColor: 'red',
|
||||||
cursor: 'col-resize',
|
borderWidth: debug ? 1 : 0,
|
||||||
boxSizing: 'border-box',
|
borderStyle: 'solid',
|
||||||
opacity: 0,
|
cursor: 'col-resize',
|
||||||
}, this.props.style);
|
boxSizing: 'border-box',
|
||||||
|
opacity: 0,
|
||||||
return (
|
},
|
||||||
<div
|
this.props.style
|
||||||
style={rootStyle}
|
|
||||||
draggable={true}
|
|
||||||
onDragStart={this.onDragStart}
|
|
||||||
onDrag={this.onDrag}
|
|
||||||
onDragEnd={this.onDragEnd}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return <div style={rootStyle} draggable={true} onDragStart={this.onDragStart} onDrag={this.onDrag} onDragEnd={this.onDragEnd} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
const smalltalk = require('smalltalk');
|
const smalltalk = require('smalltalk');
|
||||||
|
|
||||||
class Dialogs {
|
class Dialogs {
|
||||||
|
|
||||||
async alert(message, title = '') {
|
async alert(message, title = '') {
|
||||||
await smalltalk.alert(title, message);
|
await smalltalk.alert(title, message);
|
||||||
}
|
}
|
||||||
@@ -25,7 +24,6 @@ class Dialogs {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const dialogs = new Dialogs();
|
const dialogs = new Dialogs();
|
||||||
|
@@ -17,7 +17,7 @@ ipcRenderer.on('setMarkers', (event, keywords, options) => {
|
|||||||
window.postMessage({ target: 'webview', name: 'setMarkers', data: { keywords: keywords, options: options } }, '*');
|
window.postMessage({ target: 'webview', name: 'setMarkers', data: { keywords: keywords, options: options } }, '*');
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('message', (event) => {
|
window.addEventListener('message', event => {
|
||||||
// Here we only deal with messages that are sent from the webview to the main Electron process
|
// Here we only deal with messages that are sent from the webview to the main Electron process
|
||||||
if (!event.data || event.data.target !== 'main') return;
|
if (!event.data || event.data.target !== 'main') return;
|
||||||
|
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
const { time } = require('lib/time-utils.js');
|
|
||||||
const BaseModel = require('lib/BaseModel');
|
const BaseModel = require('lib/BaseModel');
|
||||||
const markJsUtils = require('lib/markJsUtils');
|
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { bridge } = require('electron').remote.require('./bridge');
|
const { bridge } = require('electron').remote.require('./bridge');
|
||||||
const Menu = bridge().Menu;
|
const Menu = bridge().Menu;
|
||||||
@@ -8,49 +6,62 @@ const MenuItem = bridge().MenuItem;
|
|||||||
const eventManager = require('../../eventManager');
|
const eventManager = require('../../eventManager');
|
||||||
const InteropService = require('lib/services/InteropService');
|
const InteropService = require('lib/services/InteropService');
|
||||||
const InteropServiceHelper = require('../../InteropServiceHelper.js');
|
const InteropServiceHelper = require('../../InteropServiceHelper.js');
|
||||||
const Search = require('lib/models/Search');
|
|
||||||
const Note = require('lib/models/Note');
|
const Note = require('lib/models/Note');
|
||||||
const SearchEngine = require('lib/services/SearchEngine');
|
const { substrWithEllipsis } = require('lib/string-utils');
|
||||||
const { replaceRegexDiacritics, pregQuote, substrWithEllipsis } = require('lib/string-utils');
|
|
||||||
|
|
||||||
class NoteListUtils {
|
class NoteListUtils {
|
||||||
|
|
||||||
static makeContextMenu(noteIds, props) {
|
static makeContextMenu(noteIds, props) {
|
||||||
const notes = noteIds.map((id) => BaseModel.byId(props.notes, id));
|
const notes = noteIds.map(id => BaseModel.byId(props.notes, id));
|
||||||
|
|
||||||
let hasEncrypted = false;
|
let hasEncrypted = false;
|
||||||
for (let i = 0; i < notes.length; i++) {
|
for (let i = 0; i < notes.length; i++) {
|
||||||
if (!!notes[i].encryption_applied) hasEncrypted = true;
|
if (notes[i].encryption_applied) hasEncrypted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const menu = new Menu()
|
const menu = new Menu();
|
||||||
|
|
||||||
if (!hasEncrypted) {
|
if (!hasEncrypted) {
|
||||||
menu.append(new MenuItem({label: _('Add or remove tags'), enabled: noteIds.length === 1, click: async () => {
|
menu.append(
|
||||||
props.dispatch({
|
new MenuItem({
|
||||||
type: 'WINDOW_COMMAND',
|
label: _('Add or remove tags'),
|
||||||
name: 'setTags',
|
enabled: noteIds.length === 1,
|
||||||
noteId: noteIds[0],
|
click: async () => {
|
||||||
});
|
props.dispatch({
|
||||||
}}));
|
type: 'WINDOW_COMMAND',
|
||||||
|
name: 'setTags',
|
||||||
|
noteId: noteIds[0],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
menu.append(new MenuItem({label: _('Duplicate'), click: async () => {
|
menu.append(
|
||||||
for (let i = 0; i < noteIds.length; i++) {
|
new MenuItem({
|
||||||
const note = await Note.load(noteIds[i]);
|
label: _('Duplicate'),
|
||||||
await Note.duplicate(noteIds[i], {
|
click: async () => {
|
||||||
uniqueTitle: _('%s - Copy', note.title),
|
for (let i = 0; i < noteIds.length; i++) {
|
||||||
});
|
const note = await Note.load(noteIds[i]);
|
||||||
}
|
await Note.duplicate(noteIds[i], {
|
||||||
}}));
|
uniqueTitle: _('%s - Copy', note.title),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
if (noteIds.length <= 1) {
|
if (noteIds.length <= 1) {
|
||||||
menu.append(new MenuItem({label: _('Switch between note and to-do type'), click: async () => {
|
menu.append(
|
||||||
for (let i = 0; i < noteIds.length; i++) {
|
new MenuItem({
|
||||||
const note = await Note.load(noteIds[i]);
|
label: _('Switch between note and to-do type'),
|
||||||
await Note.save(Note.toggleIsTodo(note), { userSideValidation: true });
|
click: async () => {
|
||||||
eventManager.emit('noteTypeToggle', { noteId: note.id });
|
for (let i = 0; i < noteIds.length; i++) {
|
||||||
}
|
const note = await Note.load(noteIds[i]);
|
||||||
}}));
|
await Note.save(Note.toggleIsTodo(note), { userSideValidation: true });
|
||||||
|
eventManager.emit('noteTypeToggle', { noteId: note.id });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
const switchNoteType = async (noteIds, type) => {
|
const switchNoteType = async (noteIds, type) => {
|
||||||
for (let i = 0; i < noteIds.length; i++) {
|
for (let i = 0; i < noteIds.length; i++) {
|
||||||
@@ -60,26 +71,41 @@ class NoteListUtils {
|
|||||||
await Note.save(newNote, { userSideValidation: true });
|
await Note.save(newNote, { userSideValidation: true });
|
||||||
eventManager.emit('noteTypeToggle', { noteId: note.id });
|
eventManager.emit('noteTypeToggle', { noteId: note.id });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
menu.append(new MenuItem({label: _('Switch to note type'), click: async () => {
|
menu.append(
|
||||||
await switchNoteType(noteIds, 'note');
|
new MenuItem({
|
||||||
}}));
|
label: _('Switch to note type'),
|
||||||
|
click: async () => {
|
||||||
|
await switchNoteType(noteIds, 'note');
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
menu.append(new MenuItem({label: _('Switch to to-do type'), click: async () => {
|
menu.append(
|
||||||
await switchNoteType(noteIds, 'todo');
|
new MenuItem({
|
||||||
}}));
|
label: _('Switch to to-do type'),
|
||||||
|
click: async () => {
|
||||||
|
await switchNoteType(noteIds, 'todo');
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.append(new MenuItem({label: _('Copy Markdown link'), click: async () => {
|
menu.append(
|
||||||
const { clipboard } = require('electron');
|
new MenuItem({
|
||||||
const links = [];
|
label: _('Copy Markdown link'),
|
||||||
for (let i = 0; i < noteIds.length; i++) {
|
click: async () => {
|
||||||
const note = await Note.load(noteIds[i]);
|
const { clipboard } = require('electron');
|
||||||
links.push(Note.markdownTag(note));
|
const links = [];
|
||||||
}
|
for (let i = 0; i < noteIds.length; i++) {
|
||||||
clipboard.writeText(links.join(' '));
|
const note = await Note.load(noteIds[i]);
|
||||||
}}));
|
links.push(Note.markdownTag(note));
|
||||||
|
}
|
||||||
|
clipboard.writeText(links.join(' '));
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const exportMenu = new Menu();
|
const exportMenu = new Menu();
|
||||||
|
|
||||||
@@ -89,28 +115,43 @@ class NoteListUtils {
|
|||||||
const module = ioModules[i];
|
const module = ioModules[i];
|
||||||
if (module.type !== 'exporter') continue;
|
if (module.type !== 'exporter') continue;
|
||||||
|
|
||||||
exportMenu.append(new MenuItem({ label: module.fullLabel() , click: async () => {
|
exportMenu.append(
|
||||||
await InteropServiceHelper.export(props.dispatch.bind(this), module, { sourceNoteIds: noteIds });
|
new MenuItem({
|
||||||
}}));
|
label: module.fullLabel(),
|
||||||
|
click: async () => {
|
||||||
|
await InteropServiceHelper.export(props.dispatch.bind(this), module, { sourceNoteIds: noteIds });
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noteIds.length === 1) {
|
if (noteIds.length === 1) {
|
||||||
exportMenu.append(new MenuItem({ label: 'PDF - ' + _('PDF File') , click: () => {
|
exportMenu.append(
|
||||||
props.dispatch({
|
new MenuItem({
|
||||||
type: 'WINDOW_COMMAND',
|
label: 'PDF - ' + _('PDF File'),
|
||||||
name: 'exportPdf',
|
click: () => {
|
||||||
});
|
props.dispatch({
|
||||||
}}));
|
type: 'WINDOW_COMMAND',
|
||||||
|
name: 'exportPdf',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const exportMenuItem = new MenuItem({label: _('Export'), submenu: exportMenu});
|
const exportMenuItem = new MenuItem({ label: _('Export'), submenu: exportMenu });
|
||||||
|
|
||||||
menu.append(exportMenuItem);
|
menu.append(exportMenuItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.append(new MenuItem({label: _('Delete'), click: async () => {
|
menu.append(
|
||||||
await this.confirmDeleteNotes(noteIds);
|
new MenuItem({
|
||||||
}}));
|
label: _('Delete'),
|
||||||
|
click: async () => {
|
||||||
|
await this.confirmDeleteNotes(noteIds);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
@@ -135,7 +176,6 @@ class NoteListUtils {
|
|||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
await Note.batchDelete(noteIds);
|
await Note.batchDelete(noteIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = NoteListUtils;
|
module.exports = NoteListUtils;
|
Reference in New Issue
Block a user