feat: show call participants in room list (Discord-style)
Docker / Docker Buildx (push) Has been cancelled
Build Debian package / Build package (release) Has been cancelled
Build and Deploy / prepare (release) Has been cancelled
Deploy release / Deploy to Cloudflare Pages (release) Has been cancelled
Build and Deploy / Trigger Pro pipeline (release) Has been cancelled
Build and Deploy / Windows arm64 (release) Has been cancelled
Build and Deploy / Windows x64 (release) Has been cancelled
Build and Deploy / macOS (release) Has been cancelled
Build and Deploy / Linux amd64 (sqlcipher static) (release) Has been cancelled
Build and Deploy / Linux arm64 (sqlcipher static) (release) Has been cancelled
Build and Deploy / ${{ needs.prepare.outputs.deploy == 'true' && 'Deploy' || 'Deploy (dry-run)' }} (release) Has been cancelled
Build and Deploy / Deploy builds to ESS (release) Has been cancelled
Docker / Docker Buildx (push) Has been cancelled
Build Debian package / Build package (release) Has been cancelled
Build and Deploy / prepare (release) Has been cancelled
Deploy release / Deploy to Cloudflare Pages (release) Has been cancelled
Build and Deploy / Trigger Pro pipeline (release) Has been cancelled
Build and Deploy / Windows arm64 (release) Has been cancelled
Build and Deploy / Windows x64 (release) Has been cancelled
Build and Deploy / macOS (release) Has been cancelled
Build and Deploy / Linux amd64 (sqlcipher static) (release) Has been cancelled
Build and Deploy / Linux arm64 (sqlcipher static) (release) Has been cancelled
Build and Deploy / ${{ needs.prepare.outputs.deploy == 'true' && 'Deploy' || 'Deploy (dry-run)' }} (release) Has been cancelled
Build and Deploy / Deploy builds to ESS (release) Has been cancelled
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
// Note: eslint-plugin-jsdoc doesn't like import types as parameters, so we
|
||||
// get around it with @typedef
|
||||
/**
|
||||
* @typedef {import("@element-hq/element-web-module-api").Api} Api
|
||||
*/
|
||||
|
||||
export default class CustomComponentModule {
|
||||
static moduleApiVersion = "^1.2.0";
|
||||
/**
|
||||
* Basic module for testing.
|
||||
* @param {Api} api API object
|
||||
*/
|
||||
constructor(api) {
|
||||
this.api = api;
|
||||
this.api.customComponents.registerMessageRenderer(
|
||||
(evt) => evt.content.body === "Do not show edits",
|
||||
(_props, originalComponent) => {
|
||||
return originalComponent();
|
||||
},
|
||||
{ allowEditingEvent: false },
|
||||
);
|
||||
this.api.customComponents.registerMessageRenderer(
|
||||
(evt) => evt.content.body === "Fall through here",
|
||||
(props) => {
|
||||
const body = props.mxEvent.content.body;
|
||||
return `Fallthrough text for ${body}`;
|
||||
},
|
||||
);
|
||||
this.api.customComponents.registerMessageRenderer(
|
||||
(evt) => {
|
||||
if (evt.content.body === "Crash the filter!") {
|
||||
throw new Error("Fail test!");
|
||||
}
|
||||
return false;
|
||||
},
|
||||
() => {
|
||||
return `Should not render!`;
|
||||
},
|
||||
);
|
||||
this.api.customComponents.registerMessageRenderer(
|
||||
(evt) => evt.content.body === "Crash the renderer!",
|
||||
() => {
|
||||
throw new Error("Fail test!");
|
||||
},
|
||||
);
|
||||
|
||||
this.api.customComponents.registerMessageRenderer(
|
||||
(mxEvent) => mxEvent.type === "m.room.message" && mxEvent.content.msgtype === "m.image",
|
||||
(_props, originalComponent) => {
|
||||
return originalComponent();
|
||||
},
|
||||
{ allowDownloadingMedia: async (mxEvent) => mxEvent.content.body !== "bad.png" },
|
||||
);
|
||||
|
||||
// Order is specific here to avoid this overriding the other renderers
|
||||
this.api.customComponents.registerMessageRenderer("m.room.message", (props, originalComponent) => {
|
||||
const body = props.mxEvent.content.body;
|
||||
if (body === "Do not replace me") {
|
||||
return originalComponent();
|
||||
} else if (body === "Fall through here") {
|
||||
return null;
|
||||
}
|
||||
return `Custom text for ${body}`;
|
||||
});
|
||||
}
|
||||
async load() {}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
export default class ExampleModule {
|
||||
static moduleApiVersion = "^1.0.0";
|
||||
constructor(api) {
|
||||
this.api = api;
|
||||
|
||||
this.api.i18n.register({
|
||||
key: {
|
||||
en: "%(brand)s module loading successful!",
|
||||
de: "%(brand)s-Module erfolgreich geladen!",
|
||||
},
|
||||
});
|
||||
}
|
||||
async load() {
|
||||
const brand = this.api.config.get("brand");
|
||||
alert(this.api.i18n.translate("key", { brand }));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<!doctype html>
|
||||
<style>
|
||||
body {
|
||||
background: rgb(139, 192, 253);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- element-call.spec.ts will insert the widget API in this block -->
|
||||
<script>
|
||||
widgetCodeHere;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<p>Fake Element Call</p>
|
||||
<p>State: <span id="state">Loading</span></p>
|
||||
<button id="send-button">Send Room Message</button>
|
||||
</div>
|
||||
|
||||
<!-- Minimal fake implementation of Element Call. Just enough for testing the leagkin widgets.-->
|
||||
<script>
|
||||
const stateIndicator = document.querySelector("#state");
|
||||
const { WidgetApi, WidgetApiToWidgetAction, MatrixCapabilities } = mxwidgets();
|
||||
const widgetId = new URLSearchParams(window.location.search).get("widgetId");
|
||||
const params = new URLSearchParams(window.location.hash.slice(1));
|
||||
|
||||
const roomId = params.get("roomId");
|
||||
const api = new WidgetApi(widgetId, "*");
|
||||
|
||||
document.querySelector("#send-button").onclick = async () => {
|
||||
await api.sendRoomEvent(
|
||||
"m.room.message",
|
||||
{ msgtype: "m.text", body: "I sent this once!!" },
|
||||
roomId,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
};
|
||||
|
||||
api.requestCapability(MatrixCapabilities.AlwaysOnScreen);
|
||||
api.requestCapability(`org.matrix.msc2762.timeline:${roomId}`);
|
||||
api.requestCapabilityToSendMessage("m.text");
|
||||
|
||||
api.on("ready", (ev) => {
|
||||
stateIndicator.innerHTML = "Ready";
|
||||
});
|
||||
|
||||
// Start the messaging
|
||||
api.start();
|
||||
|
||||
// If waitForIframeLoad is false, tell the client that we're good to go
|
||||
api.sendContentLoaded();
|
||||
</script>
|
||||
@@ -0,0 +1,87 @@
|
||||
<!doctype html>
|
||||
<style>
|
||||
body {
|
||||
background: rgb(139, 192, 253);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- element-call.spec.ts will insert the widget API in this block -->
|
||||
<script>
|
||||
widgetCodeHere;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<p>Fake Element Call</p>
|
||||
<p>State: <span id="state">Loading</span></p>
|
||||
<button id="join-button">Join Call</button>
|
||||
<button id="close-button">Close</button>
|
||||
</div>
|
||||
|
||||
<!-- Minimal fake implementation of Element Call. Just enough for testing persistent widgets.-->
|
||||
<script>
|
||||
const content = {
|
||||
"application": "m.call",
|
||||
"call_id": "",
|
||||
"device_id": "gycSobuY0z",
|
||||
"expires": 14400000,
|
||||
"foci_preferred": [
|
||||
{
|
||||
livekit_alias: "any-alias",
|
||||
livekit_service_url: "https://example.org",
|
||||
type: "livekit",
|
||||
},
|
||||
],
|
||||
"focus_active": {
|
||||
focus_selection: "oldest_membership",
|
||||
type: "livekit",
|
||||
},
|
||||
"m.call.intent": "video",
|
||||
"scope": "m.room",
|
||||
};
|
||||
const stateIndicator = document.querySelector("#state");
|
||||
const { WidgetApi, WidgetApiToWidgetAction, MatrixCapabilities } = mxwidgets();
|
||||
const widgetId = new URLSearchParams(window.location.search).get("widgetId");
|
||||
const params = new URLSearchParams(window.location.hash.slice(1));
|
||||
const userId = params.get("userId");
|
||||
const deviceId = params.get("deviceId");
|
||||
const roomId = params.get("roomId");
|
||||
const api = new WidgetApi(widgetId, "*");
|
||||
|
||||
const stateKey = `_${userId}_${deviceId}_m.call`;
|
||||
|
||||
async function hangup() {
|
||||
await api.sendStateEvent("org.matrix.msc3401.call.member", stateKey, {}, roomId);
|
||||
await api.setAlwaysOnScreen(false);
|
||||
await api.transport.send("io.element.close", {});
|
||||
stateIndicator.innerHTML = "Ended";
|
||||
}
|
||||
|
||||
document.querySelector("#join-button").onclick = async () => {
|
||||
await api.setAlwaysOnScreen(true);
|
||||
await api.transport.send("io.element.join", {});
|
||||
await api.sendStateEvent("org.matrix.msc3401.call.member", stateKey, content, roomId);
|
||||
stateIndicator.innerHTML = "In call";
|
||||
};
|
||||
|
||||
document.querySelector("#close-button").onclick = () => {
|
||||
hangup();
|
||||
};
|
||||
|
||||
api.requestCapability(MatrixCapabilities.AlwaysOnScreen);
|
||||
api.requestCapability(`org.matrix.msc2762.timeline:${roomId}`);
|
||||
api.requestCapabilityToSendState("org.matrix.msc3401.call.member", stateKey);
|
||||
|
||||
api.on("ready", (ev) => {
|
||||
stateIndicator.innerHTML = "Ready";
|
||||
// Pretend to join a call.
|
||||
});
|
||||
api.on("action:im.vector.hangup", async () => {
|
||||
await hangup();
|
||||
});
|
||||
|
||||
// Start the messaging
|
||||
api.start();
|
||||
|
||||
// If waitForIframeLoad is false, tell the client that we're good to go
|
||||
api.sendContentLoaded();
|
||||
</script>
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
export function getSampleFilePath(file: string): string {
|
||||
return join(__dirname, file);
|
||||
}
|
||||
|
||||
export function readSampleFile(file: string, encoding: null): Promise<NonSharedBuffer>;
|
||||
export function readSampleFile(file: string, encoding?: BufferEncoding): Promise<string>;
|
||||
export function readSampleFile(
|
||||
file: string,
|
||||
encoding: BufferEncoding | null = "utf-8",
|
||||
): Promise<NonSharedBuffer | string> {
|
||||
return readFile(getSampleFilePath(file), encoding);
|
||||
}
|
||||
|
||||
export function readSampleFileSync(file: string, encoding: null): NonSharedBuffer;
|
||||
export function readSampleFileSync(file: string, encoding?: BufferEncoding): string;
|
||||
export function readSampleFileSync(file: string, encoding: BufferEncoding | null = "utf-8"): NonSharedBuffer | string {
|
||||
return readFileSync(getSampleFilePath(file), encoding);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"versions": [
|
||||
"r0.0.1",
|
||||
"r0.1.0",
|
||||
"r0.2.0",
|
||||
"r0.3.0",
|
||||
"r0.4.0",
|
||||
"r0.5.0",
|
||||
"r0.6.0",
|
||||
"r0.6.1",
|
||||
"v1.1",
|
||||
"v1.2",
|
||||
"v1.3",
|
||||
"v1.4"
|
||||
],
|
||||
"unstable_features": {
|
||||
"org.matrix.label_based_filtering": true,
|
||||
"org.matrix.e2e_cross_signing": true,
|
||||
"org.matrix.msc2432": true,
|
||||
"uk.half-shot.msc2666.mutual_rooms": true,
|
||||
"io.element.e2ee_forced.public": false,
|
||||
"io.element.e2ee_forced.private": false,
|
||||
"io.element.e2ee_forced.trusted_private": false,
|
||||
"org.matrix.msc3026.busy_presence": false,
|
||||
"org.matrix.msc2285.stable": true,
|
||||
"org.matrix.msc3827.stable": true,
|
||||
"org.matrix.msc2716": false,
|
||||
"org.matrix.msc3030": false,
|
||||
"org.matrix.msc3440.stable": true,
|
||||
"org.matrix.msc3771": true,
|
||||
"org.matrix.msc3773": false,
|
||||
"fi.mau.msc2815": false,
|
||||
"org.matrix.msc3882": false,
|
||||
"org.matrix.msc3881": false,
|
||||
"org.matrix.msc3874": false,
|
||||
"org.matrix.msc3886": false,
|
||||
"org.matrix.msc3912": false
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user