2017-12-07 03:12:59 +08:00
|
|
|
import { remote } from 'electron';
|
|
|
|
import {
|
|
|
|
React,
|
|
|
|
AccountStore,
|
|
|
|
SignatureStore,
|
|
|
|
Actions,
|
|
|
|
FocusedPerspectiveStore,
|
|
|
|
Utils,
|
|
|
|
} from 'mailspring-exports';
|
Totally overhauled composer based on Slate (#524)
* Remove the composer contenteditable, replace with basic <textarea>
* Beginning broader cleanup of draft session
* DraftJS composer with color, style support
* Serialization/unserialization of basic styles, toolbar working
* WIP
* Switch to draft-js-plugins approach, need to revisit HTML
* Move HTML conversion functionality into plugins
* Add spellcheck context menu to editor
* Initial work on quoted text
* Further work on quoted text
* BLOCK approach
* Entity approach - better, does not bump out to top level
* Hiding and showing quoted text via CSS
* Get rid of ability to inject another subject line component
* Clean up specs, DraftFactory to ES6
* Remove old initial focus hack
* Fix focusing, initial text selection
* Remove participant “collapsing” support, it can be confusing
* Correctly terminate links on carriage returns
* Initial signature support, allow removal of uneditable blocks
* Sync body string with body editorstate
* Simplify draft editor session, finish signatures
* Templates
* Minor fixes
* Simplify link/open tracking, ensure it works
* Reorg composer, rework template editor
* Omg the slowness is all the stupid emoji button
* Polish and small fixes
* Performance improvements, new templates UI
* Don’t assume nodes are elements
* Fix for sending drafts twice due to back-to-back saves
* Fix order of operations on app quit to save drafts reliably
* Improve DraftJS-Convert whitespace handling
* Use contentID throughout attachment lifecycle
* Try to fix images
* Switch to Slate instead of DraftJS… much better
* Fix newline handling
* Bug fixes
* Cleanup
* Finish templates plugin
* Clean up text editing / support for Gmail email styles
* Support for color + size on the same node, clean trailing whitespace
* Restore emoji typeahead / emoji picker
* Fix scrolling in template editor
* Fix specs
* Fix newlines
* Re-implement spellcheck to be faster
* Make spellcheck decorator changes invisible to the undo/redo stack
* Remove comment
* Polish themplates panel
* Fix #521
2018-01-12 07:55:56 +08:00
|
|
|
import { Flexbox, EditableList } from 'mailspring-component-kit';
|
2016-03-15 08:04:40 +08:00
|
|
|
|
2017-12-07 03:12:59 +08:00
|
|
|
import { ResolveSignatureData, RenderSignatureData, DataShape } from './constants';
|
Totally overhauled composer based on Slate (#524)
* Remove the composer contenteditable, replace with basic <textarea>
* Beginning broader cleanup of draft session
* DraftJS composer with color, style support
* Serialization/unserialization of basic styles, toolbar working
* WIP
* Switch to draft-js-plugins approach, need to revisit HTML
* Move HTML conversion functionality into plugins
* Add spellcheck context menu to editor
* Initial work on quoted text
* Further work on quoted text
* BLOCK approach
* Entity approach - better, does not bump out to top level
* Hiding and showing quoted text via CSS
* Get rid of ability to inject another subject line component
* Clean up specs, DraftFactory to ES6
* Remove old initial focus hack
* Fix focusing, initial text selection
* Remove participant “collapsing” support, it can be confusing
* Correctly terminate links on carriage returns
* Initial signature support, allow removal of uneditable blocks
* Sync body string with body editorstate
* Simplify draft editor session, finish signatures
* Templates
* Minor fixes
* Simplify link/open tracking, ensure it works
* Reorg composer, rework template editor
* Omg the slowness is all the stupid emoji button
* Polish and small fixes
* Performance improvements, new templates UI
* Don’t assume nodes are elements
* Fix for sending drafts twice due to back-to-back saves
* Fix order of operations on app quit to save drafts reliably
* Improve DraftJS-Convert whitespace handling
* Use contentID throughout attachment lifecycle
* Try to fix images
* Switch to Slate instead of DraftJS… much better
* Fix newline handling
* Bug fixes
* Cleanup
* Finish templates plugin
* Clean up text editing / support for Gmail email styles
* Support for color + size on the same node, clean trailing whitespace
* Restore emoji typeahead / emoji picker
* Fix scrolling in template editor
* Fix specs
* Fix newlines
* Re-implement spellcheck to be faster
* Make spellcheck decorator changes invisible to the undo/redo stack
* Remove comment
* Polish themplates panel
* Fix #521
2018-01-12 07:55:56 +08:00
|
|
|
import SignatureAccountDefaultPicker from './signature-account-default-picker';
|
|
|
|
import SignatureTemplatePicker from './signature-template-picker';
|
|
|
|
import SignaturePhotoPicker from './signature-photo-picker';
|
|
|
|
import Templates from './templates';
|
2016-07-12 03:28:37 +08:00
|
|
|
|
2017-12-07 03:12:59 +08:00
|
|
|
class SignatureEditor extends React.Component {
|
|
|
|
_onBaseFieldChange = event => {
|
|
|
|
const { id, value } = event.target;
|
|
|
|
const sig = this.props.signature;
|
|
|
|
Actions.upsertSignature(Object.assign({}, sig, { [id]: value }), sig.id);
|
2017-09-27 02:33:08 +08:00
|
|
|
};
|
2016-07-12 03:28:37 +08:00
|
|
|
|
2017-12-07 03:12:59 +08:00
|
|
|
_onDataFieldChange = event => {
|
|
|
|
const { id, value } = event.target;
|
|
|
|
const sig = this.props.signature;
|
|
|
|
|
|
|
|
// If you have raw selected and are switching back to a template,
|
|
|
|
// display a warning UNLESS the html is an unmodified template HTML
|
|
|
|
if (id === 'templateName' && !sig.data.templateName && value) {
|
|
|
|
const htmlMatchesATemplate = Templates.find(
|
|
|
|
t => sig.body === RenderSignatureData(Object.assign({}, sig.data, { templateName: t.name }))
|
|
|
|
);
|
|
|
|
if (!htmlMatchesATemplate) {
|
|
|
|
const idx = remote.dialog.showMessageBox({
|
|
|
|
type: 'warning',
|
|
|
|
buttons: ['Cancel', 'Continue'],
|
|
|
|
message: 'Revert custom HTML?',
|
|
|
|
detail:
|
|
|
|
"Switching back to a signature template will overwrite the custom HTML you've entered.",
|
|
|
|
});
|
|
|
|
if (idx === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2016-07-29 05:46:01 +08:00
|
|
|
}
|
2017-12-07 03:12:59 +08:00
|
|
|
|
|
|
|
// apply change
|
|
|
|
sig.data = Object.assign({}, sig.data, { [id]: value });
|
|
|
|
|
|
|
|
// re-render
|
|
|
|
if (sig.data.templateName) {
|
|
|
|
const template = Templates.find(t => t.name === sig.data.templateName);
|
|
|
|
if (template) {
|
|
|
|
sig.body = RenderSignatureData(sig.data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Actions.upsertSignature(sig, sig.id);
|
2017-09-27 02:33:08 +08:00
|
|
|
};
|
2016-07-12 03:28:37 +08:00
|
|
|
|
2017-12-07 03:12:59 +08:00
|
|
|
render() {
|
|
|
|
const { accountsAndAliases, defaults } = this.props;
|
2016-07-12 03:28:37 +08:00
|
|
|
|
2017-12-07 03:12:59 +08:00
|
|
|
let signature = this.props.signature;
|
|
|
|
let empty = false;
|
|
|
|
if (!signature) {
|
|
|
|
signature = { data: { templateName: Templates[0].name } };
|
|
|
|
empty = true;
|
|
|
|
}
|
|
|
|
const data = signature.data || {};
|
|
|
|
const resolvedData = ResolveSignatureData(data);
|
2016-07-22 02:18:21 +08:00
|
|
|
|
2016-07-12 03:28:37 +08:00
|
|
|
return (
|
2017-12-07 03:12:59 +08:00
|
|
|
<div className={`signature-wrap ${empty && 'empty'}`}>
|
|
|
|
<div className="section basic-info">
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
id="title"
|
|
|
|
placeholder="Name"
|
|
|
|
value={signature.title || ''}
|
|
|
|
onChange={this._onBaseFieldChange}
|
|
|
|
/>
|
|
|
|
<div style={{ flex: 1 }} />
|
|
|
|
<SignatureAccountDefaultPicker
|
|
|
|
signature={signature}
|
|
|
|
accountsAndAliases={accountsAndAliases}
|
|
|
|
defaults={defaults}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="section preview">
|
|
|
|
<div className="label">Preview</div>
|
|
|
|
<div className="preview" dangerouslySetInnerHTML={{ __html: signature.body }} />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="section">
|
|
|
|
<SignatureTemplatePicker resolvedData={resolvedData} onChange={this._onDataFieldChange} />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{!resolvedData.templateName
|
|
|
|
? [
|
|
|
|
<div key="header" className="section-header">
|
|
|
|
Raw HTML Source
|
|
|
|
</div>,
|
|
|
|
<textarea
|
|
|
|
id="body"
|
|
|
|
key="textarea"
|
|
|
|
className="section raw-html"
|
|
|
|
spellCheck={false}
|
|
|
|
onChange={this._onBaseFieldChange}
|
|
|
|
value={signature.body || ''}
|
|
|
|
/>,
|
|
|
|
]
|
|
|
|
: [
|
|
|
|
<div key="header" className="section-header">
|
|
|
|
Information
|
|
|
|
</div>,
|
|
|
|
<div key="section" className="section information">
|
|
|
|
{DataShape.map(item => (
|
|
|
|
<div className="field" key={item.key}>
|
|
|
|
<label>{item.label}</label>
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
onChange={this._onDataFieldChange}
|
|
|
|
placeholder={item.placeholder}
|
|
|
|
id={item.key}
|
|
|
|
value={data[item.key] || ''}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
<SignaturePhotoPicker
|
|
|
|
id={signature.id}
|
|
|
|
data={data}
|
|
|
|
resolvedURL={resolvedData.photoURL}
|
|
|
|
onChange={this._onDataFieldChange}
|
|
|
|
/>
|
|
|
|
</div>,
|
|
|
|
]}
|
|
|
|
</div>
|
2017-09-27 02:33:08 +08:00
|
|
|
);
|
2016-03-15 08:04:40 +08:00
|
|
|
}
|
2017-12-07 03:12:59 +08:00
|
|
|
}
|
2016-03-15 08:04:40 +08:00
|
|
|
|
2017-12-07 03:12:59 +08:00
|
|
|
export default class PreferencesSignatures extends React.Component {
|
|
|
|
static displayName = 'PreferencesSignatures';
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
this.state = this._getStateFromStores();
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
this.unsubscribers = [SignatureStore.listen(this._onChange)];
|
2016-03-15 08:04:40 +08:00
|
|
|
}
|
|
|
|
|
2017-12-07 03:12:59 +08:00
|
|
|
componentWillUnmount() {
|
|
|
|
this.unsubscribers.forEach(unsubscribe => unsubscribe());
|
2016-03-15 08:04:40 +08:00
|
|
|
}
|
|
|
|
|
2017-12-07 03:12:59 +08:00
|
|
|
_onChange = () => {
|
|
|
|
this.setState(this._getStateFromStores());
|
|
|
|
};
|
|
|
|
|
|
|
|
_getStateFromStores() {
|
|
|
|
return {
|
|
|
|
signatures: SignatureStore.getSignatures(),
|
|
|
|
selectedSignature: SignatureStore.selectedSignature(),
|
|
|
|
defaults: SignatureStore.getDefaults(),
|
|
|
|
accountsAndAliases: AccountStore.aliases(),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
_onAddSignature = () => {
|
|
|
|
const activeIds = FocusedPerspectiveStore.current().accountIds || AccountStore.accountIds();
|
|
|
|
const activeAccount = AccountStore.accountForId(activeIds[0]);
|
|
|
|
const id = Utils.generateTempId();
|
|
|
|
|
|
|
|
let data = {};
|
|
|
|
let body = null;
|
|
|
|
if (this.state.selectedSignature) {
|
|
|
|
data = Object.assign({}, this.state.selectedSignature.data);
|
|
|
|
body = this.state.selectedSignature.body;
|
|
|
|
} else {
|
|
|
|
data = {
|
|
|
|
templateName: Templates[0].name,
|
|
|
|
name: activeAccount.name,
|
|
|
|
email: activeAccount.emailAddress,
|
|
|
|
};
|
|
|
|
body = RenderSignatureData(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
Actions.upsertSignature({ id, title: 'Untitled', body, data }, id);
|
|
|
|
Actions.selectSignature(id);
|
|
|
|
};
|
|
|
|
|
|
|
|
_onDeleteSignature = signature => {
|
|
|
|
Actions.removeSignature(signature);
|
|
|
|
};
|
|
|
|
|
|
|
|
_onEditSignatureTitle = nextTitle => {
|
|
|
|
const { title, ...rest } = this.state.selectedSignature;
|
|
|
|
Actions.upsertSignature({ title: nextTitle, ...rest }, rest.id);
|
|
|
|
};
|
|
|
|
|
|
|
|
_onSelectSignature = sig => {
|
|
|
|
Actions.selectSignature(sig.id);
|
|
|
|
};
|
|
|
|
|
2016-07-12 03:28:37 +08:00
|
|
|
_renderSignatures() {
|
2017-09-27 02:33:08 +08:00
|
|
|
const sigArr = Object.values(this.state.signatures);
|
2017-12-07 03:12:59 +08:00
|
|
|
|
2016-03-15 08:04:40 +08:00
|
|
|
return (
|
2016-07-12 03:28:37 +08:00
|
|
|
<Flexbox>
|
|
|
|
<EditableList
|
|
|
|
showEditIcon
|
|
|
|
className="signature-list"
|
|
|
|
items={sigArr}
|
2017-12-07 03:12:59 +08:00
|
|
|
itemContent={sig => sig.title}
|
2016-07-12 03:28:37 +08:00
|
|
|
onCreateItem={this._onAddSignature}
|
|
|
|
onDeleteItem={this._onDeleteSignature}
|
2017-12-07 03:12:59 +08:00
|
|
|
onItemEdited={this._onEditSignatureTitle}
|
2016-07-12 03:28:37 +08:00
|
|
|
onSelectItem={this._onSelectSignature}
|
|
|
|
selected={this.state.selectedSignature}
|
|
|
|
/>
|
2017-12-07 03:12:59 +08:00
|
|
|
<SignatureEditor
|
|
|
|
signature={this.state.selectedSignature}
|
|
|
|
defaults={this.state.defaults}
|
|
|
|
accountsAndAliases={this.state.accountsAndAliases}
|
|
|
|
/>
|
2016-07-12 03:28:37 +08:00
|
|
|
</Flexbox>
|
2017-09-27 02:33:08 +08:00
|
|
|
);
|
2016-03-15 08:04:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
2016-07-12 03:28:37 +08:00
|
|
|
<div className="preferences-signatures-container">
|
2017-09-27 02:33:08 +08:00
|
|
|
<section>{this._renderSignatures()}</section>
|
2016-07-12 03:28:37 +08:00
|
|
|
</div>
|
2017-09-27 02:33:08 +08:00
|
|
|
);
|
2016-03-15 08:04:40 +08:00
|
|
|
}
|
|
|
|
}
|