feat: dark mode fix, call wallpaper, setTheme error handling, Sentry filter
CI / Build & Quality Checks (push) Failing after 6m11s
CI / Build & Quality Checks (push) Failing after 6m11s
- CallEmbed: inject :root { color-scheme } into iframe so EC respects Cinny
theme regardless of OS preference (fixes white background in dark mode)
- CallEmbed: store themeKind, update color-scheme CSS on live setTheme() calls
- CallEmbed: catch transport.send() rejection in setTheme() to prevent
unhandled promise rejection when widget not ready yet (fixes REACT-8)
- CallEmbed: html + body both set to background:none so wallpaper shows through
- CallEmbedProvider: apply chatBackground wallpaper style to call embed
container in full-view mode (not PiP) -- wallpapers carry over to calls
- useCallEmbed: pass themeKind through to CallEmbed constructor
- index.tsx: ignoreErrors: [Request timed out] to suppress matrixRTC
heartbeat timeouts (REACT-9) from Sentry noise
- README: document 0.19.4, positioning fix, dark mode fix, wallpaper,
millify Rolldown interop fix, Sentry noise filter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -47,6 +47,10 @@ import { useMediaAuthentication } from '../hooks/useMediaAuthentication';
|
||||
import { mxcUrlToHttp, getMxIdLocalPart } from '../utils/matrix';
|
||||
import { RoomAvatar, RoomIcon } from './room-avatar';
|
||||
import { useRoomNavigate } from '../hooks/useRoomNavigate';
|
||||
import { getChatBg } from '../features/lotus/chatBackground';
|
||||
import { useTheme, ThemeKind } from '../hooks/useTheme';
|
||||
import { useSetting } from '../state/hooks/settings';
|
||||
import { settingsAtom } from '../state/settings';
|
||||
import { getStateEvent, getMemberDisplayName } from '../utils/room';
|
||||
import { StateEvent } from '../../types/matrix/room';
|
||||
import { getPowersLevelFromMatrixEvent } from '../hooks/usePowerLevels';
|
||||
@@ -412,6 +416,14 @@ export function CallEmbedProvider({ children }: CallEmbedProviderProps) {
|
||||
const pipMode = callActive && !inCallRoom;
|
||||
const { navigateRoom } = useRoomNavigate();
|
||||
|
||||
const theme = useTheme();
|
||||
const isDark = theme.kind === ThemeKind.Dark;
|
||||
const [chatBackground] = useSetting(settingsAtom, 'chatBackground');
|
||||
const wallpaperStyle = React.useMemo(
|
||||
() => getChatBg(chatBackground, isDark),
|
||||
[chatBackground, isDark],
|
||||
);
|
||||
|
||||
const pipDragRef = React.useRef<{
|
||||
startX: number;
|
||||
startY: number;
|
||||
@@ -692,6 +704,7 @@ export function CallEmbedProvider({ children }: CallEmbedProviderProps) {
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '50%',
|
||||
...(callVisible && !pipMode ? wallpaperStyle : {}),
|
||||
}}
|
||||
ref={callEmbedRef}
|
||||
>
|
||||
|
||||
@@ -65,7 +65,7 @@ export const createCallEmbed = (
|
||||
const controlState =
|
||||
pref && new CallControlState(forceAudioOff ? false : pref.microphone, pref.video, pref.sound);
|
||||
|
||||
const embed = new CallEmbed(mx, room, widget, container, controlState);
|
||||
const embed = new CallEmbed(mx, room, widget, container, controlState, themeKind);
|
||||
|
||||
return embed;
|
||||
};
|
||||
|
||||
@@ -52,6 +52,8 @@ export class CallEmbed {
|
||||
|
||||
private styleRetryObserver?: MutationObserver;
|
||||
|
||||
private themeKind: ElementCallThemeKind = 'dark';
|
||||
|
||||
// Arrow-function class fields so dispose() passes the exact same reference to mx.off()
|
||||
private readonly boundOnEvent = (ev: MatrixEvent) => this.onEvent(ev);
|
||||
|
||||
@@ -175,6 +177,7 @@ export class CallEmbed {
|
||||
widget: Widget,
|
||||
container: HTMLElement,
|
||||
initialControlState?: CallControlState,
|
||||
themeKind: ElementCallThemeKind = 'dark',
|
||||
) {
|
||||
const iframe = CallEmbed.getIframe(
|
||||
widget.getCompleteUrl({ currentUserId: mx.getSafeUserId() }),
|
||||
@@ -189,6 +192,7 @@ export class CallEmbed {
|
||||
this.room = room;
|
||||
this.iframe = iframe;
|
||||
this.container = container;
|
||||
this.themeKind = themeKind;
|
||||
|
||||
const controlState = initialControlState ?? new CallControlState(true, false, true);
|
||||
this.control = new CallControl(controlState, call, iframe);
|
||||
@@ -218,9 +222,17 @@ export class CallEmbed {
|
||||
}
|
||||
|
||||
public setTheme(theme: ElementCallThemeKind) {
|
||||
return this.call.transport.send(WidgetApiToWidgetAction.ThemeChange, {
|
||||
name: theme,
|
||||
});
|
||||
this.themeKind = theme;
|
||||
const doc = this.document;
|
||||
if (doc && this.joined) {
|
||||
const styleEl = doc.getElementById('lotus-ec-styles');
|
||||
if (styleEl) styleEl.textContent = this.buildStyleContent();
|
||||
}
|
||||
return this.call.transport
|
||||
.send(WidgetApiToWidgetAction.ThemeChange, { name: theme })
|
||||
.catch(() => {
|
||||
// Widget transport not ready yet; theme URL param is the fallback
|
||||
});
|
||||
}
|
||||
|
||||
public hangup() {
|
||||
@@ -304,22 +316,28 @@ export class CallEmbed {
|
||||
this.control.forceState(this.initialState);
|
||||
}
|
||||
|
||||
private buildStyleContent(): string {
|
||||
return [
|
||||
'html, body { background: none !important; }',
|
||||
`:root { color-scheme: ${this.themeKind}; }`,
|
||||
'[style*="height: 0"][style*="z-index: 1"][style*="align-self: center"] { display: none !important; }',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
private applyStyles(): void {
|
||||
const doc = this.document;
|
||||
if (!doc) return;
|
||||
|
||||
doc.body.style.setProperty('background', 'none', 'important');
|
||||
|
||||
// Inject CSS for things that can't be reliably caught by DOM timing
|
||||
if (!doc.getElementById('lotus-ec-styles')) {
|
||||
const style = doc.createElement('style');
|
||||
style.id = 'lotus-ec-styles';
|
||||
style.textContent = [
|
||||
'body { background: none !important; }',
|
||||
// Hide "using to Device key transport" status line
|
||||
'[style*="height: 0"][style*="z-index: 1"][style*="align-self: center"] { display: none !important; }',
|
||||
].join('\n');
|
||||
style.textContent = this.buildStyleContent();
|
||||
(doc.head ?? doc.body).appendChild(style);
|
||||
} else {
|
||||
const styleEl = doc.getElementById('lotus-ec-styles');
|
||||
if (styleEl) styleEl.textContent = this.buildStyleContent();
|
||||
}
|
||||
|
||||
// Hide EC built-in controls (we provide our own)
|
||||
|
||||
@@ -22,6 +22,8 @@ if (sentryDsn) {
|
||||
sendDefaultPii: false,
|
||||
// Forward Sentry logs to the dashboard
|
||||
enableLogs: true,
|
||||
// Suppress benign PostmessageTransport / matrixRTC heartbeat timeouts (upstream library noise)
|
||||
ignoreErrors: ['Request timed out'],
|
||||
beforeSend(event) {
|
||||
// Drop any event that may have leaked an access token into breadcrumbs/data
|
||||
if (JSON.stringify(event).includes('access_token')) return null;
|
||||
|
||||
Reference in New Issue
Block a user