mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 21:24:59 +08:00
Cache the tinted SVGs for MFileBody as data URLs (#559)
* Use a list of callbacks for things that need tinting. Rather than gutwrenching the internals of TintableSVG inside the Tinter. * Share a data: url for the tinted download svg in MFileBody * Check image exists before tinting * Add comments * Use fetch+DomParser rather than XMLHttpRequest * Remove comment about XMLHttpRequest
This commit is contained in:
parent
218ced0276
commit
6ccc825f0d
@ -153,7 +153,25 @@ function rgbToHex(rgb) {
|
|||||||
return '#' + (0x1000000 + val).toString(16).slice(1)
|
return '#' + (0x1000000 + val).toString(16).slice(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List of functions to call when the tint changes.
|
||||||
|
const tintables = [];
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Register a callback to fire when the tint changes.
|
||||||
|
* This is used to rewrite the tintable SVGs with the new tint.
|
||||||
|
*
|
||||||
|
* It's not possible to unregister a tintable callback. So this can only be
|
||||||
|
* used to register a static callback. If a set of tintables will change
|
||||||
|
* over time then the best bet is to register a single callback for the
|
||||||
|
* entire set.
|
||||||
|
*
|
||||||
|
* @param {Function} tintable Function to call when the tint changes.
|
||||||
|
*/
|
||||||
|
registerTintable : function(tintable) {
|
||||||
|
tintables.push(tintable);
|
||||||
|
},
|
||||||
|
|
||||||
tint: function(primaryColor, secondaryColor, tertiaryColor) {
|
tint: function(primaryColor, secondaryColor, tertiaryColor) {
|
||||||
|
|
||||||
if (!cached) {
|
if (!cached) {
|
||||||
@ -201,12 +219,9 @@ module.exports = {
|
|||||||
|
|
||||||
// tell all the SVGs to go fix themselves up
|
// tell all the SVGs to go fix themselves up
|
||||||
// we don't do this as a dispatch otherwise it will visually lag
|
// we don't do this as a dispatch otherwise it will visually lag
|
||||||
var TintableSvg = sdk.getComponent("elements.TintableSvg");
|
tintables.forEach(function(tintable) {
|
||||||
if (TintableSvg.mounts) {
|
tintable();
|
||||||
Object.keys(TintableSvg.mounts).forEach((id) => {
|
|
||||||
TintableSvg.mounts[id].tint();
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// XXX: we could just move this all into TintableSvg, but as it's so similar
|
// XXX: we could just move this all into TintableSvg, but as it's so similar
|
||||||
@ -265,5 +280,5 @@ module.exports = {
|
|||||||
svgFixup.node.setAttribute(svgFixup.attr, colors[svgFixup.index]);
|
svgFixup.node.setAttribute(svgFixup.attr, colors[svgFixup.index]);
|
||||||
}
|
}
|
||||||
if (DEBUG) console.log("applySvgFixups end");
|
if (DEBUG) console.log("applySvgFixups end");
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
@ -74,4 +74,13 @@ var TintableSvg = React.createClass({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Register with the Tinter so that we will be told if the tint changes
|
||||||
|
Tinter.registerTintable(function() {
|
||||||
|
if (TintableSvg.mounts) {
|
||||||
|
Object.keys(TintableSvg.mounts).forEach((id) => {
|
||||||
|
TintableSvg.mounts[id].tint();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = TintableSvg;
|
module.exports = TintableSvg;
|
||||||
|
@ -21,7 +21,42 @@ import filesize from 'filesize';
|
|||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import {decryptFile} from '../../../utils/DecryptFile';
|
import {decryptFile} from '../../../utils/DecryptFile';
|
||||||
|
import Tinter from '../../../Tinter';
|
||||||
|
import 'isomorphic-fetch';
|
||||||
|
import q from 'q';
|
||||||
|
|
||||||
|
// A cached tinted copy of "img/download.svg"
|
||||||
|
var tintedDownloadImageURL;
|
||||||
|
// Track a list of mounted MFileBody instances so that we can update
|
||||||
|
// the "img/download.svg" when the tint changes.
|
||||||
|
var nextMountId = 0;
|
||||||
|
const mounts = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the tinted copy of "img/download.svg" when the tint changes.
|
||||||
|
*/
|
||||||
|
function updateTintedDownloadImage() {
|
||||||
|
// Download the svg as an XML document.
|
||||||
|
// We could cache the XML response here, but since the tint rarely changes
|
||||||
|
// it's probably not worth it.
|
||||||
|
q(fetch("img/download.svg")).then(function(response) {
|
||||||
|
return response.text();
|
||||||
|
}).then(function(svgText) {
|
||||||
|
const svg = new DOMParser().parseFromString(svgText, "image/svg+xml");
|
||||||
|
// Apply the fixups to the XML.
|
||||||
|
const fixups = Tinter.calcSvgFixups([{contentDocument: svg}]);
|
||||||
|
Tinter.applySvgFixups(fixups);
|
||||||
|
// Encoded the fixed up SVG as a data URL.
|
||||||
|
const svgString = new XMLSerializer().serializeToString(svg);
|
||||||
|
tintedDownloadImageURL = "data:image/svg+xml;base64," + window.btoa(svgString);
|
||||||
|
// Notify each mounted MFileBody that the URL has changed.
|
||||||
|
Object.keys(mounts).forEach(function(id) {
|
||||||
|
mounts[id].tint();
|
||||||
|
});
|
||||||
|
}).done();
|
||||||
|
}
|
||||||
|
|
||||||
|
Tinter.registerTintable(updateTintedDownloadImage);
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'MFileBody',
|
displayName: 'MFileBody',
|
||||||
@ -70,6 +105,12 @@ module.exports = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
|
// Add this to the list of mounted components to receive notifications
|
||||||
|
// when the tint changes.
|
||||||
|
this.id = nextMountId++;
|
||||||
|
mounts[this.id] = this;
|
||||||
|
this.tint();
|
||||||
|
// Check whether we need to decrypt the file content.
|
||||||
const content = this.props.mxEvent.getContent();
|
const content = this.props.mxEvent.getContent();
|
||||||
if (content.file !== undefined && this.state.decryptedUrl === null) {
|
if (content.file !== undefined && this.state.decryptedUrl === null) {
|
||||||
decryptFile(content.file).done((url) => {
|
decryptFile(content.file).done((url) => {
|
||||||
@ -84,12 +125,23 @@ module.exports = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentWillUnmount: function() {
|
||||||
|
// Remove this from the list of mounted components
|
||||||
|
delete mounts[this.id];
|
||||||
|
},
|
||||||
|
|
||||||
|
tint: function() {
|
||||||
|
// Update our tinted copy of "img/download.svg"
|
||||||
|
if (this.refs.downloadImage) {
|
||||||
|
this.refs.downloadImage.src = tintedDownloadImageURL;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const content = this.props.mxEvent.getContent();
|
const content = this.props.mxEvent.getContent();
|
||||||
|
|
||||||
const text = this.presentableTextForFile(content);
|
const text = this.presentableTextForFile(content);
|
||||||
|
|
||||||
var TintableSvg = sdk.getComponent("elements.TintableSvg");
|
|
||||||
if (content.file !== undefined && this.state.decryptedUrl === null) {
|
if (content.file !== undefined && this.state.decryptedUrl === null) {
|
||||||
|
|
||||||
// Need to decrypt the attachment
|
// Need to decrypt the attachment
|
||||||
@ -155,7 +207,7 @@ module.exports = React.createClass({
|
|||||||
<span className="mx_MFileBody">
|
<span className="mx_MFileBody">
|
||||||
<div className="mx_MImageBody_download">
|
<div className="mx_MImageBody_download">
|
||||||
<a href={contentUrl} target="_blank" rel="noopener" download={downloadAttr}>
|
<a href={contentUrl} target="_blank" rel="noopener" download={downloadAttr}>
|
||||||
<TintableSvg src="img/download.svg" width="12" height="14"/>
|
<img src={tintedDownloadImageURL} width="12" height="14" ref="downloadImage"/>
|
||||||
Download {text}
|
Download {text}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user