mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-23 03:25:12 +01:00
draft code in matrix layer to create room
This commit is contained in:
parent
65dcf8bc36
commit
348de312f9
@ -18,6 +18,7 @@ limitations under the License.
|
|||||||
import {Room} from "./room/Room.js";
|
import {Room} from "./room/Room.js";
|
||||||
import {ArchivedRoom} from "./room/ArchivedRoom.js";
|
import {ArchivedRoom} from "./room/ArchivedRoom.js";
|
||||||
import {RoomStatus} from "./room/RoomStatus.js";
|
import {RoomStatus} from "./room/RoomStatus.js";
|
||||||
|
import {RoomBeingCreated} from "./room/create";
|
||||||
import {Invite} from "./room/Invite.js";
|
import {Invite} from "./room/Invite.js";
|
||||||
import {Pusher} from "./push/Pusher";
|
import {Pusher} from "./push/Pusher";
|
||||||
import { ObservableMap } from "../observable/index.js";
|
import { ObservableMap } from "../observable/index.js";
|
||||||
@ -63,6 +64,7 @@ export class Session {
|
|||||||
this._activeArchivedRooms = new Map();
|
this._activeArchivedRooms = new Map();
|
||||||
this._invites = new ObservableMap();
|
this._invites = new ObservableMap();
|
||||||
this._inviteUpdateCallback = (invite, params) => this._invites.update(invite.id, params);
|
this._inviteUpdateCallback = (invite, params) => this._invites.update(invite.id, params);
|
||||||
|
this._roomsBeingCreated = new ObservableMap();
|
||||||
this._user = new User(sessionInfo.userId);
|
this._user = new User(sessionInfo.userId);
|
||||||
this._deviceMessageHandler = new DeviceMessageHandler({storage});
|
this._deviceMessageHandler = new DeviceMessageHandler({storage});
|
||||||
this._olm = olm;
|
this._olm = olm;
|
||||||
@ -421,7 +423,7 @@ export class Session {
|
|||||||
// load rooms
|
// load rooms
|
||||||
const rooms = await txn.roomSummary.getAll();
|
const rooms = await txn.roomSummary.getAll();
|
||||||
const roomLoadPromise = Promise.all(rooms.map(async summary => {
|
const roomLoadPromise = Promise.all(rooms.map(async summary => {
|
||||||
const room = this.createRoom(summary.roomId, pendingEventsByRoomId.get(summary.roomId));
|
const room = this.createJoinedRoom(summary.roomId, pendingEventsByRoomId.get(summary.roomId));
|
||||||
await log.wrap("room", log => room.load(summary, txn, log));
|
await log.wrap("room", log => room.load(summary, txn, log));
|
||||||
this._rooms.add(room.id, room);
|
this._rooms.add(room.id, room);
|
||||||
}));
|
}));
|
||||||
@ -530,7 +532,7 @@ export class Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
createRoom(roomId, pendingEvents) {
|
createJoinedRoom(roomId, pendingEvents) {
|
||||||
return new Room({
|
return new Room({
|
||||||
roomId,
|
roomId,
|
||||||
getSyncToken: this._getSyncToken,
|
getSyncToken: this._getSyncToken,
|
||||||
@ -580,6 +582,20 @@ export class Session {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get roomsBeingCreated() {
|
||||||
|
return this._roomsBeingCreated;
|
||||||
|
}
|
||||||
|
|
||||||
|
createRoom(type, isEncrypted, explicitName, topic, invites) {
|
||||||
|
const localId = `room-being-created-${this.platform.random()}`;
|
||||||
|
const roomBeingCreated = new RoomBeingCreated(localId, type, isEncrypted, explicitName, topic, invites);
|
||||||
|
this._roomsBeingCreated.set(localId, roomBeingCreated);
|
||||||
|
this._platform.logger.runDetached("create room", async log => {
|
||||||
|
roomBeingCreated.start(this._hsApi, log);
|
||||||
|
});
|
||||||
|
return localId;
|
||||||
|
}
|
||||||
|
|
||||||
async obtainSyncLock(syncResponse) {
|
async obtainSyncLock(syncResponse) {
|
||||||
const toDeviceEvents = syncResponse.to_device?.events;
|
const toDeviceEvents = syncResponse.to_device?.events;
|
||||||
if (Array.isArray(toDeviceEvents) && toDeviceEvents.length) {
|
if (Array.isArray(toDeviceEvents) && toDeviceEvents.length) {
|
||||||
@ -667,6 +683,13 @@ export class Session {
|
|||||||
for (const rs of roomStates) {
|
for (const rs of roomStates) {
|
||||||
if (rs.shouldAdd) {
|
if (rs.shouldAdd) {
|
||||||
this._rooms.add(rs.id, rs.room);
|
this._rooms.add(rs.id, rs.room);
|
||||||
|
for (const roomBeingCreated of this._roomsBeingCreated) {
|
||||||
|
if (roomBeingCreated.roomId === rs.id) {
|
||||||
|
roomBeingCreated.notifyJoinedRoom();
|
||||||
|
this._roomsBeingCreated.delete(roomBeingCreated.localId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (rs.shouldRemove) {
|
} else if (rs.shouldRemove) {
|
||||||
this._rooms.remove(rs.id);
|
this._rooms.remove(rs.id);
|
||||||
}
|
}
|
||||||
|
@ -392,7 +392,7 @@ export class Sync {
|
|||||||
// we receive also gets written.
|
// we receive also gets written.
|
||||||
// In any case, don't create a room for a rejected invite
|
// In any case, don't create a room for a rejected invite
|
||||||
if (!room && (membership === "join" || (isInitialSync && membership === "leave"))) {
|
if (!room && (membership === "join" || (isInitialSync && membership === "leave"))) {
|
||||||
room = this._session.createRoom(roomId);
|
room = this._session.createJoinedRoom(roomId);
|
||||||
isNewRoom = true;
|
isNewRoom = true;
|
||||||
}
|
}
|
||||||
if (room) {
|
if (room) {
|
||||||
|
@ -57,3 +57,15 @@ export function verifyEd25519Signature(olmUtil, userId, deviceOrKeyId, ed25519Ke
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createRoomEncryptionEvent() {
|
||||||
|
return {
|
||||||
|
"type": "m.room.encryption",
|
||||||
|
"state_key": "",
|
||||||
|
"content": {
|
||||||
|
"algorithm": MEGOLM_ALGORITHM,
|
||||||
|
"rotation_period_ms": 604800000,
|
||||||
|
"rotation_period_msgs": 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -263,20 +263,29 @@ export class HomeServerApi {
|
|||||||
return this._post(`/logout`, {}, {}, options);
|
return this._post(`/logout`, {}, {}, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDehydratedDevice(options: IRequestOptions): IHomeServerRequest {
|
getDehydratedDevice(options: IRequestOptions = {}): IHomeServerRequest {
|
||||||
options.prefix = DEHYDRATION_PREFIX;
|
options.prefix = DEHYDRATION_PREFIX;
|
||||||
return this._get(`/dehydrated_device`, undefined, undefined, options);
|
return this._get(`/dehydrated_device`, undefined, undefined, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
createDehydratedDevice(payload: Record<string, any>, options: IRequestOptions): IHomeServerRequest {
|
createDehydratedDevice(payload: Record<string, any>, options: IRequestOptions = {}): IHomeServerRequest {
|
||||||
options.prefix = DEHYDRATION_PREFIX;
|
options.prefix = DEHYDRATION_PREFIX;
|
||||||
return this._put(`/dehydrated_device`, {}, payload, options);
|
return this._put(`/dehydrated_device`, {}, payload, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
claimDehydratedDevice(deviceId: string, options: IRequestOptions): IHomeServerRequest {
|
claimDehydratedDevice(deviceId: string, options: IRequestOptions = {}): IHomeServerRequest {
|
||||||
options.prefix = DEHYDRATION_PREFIX;
|
options.prefix = DEHYDRATION_PREFIX;
|
||||||
return this._post(`/dehydrated_device/claim`, {}, {device_id: deviceId}, options);
|
return this._post(`/dehydrated_device/claim`, {}, {device_id: deviceId}, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
profile(userId: string, options?: IRequestOptions): IHomeServerRequest {
|
||||||
|
return this._get(`/profile/${encodeURIComponent(userId)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
createRoom(payload: Record<string, any>, options?: IRequestOptions): IHomeServerRequest {
|
||||||
|
return this._post(`/createRoom`, {}, payload, options);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
import {Request as MockRequest} from "../../mocks/Request.js";
|
import {Request as MockRequest} from "../../mocks/Request.js";
|
||||||
|
165
src/matrix/room/create.ts
Normal file
165
src/matrix/room/create.ts
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||||
|
|
||||||
|
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 {calculateRoomName} from "./members/Heroes";
|
||||||
|
import {createRoomEncryptionEvent} from "../e2ee/common";
|
||||||
|
import {EventEmitter} from "../../utils/EventEmitter";
|
||||||
|
|
||||||
|
import type {StateEvent} from "../storage/types";
|
||||||
|
import type {HomeServerApi} from "../net/HomeServerApi";
|
||||||
|
import type {ILogItem} from "../../logging/types";
|
||||||
|
|
||||||
|
type CreateRoomPayload = {
|
||||||
|
is_direct?: boolean;
|
||||||
|
preset?: string;
|
||||||
|
name?: string;
|
||||||
|
topic?: string;
|
||||||
|
invite?: string[];
|
||||||
|
initial_state?: StateEvent[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum RoomType {
|
||||||
|
DirectMessage,
|
||||||
|
Private,
|
||||||
|
Public
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultE2EEStatusForType(type: RoomType): boolean {
|
||||||
|
switch (type) {
|
||||||
|
case RoomType.DirectMessage:
|
||||||
|
case RoomType.Private:
|
||||||
|
return true;
|
||||||
|
case RoomType.Public:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function presetForType(type: RoomType): string {
|
||||||
|
switch (type) {
|
||||||
|
case RoomType.DirectMessage:
|
||||||
|
return "trusted_private_chat";
|
||||||
|
case RoomType.Private:
|
||||||
|
return "private_chat";
|
||||||
|
case RoomType.Public:
|
||||||
|
return "public_chat";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RoomBeingCreated extends EventEmitter<{change: never, joined: string}> {
|
||||||
|
private _roomId?: string;
|
||||||
|
private profiles: Profile[] = [];
|
||||||
|
|
||||||
|
public readonly isEncrypted: boolean;
|
||||||
|
public readonly name: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly localId: string,
|
||||||
|
private readonly type: RoomType,
|
||||||
|
isEncrypted: boolean | undefined,
|
||||||
|
private readonly explicitName: string | undefined,
|
||||||
|
private readonly topic: string | undefined,
|
||||||
|
private readonly inviteUserIds: string[] | undefined,
|
||||||
|
log: ILogItem
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
this.isEncrypted = isEncrypted === undefined ? defaultE2EEStatusForType(this.type) : isEncrypted;
|
||||||
|
if (explicitName) {
|
||||||
|
this.name = explicitName;
|
||||||
|
} else {
|
||||||
|
const summaryData = {
|
||||||
|
joinCount: 1, // ourselves
|
||||||
|
inviteCount: (this.inviteUserIds?.length || 0)
|
||||||
|
};
|
||||||
|
this.name = calculateRoomName(this.profiles, summaryData, log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async start(hsApi: HomeServerApi, log: ILogItem): Promise<void> {
|
||||||
|
await Promise.all([
|
||||||
|
this.loadProfiles(hsApi, log),
|
||||||
|
this.create(hsApi, log),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async create(hsApi: HomeServerApi, log: ILogItem): Promise<void> {
|
||||||
|
const options: CreateRoomPayload = {
|
||||||
|
is_direct: this.type === RoomType.DirectMessage,
|
||||||
|
preset: presetForType(this.type)
|
||||||
|
};
|
||||||
|
if (this.explicitName) {
|
||||||
|
options.name = this.explicitName;
|
||||||
|
}
|
||||||
|
if (this.topic) {
|
||||||
|
options.topic = this.topic;
|
||||||
|
}
|
||||||
|
if (this.inviteUserIds) {
|
||||||
|
options.invite = this.inviteUserIds;
|
||||||
|
}
|
||||||
|
if (this.isEncrypted) {
|
||||||
|
options.initial_state = [createRoomEncryptionEvent()];
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await hsApi.createRoom(options, {log}).response();
|
||||||
|
this._roomId = response["room_id"];
|
||||||
|
this.emit("change");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async loadProfiles(hsApi: HomeServerApi, log: ILogItem): Promise<void> {
|
||||||
|
// only load profiles if we need it for the room name and avatar
|
||||||
|
if (!this.explicitName && this.inviteUserIds) {
|
||||||
|
this.profiles = await loadProfiles(this.inviteUserIds, hsApi, log);
|
||||||
|
this.emit("change");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyJoinedRoom() {
|
||||||
|
this.emit("joined", this._roomId);
|
||||||
|
}
|
||||||
|
|
||||||
|
get avatarUrl(): string | undefined {
|
||||||
|
return this.profiles[0]?.avatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
get roomId(): string | undefined {
|
||||||
|
return this._roomId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadProfiles(userIds: string[], hsApi: HomeServerApi, log: ILogItem): Promise<Profile[]> {
|
||||||
|
const profiles = await Promise.all(userIds.map(async userId => {
|
||||||
|
const response = await hsApi.profile(userId, {log}).response();
|
||||||
|
return new Profile(userId, response.displayname as string, response.avatar_url as string);
|
||||||
|
}));
|
||||||
|
profiles.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
return profiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IProfile {
|
||||||
|
get userId(): string;
|
||||||
|
get displayName(): string;
|
||||||
|
get avatarUrl(): string;
|
||||||
|
get name(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Profile implements IProfile {
|
||||||
|
constructor(
|
||||||
|
public readonly userId: string,
|
||||||
|
public readonly displayName: string,
|
||||||
|
public readonly avatarUrl: string
|
||||||
|
) {}
|
||||||
|
|
||||||
|
get name() { return this.displayName || this.userId; }
|
||||||
|
}
|
@ -16,7 +16,7 @@ limitations under the License.
|
|||||||
|
|
||||||
import {RoomMember} from "./RoomMember.js";
|
import {RoomMember} from "./RoomMember.js";
|
||||||
|
|
||||||
function calculateRoomName(sortedMembers, summaryData, log) {
|
export function calculateRoomName(sortedMembers, summaryData, log) {
|
||||||
const countWithoutMe = summaryData.joinCount + summaryData.inviteCount - 1;
|
const countWithoutMe = summaryData.joinCount + summaryData.inviteCount - 1;
|
||||||
if (sortedMembers.length >= countWithoutMe) {
|
if (sortedMembers.length >= countWithoutMe) {
|
||||||
if (sortedMembers.length > 1) {
|
if (sortedMembers.length > 1) {
|
||||||
|
Loading…
Reference in New Issue
Block a user