fix(calls): harden ML denoise shim against static; fix lint/format
ML noise suppression produced loud static on real calls. RNNoise requires mono 48kHz float input; feeding it stereo or wrong-rate data is the classic cause of that static. Harden the shim: - request mono (channelCount:1) + 48kHz capture - run a 48kHz AudioContext and BAIL to the raw mic if the browser won't give a true 48kHz context (wrong-rate data -> static) - force the worklet node to explicit mono in/out - use the non-SIMD rnnoise.wasm (SIMD build artifacts on some GPUs) - share one AudioContext across captures Also fix the two CI-blocking eslint errors (unused vars in UrlPreviewCard and useLocalMessageSearch) and apply repo-wide prettier formatting so check:eslint and check:prettier pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,11 @@ type AvatarDecorationProps = {
|
||||
inset?: number;
|
||||
};
|
||||
|
||||
export function AvatarDecoration({ userId, children, inset = DEFAULT_INSET }: AvatarDecorationProps) {
|
||||
export function AvatarDecoration({
|
||||
userId,
|
||||
children,
|
||||
inset = DEFAULT_INSET,
|
||||
}: AvatarDecorationProps) {
|
||||
const slug = useAvatarDecoration(userId);
|
||||
|
||||
if (!slug) {
|
||||
|
||||
@@ -282,9 +282,7 @@ export function PollContent({
|
||||
style={{
|
||||
padding: '7px 12px',
|
||||
borderRadius: '8px',
|
||||
background: selected
|
||||
? 'var(--accent-cyan-dim)'
|
||||
: 'rgba(255,255,255,0.04)',
|
||||
background: selected ? 'var(--accent-cyan-dim)' : 'rgba(255,255,255,0.04)',
|
||||
border: `1.5px solid ${selected ? 'var(--accent-cyan)' : 'var(--border-color)'}`,
|
||||
fontSize: '0.88rem',
|
||||
lineHeight: 1.4,
|
||||
@@ -308,9 +306,7 @@ export function PollContent({
|
||||
inset: 0,
|
||||
right: 'auto',
|
||||
width: `${pct}%`,
|
||||
background: selected
|
||||
? 'var(--accent-cyan-dim)'
|
||||
: 'rgba(255,255,255,0.03)',
|
||||
background: selected ? 'var(--accent-cyan-dim)' : 'rgba(255,255,255,0.03)',
|
||||
pointerEvents: 'none',
|
||||
transition: 'width 0.3s ease',
|
||||
}}
|
||||
|
||||
@@ -109,9 +109,7 @@ function HalloweenOverlay({ reduced }: { reduced: boolean }) {
|
||||
height: `${size}px`,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: isOrange ? 'rgba(255,100,0,0.75)' : 'rgba(160,0,255,0.7)',
|
||||
boxShadow: isOrange
|
||||
? '0 0 8px rgba(255,100,0,0.5)'
|
||||
: '0 0 8px rgba(160,0,255,0.5)',
|
||||
boxShadow: isOrange ? '0 0 8px rgba(255,100,0,0.5)' : '0 0 8px rgba(160,0,255,0.5)',
|
||||
animation: `${animSeasonFall} ${duration}s ease-in ${delay}s infinite`,
|
||||
}}
|
||||
/>
|
||||
@@ -379,8 +377,9 @@ function LunarNewYearOverlay({ reduced }: { reduced: boolean }) {
|
||||
position: 'absolute',
|
||||
left: `${left}%`,
|
||||
top: `${top}%`,
|
||||
animation:
|
||||
reduced ? 'none' : `${animBob} ${duration}s ease-in-out ${delay}s infinite`,
|
||||
animation: reduced
|
||||
? 'none'
|
||||
: `${animBob} ${duration}s ease-in-out ${delay}s infinite`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
@@ -419,10 +418,9 @@ function LunarNewYearOverlay({ reduced }: { reduced: boolean }) {
|
||||
height: '14px',
|
||||
backgroundColor: '#ffd700',
|
||||
margin: '0 auto',
|
||||
animation:
|
||||
reduced
|
||||
? 'none'
|
||||
: `${animTasselSway} ${duration * 0.6}s ease-in-out ${delay}s infinite`,
|
||||
animation: reduced
|
||||
? 'none'
|
||||
: `${animTasselSway} ${duration * 0.6}s ease-in-out ${delay}s infinite`,
|
||||
transformOrigin: 'top center',
|
||||
}}
|
||||
/>
|
||||
@@ -789,8 +787,7 @@ export function SeasonalPreview({ theme }: { theme: SeasonTheme }) {
|
||||
export function SeasonalEffect() {
|
||||
const settings = useAtomValue(settingsAtom);
|
||||
const reduced =
|
||||
typeof window !== 'undefined' &&
|
||||
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
const theme = useMemo<SeasonTheme | null>(() => {
|
||||
const override = settings.seasonalThemeOverride ?? 'auto';
|
||||
|
||||
@@ -1651,7 +1651,6 @@ function GenericCard({
|
||||
const title = prev['og:title'] ?? '';
|
||||
const description = prev['og:description'] ?? '';
|
||||
const siteName = typeof prev['og:site_name'] === 'string' ? prev['og:site_name'] : undefined;
|
||||
const domain = getDomain(url);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -216,8 +216,7 @@ export function MessageSearch({
|
||||
// term === undefined → no search started
|
||||
// term === '' → sender-only search (from:user with no body text)
|
||||
// term === 'foo' → normal text search
|
||||
const hasActiveSearch =
|
||||
msgSearchParams.term !== undefined || !!msgSearchParams.senders?.length;
|
||||
const hasActiveSearch = msgSearchParams.term !== undefined || !!msgSearchParams.senders?.length;
|
||||
const senderOnlyMode = !msgSearchParams.term && !!msgSearchParams.senders?.length;
|
||||
|
||||
// Run synchronous client-side search immediately.
|
||||
@@ -534,52 +533,53 @@ export function MessageSearch({
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{localResult && (senderOnlyMode ? localResult.groups.length > 0 : localResult.encryptedRoomsCount > 0) && (
|
||||
<Box direction="Column" gap="300">
|
||||
<Box direction="Column" gap="200">
|
||||
<Box alignItems="Center" gap="200">
|
||||
<Icon size="200" src={senderOnlyMode ? Icons.User : Icons.Lock} />
|
||||
<Text size="H5">{senderOnlyMode ? 'Messages from user' : 'Encrypted Rooms'}</Text>
|
||||
{!senderOnlyMode && (
|
||||
<Text size="T200" style={{ opacity: 0.55 }}>
|
||||
{`${localResult.searchedRoomsCount} / ${localResult.encryptedRoomsCount} cached`}
|
||||
</Text>
|
||||
)}
|
||||
{localResult &&
|
||||
(senderOnlyMode ? localResult.groups.length > 0 : localResult.encryptedRoomsCount > 0) && (
|
||||
<Box direction="Column" gap="300">
|
||||
<Box direction="Column" gap="200">
|
||||
<Box alignItems="Center" gap="200">
|
||||
<Icon size="200" src={senderOnlyMode ? Icons.User : Icons.Lock} />
|
||||
<Text size="H5">{senderOnlyMode ? 'Messages from user' : 'Encrypted Rooms'}</Text>
|
||||
{!senderOnlyMode && (
|
||||
<Text size="T200" style={{ opacity: 0.55 }}>
|
||||
{`${localResult.searchedRoomsCount} / ${localResult.encryptedRoomsCount} cached`}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
<Text size="T300" priority="300">
|
||||
{senderOnlyMode
|
||||
? `Showing locally cached messages from this user across all rooms. Open more rooms or load history below to extend coverage.`
|
||||
: localResult.groups.length > 0
|
||||
? `Showing locally cached messages from ${localResult.searchedRoomsCount} encrypted room${localResult.searchedRoomsCount !== 1 ? 's' : ''}. Load more history below to extend coverage.`
|
||||
: `No matches in your local cache. Load messages below to search further back.`}
|
||||
</Text>
|
||||
<Line size="300" variant="Surface" />
|
||||
</Box>
|
||||
<Text size="T300" priority="300">
|
||||
{senderOnlyMode
|
||||
? `Showing locally cached messages from this user across all rooms. Open more rooms or load history below to extend coverage.`
|
||||
: localResult.groups.length > 0
|
||||
? `Showing locally cached messages from ${localResult.searchedRoomsCount} encrypted room${localResult.searchedRoomsCount !== 1 ? 's' : ''}. Load more history below to extend coverage.`
|
||||
: `No matches in your local cache. Load messages below to search further back.`}
|
||||
</Text>
|
||||
<Line size="300" variant="Surface" />
|
||||
{localResult.groups.length > 0 && (
|
||||
<Box direction="Column" gap="300">
|
||||
{localResult.groups.map((group) => {
|
||||
const groupRoom = mx.getRoom(group.roomId);
|
||||
if (!groupRoom) return null;
|
||||
return (
|
||||
<SearchResultGroup
|
||||
key={group.roomId}
|
||||
room={groupRoom}
|
||||
highlights={[msgSearchParams.term ?? '']}
|
||||
items={group.items}
|
||||
mediaAutoLoad={mediaAutoLoad}
|
||||
urlPreview={urlPreview}
|
||||
onOpen={navigateRoom}
|
||||
legacyUsernameColor={legacyUsernameColor || mDirects.has(groupRoom.roomId)}
|
||||
hour24Clock={hour24Clock}
|
||||
dateFormatString={dateFormatString}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
<EncryptedRoomCachePanel roomIds={localSearchRooms} onLoaded={handleCacheLoaded} />
|
||||
</Box>
|
||||
{localResult.groups.length > 0 && (
|
||||
<Box direction="Column" gap="300">
|
||||
{localResult.groups.map((group) => {
|
||||
const groupRoom = mx.getRoom(group.roomId);
|
||||
if (!groupRoom) return null;
|
||||
return (
|
||||
<SearchResultGroup
|
||||
key={group.roomId}
|
||||
room={groupRoom}
|
||||
highlights={[msgSearchParams.term ?? '']}
|
||||
items={group.items}
|
||||
mediaAutoLoad={mediaAutoLoad}
|
||||
urlPreview={urlPreview}
|
||||
onOpen={navigateRoom}
|
||||
legacyUsernameColor={legacyUsernameColor || mDirects.has(groupRoom.roomId)}
|
||||
hour24Clock={hour24Clock}
|
||||
dateFormatString={dateFormatString}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
<EncryptedRoomCachePanel roomIds={localSearchRooms} onLoaded={handleCacheLoaded} />
|
||||
</Box>
|
||||
)}
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<Box
|
||||
|
||||
@@ -75,8 +75,7 @@ export const useLocalMessageSearch = () => {
|
||||
if (senderOnlyMode) continue;
|
||||
const evType = event.getType();
|
||||
const isSticker = evType === 'm.sticker';
|
||||
const isPoll =
|
||||
evType === 'm.poll.start' || evType === 'org.matrix.msc3381.poll.start';
|
||||
const isPoll = evType === 'm.poll.start' || evType === 'org.matrix.msc3381.poll.start';
|
||||
if (!isSticker && !isPoll) continue;
|
||||
}
|
||||
|
||||
@@ -90,9 +89,7 @@ export const useLocalMessageSearch = () => {
|
||||
// Sender-only mode: no text filter needed
|
||||
if (!senderOnlyMode) {
|
||||
const evType = event.getType();
|
||||
const isSticker = evType === 'm.sticker';
|
||||
const isPoll =
|
||||
evType === 'm.poll.start' || evType === 'org.matrix.msc3381.poll.start';
|
||||
const isPoll = evType === 'm.poll.start' || evType === 'org.matrix.msc3381.poll.start';
|
||||
|
||||
let body = '';
|
||||
let formattedBody = '';
|
||||
|
||||
@@ -37,11 +37,7 @@ export function usePresenceUpdater() {
|
||||
return mx
|
||||
.setPresence({
|
||||
presence: 'unavailable',
|
||||
...(statusMsg
|
||||
? { status_msg: statusMsg }
|
||||
: status
|
||||
? { status_msg: status }
|
||||
: {}),
|
||||
...(statusMsg ? { status_msg: statusMsg } : status ? { status_msg: status } : {}),
|
||||
})
|
||||
.catch(() => undefined);
|
||||
};
|
||||
|
||||
@@ -21,14 +21,11 @@ export function useUserNotes(): {
|
||||
|
||||
useAccountDataCallback(
|
||||
mx,
|
||||
useCallback(
|
||||
(evt) => {
|
||||
if (evt.getType() === NOTES_KEY) {
|
||||
setNotes(evt.getContent<UserNotesContent>() ?? {});
|
||||
}
|
||||
},
|
||||
[],
|
||||
),
|
||||
useCallback((evt) => {
|
||||
if (evt.getType() === NOTES_KEY) {
|
||||
setNotes(evt.getContent<UserNotesContent>() ?? {});
|
||||
}
|
||||
}, []),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user