Compare commits

..
Author SHA1 Message Date
Lotus CIandClaude Opus 5.5 42040b6412 chore(types): fewer any casts; eslint warning ratchet 36 -> 29 (#210)
CI / Build & Quality Checks (pull_request) Successful in 3m23s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 17s
CI / Playwright smoke (e2e) (pull_request) Successful in 18m6s
- Developer tools (send event, state event editor, room account data) and
  setMarkedUnread go through the typed helpers in utils/room and
  utils/accountData (new setRoomAccountData) instead of casting at the
  call site; sendStateEvent accepts a developer-chosen type string.
- SecretStorage passes the key description as the SDK's
  SecretStorageKeyDescription, CallEmbed replies with an IWidgetApiRequest,
  and fillMissingPowers writes through a Record instead of `as any`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
2026-10-20 19:49:15 -04:00
jared ccbafdb147 Merge pull request 'chore(deps): element-call lotus.28, soundboard in voice channels with a call policy (EC #47)' (#438) from chore/soundboard-mic-mix into lotus
CI / Build & Quality Checks (push) Successful in 4m12s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 15s
CI / Trigger Desktop Build (push) Successful in 4s
CI / Playwright smoke (e2e) (push) Successful in 24m18s
CI / Build & Quality Checks (pull_request) Canceled after 0s
CI / Trigger Desktop Build (pull_request) Canceled after 0s
CI / Secret scan (gitleaks) (pull_request) Canceled after 0s
CI / Docker image build & smoke test (pull_request) Canceled after 0s
CI / Playwright smoke (e2e) (pull_request) Canceled after 0s
2026-10-20 00:04:51 -04:00
Lotus CIandClaude Opus 5.5 60c0b9ad30 chore(deps): pin @lotusguild/element-call-embedded 0.25.0-lotus.28 (element-call#47)
CI / Build & Quality Checks (pull_request) Successful in 1m47s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 10s
CI / Playwright smoke (e2e) (pull_request) Successful in 19m10s
lotus.28 plays soundboard clips in voice channels with a call policy by
mixing them into the published microphone (element-call#59), instead of
answering not_permitted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
2026-10-19 23:43:25 -04:00
jared 1873285371 Merge pull request 'fix(call): one-row phone call bar with self-view; screenshare-audio mute only when useful (#265, #277)' (#437) from fix/phone-call-controls into lotus
CI / Build & Quality Checks (push) Successful in 1m43s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 7s
CI / Trigger Desktop Build (push) Successful in 6s
CI / Playwright smoke (e2e) (push) Successful in 14m46s
2026-10-19 18:11:32 -04:00
11 changed files with 41 additions and 21 deletions
+4 -4
View File
@@ -81,7 +81,7 @@
},
"devDependencies": {
"@axe-core/playwright": "4.13.0",
"@lotusguild/element-call-embedded": "0.25.0-lotus.27",
"@lotusguild/element-call-embedded": "0.25.0-lotus.28",
"@playwright/test": "1.63.0",
"@rollup/plugin-inject": "5.0.5",
"@rollup/plugin-wasm": "6.2.2",
@@ -2695,9 +2695,9 @@
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
},
"node_modules/@lotusguild/element-call-embedded": {
"version": "0.25.0-lotus.27",
"resolved": "https://code.lotusguild.org/api/packages/LotusGuild/npm/%40lotusguild%2Felement-call-embedded/-/0.25.0-lotus.27/element-call-embedded-0.25.0-lotus.27.tgz",
"integrity": "sha512-24ZAvGR/t0C6LWFygZkvtk7HalPUvjERDWWkxpJJf4AZCDOJGuhoOuBrjmO+tUGwIrgLOTN6EFAWSaPpTU9rlQ==",
"version": "0.25.0-lotus.28",
"resolved": "https://code.lotusguild.org/api/packages/LotusGuild/npm/%40lotusguild%2Felement-call-embedded/-/0.25.0-lotus.28/element-call-embedded-0.25.0-lotus.28.tgz",
"integrity": "sha512-IFBHHSVw0CzrrLgx3oIIcJmBB4YMb8at4oVuSKXifZ7MmyHydxg9U3sTlZGgLUjMVrrAXj4gFzCksGHet4LSQw==",
"dev": true
},
"node_modules/@matrix-org/matrix-sdk-crypto-wasm": {
+2 -2
View File
@@ -12,7 +12,7 @@
"build": "vite build",
"preview": "vite preview",
"lint": "npm run check:eslint && npm run check:prettier",
"check:eslint": "eslint src/* --max-warnings 36",
"check:eslint": "eslint src/* --max-warnings 29",
"check:prettier": "prettier --check .",
"fix:prettier": "prettier --write .",
"typecheck": "tsc --noEmit",
@@ -108,7 +108,7 @@
},
"devDependencies": {
"@axe-core/playwright": "4.13.0",
"@lotusguild/element-call-embedded": "0.25.0-lotus.27",
"@lotusguild/element-call-embedded": "0.25.0-lotus.28",
"@playwright/test": "1.63.0",
"@rollup/plugin-inject": "5.0.5",
"@rollup/plugin-wasm": "6.2.2",
+9 -2
View File
@@ -1,6 +1,7 @@
import React, { FormEventHandler, useCallback } from 'react';
import { Box, Text, Button, Spinner, color } from 'folds';
import { decodeRecoveryKey, deriveRecoveryKeyFromPassphrase } from 'matrix-js-sdk/lib/crypto-api';
import type { SecretStorageKeyDescription } from 'matrix-js-sdk/lib/secret-storage';
import { PasswordInput } from './password-input';
import {
SecretStorageKeyContent,
@@ -39,7 +40,10 @@ export function SecretStorageRecoveryPassphrase({
bits,
);
const match = await mx.secretStorage.checkKey(decodedRecoveryKey, keyContent as any);
const match = await mx.secretStorage.checkKey(
decodedRecoveryKey,
keyContent as SecretStorageKeyDescription,
);
if (!match) {
throw new Error('Invalid recovery passphrase.');
@@ -132,7 +136,10 @@ export function SecretStorageRecoveryKey({
async (recoveryKey) => {
const decodedRecoveryKey = decodeRecoveryKey(recoveryKey);
const match = await mx.secretStorage.checkKey(decodedRecoveryKey, keyContent as any);
const match = await mx.secretStorage.checkKey(
decodedRecoveryKey,
keyContent as SecretStorageKeyDescription,
);
if (!match) {
throw new Error('Invalid recovery key.');
@@ -30,6 +30,7 @@ import {
AccountDataSubmitCallback,
} from '../../../components/AccountDataEditor';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { setRoomAccountData } from '../../../utils/accountData';
type DeveloperToolsProps = {
requestClose: () => void;
@@ -58,8 +59,7 @@ export function DeveloperTools({ requestClose }: DeveloperToolsProps) {
const submitAccountData: AccountDataSubmitCallback = useCallback(
async (type, content) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await mx.setRoomAccountData(room.roomId, type as any, content);
await setRoomAccountData(mx, room.roomId, type, content);
},
[mx, room.roomId],
);
@@ -1,5 +1,5 @@
import React, { useCallback, useRef, useState, FormEventHandler, useEffect } from 'react';
import { MatrixError } from 'matrix-js-sdk';
import { IContent, MatrixError } from 'matrix-js-sdk';
import {
Box,
Chip,
@@ -22,6 +22,7 @@ import { useTextAreaCodeEditor } from '../../../hooks/useTextAreaCodeEditor';
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
import { syntaxErrorPosition } from '../../../utils/dom';
import { Cursor } from '../../../plugins/text-area';
import { sendRoomEvent, sendStateEvent } from '../../../utils/room';
const EDITOR_INTENT_SPACE_COUNT = 2;
@@ -51,9 +52,9 @@ export function SendRoomEvent({ type, stateKey, requestClose }: SendRoomEventPro
useCallback(
(evtType, evtStateKey, evtContent) => {
if (typeof evtStateKey === 'string') {
return mx.sendStateEvent(room.roomId, evtType as any, evtContent, evtStateKey);
return sendStateEvent(mx, room.roomId, evtType, evtContent, evtStateKey);
}
return mx.sendEvent(room.roomId, evtType as any, evtContent);
return sendRoomEvent(mx, room.roomId, null, evtType, evtContent as IContent);
},
[mx, room],
),
@@ -31,6 +31,7 @@ import { usePowerLevels } from '../../../hooks/usePowerLevels';
import { useTextAreaCodeEditor } from '../../../hooks/useTextAreaCodeEditor';
import { useRoomCreators } from '../../../hooks/useRoomCreators';
import { useRoomPermissions } from '../../../hooks/useRoomPermissions';
import { sendStateEvent } from '../../../utils/room';
const EDITOR_INTENT_SPACE_COUNT = 2;
@@ -59,7 +60,7 @@ function StateEventEdit({ type, stateKey, content, requestClose }: StateEventEdi
const [submitState, submit] = useAsyncCallback<object, MatrixError, [object]>(
useCallback(
(c) => mx.sendStateEvent(room.roomId, type as any, c, stateKey),
(c) => sendStateEvent(mx, room.roomId, type, c, stateKey),
[mx, room, type, stateKey],
),
);
+1 -1
View File
@@ -46,7 +46,7 @@ const fillMissingPowers = (powerLevels: IPowerLevels): IPowerLevels =>
const keys = Object.keys(DEFAULT_POWER_LEVELS) as unknown as (keyof IPowerLevels)[];
keys.forEach((key) => {
if (draftPl[key] === undefined) {
draftPl[key] = DEFAULT_POWER_LEVELS[key] as any;
(draftPl as Record<string, unknown>)[key] = DEFAULT_POWER_LEVELS[key];
}
});
if (draftPl.notifications && typeof draftPl.notifications.room !== 'number') {
+2 -1
View File
@@ -15,6 +15,7 @@ import {
WidgetApiFromWidgetAction,
WidgetApiToWidgetAction,
WidgetDriver,
IWidgetApiRequest,
} from 'matrix-widget-api';
import { CallWidgetDriver } from './CallWidgetDriver';
import { trimTrailingSlash } from '../../utils/common';
@@ -796,7 +797,7 @@ export class CallEmbed {
const wrapped = (ev: CustomEvent<T>) => {
ev.preventDefault();
// Reply with success so the widget transport doesn't time out waiting for a response
this.call.transport.reply(ev.detail as any, {});
this.call.transport.reply(ev.detail as unknown as IWidgetApiRequest, {});
callback(ev);
};
return this.listenEvent(`action:${type}`, wrapped);
+3 -4
View File
@@ -2,6 +2,7 @@ import { atom, useSetAtom } from 'jotai';
import { MatrixClient, MatrixEvent, Room, RoomEvent, RoomEventHandlerMap } from 'matrix-js-sdk';
import { useEffect } from 'react';
import { AccountDataEvent } from '../../../types/matrix/accountData';
import { setRoomAccountData } from '../../utils/accountData';
// MSC2867 — "mark a room as unread". A per-room account-data flag `{ unread }`.
// Stable type `m.marked_unread`; servers/clients predating the stabilization use
@@ -25,11 +26,9 @@ export const setMarkedUnread = (
unread: boolean,
): Promise<unknown> =>
Promise.all([
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mx.setRoomAccountData(roomId, AccountDataEvent.MarkedUnread as any, { unread }),
setRoomAccountData(mx, roomId, AccountDataEvent.MarkedUnread, { unread }),
// Best-effort mirror for older servers; never fail the primary write on it.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mx.setRoomAccountData(roomId, UNSTABLE_MARKED_UNREAD as any, { unread }).catch(() => undefined),
setRoomAccountData(mx, roomId, UNSTABLE_MARKED_UNREAD, { unread }).catch(() => undefined),
]);
export const receiptIsMine = (event: MatrixEvent, userId: string): boolean => {
+11
View File
@@ -18,6 +18,17 @@ export function getAccountData<T>(
return event?.getContent() as T | undefined;
}
/** Room account data under a custom or developer-chosen type (same cast). */
export function setRoomAccountData<T>(
mx: MatrixClient,
roomId: string,
eventType: string,
content: T,
): Promise<unknown> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (mx as any).setRoomAccountData(roomId, eventType, content);
}
export function setAccountData<T>(
mx: MatrixClient,
eventType: AccountDataEvent | string,
+1 -1
View File
@@ -60,7 +60,7 @@ export const getStateEvents = (room: Room, eventType: StateEvent): MatrixEvent[]
export function sendStateEvent<T extends object>(
mx: MatrixClient,
roomId: string,
eventType: StateEvent,
eventType: StateEvent | string,
content: T,
stateKey = '',
): Promise<ISendEventResponse> {