-
-
Notifications
You must be signed in to change notification settings - Fork 827
Improve UX for Jitsi by adding local echo for widgets #2035
Conversation
* 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
@@ -1,6 +1,6 @@ | |||
/* | |||
Copyright 2017 Vector Creations Ltd | |||
Copyright 2017 New Vector Ltd | |||
Copyright 2017, 2018 New Vector Ltd |
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 it happens this does need a (c) 2018
Also up the timeout because matrix.org is that slow
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.
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.
src/actions/MatrixActionCreators.js
Outdated
* @returns {EventDecryptedAction} an action of type `MatrixActions.Event.decrypted`. | ||
*/ | ||
function createRoomStateEventsAction(matrixClient, event, state) { | ||
return { action: 'MatrixActions.RoomState.events', event, state }; |
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 is unused.
src/stores/WidgetEchoStore.js
Outdated
* @param {MatrixEvent[]} currentRoomWidgets Current widgets for the room | ||
* @returns {MatrixEvent[]} List of widgets in the room, minus any pending removal | ||
*/ | ||
getEchoedRoomWidgets(room, currentRoomWidgets) { |
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 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, |
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.
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).
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.
yep, true, it just means changing everything else to add that layer of abstraction
src/stores/WidgetEchoStore.js
Outdated
|
||
for (const w of currentRoomWidgets) { | ||
const widgetId = w.getStateKey(); | ||
if (roomEchoState && roomEchoState[widgetId] && Object.keys(roomEchoState[widgetId]).length === 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.
Including roomEchoState
is redundant here because Object.assign({}, ...)
should at least return an object.
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 not so clear why Object.keys
are checked for length. Why would the echo state contain an object {}
?
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.
And sometimes it's this, but other times it's === {}
. Good to be consistent.
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.
ah yep.
Why would the echo state contain an object {}
In the case where it's been deleted locally - I should doc this.
src/stores/WidgetEchoStore.js
Outdated
|
||
this._roomWidgetEcho = { | ||
// roomId: { | ||
// widgetId: [object] |
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's worth commenting what object is stored here.
src/stores/WidgetEchoStore.js
Outdated
|
||
for (const w of currentRoomWidgets) { | ||
const widgetId = w.getStateKey(); | ||
delete roomEchoState[widgetId]; |
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 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?
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.
Have added some comments - I think the reason for the above is just again to take logic out of the view.
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 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...
src/stores/WidgetEchoStore.js
Outdated
} | ||
} | ||
|
||
roomHasPendingWidgets(room, currentRoomWidgets) { |
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.
Perhaps a single function that has an optional type
? called roomHasPendingWidgets
?
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, 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 |
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's worth commenting what happens when the timeout elapses.
src/CallHandler.js
Outdated
if (WidgetEchoStore.roomHasPendingWidgetsOfType(room, currentRoomWidgets, 'jitsi')) { | ||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); | ||
|
||
Modal.createTrackedDialog('Already have pending Jitsi Widget', '', ErrorDialog, { |
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.
Using an internal tracking ID that is not clearly related to what is shown in the modal might be confusing.
src/stores/WidgetEchoStore.js
Outdated
if (this._roomWidgetEcho[room.roomId] === undefined) this._roomWidgetEcho[room.roomId] = {}; | ||
|
||
this._roomWidgetEcho[room.roomId][widgetId] = state; | ||
this.emit('updateRoomWidgetEcho'); |
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 is the only type of event emitted, why not "update" or smth?
As it wasn't used in the end
src/CallHandler.js
Outdated
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); | ||
|
||
Modal.createTrackedDialog('Already have pending Jitsi Widget', '', ErrorDialog, { | ||
Modal.createTrackedDialog('Call being placed', '', ErrorDialog, { |
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.
On paper this looks like we're tracking a call being placed. I would suggest "Call already in progress".
src/stores/WidgetEchoStore.js
Outdated
//} 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 |
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.
typo ("rason")
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.
Also, what is that reason?
src/stores/WidgetEchoStore.js
Outdated
// 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) { |
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.
Strongly suggest negating this instead of having an empty if
clause.
src/stores/WidgetEchoStore.js
Outdated
|
||
for (const w of currentRoomWidgets) { | ||
const widgetId = w.getStateKey(); | ||
delete roomEchoState[widgetId]; |
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 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...
src/utils/WidgetUtils.js
Outdated
@@ -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 |
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.
Which promise though?
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 :) |
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. |
one pending
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.