-
-
Notifications
You must be signed in to change notification settings - Fork 827
Make SVGs and CSS dynamically recolourable #77
Conversation
Could particularly do with review on Tinter.js - is it safe to store state in plain old vars like this, or should it be all bundled up into an exported object? Or is that just hygiene? |
@@ -42,6 +43,11 @@ var commands = { | |||
return reject("Usage: /nick <display_name>"); | |||
}, | |||
|
|||
tint: function(room_id, args) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(most of) the other commands have a comment saying what they do. wouldn't hurt here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
It should be safe but in our case it isn't. It's usually safe because So what does this actually mean? It means that as it stands, if module.exports = {
...
};
if (!global.mxTinter) { // this attaches to 'window' (!)
global.mxTinter = module.exports;
}
module.exports = global.mxTinter; You would need to move all your |
for (var k = 0; k < cssAttrs.length; k++) { | ||
var attr = cssAttrs[k]; | ||
for (var l = 0; l < keyRgb.length; l++) { | ||
if (rule.style && rule.style[attr] === keyRgb[l]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add if (!rule.style) continue;
at line 88, and simplify this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, although it's very rare for a CSS rule to not have a style (it happens with weird rules like animation data which don't actually define styles), so it doesn't buy us much. have done it anyway.
…onload rather than react synthetic events
I've addressed all the feedback other than:
Assuming we're agreed, I'll shift these to be maintenance bugs to address when we're out of crunch. |
@kegsay PTAL |
It's also worth noting that I was deliberately caching the required fixups for SVGs in the singleton Tinter rather than recalculating them every time onLoad (given fixups are still valid from previous loads). Given SVGs could theoretically be quite big, wasting time re-walking them everytime they reload to generate fixups would chew CPU and time for no reason at all. ...oh, except I ended up backing out that caching. ignore me. yup, it leaks, as the XXX comment says. |
WFM. You've put a big warning up so I'm happy as is.
So I had a look at how badly it leaks. My test was as follows:
On This equates to about 3MB per room swap. This is too much for a long-lived web app. I don't mind fixing the leak for you if you're stretched for time, but I would be unhappy leaving it as-is. |
i'm very surprised the leak is this bad. will fix it. suggestions on how to refer to the set of tintablesvgs when the user runs /tint?
|
You just need to invert the control flow a bit. When people run /tint you should emit an event on the dispatcher (e.g. |
So the full flow would look like: // SlashCommands.js
tint: function(newColour) {
Tinter.setTint(newColour);
dispatcher.dispatch({
action: "tint_change"
});
}
// ============
// TintableSvg.js
componentWillMount: function() {
dis.register(this._onAction);
},
componentDidMount: function() {
Tinter.applyTint(ReactDOM.findDOMNode(this));
},
_onAction: function(payload) {
if (payload.action !== "tint_change") { return; }
Tinter.applyTint(ReactDOM.findDOMNode(this));
}
// ============
// Tinter.js
setTint: function(newColour) {
currentColour = newColour;
},
applyTint: function(domNode) {
// apply `currentColour` to `domNode`
} And make sure that Tinter never persists the passed domNode. It should only persist the current colour. |
oh, sorry, yes. was being thick. |
…storing SVG DOM fragments in Tinter to avoid leaking them
0f52c0a fixes the leak by making TintableSvgs responsible for updating their own tints, and responding to tint_update dispatch events. However I've kept the existing functions in Tinter.js rather than refactoring as per Kegan's full suggestion, as it retains symmetry with the half which is still needed for fixing up CSS (rather than SVGs), and i don't want to burn yet more time on this. @kegsay: PTAL |
|
||
onAction: function(payload) { | ||
if (payload.action !== 'tint_update') return; | ||
Tinter.applySvgFixups(this.fixups); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is called prior to onLoad
then this will NPE on Tinter.js:192
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why? this.fixups is initialised to [], not null?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, right you are. However, the way you're doing it isn't what you intend I think. You're defining fixups
on :27
as a prototype property, which is shared across all instances. You can't define instance properties like that, you need to use:
componentWillMount: function() {
this.fixups = [];
}
Without this, all TintableSvg
elements would share the same fixups
array(!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops, fixed, thanks
LGTM - Did more heap dumps and now we no longer leak :) |
Make SVGs and CSS dynamically recolourable
added missing strings
I'm not proud of this.
Turns any colour-themed SVGs from img tags into object tags to make them dynamically editable from JS, and implements a simple /tint command to let you clobber the CSS and appropriate SVG attributes with a custom colour scheme.
Requires facebook/react#5781 to work, as otherwise react doesn't understand onLoad on object tags.