mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 13:14:58 +08:00
Merge pull request #6684 from matrix-org/travis/cross-room
Add support for MSC2762's timeline functionality
This commit is contained in:
commit
3046f0ed85
@ -83,7 +83,7 @@
|
||||
"linkifyjs": "^2.1.9",
|
||||
"lodash": "^4.17.20",
|
||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
||||
"matrix-widget-api": "^0.1.0-beta.15",
|
||||
"matrix-widget-api": "^0.1.0-beta.16",
|
||||
"minimist": "^1.2.5",
|
||||
"opus-recorder": "^8.0.3",
|
||||
"pako": "^2.0.3",
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2020 - 2021 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.
|
||||
@ -20,6 +20,7 @@ import { _t } from "../../../languageHandler";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
import {
|
||||
Capability,
|
||||
isTimelineCapability,
|
||||
Widget,
|
||||
WidgetEventCapability,
|
||||
WidgetKind,
|
||||
@ -30,6 +31,7 @@ import DialogButtons from "../elements/DialogButtons";
|
||||
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
|
||||
import { CapabilityText } from "../../../widgets/CapabilityText";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { lexicographicCompare } from "matrix-js-sdk/src/utils";
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
requestedCapabilities: Set<Capability>;
|
||||
@ -91,7 +93,20 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent<
|
||||
}
|
||||
|
||||
public render() {
|
||||
const checkboxRows = Object.entries(this.state.booleanStates).map(([cap, isChecked], i) => {
|
||||
// We specifically order the timeline capabilities down to the bottom. The capability text
|
||||
// generation cares strongly about this.
|
||||
const orderedCapabilities = Object.entries(this.state.booleanStates).sort(([capA], [capB]) => {
|
||||
const isTimelineA = isTimelineCapability(capA);
|
||||
const isTimelineB = isTimelineCapability(capB);
|
||||
|
||||
if (!isTimelineA && !isTimelineB) return lexicographicCompare(capA, capB);
|
||||
if (isTimelineA && !isTimelineB) return 1;
|
||||
if (!isTimelineA && isTimelineB) return -1;
|
||||
if (isTimelineA && isTimelineB) return lexicographicCompare(capA, capB);
|
||||
|
||||
return 0;
|
||||
});
|
||||
const checkboxRows = orderedCapabilities.map(([cap, isChecked], i) => {
|
||||
const text = CapabilityText.for(cap, this.props.widgetKind);
|
||||
const byline = text.byline
|
||||
? <span className="mx_WidgetCapabilitiesPromptDialog_byline">{ text.byline }</span>
|
||||
|
@ -601,6 +601,8 @@
|
||||
"See when anyone posts a sticker to your active room": "See when anyone posts a sticker to your active room",
|
||||
"with an empty state key": "with an empty state key",
|
||||
"with state key %(stateKey)s": "with state key %(stateKey)s",
|
||||
"The above, but in any room you are joined or invited to as well": "The above, but in any room you are joined or invited to as well",
|
||||
"The above, but in <Room /> as well": "The above, but in <Room /> as well",
|
||||
"Send <b>%(eventType)s</b> events as you in this room": "Send <b>%(eventType)s</b> events as you in this room",
|
||||
"See <b>%(eventType)s</b> events posted to this room": "See <b>%(eventType)s</b> events posted to this room",
|
||||
"Send <b>%(eventType)s</b> events as you in your active room": "Send <b>%(eventType)s</b> events as you in your active room",
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
|
||||
* Copyright 2020 - 2021 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.
|
||||
@ -418,13 +418,11 @@ export class StopGapWidget extends EventEmitter {
|
||||
private onEvent = (ev: MatrixEvent) => {
|
||||
MatrixClientPeg.get().decryptEventIfNeeded(ev);
|
||||
if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return;
|
||||
if (ev.getRoomId() !== this.eventListenerRoomId) return;
|
||||
this.feedEvent(ev);
|
||||
};
|
||||
|
||||
private onEventDecrypted = (ev: MatrixEvent) => {
|
||||
if (ev.isDecryptionFailure()) return;
|
||||
if (ev.getRoomId() !== this.eventListenerRoomId) return;
|
||||
this.feedEvent(ev);
|
||||
};
|
||||
|
||||
@ -469,7 +467,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
this.readUpToMap[ev.getRoomId()] = ev.getId();
|
||||
|
||||
const raw = ev.getEffectiveEvent();
|
||||
this.messaging.feedEvent(raw).catch(e => {
|
||||
this.messaging.feedEvent(raw, this.eventListenerRoomId).catch(e => {
|
||||
console.error("Error sending event to widget: ", e);
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
* Copyright 2020 - 2021 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.
|
||||
@ -23,6 +23,7 @@ import {
|
||||
MatrixCapabilities,
|
||||
OpenIDRequestState,
|
||||
SimpleObservable,
|
||||
Symbols,
|
||||
Widget,
|
||||
WidgetDriver,
|
||||
WidgetEventCapability,
|
||||
@ -42,7 +43,8 @@ import { CHAT_EFFECTS } from "../../effects";
|
||||
import { containsEmoji } from "../../effects/utils";
|
||||
import dis from "../../dispatcher/dispatcher";
|
||||
import { tryTransformPermalinkToLocalHref } from "../../utils/permalinks/Permalinks";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { IEvent, MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { Room } from "matrix-js-sdk";
|
||||
|
||||
// TODO: Purge this from the universe
|
||||
|
||||
@ -133,9 +135,14 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
||||
return allAllowed;
|
||||
}
|
||||
|
||||
public async sendEvent(eventType: string, content: any, stateKey: string = null): Promise<ISendEventDetails> {
|
||||
public async sendEvent(
|
||||
eventType: string,
|
||||
content: any,
|
||||
stateKey: string = null,
|
||||
targetRoomId: string = null,
|
||||
): Promise<ISendEventDetails> {
|
||||
const client = MatrixClientPeg.get();
|
||||
const roomId = ActiveRoomObserver.activeRoomId;
|
||||
const roomId = targetRoomId || ActiveRoomObserver.activeRoomId;
|
||||
|
||||
if (!client || !roomId) throw new Error("Not in a room or not attached to a client");
|
||||
|
||||
@ -162,48 +169,68 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
||||
return { roomId, eventId: r.event_id };
|
||||
}
|
||||
|
||||
public async readRoomEvents(eventType: string, msgtype: string | undefined, limit: number): Promise<object[]> {
|
||||
limit = limit > 0 ? Math.min(limit, Number.MAX_SAFE_INTEGER) : Number.MAX_SAFE_INTEGER; // relatively arbitrary
|
||||
|
||||
private pickRooms(roomIds: (string | Symbols.AnyRoom)[] = null): Room[] {
|
||||
const client = MatrixClientPeg.get();
|
||||
const roomId = ActiveRoomObserver.activeRoomId;
|
||||
const room = client.getRoom(roomId);
|
||||
if (!client || !roomId || !room) throw new Error("Not in a room or not attached to a client");
|
||||
if (!client) throw new Error("Not attached to a client");
|
||||
|
||||
const results: MatrixEvent[] = [];
|
||||
const events = room.getLiveTimeline().getEvents(); // timelines are most recent last
|
||||
for (let i = events.length - 1; i > 0; i--) {
|
||||
if (results.length >= limit) break;
|
||||
|
||||
const ev = events[i];
|
||||
if (ev.getType() !== eventType || ev.isState()) continue;
|
||||
if (eventType === EventType.RoomMessage && msgtype && msgtype !== ev.getContent()['msgtype']) continue;
|
||||
results.push(ev);
|
||||
}
|
||||
|
||||
return results.map(e => e.getEffectiveEvent());
|
||||
const targetRooms = roomIds
|
||||
? (roomIds.includes(Symbols.AnyRoom) ? client.getVisibleRooms() : roomIds.map(r => client.getRoom(r)))
|
||||
: [client.getRoom(ActiveRoomObserver.activeRoomId)];
|
||||
return targetRooms.filter(r => !!r);
|
||||
}
|
||||
|
||||
public async readStateEvents(eventType: string, stateKey: string | undefined, limit: number): Promise<object[]> {
|
||||
limit = limit > 0 ? Math.min(limit, Number.MAX_SAFE_INTEGER) : Number.MAX_SAFE_INTEGER; // relatively arbitrary
|
||||
public async readRoomEvents(
|
||||
eventType: string,
|
||||
msgtype: string | undefined,
|
||||
limitPerRoom: number,
|
||||
roomIds: (string | Symbols.AnyRoom)[] = null,
|
||||
): Promise<object[]> {
|
||||
limitPerRoom = limitPerRoom > 0 ? Math.min(limitPerRoom, Number.MAX_SAFE_INTEGER) : Number.MAX_SAFE_INTEGER; // relatively arbitrary
|
||||
|
||||
const client = MatrixClientPeg.get();
|
||||
const roomId = ActiveRoomObserver.activeRoomId;
|
||||
const room = client.getRoom(roomId);
|
||||
if (!client || !roomId || !room) throw new Error("Not in a room or not attached to a client");
|
||||
const rooms = this.pickRooms(roomIds);
|
||||
const allResults: IEvent[] = [];
|
||||
for (const room of rooms) {
|
||||
const results: MatrixEvent[] = [];
|
||||
const events = room.getLiveTimeline().getEvents(); // timelines are most recent last
|
||||
for (let i = events.length - 1; i > 0; i--) {
|
||||
if (results.length >= limitPerRoom) break;
|
||||
|
||||
const results: MatrixEvent[] = [];
|
||||
const state: Map<string, MatrixEvent> = room.currentState.events.get(eventType);
|
||||
if (state) {
|
||||
if (stateKey === "" || !!stateKey) {
|
||||
const forKey = state.get(stateKey);
|
||||
if (forKey) results.push(forKey);
|
||||
} else {
|
||||
results.push(...Array.from(state.values()));
|
||||
const ev = events[i];
|
||||
if (ev.getType() !== eventType || ev.isState()) continue;
|
||||
if (eventType === EventType.RoomMessage && msgtype && msgtype !== ev.getContent()['msgtype']) continue;
|
||||
results.push(ev);
|
||||
}
|
||||
}
|
||||
|
||||
return results.slice(0, limit).map(e => e.event);
|
||||
results.forEach(e => allResults.push(e.getEffectiveEvent()));
|
||||
}
|
||||
return allResults;
|
||||
}
|
||||
|
||||
public async readStateEvents(
|
||||
eventType: string,
|
||||
stateKey: string | undefined,
|
||||
limitPerRoom: number,
|
||||
roomIds: (string | Symbols.AnyRoom)[] = null,
|
||||
): Promise<object[]> {
|
||||
limitPerRoom = limitPerRoom > 0 ? Math.min(limitPerRoom, Number.MAX_SAFE_INTEGER) : Number.MAX_SAFE_INTEGER; // relatively arbitrary
|
||||
|
||||
const rooms = this.pickRooms(roomIds);
|
||||
const allResults: IEvent[] = [];
|
||||
for (const room of rooms) {
|
||||
const results: MatrixEvent[] = [];
|
||||
const state: Map<string, MatrixEvent> = room.currentState.events.get(eventType);
|
||||
if (state) {
|
||||
if (stateKey === "" || !!stateKey) {
|
||||
const forKey = state.get(stateKey);
|
||||
if (forKey) results.push(forKey);
|
||||
} else {
|
||||
results.push(...Array.from(state.values()));
|
||||
}
|
||||
}
|
||||
|
||||
results.slice(0, limitPerRoom).forEach(e => allResults.push(e.getEffectiveEvent()));
|
||||
}
|
||||
return allResults;
|
||||
}
|
||||
|
||||
public async askOpenID(observer: SimpleObservable<IOpenIDUpdate>) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2020 - 2021 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.
|
||||
@ -14,11 +14,22 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { Capability, EventDirection, MatrixCapabilities, WidgetEventCapability, WidgetKind } from "matrix-widget-api";
|
||||
import {
|
||||
Capability,
|
||||
EventDirection,
|
||||
getTimelineRoomIDFromCapability,
|
||||
isTimelineCapability,
|
||||
isTimelineCapabilityFor,
|
||||
MatrixCapabilities, Symbols,
|
||||
WidgetEventCapability,
|
||||
WidgetKind,
|
||||
} from "matrix-widget-api";
|
||||
import { _t, _td, TranslatedString } from "../languageHandler";
|
||||
import { EventType, MsgType } from "matrix-js-sdk/src/@types/event";
|
||||
import { ElementWidgetCapabilities } from "../stores/widgets/ElementWidgetCapabilities";
|
||||
import React from "react";
|
||||
import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||
import TextWithTooltip from "../components/views/elements/TextWithTooltip";
|
||||
|
||||
type GENERIC_WIDGET_KIND = "generic"; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
const GENERIC_WIDGET_KIND: GENERIC_WIDGET_KIND = "generic";
|
||||
@ -138,8 +149,31 @@ export class CapabilityText {
|
||||
if (textForKind[GENERIC_WIDGET_KIND]) return { primary: _t(textForKind[GENERIC_WIDGET_KIND]) };
|
||||
|
||||
// ... we'll fall through to the generic capability processing at the end of this
|
||||
// function if we fail to locate a simple string and the capability isn't for an
|
||||
// event.
|
||||
// function if we fail to generate a string for the capability.
|
||||
}
|
||||
|
||||
// Try to handle timeline capabilities. The text here implies that the caller has sorted
|
||||
// the timeline caps to the end for UI purposes.
|
||||
if (isTimelineCapability(capability)) {
|
||||
if (isTimelineCapabilityFor(capability, Symbols.AnyRoom)) {
|
||||
return { primary: _t("The above, but in any room you are joined or invited to as well") };
|
||||
} else {
|
||||
const roomId = getTimelineRoomIDFromCapability(capability);
|
||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||
return {
|
||||
primary: _t("The above, but in <Room /> as well", {}, {
|
||||
Room: () => {
|
||||
if (room) {
|
||||
return <TextWithTooltip tooltip={room.getCanonicalAlias() ?? roomId}>
|
||||
<b>{ room.name }</b>
|
||||
</TextWithTooltip>;
|
||||
} else {
|
||||
return <b><code>{ roomId }</code></b>;
|
||||
}
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't have a super simple line of text, so try processing the capability as the
|
||||
|
@ -5827,10 +5827,10 @@ matrix-react-test-utils@^0.2.3:
|
||||
"@babel/traverse" "^7.13.17"
|
||||
walk "^2.3.14"
|
||||
|
||||
matrix-widget-api@^0.1.0-beta.15:
|
||||
version "0.1.0-beta.15"
|
||||
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-0.1.0-beta.15.tgz#b02511f93fe1a3634868b6e246d736107f182745"
|
||||
integrity sha512-sWmtb8ZarSbHVbk5ni7IHBR9jOh7m1+5R4soky0fEO9VKl+MN7skT0+qNux3J9WuUAu2D80dZW9xPUT9cxfxbg==
|
||||
matrix-widget-api@^0.1.0-beta.16:
|
||||
version "0.1.0-beta.16"
|
||||
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-0.1.0-beta.16.tgz#32655f05cab48239b97fe4111a1d0858f2aad61a"
|
||||
integrity sha512-9zqaNLaM14YDHfFb7WGSUOivGOjYw+w5Su84ZfOl6A4IUy1xT9QPp0nsSA8wNfz0LpxOIPn3nuoF8Tn/40F5tg==
|
||||
dependencies:
|
||||
"@types/events" "^3.0.0"
|
||||
events "^3.2.0"
|
||||
|
Loading…
Reference in New Issue
Block a user