mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-22 19:14:52 +01:00
Add tile for verification
This commit is contained in:
parent
686c6092f8
commit
1c1a713ea2
@ -35,7 +35,7 @@ export type SegmentType = {
|
|||||||
"members": true;
|
"members": true;
|
||||||
"member": string;
|
"member": string;
|
||||||
"device-verification": string | boolean;
|
"device-verification": string | boolean;
|
||||||
"verification": boolean;
|
"verification": string | boolean;
|
||||||
"join-room": true;
|
"join-room": true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -70,9 +70,12 @@ export class RightPanelViewModel extends ViewModel {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
this._hookUpdaterToSegment("verification", DeviceVerificationViewModel, () => {
|
this._hookUpdaterToSegment("verification", DeviceVerificationViewModel, () => {
|
||||||
|
const id = this.navigation.path.get("verification").value;
|
||||||
|
const request = this._session?.crossSigning.get()?.receivedSASVerifications.get(id);
|
||||||
return {
|
return {
|
||||||
session: this._session,
|
session: this._session,
|
||||||
room: this._room,
|
room: this._room,
|
||||||
|
request,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ export enum TileShape {
|
|||||||
Video = "video",
|
Video = "video",
|
||||||
DateHeader = "date-header",
|
DateHeader = "date-header",
|
||||||
Call = "call",
|
Call = "call",
|
||||||
|
Verification = "verification",
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should we imply inheriting from view model here?
|
// 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 {EncryptionEnabledTile} from "./EncryptionEnabledTile.js";
|
||||||
import {MissingAttachmentTile} from "./MissingAttachmentTile.js";
|
import {MissingAttachmentTile} from "./MissingAttachmentTile.js";
|
||||||
import {CallTile} from "./CallTile.js";
|
import {CallTile} from "./CallTile.js";
|
||||||
|
import {VerificationTile} from "./VerificationTile.js";
|
||||||
|
|
||||||
import type {ITile, TileShape} from "./ITile";
|
import type {ITile, TileShape} from "./ITile";
|
||||||
import type {Room} from "../../../../../matrix/room/Room";
|
import type {Room} from "../../../../../matrix/room/Room";
|
||||||
@ -73,6 +74,15 @@ export function tileClassForEntry(entry: TimelineEntry, options: Options): TileC
|
|||||||
return FileTile;
|
return FileTile;
|
||||||
case "m.location":
|
case "m.location":
|
||||||
return LocationTile;
|
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:
|
default:
|
||||||
// unknown msgtype not rendered
|
// unknown msgtype not rendered
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -132,6 +132,10 @@ export class Session {
|
|||||||
return this._callHandler;
|
return this._callHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get features() {
|
||||||
|
return this._features;
|
||||||
|
}
|
||||||
|
|
||||||
_setupCallHandler() {
|
_setupCallHandler() {
|
||||||
this._callHandler = new CallHandler({
|
this._callHandler = new CallHandler({
|
||||||
clock: this._platform.clock,
|
clock: this._platform.clock,
|
||||||
|
@ -14,6 +14,10 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
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 {
|
export class SASRequest {
|
||||||
constructor(public readonly startingMessage: any) {}
|
constructor(public readonly startingMessage: any) {}
|
||||||
|
|
||||||
@ -26,6 +30,11 @@ export class SASRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get id(): string {
|
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.
|
* startingMessage may be the ready message or the start message.
|
||||||
*/
|
*/
|
||||||
this.id = startingMessage.content.transaction_id;
|
this.id = startingMessage.id;
|
||||||
this.receivedMessages.set(startingMessage.type, startingMessage);
|
const type = startingMessage.content?.msgtype ?? startingMessage.eventType;
|
||||||
|
this.receivedMessages.set(type, startingMessage);
|
||||||
this.otherUserDeviceId = startingMessage.content.from_device;
|
this.otherUserDeviceId = startingMessage.content.from_device;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1510,3 +1510,31 @@ button.RoomDetailsView_row::after {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: inline-block;
|
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 {MissingAttachmentView} from "./timeline/MissingAttachmentView.js";
|
||||||
import {AnnouncementView} from "./timeline/AnnouncementView.js";
|
import {AnnouncementView} from "./timeline/AnnouncementView.js";
|
||||||
import {RedactedView} from "./timeline/RedactedView.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 {GapView} from "./timeline/GapView.js";
|
||||||
import {CallTileView} from "./timeline/CallTileView";
|
import {CallTileView} from "./timeline/CallTileView";
|
||||||
import {DateHeaderView} from "./timeline/DateHeaderView";
|
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 {
|
export function viewClassForTile(vm: ITile): TileViewConstructor {
|
||||||
switch (vm.shape) {
|
switch (vm.shape) {
|
||||||
@ -53,6 +54,8 @@ export function viewClassForTile(vm: ITile): TileViewConstructor {
|
|||||||
return CallTileView;
|
return CallTileView;
|
||||||
case TileShape.DateHeader:
|
case TileShape.DateHeader:
|
||||||
return DateHeaderView;
|
return DateHeaderView;
|
||||||
|
case TileShape.Verification:
|
||||||
|
return VerificationTileView;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Tiles of shape "${vm.shape}" are not supported, check the tileClassForEntry function in the view model`);
|
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