Skip to content
Closed
16 changes: 15 additions & 1 deletion src/domain/session/RoomViewModelObservable.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,24 @@ export class RoomViewModelObservable extends ObservableValue {
} else if (status & RoomStatus.Archived) {
return await this._sessionViewModel._createArchivedRoomViewModel(this.id);
} else {
return this._sessionViewModel._createUnknownRoomViewModel(this.id);
return this._sessionViewModel._createUnknownRoomViewModel(this.id, this._isWorldReadablePromise());
}
}

async _isWorldReadablePromise() {
const {session} = this._sessionViewModel._client;
const isWorldReadable = await session.isWorldReadableRoom(this.id);
if (isWorldReadable) {
const vm = await this._sessionViewModel._createWorldReadableRoomViewModel(this.id);
if (vm) {
this.get()?.dispose();
this.set(vm);
return true;
}
}
return false;
}

dispose() {
if (this._statusSubscription) {
this._statusSubscription = this._statusSubscription();
Expand Down
15 changes: 12 additions & 3 deletions src/domain/session/SessionViewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ limitations under the License.
import {LeftPanelViewModel} from "./leftpanel/LeftPanelViewModel.js";
import {RoomViewModel} from "./room/RoomViewModel.js";
import {UnknownRoomViewModel} from "./room/UnknownRoomViewModel.js";
import {WorldReadableRoomViewModel} from "./room/WorldReadableRoomViewModel.js";
import {InviteViewModel} from "./room/InviteViewModel.js";
import {RoomBeingCreatedViewModel} from "./room/RoomBeingCreatedViewModel.js";
import {LightboxViewModel} from "./room/LightboxViewModel.js";
Expand Down Expand Up @@ -231,12 +232,20 @@ export class SessionViewModel extends ViewModel {
return null;
}

async _createUnknownRoomViewModel(roomIdOrAlias) {
const roomVM = new UnknownRoomViewModel(this.childOptions({
_createUnknownRoomViewModel(roomIdOrAlias, isWorldReadablePromise) {
return new UnknownRoomViewModel(this.childOptions({
roomIdOrAlias,
session: this._client.session,
isWorldReadablePromise: isWorldReadablePromise
}));
void roomVM.load();
}

async _createWorldReadableRoomViewModel(roomIdOrAlias) {
const roomVM = new WorldReadableRoomViewModel(this.childOptions({
room: await this._client.session.loadWorldReadableRoom(roomIdOrAlias),
session: this._client.session,
}));
roomVM.load();
return roomVM;
}

Expand Down
4 changes: 2 additions & 2 deletions src/domain/session/room/RoomViewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class RoomViewModel extends ErrorReportViewModel {
this._composerVM = null;
if (room.isArchived) {
this._composerVM = this.track(new ArchivedViewModel(this.childOptions({archivedRoom: room})));
} else {
} else if (!room.isWorldReadable) {
this._recreateComposerOnPowerLevelChange();
}
this._clearUnreadTimout = null;
Expand Down Expand Up @@ -218,7 +218,7 @@ export class RoomViewModel extends ErrorReportViewModel {
}
}
}

_sendMessage(message, replyingTo) {
return this.logAndCatch("RoomViewModel.sendMessage", async log => {
let success = false;
Expand Down
59 changes: 7 additions & 52 deletions src/domain/session/room/UnknownRoomViewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,21 @@ limitations under the License.
*/

import {ViewModel} from "../../ViewModel";
import {TimelineViewModel} from "./timeline/TimelineViewModel";
import {tileClassForEntry as defaultTileClassForEntry} from "./timeline/tiles/index";
import {getAvatarHttpUrl} from "../../avatar";

export class UnknownRoomViewModel extends ViewModel {
constructor(options) {
super(options);
const {roomIdOrAlias, session} = options;
const {roomIdOrAlias, session, isWorldReadablePromise} = options;
this._session = session;
this.roomIdOrAlias = roomIdOrAlias;
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() {
return this._room;
this.checkingPreviewCapability = true;
isWorldReadablePromise.then(() => {
this.checkingPreviewCapability = false;
this.emitChange('checkingPreviewCapability');
})
}

get error() {
Expand Down Expand Up @@ -61,49 +58,7 @@ export class UnknownRoomViewModel extends ViewModel {
return this._busy;
}

get checkingPreviewCapability() {
return this._checkingPreviewCapability;
}

get kind() {
return this._worldReadable ? "worldReadableRoom" : "unknown";
}

get timelineViewModel() {
return this._timelineVM;
}

avatarUrl(size) {
return getAvatarHttpUrl(this._room.avatarUrl, size, this.platform, this._room.mediaRepository);
}

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();
this._tileOptions = this.childOptions({
roomVM: this,
timeline,
tileClassForEntry: defaultTileClassForEntry,
});
this._timelineVM = this.track(new TimelineViewModel(this.childOptions({
tileOptions: this._tileOptions,
timeline,
})));
this.emitChange("timelineViewModel");
} catch (err) {
console.error(`room.openTimeline(): ${err.message}:\n${err.stack}`);
this._timelineError = err;
this.emitChange("error");
}
return "unknown";
}
}
47 changes: 47 additions & 0 deletions src/domain/session/room/WorldReadableRoomViewModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {RoomViewModel} from "./RoomViewModel";

export class WorldReadableRoomViewModel extends RoomViewModel {
constructor(options) {
options.room.isWorldReadable = true;
Copy link
Member Author

Choose a reason for hiding this comment

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

@psrpinto Do you think this is fine or it warrants the need of creating a WorldReadableRoom like ArchivedRoom just because we need to differentiate between the room instance of a regular room and WorldReadable room in RoomViewModel?

super(options);
this._room = options.room;
this._session = options.session;
this._error = null;
this._busy = false;
}

get kind() {
return "preview";
}

get busy() {
return this._busy;
}

async join() {
this._busy = true;
this.emitChange("busy");
try {
const roomId = await this._session.joinRoom(this._room.id);
// navigate to roomId if we were at the alias
// so we're subscribed to the right room status
// and we'll switch to the room view model once
// the join is synced
this.navigation.push("room", roomId);
// keep busy on true while waiting for the join to sync
} catch (err) {
this._error = err;
this._busy = false;
this.emitChange("error");
}
}

dispose() {
super.dispose();

// if joining the room, _busy would be true and in that case don't delete records
if (!this._busy) {
void this._session.deleteWorldReadableRoomData(this._room.id);
}
}
}
26 changes: 20 additions & 6 deletions src/matrix/Session.js
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,8 @@ export class Session {
mediaRepository: this._mediaRepository,
pendingEvents: [],
user: this._user,
platform: this._platform
platform: this._platform,
roomStateHandler: this._roomStateHandler
});
}

Expand Down Expand Up @@ -1106,15 +1107,13 @@ export class Session {
const response = await this._hsApi.messages(roomId, options, {log}).response();
log.set("/messages endpoint response", response);

await this.deleteWorldReadableRoomData(roomId, log);

const txn = await this._storage.readWriteTxn([
this._storage.storeNames.timelineFragments,
this._storage.storeNames.timelineEvents,
]);

// clear old records for this room
txn.timelineFragments.removeAllForRoom(roomId);
txn.timelineEvents.removeAllForRoom(roomId);

// insert fragment and event records for this room
const fragment = {
roomId: roomId,
Expand All @@ -1140,14 +1139,29 @@ export class Session {
});
}

async deleteWorldReadableRoomData(roomId, log = null) {
return this._platform.logger.wrapOrRun(log, "deleteWorldReadableRoomData", async log => {
log.set("id", roomId);

const txn = await this._storage.readWriteTxn([
this._storage.storeNames.timelineFragments,
this._storage.storeNames.timelineEvents,
]);

// clear old records for this room
txn.timelineFragments.removeAllForRoom(roomId);
txn.timelineEvents.removeAllForRoom(roomId);
});
}

joinRoom(roomIdOrAlias, log = null) {
return this._platform.logger.wrapOrRun(log, "joinRoom", async log => {
const body = await this._hsApi.joinIdOrAlias(roomIdOrAlias, {log}).response();
return body.room_id;
});
}

isWorldReadableRoom(roomIdOrAlias, log = null) {
async isWorldReadableRoom(roomIdOrAlias, log = null) {
return this._platform.logger.wrapOrRun(log, "isWorldReadableRoom", async log => {
try {
let roomId;
Expand Down
3 changes: 3 additions & 0 deletions src/platform/web/ui/session/SessionView.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ limitations under the License.
import {LeftPanelView} from "./leftpanel/LeftPanelView.js";
import {RoomView} from "./room/RoomView.js";
import {UnknownRoomView} from "./room/UnknownRoomView.js";
import {WorldReadableRoomView} from "./room/WorldReadableRoomView.js";
import {RoomBeingCreatedView} from "./room/RoomBeingCreatedView.js";
import {InviteView} from "./room/InviteView.js";
import {LightboxView} from "./room/LightboxView.js";
Expand Down Expand Up @@ -60,6 +61,8 @@ export class SessionView extends TemplateView {
return new RoomView(vm.currentRoomViewModel, viewClassForTile);
} else if (vm.currentRoomViewModel.kind === "roomBeingCreated") {
return new RoomBeingCreatedView(vm.currentRoomViewModel);
} else if (vm.currentRoomViewModel.kind === "preview") {
return new WorldReadableRoomView(vm.currentRoomViewModel);
} else {
return new UnknownRoomView(vm.currentRoomViewModel);
}
Expand Down
86 changes: 18 additions & 68 deletions src/platform/web/ui/session/room/UnknownRoomView.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,78 +14,28 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

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

export class UnknownRoomView extends TemplateView {

constructor(vm) {
super(vm);
}

render(t, vm) {
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.button({
className: "button-action primary",
onClick: () => vm.join(),
disabled: vm => vm.busy,
}, vm.i18n`Join room`),
t.br(),
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),
]),
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, 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`)
])
])
]);
t.button({
className: "button-action primary",
onClick: () => vm.join(),
disabled: vm => vm.busy,
}, vm.i18n`Join room`),
t.br(),
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))
]));
}
}
Loading