Render verification UI in right-panel

This commit is contained in:
RMidhunSuresh 2023-04-17 22:47:37 +05:30
parent f3efef365d
commit 2b9f508a8f
9 changed files with 136 additions and 23 deletions

View File

@ -25,10 +25,12 @@ import {VerificationCompleteViewModel} from "./stages/VerificationCompleteViewMo
import type {Session} from "../../../matrix/Session.js"; import type {Session} from "../../../matrix/Session.js";
import type {SASVerification} from "../../../matrix/verification/SAS/SASVerification"; import type {SASVerification} from "../../../matrix/verification/SAS/SASVerification";
import type {SASRequest} from "../../../matrix/verification/SAS/SASRequest"; import type {SASRequest} from "../../../matrix/verification/SAS/SASRequest";
import type {Room} from "../../../matrix/room/Room.js";
type Options = BaseOptions & { type Options = BaseOptions & {
session: Session; session: Session;
request: SASRequest; request?: SASRequest;
room?: Room;
}; };
export class DeviceVerificationViewModel extends ErrorReportViewModel<SegmentType, Options> { export class DeviceVerificationViewModel extends ErrorReportViewModel<SegmentType, Options> {
@ -37,27 +39,51 @@ export class DeviceVerificationViewModel extends ErrorReportViewModel<SegmentTyp
constructor(options: Readonly<Options>) { constructor(options: Readonly<Options>) {
super(options); super(options);
const sasRequest = options.request; this.init(options);
if (options.request) {
this.start(sasRequest);
}
else {
// We are about to send the request
this.start(this.getOption("session").userId);
}
} }
private async start(requestOrUserId: SASRequest | string) { private async init(options: Options): Promise<void> {
const room = options.room;
let requestOrUserId: SASRequest | string;
if (options.request) {
requestOrUserId = options.request;
}
else if (room) {
requestOrUserId = await this.findMemberFromRoom(room);
}
else {
requestOrUserId = this.getOption("session").userId;
}
await this.start(requestOrUserId, room);
}
private async start(requestOrUserId: SASRequest | string, room?: Room) {
await this.logAndCatch("DeviceVerificationViewModel.start", (log) => { await this.logAndCatch("DeviceVerificationViewModel.start", (log) => {
const crossSigning = this.getOption("session").crossSigning.get(); const crossSigning = this.getOption("session").crossSigning.get();
this.sas = crossSigning.startVerification(requestOrUserId, log); this.sas = crossSigning.startVerification(requestOrUserId, room, log);
this.addEventListeners(); this.addEventListeners();
if (typeof requestOrUserId === "string") { if (typeof requestOrUserId === "string") {
this.updateCurrentStageViewModel(new WaitingForOtherUserViewModel(this.childOptions({ sas: this.sas }))); this.updateCurrentStageViewModel(new WaitingForOtherUserViewModel(this.childOptions({ sas: this.sas })));
} }
return crossSigning.signDevice(this.sas, log); return this.sas.verify();
// return crossSigning.signDevice(this.sas, log);
}); });
} }
private async findMemberFromRoom(room: Room) {
const memberlist = await room.loadMemberList();
const members = memberlist.members;
if (members.size !== 2) {
throw new Error("There are more than two members in this room!");
}
const ourUserId = this.getOption("session").userId;
for (const userId of members.keys()) {
if (userId !== ourUserId) {
memberlist.release();
return userId;
}
}
}
private addEventListeners() { private addEventListeners() {
this.track(this.sas.disposableOn("SelectVerificationStage", (stage) => { this.track(this.sas.disposableOn("SelectVerificationStage", (stage) => {
@ -79,7 +105,7 @@ export class DeviceVerificationViewModel extends ErrorReportViewModel<SegmentTyp
})); }));
this.track(this.sas.disposableOn("VerificationCompleted", (deviceId) => { this.track(this.sas.disposableOn("VerificationCompleted", (deviceId) => {
this.updateCurrentStageViewModel( this.updateCurrentStageViewModel(
new VerificationCompleteViewModel(this.childOptions({ deviceId: deviceId! })) new VerificationCompleteViewModel(this.childOptions({ deviceId: deviceId!, sas: this.sas }))
); );
})); }));
} }
@ -100,4 +126,8 @@ export class DeviceVerificationViewModel extends ErrorReportViewModel<SegmentTyp
get currentStageViewModel() { get currentStageViewModel() {
return this._currentStageViewModel; return this._currentStageViewModel;
} }
get type(): string {
return "verification";
}
} }

View File

@ -48,7 +48,15 @@ export class SelectMethodViewModel extends ErrorReportViewModel<SegmentType, Opt
return this.options.stage.otherDeviceName; return this.options.stage.otherDeviceName;
} }
get otherUserId() {
return this.getOption("sas").otherUserId;
}
get kind(): string { get kind(): string {
return "select-method"; return "select-method";
} }
get isCrossSigningAnotherUser(): boolean {
return this.getOption("sas").isCrossSigningAnotherUser;
}
} }

View File

@ -18,10 +18,12 @@ import {SegmentType} from "../../../navigation/index";
import {ErrorReportViewModel} from "../../../ErrorReportViewModel"; import {ErrorReportViewModel} from "../../../ErrorReportViewModel";
import type {Options as BaseOptions} from "../../../ViewModel"; import type {Options as BaseOptions} from "../../../ViewModel";
import type {Session} from "../../../../matrix/Session.js"; import type {Session} from "../../../../matrix/Session.js";
import type {SASVerification} from "../../../../matrix/verification/SAS/SASVerification";
type Options = BaseOptions & { type Options = BaseOptions & {
deviceId: string; deviceId: string;
session: Session; session: Session;
sas: SASVerification;
}; };
export class VerificationCompleteViewModel extends ErrorReportViewModel<SegmentType, Options> { export class VerificationCompleteViewModel extends ErrorReportViewModel<SegmentType, Options> {
@ -29,6 +31,10 @@ export class VerificationCompleteViewModel extends ErrorReportViewModel<SegmentT
return this.options.deviceId; return this.options.deviceId;
} }
get otherUsername(): string {
return this.getOption("sas").otherUserId;
}
gotoSettings() { gotoSettings() {
this.navigation.push("settings", true); this.navigation.push("settings", true);
} }
@ -36,4 +42,13 @@ export class VerificationCompleteViewModel extends ErrorReportViewModel<SegmentT
get kind(): string { get kind(): string {
return "verification-completed"; return "verification-completed";
} }
get verificationSuccessfulMessage(): string {
if (this.getOption("sas").isCrossSigningAnotherUser) {
return this.i18n`You successfully verified user ${this.otherUsername}`;
}
else {
return this.i18n`You successfully verified device ${this.otherDeviceId}`;
}
}
} }

View File

@ -53,6 +53,8 @@ export class SASVerification extends EventEmitter<SASProgressEvents> implements
public finished: boolean = false; public finished: boolean = false;
public readonly channel: IChannel; public readonly channel: IChannel;
private timeout: Timeout; private timeout: Timeout;
public otherUserId: string;
private ourUserId: string;
constructor(options: Options) { constructor(options: Options) {
super(); super();
@ -60,6 +62,8 @@ export class SASVerification extends EventEmitter<SASProgressEvents> implements
const olmSas = new olm.SAS(); const olmSas = new olm.SAS();
this.olmSas = olmSas; this.olmSas = olmSas;
this.channel = channel; this.channel = channel;
this.otherUserId = options.otherUserId;
this.ourUserId = options.ourUserId;
this.setupCancelAfterTimeout(clock); this.setupCancelAfterTimeout(clock);
const stageOptions = {...options, olmSas, eventEmitter: this}; const stageOptions = {...options, olmSas, eventEmitter: this};
if (channel.getReceivedMessage(VerificationEventType.Start)) { if (channel.getReceivedMessage(VerificationEventType.Start)) {
@ -118,6 +122,10 @@ export class SASVerification extends EventEmitter<SASProgressEvents> implements
get otherDeviceId(): string { get otherDeviceId(): string {
return this.channel.otherUserDeviceId; return this.channel.otherUserDeviceId;
} }
get isCrossSigningAnotherUser(): boolean {
return !(this.otherUserId === this.ourUserId);
}
} }
import {HomeServer} from "../../../mocks/HomeServer.js"; import {HomeServer} from "../../../mocks/HomeServer.js";

View File

@ -225,9 +225,14 @@ export class RoomChannel extends Disposables implements IChannel {
} }
setStartMessage(entry) { setStartMessage(entry) {
const clone = entry.clone(); if (!entry.content["m.relates_to"]) {
clone.content["m.relates_to"] = clone.event.content["m.relates_to"]; const clone = entry.clone();
this.startMessage = clone; clone.content["m.relates_to"] = clone.event.content["m.relates_to"];
this.startMessage = clone;
}
else {
this.startMessage = entry;
}
this._initiatedByUs = entry.content.from_device === this.ourDeviceId; this._initiatedByUs = entry.content.from_device === this.ourDeviceId;
} }

View File

@ -1399,6 +1399,10 @@ button.RoomDetailsView_row::after {
justify-content: center; justify-content: center;
} }
.VerifyEmojisView {
width: 100%;
}
.VerificationCompleteView, .VerificationCompleteView,
.DeviceVerificationView, .DeviceVerificationView,
.SelectMethodView { .SelectMethodView {
@ -1406,6 +1410,13 @@ button.RoomDetailsView_row::after {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
width: 100%;
}
.SelectMethodView > div,
.SelectMethodView__heading,
.SelectMethodView__title {
width: inherit;
} }
.VerificationCompleteView__heading, .VerificationCompleteView__heading,
@ -1427,6 +1438,15 @@ button.RoomDetailsView_row::after {
padding: 16px; padding: 16px;
} }
.VerificationCancelledView__title,
.VerificationCompleteView__title,
.VerifyEmojisView__title,
.SelectMethodView__title,
.WaitingForOtherUserView__title {
font-size: 1.5em;
text-align: center;
}
.VerificationCompleteView__title, .VerificationCompleteView__title,
.VerifyEmojisView__title, .VerifyEmojisView__title,
.SelectMethodView__title, .SelectMethodView__title,
@ -1454,6 +1474,7 @@ button.RoomDetailsView_row::after {
display: flex; display: flex;
justify-content: center; justify-content: center;
gap: 16px; gap: 16px;
flex-wrap: wrap;
} }
.EmojiContainer__emoji { .EmojiContainer__emoji {
@ -1482,3 +1503,10 @@ button.RoomDetailsView_row::after {
width: 128px; width: 128px;
height: 128px; height: 128px;
} }
.SelectMethodView__name {
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
display: inline-block;
}

View File

@ -19,6 +19,7 @@ import {RoomDetailsView} from "./RoomDetailsView.js";
import {MemberListView} from "./MemberListView.js"; import {MemberListView} from "./MemberListView.js";
import {LoadingView} from "../../general/LoadingView.js"; import {LoadingView} from "../../general/LoadingView.js";
import {MemberDetailsView} from "./MemberDetailsView.js"; import {MemberDetailsView} from "./MemberDetailsView.js";
import {DeviceVerificationView} from "../verification/DeviceVerificationView";
export class RightPanelView extends TemplateView { export class RightPanelView extends TemplateView {
render(t) { render(t) {
@ -39,6 +40,8 @@ export class RightPanelView extends TemplateView {
return new MemberListView(vm); return new MemberListView(vm);
case "member-details": case "member-details":
return new MemberDetailsView(vm); return new MemberDetailsView(vm);
case "verification":
return new DeviceVerificationView(vm);
default: default:
return new LoadingView(); return new LoadingView();
} }

View File

@ -27,11 +27,9 @@ export class SelectMethodView extends TemplateView<SelectMethodViewModel> {
} }
else return t.div([ else return t.div([
t.div({ className: "SelectMethodView__heading" }, [ t.div({ className: "SelectMethodView__heading" }, [
t.h2( { className: "SelectMethodView__title" }, vm.i18n`Verify device '${vm.deviceName}' by comparing emojis?`), t.h2( { className: "SelectMethodView__title" }, this.getHeading(t, vm)),
]), ]),
t.p({ className: "SelectMethodView__description" }, t.p({ className: "SelectMethodView__description" }, this.getSubheading(vm)),
vm.i18n`You are about to verify your other device by comparing emojis.`
),
t.div({ className: "SelectMethodView__actions" }, [ t.div({ className: "SelectMethodView__actions" }, [
t.button( t.button(
{ {
@ -59,4 +57,24 @@ export class SelectMethodView extends TemplateView<SelectMethodViewModel> {
}), }),
]); ]);
} }
getHeading(t: Builder<SelectMethodViewModel>, vm: SelectMethodViewModel) {
if (vm.isCrossSigningAnotherUser) {
return [vm.i18n`Verify user `, t.span({
className: "SelectMethodView__name"
}, vm.otherUserId), vm.i18n` by comparing emojis?`];
} else {
return [vm.i18n`Verify device`, t.span({
className: "SelectMethodView__name"
}, vm.deviceName), vm.i18n` by comparing emojis?`];
}
}
getSubheading(vm: SelectMethodViewModel): string {
if (vm.isCrossSigningAnotherUser) {
return vm.i18n`You are about to verify user (${vm.otherUserId}) by comparing emojis.`;
} else {
return vm.i18n`You are about to verify your other device (${vm.deviceName}) by comparing emojis.`;
}
}
} }

View File

@ -28,9 +28,7 @@ export class VerificationCompleteView extends TemplateView<VerificationCompleteV
), ),
]), ]),
t.p( t.p(
{ className: "VerificationCompleteView__description" }, { className: "VerificationCompleteView__description" }, vm.verificationSuccessfulMessage),
vm.i18n`You successfully verified device ${vm.otherDeviceId}`
),
t.div({ className: "VerificationCompleteView__actions" }, [ t.div({ className: "VerificationCompleteView__actions" }, [
t.button({ t.button({
className: { className: {