mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-23 08:46:07 +08:00
151 lines
4.5 KiB
React
151 lines
4.5 KiB
React
|
import React from 'react'
|
||
|
import {
|
||
|
DraftHelpers,
|
||
|
MessageUtils,
|
||
|
MessageBodyProcessor,
|
||
|
QuotedHTMLTransformer,
|
||
|
AttachmentStore,
|
||
|
} from 'nylas-exports';
|
||
|
import {
|
||
|
InjectedComponentSet,
|
||
|
RetinaImg,
|
||
|
} from 'nylas-component-kit';
|
||
|
|
||
|
import EmailFrame from './email-frame';
|
||
|
import {encodedAttributeForFile} from './inline-image-listeners';
|
||
|
|
||
|
const TransparentPixel = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNikAQAACIAHF/uBd8AAAAASUVORK5CYII="
|
||
|
|
||
|
export default class MessageItemBody extends React.Component {
|
||
|
static displayName = 'MessageItemBody';
|
||
|
static propTypes = {
|
||
|
message: React.PropTypes.object.isRequired,
|
||
|
downloads: React.PropTypes.object.isRequired,
|
||
|
};
|
||
|
|
||
|
constructor(props, context) {
|
||
|
super(props, context);
|
||
|
this._mounted = false;
|
||
|
this.state = {
|
||
|
showQuotedText: DraftHelpers.isForwardedMessage(this.props.message),
|
||
|
processedBody: null,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
componentWillMount() {
|
||
|
this._unsub = MessageBodyProcessor.subscribe(this.props.message, (processedBody) =>
|
||
|
this.setState({processedBody})
|
||
|
);
|
||
|
}
|
||
|
|
||
|
componentDidMount() {
|
||
|
this._mounted = true;
|
||
|
}
|
||
|
|
||
|
componentWillReceiveProps(nextProps) {
|
||
|
if (nextProps.message.id !== this.props.message.id) {
|
||
|
if (this._unsub) { this._unsub() }
|
||
|
this._unsub = MessageBodyProcessor.subscribe(nextProps.message, (processedBody) =>
|
||
|
this.setState({processedBody})
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
componentWillUnmount() {
|
||
|
this._mounted = false;
|
||
|
if (this._unsub) { this._unsub() }
|
||
|
}
|
||
|
|
||
|
_onToggleQuotedText = () => {
|
||
|
this.setState({
|
||
|
showQuotedText: !this.state.showQuotedText,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_mergeBodyWithFiles(body) {
|
||
|
let merged = body;
|
||
|
|
||
|
// Replace cid: references with the paths to downloaded files
|
||
|
for (const file of this.props.message.files) {
|
||
|
const download = this.props.downloads[file.id];
|
||
|
|
||
|
// Note: I don't like doing this with RegExp before the body is inserted into
|
||
|
// the DOM, but we want to avoid "could not load cid://" in the console.
|
||
|
|
||
|
if (download && download.state !== 'finished') {
|
||
|
const inlineImgRegexp = new RegExp(`<\\s*img.*src=['"]cid:${file.contentId}['"][^>]*>`, 'gi')
|
||
|
// Render a spinner
|
||
|
merged = merged.replace(inlineImgRegexp, () =>
|
||
|
'<img alt="spinner.gif" src="nylas://message-list/assets/spinner.gif" style="-webkit-user-drag: none;">'
|
||
|
);
|
||
|
} else {
|
||
|
// Render the completed download. We include data-nylas-file so that if the image fails
|
||
|
// to load, we can parse the file out and call `Actions.fetchFile` to retrieve it.
|
||
|
// (Necessary when attachment download mode is set to "manual")
|
||
|
const cidRegexp = new RegExp(`cid:${file.contentId}(['"])`, 'gi')
|
||
|
merged = merged.replace(cidRegexp, (text, quoteCharacter) =>
|
||
|
`file://${AttachmentStore.pathForFile(file)}${quoteCharacter} data-nylas-file="${encodedAttributeForFile(file)}" `
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Replace remaining cid: references - we will not display them since they'll
|
||
|
// throw "unknown ERR_UNKNOWN_URL_SCHEME". Show a transparent pixel so that there's
|
||
|
// no "missing image" region shown, just a space.
|
||
|
merged = merged.replace(MessageUtils.cidRegex, `src="${TransparentPixel}"`)
|
||
|
|
||
|
return merged;
|
||
|
}
|
||
|
|
||
|
_renderBody() {
|
||
|
const {message} = this.props;
|
||
|
const {showQuotedText, processedBody} = this.state;
|
||
|
|
||
|
if (typeof message.body === "string" && typeof processedBody === "string") {
|
||
|
return (
|
||
|
<EmailFrame
|
||
|
showQuotedText={showQuotedText}
|
||
|
content={this._mergeBodyWithFiles(processedBody)}
|
||
|
message={message}
|
||
|
/>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return (
|
||
|
<div className="message-body-loading">
|
||
|
<RetinaImg
|
||
|
name="inline-loading-spinner.gif"
|
||
|
mode={RetinaImg.Mode.ContentDark}
|
||
|
style={{width: 14, height: 14}}
|
||
|
/>
|
||
|
</div>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
_renderQuotedTextControl() {
|
||
|
if (!QuotedHTMLTransformer.hasQuotedHTML(this.props.message.body)) {
|
||
|
return null;
|
||
|
}
|
||
|
return (
|
||
|
<a className="quoted-text-control" onClick={this._onToggleQuotedText}>
|
||
|
<span className="dots">•••</span>
|
||
|
</a>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
render() {
|
||
|
return (
|
||
|
<span>
|
||
|
<InjectedComponentSet
|
||
|
matching={{role: "message:BodyHeader"}}
|
||
|
exposedProps={{message: this.props.message}}
|
||
|
direction="column"
|
||
|
style={{width: '100%'}}
|
||
|
/>
|
||
|
{this._renderBody()}
|
||
|
{this._renderQuotedTextControl()}
|
||
|
</span>
|
||
|
);
|
||
|
}
|
||
|
}
|