-
-
Notifications
You must be signed in to change notification settings - Fork 827
Improve widget rendering on prop updates #1548
Conversation
this.updateWidgetContent(); | ||
}, | ||
|
||
// Update widget content |
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.
Redundant comment
|
||
// Update widget content | ||
updateWidgetContent() { | ||
this.setState(this._getInitialState()); |
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.
The name "getInitialState" isn't really accurate if the state that it returns is going to be updating.
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.
Fair enough. I've renamed it to "_getNewUrlState".
}, | ||
|
||
componentWillUnmount() { | ||
window.removeEventListener('message', this._onMessage); | ||
}, | ||
|
||
componentDidUpdate(prevProps) { |
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.
I think you want to do this before an update happens, i.e. using https://reactjs.org/docs/react-component.html#componentwillreceiveprops
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.
This was intentional, so that the _getInitialState function (now renamed to _getNewUrlState) could use the current properties, whether called from the initial load (getInitialState), or when the properties have been updated. By switching it around we have to pass the properties to be used ... which seems clunkier to me, and needs a comment to explain why we are passing props to the function, rather than just using "this.props".
Anyway, I've gone and changed it around, as suggested...
@@ -333,6 +359,7 @@ export default React.createClass({ | |||
alt={_t('Edit')} | |||
title={_t('Edit')} | |||
onClick={this._onEditClick} | |||
onLoad={this._onLoaded} |
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.
This seems like a bizarre thing to be doing. I don't think this image's rendering has anything to do with the loading state of scalar.
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.
Meh... you're quite right, this should be on the iframe. Thanks for catching that!
loading: true, | ||
}); | ||
this._scalarClient = new ScalarAuthClient(); | ||
if(! this._scalarClient) { |
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.
spurious space
if (!this.isScalarUrl()) { | ||
return; | ||
} | ||
// Fetch the token before loading the iframe as we need to mangle the URL | ||
this.setState({ | ||
loading: true, |
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 no longer needed, the partner loading: false
s should be removed too. But it would be strange to not display this loading state, no? Unless this happens too quickly for a spinner to be worth it?
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.
The loading state is now set to true in the _getInitialState function on line 64/65. The loading state spinner is shown until the onload event of the iframe is called. Leaving this as is.
u.search = "?scalar_token=" + encodeURIComponent(token); | ||
} else { | ||
u.search += "&scalar_token=" + encodeURIComponent(token); | ||
const params = new URLSearchParams(u.search); |
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.
Looks like we already have a library for this - riot-web uses querystring
(looking at url_utils.js
)... although it doesn't seem to be marked as a dependency.
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.
Urghhh... undocumented dependencies. Yay. I'll switch to using that then. I'll also add querystring in to the package.json to make it more obvious that this library is available.
@lukebarnard1 -- Sorry, this one got lost / delayed. Can you please take a quick look (again) and let me know if it is good to go? Thanks |
package.json
Outdated
@@ -74,6 +74,8 @@ | |||
"matrix-js-sdk": "0.8.5", | |||
"optimist": "^0.6.1", | |||
"prop-types": "^15.5.8", | |||
"qs": "^6.5.1", | |||
"querystring": "^0.2.0", |
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.
I don't think this is used.
const widgetPermissionId = [this.props.room.roomId, encodeURIComponent(this.props.url)].join('_'); | ||
/** | ||
* Set initial component state when the App wUrl (widget URL) is being updated | ||
* @param {Object} props The component props *must* be passed (rather than using this.props) so that it can be called with future props, e.g. from componentWillReceiveProps. |
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.
This doc seems to be explaining why props
is here instead of what it is. Better to say what it is (i.e. newProps
- the new properties of the component). The explanation could go in the description for the function itself.
const hasPermissionToLoad = localStorage.getItem(widgetPermissionId); | ||
return { | ||
loading: false, | ||
widgetUrl: this.props.url, | ||
initialising: true, // True while we are mangling the widget URL |
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.
ideally, this would be set to true in the function that also sets it to false, then the explanation isn't needed
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.
Both the initialising and loading flags need to be set as initial state (before other functions are called). Normally this would be set in getInitialState. However, as this code is reused to reset state at other points in the lifecycle (on URL change), I've pulled it out in to a separate function that is called from getInitialState. Unless you have major concerns, I'll leave this as is.
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.
It's just nicer to read, instead of wondering "oh why did loading
get set to false here?... where did it get to true... ah way over 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.
Sure, that makes sense. However, given the reason for doing it like this (as described in the comment above), do you still think that changes are needed? If so, I'm not understanding the change that you are suggesting (sorry for the confusion).
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.
We've discussed this out of band and (hopefully) cleared up the confusion on this one. I have changed it to explicitly set the state to 'initialising: true' in the setScalarToken function.
loading: false, | ||
widgetUrl: this.props.url, | ||
initialising: true, // True while we are mangling the widget URL | ||
loading: true, // True while the iframe content is loading |
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.
likewise for this one
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.
As discussed, leaving this for the time being. However, may need a refactor for improved clarity in future.
widgetPermissionId: widgetPermissionId, | ||
// Assume that widget has permission to load if we are the user who added it to the room, or if explicitly granted by the user | ||
hasPermissionToLoad: hasPermissionToLoad === 'true' || this.props.userId === this.props.creatorUserId, | ||
hasPermissionToLoad: hasPermissionToLoad === 'true' || props.userId === props.creatorUserId, |
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.
I think it'd be clearer to do the comparison to the string at the point you take this out of localStorage.
appTileBody = ( | ||
<div className='mx_AppTileBody mx_AppLoading'> | ||
<MessageSpinner msg='Loading...' /> | ||
</div> | ||
); | ||
} else if (this.state.hasPermissionToLoad == true) { | ||
const loadingElement = ( | ||
<div className="mx_AppTileBody"> | ||
<AppWarning |
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.
could probably fit this on one line?
} else { | ||
appTileBody = ( | ||
<div className="mx_AppTileBody"> | ||
<div className={this.loading ? 'mx_AppTileBody mx_AppLoading' : 'mx_AppTileBody'}> |
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.
I think you mean this.state.loading
} else { | ||
appTileBody = ( | ||
<div className="mx_AppTileBody"> | ||
<div className={this.loading ? 'mx_AppTileBody mx_AppLoading' : 'mx_AppTileBody'}> | ||
{ this.loading && Element } |
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.
Not sure what Element
is
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.
Yeah, that's just weird ... not sure how that got committed. Looks like autocomplete cruft? Embarrassing. Thanks for catching that.
@lukebarnard1 - Can you please take another look? Thanks. |
Depends upon - element-hq/element-web#5555 |
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.
Could you make sure your comments are a reasonable width - some go up to 150 characters.
package.json
Outdated
@@ -74,6 +74,7 @@ | |||
"matrix-js-sdk": "0.8.5", | |||
"optimist": "^0.6.1", | |||
"prop-types": "^15.5.8", | |||
"qs": "^6.5.1", |
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.
I forgot about this. We already use querystring in riot-web. Let's use that? (See url_utils.js in riot-web)
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.
Ok, I've updated again to reflect (I had querystring earlier in the PR, but reverted to qs as it is already being installed as a dep of other imports in react-sdk).
It's frustrating that riot-web's package.json doesn't list querystring as a dep., which would have made our use / choice of it more obvious.
* Component initialisation is only complete when this function has resolved | ||
*/ | ||
setScalarToken() { | ||
this.setState({initialising: true}); |
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.
Could do this immediately preceding the getScalarToken? Then you don't have to do the initialising: false on line 138.
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.
You do still need to always set initialising to false, as it is always set to true in the initialiser. Leaving this as is.
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, that's true.
I've reduced comment length. If this is required, maybe this should be set in eslint - it's currently set to ignore? |
probably |
Taking a different approach to #1542.