Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Improve widget rendering on prop updates #1548

Merged
merged 28 commits into from
Nov 10, 2017
Merged

Conversation

rxl881
Copy link
Contributor

@rxl881 rxl881 commented Oct 27, 2017

Taking a different approach to #1542.

this.updateWidgetContent();
},

// Update widget content
Copy link
Contributor

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());
Copy link
Contributor

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.

Copy link
Contributor Author

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) {
Copy link
Contributor

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

Copy link
Contributor Author

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}
Copy link
Contributor

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.

Copy link
Contributor Author

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) {
Copy link
Contributor

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,
Copy link
Contributor

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: falses 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?

Copy link
Contributor Author

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);
Copy link
Contributor

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.

Copy link
Contributor Author

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 lukebarnard1 assigned rxl881 and unassigned lukebarnard1 Oct 30, 2017
@rxl881
Copy link
Contributor Author

rxl881 commented Nov 7, 2017

@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

@rxl881 rxl881 assigned lukebarnard1 and unassigned rxl881 Nov 7, 2017
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",
Copy link
Contributor

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.
Copy link
Contributor

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
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor

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"

Copy link
Contributor Author

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).

Copy link
Contributor Author

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

likewise for this one

Copy link
Contributor Author

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,
Copy link
Contributor

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
Copy link
Contributor

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'}>
Copy link
Contributor

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 }
Copy link
Contributor

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

Copy link
Contributor Author

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.

@rxl881
Copy link
Contributor Author

rxl881 commented Nov 9, 2017

@lukebarnard1 - Can you please take another look? Thanks.

@rxl881
Copy link
Contributor Author

rxl881 commented Nov 9, 2017

Depends upon - element-hq/element-web#5555

Copy link
Contributor

@lukebarnard1 lukebarnard1 left a 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",
Copy link
Contributor

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)

Copy link
Contributor Author

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});
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, that's true.

@rxl881
Copy link
Contributor Author

rxl881 commented Nov 10, 2017

I've reduced comment length.

If this is required, maybe this should be set in eslint - it's currently set to ignore?

@lukebarnard1
Copy link
Contributor

If this is required, maybe this should be set in eslint - it's currently set to ignore?

probably

@lukebarnard1 lukebarnard1 merged commit 6e1cf6c into develop Nov 10, 2017
@rxl881 rxl881 deleted the rxl881/widgetrendering branch November 13, 2017 18:18
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants