Skip to content

Commit

Permalink
improve UX when joining big rooms to render unknown room view early a…
Browse files Browse the repository at this point in the history
…nd loading things async and updating view as we do

This shows a spinner on UnknownRoomView stating "checking preview capability" and goes away when done
and if room is world_readable, then renders the timeline with the last 100 messages
  • Loading branch information
ashfame committed Feb 16, 2023
1 parent 2fb9cb6 commit 654cdf0
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 52 deletions.
10 changes: 3 additions & 7 deletions src/domain/session/SessionViewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,16 +232,12 @@ export class SessionViewModel extends ViewModel {
}

async _createUnknownRoomViewModel(roomIdOrAlias) {
const worldReadable = await this._client.session.isWorldReadableRoom(roomIdOrAlias);
let vm = new UnknownRoomViewModel(this.childOptions({
const roomVM = new UnknownRoomViewModel(this.childOptions({
roomIdOrAlias,
session: this._client.session,
worldReadable: worldReadable
}));
if (worldReadable) {
await vm.load();
}
return vm;
void roomVM.load();
return roomVM;
}

async _createArchivedRoomViewModel(roomId) {
Expand Down
15 changes: 13 additions & 2 deletions src/domain/session/room/UnknownRoomViewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ import {getAvatarHttpUrl} from "../../avatar";
export class UnknownRoomViewModel extends ViewModel {
constructor(options) {
super(options);
const {roomIdOrAlias, session, worldReadable} = options;
const {roomIdOrAlias, session} = options;
this._session = session;
this.roomIdOrAlias = roomIdOrAlias;
this._worldReadable = worldReadable;
this._error = null;
this._busy = false;
this._worldReadable = false; // won't know until load() finishes with isWorldReadableRoom() call
this._checkingPreviewCapability = false; // won't know until load() finishes with isWorldReadableRoom() call
}

get room() {
Expand Down Expand Up @@ -60,6 +61,10 @@ export class UnknownRoomViewModel extends ViewModel {
return this._busy;
}

get checkingPreviewCapability() {
return this._checkingPreviewCapability;
}

get kind() {
return this._worldReadable ? "worldReadableRoom" : "unknown";
}
Expand All @@ -73,9 +78,15 @@ export class UnknownRoomViewModel extends ViewModel {
}

async load() {
this._checkingPreviewCapability = true;
this._worldReadable = await this._session.isWorldReadableRoom(this.roomIdOrAlias);
this._checkingPreviewCapability = false;

if (!this._worldReadable) {
this.emitChange("checkingPreviewCapability");
return;
}

try {
this._room = await this._session.loadWorldReadableRoom(this.roomIdOrAlias);
const timeline = await this._room.openTimeline();
Expand Down
12 changes: 12 additions & 0 deletions src/platform/web/ui/css/themes/element/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,18 @@ button.link {
width: 100%;
}

.UnknownRoomView .checkingPreviewCapability {
display: flex;
flex-direction: row; /* make main axis vertical */
justify-content: center; /* center items vertically, in this case */
align-items: center; /* center items horizontally, in this case */
margin-top: 5px;
}

.UnknownRoomView .checkingPreviewCapability p {
margin-left: 5px;
}

.WorldReadableRoomView .Timeline_message:hover > .Timeline_messageOptions{
display: none;
}
Expand Down
2 changes: 1 addition & 1 deletion src/platform/web/ui/session/SessionView.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class SessionView extends TemplateView {
} else if (vm.currentRoomViewModel.kind === "roomBeingCreated") {
return new RoomBeingCreatedView(vm.currentRoomViewModel);
} else {
return new UnknownRoomView(vm.currentRoomViewModel, viewClassForTile);
return new UnknownRoomView(vm.currentRoomViewModel);
}
} else {
return new StaticView(t => t.div({className: "room-placeholder"}, t.h2(vm.i18n`Choose a room on the left side.`)));
Expand Down
102 changes: 60 additions & 42 deletions src/platform/web/ui/session/room/UnknownRoomView.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,60 +14,78 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import {TemplateView} from "../../general/TemplateView";
import {InlineTemplateView, TemplateView} from "../../general/TemplateView";
import {AvatarView} from "../../AvatarView";
import {TimelineView} from "./TimelineView";
import {TimelineLoadingView} from "./TimelineLoadingView";
import {spinner} from "../../common.js";
import {viewClassForTile} from "./common";

export class UnknownRoomView extends TemplateView {

constructor(vm, viewClassForTile) {
constructor(vm) {
super(vm);
this._viewClassForTile = viewClassForTile;
}

render(t, vm) {
if (vm.kind === 'worldReadableRoom') {
return t.main({className: "RoomView WorldReadableRoomView middle"}, [
t.div({className: "RoomHeader middle-header"}, [
t.view(new AvatarView(vm, 32)),
t.div({className: "room-description"}, [
t.h2(vm => vm.room.name),
return t.mapView(vm => vm.kind, kind => {
const unknownRoomView = new InlineTemplateView(vm, (t, m) => {
return t.main({className: "UnknownRoomView middle"}, t.div([
t.h2([
vm.i18n`You are currently not in ${vm.roomIdOrAlias}.`,
t.br(),
vm.i18n`Want to join it?`
]),
]),
t.div({className: "RoomView_body"}, [
t.div({className: "RoomView_error"}, [
t.if(vm => vm.error, t => t.div(
[
t.p({}, vm => vm.error),
t.button({ className: "RoomView_error_closerButton", onClick: evt => vm.dismissError(evt) })
])
)]),
t.mapView(vm => vm.timelineViewModel, timelineViewModel => {
return timelineViewModel ?
new TimelineView(timelineViewModel, this._viewClassForTile) :
new TimelineLoadingView(vm); // vm is just needed for i18n
}),
t.div({className: "WorldReadableRoomComposerView"}, [
t.h3(vm => vm.i18n`Join the room to participate`),
t.button({className: "joinRoomButton", onClick: () => vm.join()}, vm.i18n`Join Room`)
])
])
]);
} else {
return t.main({className: "UnknownRoomView middle"}, t.div([
t.h2([
vm.i18n`You are currently not in ${vm.roomIdOrAlias}.`,
t.button({
className: "button-action primary",
onClick: () => vm.join(),
disabled: vm => vm.busy,
}, vm.i18n`Join room`),
t.br(),
vm.i18n`Want to join it?`
t.if(vm => vm.checkingPreviewCapability, t => t.div({className: "checkingPreviewCapability"}, [
spinner(t),
t.p(vm.i18n`Checking preview capability...`)
])),
t.if(vm => vm.error, t => t.p({className: "error"}, vm.error))
]));
});
return kind === 'worldReadableRoom' ? new WorldReadableRoomView(vm) : unknownRoomView;
});
}
}

class WorldReadableRoomView extends InlineTemplateView {

constructor(value, render) {
super(value, render);
}

render(t, vm) {
return t.main({className: "RoomView WorldReadableRoomView middle"}, [
t.div({className: "RoomHeader middle-header"}, [
t.view(new AvatarView(vm, 32)),
t.div({className: "room-description"}, [
t.h2(vm => vm.room.name),
]),
t.button({
className: "button-action primary",
onClick: () => vm.join(),
disabled: vm => vm.busy,
}, vm.i18n`Join room`),
t.if(vm => vm.error, t => t.p({className: "error"}, vm.error))
]));
}
]),
t.div({className: "RoomView_body"}, [
t.div({className: "RoomView_error"}, [
t.if(vm => vm.error, t => t.div(
[
t.p({}, vm => vm.error),
t.button({className: "RoomView_error_closerButton", onClick: evt => vm.dismissError(evt)})
])
)]),
t.mapView(vm => vm.timelineViewModel, timelineViewModel => {
return timelineViewModel ?
new TimelineView(timelineViewModel, viewClassForTile) :
new TimelineLoadingView(vm); // vm is just needed for i18n
}),
t.div({className: "WorldReadableRoomComposerView"}, [
t.h3(vm => vm.i18n`Join the room to participate`),
t.button({className: "joinRoomButton", onClick: () => vm.join()}, vm.i18n`Join Room`)
])
])
]);
}
}

0 comments on commit 654cdf0

Please sign in to comment.