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

Improve UX for Jitsi by adding local echo for widgets #2035

Merged
merged 21 commits into from
Jul 19, 2018

Conversation

dbkr
Copy link
Member

@dbkr dbkr commented Jul 3, 2018

  • Show a spinner while we wait for widgets to be added
  • Hide widgets while they're pending deletion
  • Don't put another jitsi widget into the room if there's already
    one pending
  • Add an error if removing a widget fails
  • Wait longer for widgets (at least while matrix.org is slow)

Feedback welcome on the architecture of WidgetEchoStore, in particular how we pass in the current room widgets so the store isn't pulling these from a different place, implying that it would be responsible for emitting events when they change.

dbkr added 3 commits July 3, 2018 11:16
 * Show a spinner while we wait for widgets to be deleted
 * Hide widgets while they're pending deletion
 * Don't put another jitsi widget into the room if there's already
   one pending
@dbkr dbkr requested a review from a team July 3, 2018 10:25
@@ -1,6 +1,6 @@
/*
Copyright 2017 Vector Creations Ltd
Copyright 2017 New Vector Ltd
Copyright 2017, 2018 New Vector Ltd
Copy link
Member Author

Choose a reason for hiding this comment

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

As it happens this does need a (c) 2018

@dbkr dbkr removed the request for review from a team July 3, 2018 10:37
@dbkr dbkr changed the title Improve UX for Jitsi by adding local echo for widgets [WIP] Improve UX for Jitsi by adding local echo for widgets Jul 3, 2018
Also up the timeout because matrix.org is that slow
@dbkr dbkr changed the title [WIP] Improve UX for Jitsi by adding local echo for widgets Improve UX for Jitsi by adding local echo for widgets Jul 3, 2018
@dbkr dbkr requested a review from a team July 3, 2018 10:57
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.

Code-wise/commenting-wise needs a few changes.

Architecturally, I've already advised on this but I'll try and comment objectively on this implementation specifically:

  • Requiring both widget utils and the widget store to be able to render the view smells a bit like high coupling.
  • It seems odd to have to provide an array of current widgets to get the state for rendering. If the state for rendering was already in the store, the view would be simpler.
  • Overall I can't help but notice that this could be written very simply as a Flux store.

* @returns {EventDecryptedAction} an action of type `MatrixActions.Event.decrypted`.
*/
function createRoomStateEventsAction(matrixClient, event, state) {
return { action: 'MatrixActions.RoomState.events', event, state };
Copy link
Contributor

Choose a reason for hiding this comment

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

This is unused.

* @param {MatrixEvent[]} currentRoomWidgets Current widgets for the room
* @returns {MatrixEvent[]} List of widgets in the room, minus any pending removal
*/
getEchoedRoomWidgets(room, currentRoomWidgets) {
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 only need the roomId?

/**
* Gets the widgets for a room, substracting those that are pending deletion.
* Widgets that are pending addition are not included, since widgets are
* represted as MatrixEvents, so to do this we'd have to create fake MatrixEvents,
Copy link
Contributor

Choose a reason for hiding this comment

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

In theory, you could have a class that represents widget data within an event which doesn't care about MatrixEvents. Then you'd just need instantiate one of those as per usual (not a fake one).

Copy link
Member Author

Choose a reason for hiding this comment

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

yep, true, it just means changing everything else to add that layer of abstraction


for (const w of currentRoomWidgets) {
const widgetId = w.getStateKey();
if (roomEchoState && roomEchoState[widgetId] && Object.keys(roomEchoState[widgetId]).length === 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Including roomEchoState is redundant here because Object.assign({}, ...) should at least return an object.

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 not so clear why Object.keys are checked for length. Why would the echo state contain an object {}?

Copy link
Contributor

Choose a reason for hiding this comment

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

And sometimes it's this, but other times it's === {}. Good to be consistent.

Copy link
Member Author

@dbkr dbkr Jul 5, 2018

Choose a reason for hiding this comment

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

ah yep.

Why would the echo state contain an object {}

In the case where it's been deleted locally - I should doc this.


this._roomWidgetEcho = {
// roomId: {
// widgetId: [object]
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's worth commenting what object is stored here.


for (const w of currentRoomWidgets) {
const widgetId = w.getStateKey();
delete roomEchoState[widgetId];
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 not totally clear to me why the current room widgets are removed from the echo state, only for us to calculate a result and return that to the caller. Why not have the caller decide that currentRoomWidgets by nature are not pending?

Copy link
Member Author

Choose a reason for hiding this comment

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

Have added some comments - I think the reason for the above is just again to take logic out of the view.

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 this comes back to where the state gets calculated and I think I'm too tunnel-visioned on flux to be at ease with another flow...

}
}

roomHasPendingWidgets(room, currentRoomWidgets) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps a single function that has an optional type? called roomHasPendingWidgets?

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah, seemed nicer to add a wrapper function though.

@@ -19,6 +19,10 @@ import MatrixClientPeg from '../MatrixClientPeg';
import SdkConfig from "../SdkConfig";
import dis from '../dispatcher';
import * as url from "url";
import WidgetEchoStore from '../stores/WidgetEchoStore';

// How long we wait for the state event echo to come back from the server
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's worth commenting what happens when the timeout elapses.

if (WidgetEchoStore.roomHasPendingWidgetsOfType(room, currentRoomWidgets, 'jitsi')) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");

Modal.createTrackedDialog('Already have pending Jitsi Widget', '', ErrorDialog, {
Copy link
Contributor

Choose a reason for hiding this comment

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

Using an internal tracking ID that is not clearly related to what is shown in the modal might be confusing.

if (this._roomWidgetEcho[room.roomId] === undefined) this._roomWidgetEcho[room.roomId] = {};

this._roomWidgetEcho[room.roomId][widgetId] = state;
this.emit('updateRoomWidgetEcho');
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the only type of event emitted, why not "update" or smth?

const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");

Modal.createTrackedDialog('Already have pending Jitsi Widget', '', ErrorDialog, {
Modal.createTrackedDialog('Call being placed', '', ErrorDialog, {
Copy link
Contributor

Choose a reason for hiding this comment

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

On paper this looks like we're tracking a call being placed. I would suggest "Call already in progress".

//} else if (roomEchoState && roomEchoState[widgetId]) {
// echoedWidgets.push(roomEchoState[widgetId]);
// If there's no echo, or the echo still has a widget present, show the *old* widget
// we don't include widgets that have changed for the same rason we don't include new ones
Copy link
Contributor

Choose a reason for hiding this comment

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

typo ("rason")

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, what is that reason?

// echoedWidgets.push(roomEchoState[widgetId]);
// If there's no echo, or the echo still has a widget present, show the *old* widget
// we don't include widgets that have changed for the same rason we don't include new ones
if (!roomEchoState[widgetId] || Object.keys(roomEchoState[widgetId]).length !== 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Strongly suggest negating this instead of having an empty if clause.


for (const w of currentRoomWidgets) {
const widgetId = w.getStateKey();
delete roomEchoState[widgetId];
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 this comes back to where the state gets calculated and I think I'm too tunnel-visioned on flux to be at ease with another flow...

@@ -22,6 +22,7 @@ import * as url from "url";
import WidgetEchoStore from '../stores/WidgetEchoStore';

// How long we wait for the state event echo to come back from the server
// before the promise is rejected
Copy link
Contributor

Choose a reason for hiding this comment

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

Which promise though?

@bwindels
Copy link
Contributor

bwindels commented Jul 17, 2018

What should we do with this? I'm fine with not doing the improvements Luke suggested, even though I feel I can't really judge whether or not it's the right thing to do, but we should move forward or abandon this change, as it's already conflicting. Anyone else is also welcome to pitch in of course :)

@dbkr
Copy link
Member Author

dbkr commented Jul 18, 2018

Well, the experience without doing anything is terrible since there is zero feedback when pressing the call buttons, so I don't think doing nothing is an option. IMO, we should probably go with this for now, rather than push the release back further to rewrite this.

@bwindels bwindels merged commit 10e4a4f into develop Jul 19, 2018
@bwindels bwindels deleted the dbkr/widget_echo branch July 19, 2018 11:00
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.

3 participants