mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-22 11:05:03 +01:00
Add tile for verification
This commit is contained in:
parent
686c6092f8
commit
1c1a713ea2
@ -35,7 +35,7 @@ export type SegmentType = {
|
||||
"members": true;
|
||||
"member": string;
|
||||
"device-verification": string | boolean;
|
||||
"verification": boolean;
|
||||
"verification": string | boolean;
|
||||
"join-room": true;
|
||||
};
|
||||
|
||||
|
@ -70,9 +70,12 @@ export class RightPanelViewModel extends ViewModel {
|
||||
}
|
||||
);
|
||||
this._hookUpdaterToSegment("verification", DeviceVerificationViewModel, () => {
|
||||
const id = this.navigation.path.get("verification").value;
|
||||
const request = this._session?.crossSigning.get()?.receivedSASVerifications.get(id);
|
||||
return {
|
||||
session: this._session,
|
||||
room: this._room,
|
||||
request,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ export enum TileShape {
|
||||
Video = "video",
|
||||
DateHeader = "date-header",
|
||||
Call = "call",
|
||||
Verification = "verification",
|
||||
}
|
||||
|
||||
// TODO: should we imply inheriting from view model here?
|
||||
|
59
src/domain/session/room/timeline/tiles/VerificationTile.ts
Normal file
59
src/domain/session/room/timeline/tiles/VerificationTile.ts
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {SASRequest} from "../../../../../matrix/verification/SAS/SASRequest";
|
||||
import {TileShape} from "./ITile";
|
||||
import {SimpleTile} from "./SimpleTile";
|
||||
import type {EventEntry} from "../../../../../matrix/room/timeline/entries/EventEntry.js";
|
||||
import type {Options} from "./SimpleTile";
|
||||
|
||||
export class VerificationTile extends SimpleTile {
|
||||
private request: SASRequest;
|
||||
|
||||
constructor(entry: EventEntry, options: Options) {
|
||||
super(entry, options);
|
||||
this.request = new SASRequest(this.lowerEntry);
|
||||
}
|
||||
|
||||
get shape(): TileShape {
|
||||
return TileShape.Verification;
|
||||
}
|
||||
|
||||
get description(): string {
|
||||
return this.i18n`${this.sender} wants to verify`;
|
||||
}
|
||||
|
||||
accept(): void {
|
||||
const crossSigning = this.getOption("session").crossSigning.get()
|
||||
crossSigning.receivedSASVerifications.set(this.eventId, this.request);
|
||||
this.openVerificationPanel(this.eventId);
|
||||
}
|
||||
|
||||
async reject(): Promise<void> {
|
||||
// create the SasVerification object and call abort() on it
|
||||
await this.logAndCatch("VerificationTile.reject", async (log) => {
|
||||
const crossSigning = this.getOption("session").crossSigning.get();
|
||||
await this.request.reject(crossSigning, this._room, log);
|
||||
});
|
||||
}
|
||||
|
||||
private openVerificationPanel(eventId: string): void {
|
||||
let path = this.navigation.path.until("room");
|
||||
path = path.with(this.navigation.segment("right-panel", true))!;
|
||||
path = path.with(this.navigation.segment("verification", eventId))!;
|
||||
this.navigation.applyPath(path);
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ import {EncryptedEventTile} from "./EncryptedEventTile.js";
|
||||
import {EncryptionEnabledTile} from "./EncryptionEnabledTile.js";
|
||||
import {MissingAttachmentTile} from "./MissingAttachmentTile.js";
|
||||
import {CallTile} from "./CallTile.js";
|
||||
import {VerificationTile} from "./VerificationTile.js";
|
||||
|
||||
import type {ITile, TileShape} from "./ITile";
|
||||
import type {Room} from "../../../../../matrix/room/Room";
|
||||
@ -73,6 +74,15 @@ export function tileClassForEntry(entry: TimelineEntry, options: Options): TileC
|
||||
return FileTile;
|
||||
case "m.location":
|
||||
return LocationTile;
|
||||
case "m.key.verification.request":
|
||||
const isCrossSigningEnabled = !options.session.features.crossSigning;
|
||||
const userId = options.session.userId;
|
||||
if (isCrossSigningEnabled||
|
||||
entry.isLoadedFromStorage ||
|
||||
entry.sender === userId) {
|
||||
return undefined;
|
||||
}
|
||||
return VerificationTile as unknown as TileConstructor;
|
||||
default:
|
||||
// unknown msgtype not rendered
|
||||
return undefined;
|
||||
|
@ -132,6 +132,10 @@ export class Session {
|
||||
return this._callHandler;
|
||||
}
|
||||
|
||||
get features() {
|
||||
return this._features;
|
||||
}
|
||||
|
||||
_setupCallHandler() {
|
||||
this._callHandler = new CallHandler({
|
||||
clock: this._platform.clock,
|
||||
|
@ -14,6 +14,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import type {CrossSigning} from "../CrossSigning";
|
||||
import type {Room} from "../../room/Room.js";
|
||||
import type {ILogItem} from "../../../logging/types";
|
||||
|
||||
export class SASRequest {
|
||||
constructor(public readonly startingMessage: any) {}
|
||||
|
||||
@ -26,6 +30,11 @@ export class SASRequest {
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this.startingMessage.content.transaction_id;
|
||||
return this.startingMessage.content.transaction_id ?? this.startingMessage.eventId;
|
||||
}
|
||||
|
||||
async reject(crossSigning: CrossSigning, room: Room, log: ILogItem): Promise<void> {
|
||||
const sas = crossSigning.startVerification(this, room, log);
|
||||
await sas?.abort();
|
||||
}
|
||||
}
|
||||
|
@ -73,8 +73,9 @@ export class RoomChannel extends Disposables implements IChannel {
|
||||
/**
|
||||
* startingMessage may be the ready message or the start message.
|
||||
*/
|
||||
this.id = startingMessage.content.transaction_id;
|
||||
this.receivedMessages.set(startingMessage.type, startingMessage);
|
||||
this.id = startingMessage.id;
|
||||
const type = startingMessage.content?.msgtype ?? startingMessage.eventType;
|
||||
this.receivedMessages.set(type, startingMessage);
|
||||
this.otherUserDeviceId = startingMessage.content.from_device;
|
||||
}
|
||||
}
|
||||
|
@ -1510,3 +1510,31 @@ button.RoomDetailsView_row::after {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.VerificationTileView,
|
||||
.VerificationTileView__actions {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.VerificationTileView__description {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
font-size: 1.4rem;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.VerificationTileView {
|
||||
background: var(--background-color-primary--darker-5);
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.VerificationTileContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
@ -22,11 +22,12 @@ import {LocationView} from "./timeline/LocationView.js";
|
||||
import {MissingAttachmentView} from "./timeline/MissingAttachmentView.js";
|
||||
import {AnnouncementView} from "./timeline/AnnouncementView.js";
|
||||
import {RedactedView} from "./timeline/RedactedView.js";
|
||||
import {ITile, TileShape} from "../../../../../domain/session/room/timeline/tiles/ITile.js";
|
||||
import {ITile, TileShape} from "../../../../../domain/session/room/timeline/tiles/ITile";
|
||||
import {GapView} from "./timeline/GapView.js";
|
||||
import {CallTileView} from "./timeline/CallTileView";
|
||||
import {DateHeaderView} from "./timeline/DateHeaderView";
|
||||
import type {TileViewConstructor, ViewClassForEntryFn} from "./TimelineView";
|
||||
import type {TileViewConstructor} from "./TimelineView";
|
||||
import {VerificationTileView} from "./timeline/VerificationTileView";
|
||||
|
||||
export function viewClassForTile(vm: ITile): TileViewConstructor {
|
||||
switch (vm.shape) {
|
||||
@ -53,6 +54,8 @@ export function viewClassForTile(vm: ITile): TileViewConstructor {
|
||||
return CallTileView;
|
||||
case TileShape.DateHeader:
|
||||
return DateHeaderView;
|
||||
case TileShape.Verification:
|
||||
return VerificationTileView;
|
||||
default:
|
||||
throw new Error(`Tiles of shape "${vm.shape}" are not supported, check the tileClassForEntry function in the view model`);
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {Builder, TemplateView} from "../../../general/TemplateView";
|
||||
import type {VerificationTile} from "../../../../../../domain/session/room/timeline/tiles/VerificationTile";
|
||||
|
||||
export class VerificationTileView extends TemplateView<VerificationTile> {
|
||||
render(t: Builder<VerificationTile>, vm: VerificationTile) {
|
||||
return t.div( { className: "VerificationTileContainer" },
|
||||
t.div({ className: "VerificationTileView" }, [
|
||||
t.div({className: "VerificationTileView__shield"}),
|
||||
t.div({ className: "VerificationTileView__description" }, vm.description),
|
||||
t.div({ className: "VerificationTileView__actions" }, [
|
||||
t.button({ className: "VerificationTileView__accept button-action primary" }, "Accept"),
|
||||
t.button({ className: "VerificationTileView__reject button-action secondary" }, "Reject"),
|
||||
]),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
/* This is called by the parent ListView, which just has 1 listener for the whole list */
|
||||
onClick(evt) {
|
||||
if (evt.target.classList.contains("VerificationTileView__accept")) {
|
||||
this.value.accept();
|
||||
} else if (evt.target.classList.contains("VerificationTileView__reject")) {
|
||||
this.value.reject();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user