Compare commits
5 Commits
f054abfbd2
...
a77c4b6db5
| Author | SHA1 | Date | |
|---|---|---|---|
| a77c4b6db5 | |||
| cb3d2c40e5 | |||
| f50e14d7a5 | |||
| 0ead519a80 | |||
| 7d98b49a30 |
+4
-3
@@ -35,10 +35,11 @@ This document tracks identified bugs, edge cases, and architectural discrepancie
|
|||||||
### 4. DM and Group Message Calls
|
### 4. DM and Group Message Calls
|
||||||
|
|
||||||
- **File:** `cinny/src/app/components/CallEmbedProvider.tsx`
|
- **File:** `cinny/src/app/components/CallEmbedProvider.tsx`
|
||||||
- **Status:** **OPEN**
|
- **Status:** **PARTIALLY FIXED ⚠️ UNTESTED** — Volume control added. Remaining: ringtone selection, suppression during active calls.
|
||||||
- **Issue:** Incoming call ringtone is hardcoded, lacks volume control, and is suppressed if the user is already in an active call.
|
- **Issue:** Incoming call ringtone is hardcoded, lacks volume control, and is suppressed if the user is already in an active call.
|
||||||
- **Root Cause:** Ringing logic is tightly coupled to `RTCNotification` events in `CallEmbedProvider.tsx`, using a hardcoded audio file path. It lacks an abstraction for sound management or user-configurable settings for ringtones/volumes.
|
- **Root Cause:** Ringing logic is tightly coupled to `RTCNotification` events in `CallEmbedProvider.tsx`, using a hardcoded audio file path. It lacks an abstraction for sound management or user-configurable settings for ringtones/volumes.
|
||||||
- **Proposed Fix:** Migrate sound asset management to a dedicated audio service. Implement user-configurable settings for ringtone and notification volume. Update the `IncomingCallListener` to support ringing even during active calls (if appropriate) by enhancing event handling.
|
- **Fix Applied:** Added `ringtoneVolume` setting (0–100, default 70). `IncomingCall` reads this setting and applies `audioElement.volume = ringtoneVolume / 100` before `play()`. Slider added to Settings → General → Calls section.
|
||||||
|
- **Remaining:** (a) Ringtone selection (still hardcoded to `call.ogg`); (b) Suppression during active calls — not investigated.
|
||||||
|
|
||||||
### 5. Seasonal Themes and Chat Backgrounds Design
|
### 5. Seasonal Themes and Chat Backgrounds Design
|
||||||
|
|
||||||
@@ -184,7 +185,7 @@ This document tracks identified bugs, edge cases, and architectural discrepancie
|
|||||||
| Performance | Numerous event handlers (e.g., handleUserClick, handleReplyClick) lack `useCallback`, leading to unnecessary re-renders of message components. | `cinny/src/app/features/room/RoomTimeline.tsx` | OPEN |
|
| Performance | Numerous event handlers (e.g., handleUserClick, handleReplyClick) lack `useCallback`, leading to unnecessary re-renders of message components. | `cinny/src/app/features/room/RoomTimeline.tsx` | OPEN |
|
||||||
| Performance | The `submit` function and file handling callbacks (e.g., handleSendUpload) are re-created on every render, causing re-renders of the editor and toolbar components. | `cinny/src/app/features/room/RoomInput.tsx` | OPEN |
|
| Performance | The `submit` function and file handling callbacks (e.g., handleSendUpload) are re-created on every render, causing re-renders of the editor and toolbar components. | `cinny/src/app/features/room/RoomInput.tsx` | OPEN |
|
||||||
| Accessibility | `button` for edit history lacks `aria-label` | `cinny/src/app/components/message/content/FallbackContent.tsx` | FIXED ⚠️ UNTESTED — added `aria-label="View edit history"` |
|
| Accessibility | `button` for edit history lacks `aria-label` | `cinny/src/app/components/message/content/FallbackContent.tsx` | FIXED ⚠️ UNTESTED — added `aria-label="View edit history"` |
|
||||||
| Accessibility | `button` for reaction lacks `aria-label` | `cinny/src/app/components/message/Reaction.tsx` | OPEN — emoji content is already screen-reader-accessible via alt text; parent caller would need to set aria-label per reaction |
|
| Accessibility | `button` for reaction lacks `aria-label` | `cinny/src/app/components/message/Reaction.tsx` | **FIXED ⚠️ UNTESTED** — `Reaction` component now computes `aria-label="{shortcode} reaction, N people"` internally using `getShortcodeFor`; custom (mxc://) emoji falls back to "custom emoji reaction". |
|
||||||
| Accessibility | `button` for ThreadIndicator lacks `aria-label` | `cinny/src/app/components/message/Reply.tsx` | FIXED ⚠️ UNTESTED — added `aria-label="View thread"` |
|
| Accessibility | `button` for ThreadIndicator lacks `aria-label` | `cinny/src/app/components/message/Reply.tsx` | FIXED ⚠️ UNTESTED — added `aria-label="View thread"` |
|
||||||
| Accessibility | `button` for ReplyLayout lacks `aria-label` | `cinny/src/app/components/message/Reply.tsx` | FIXED ⚠️ UNTESTED — added `aria-label="Jump to original message"` |
|
| Accessibility | `button` for ReplyLayout lacks `aria-label` | `cinny/src/app/components/message/Reply.tsx` | FIXED ⚠️ UNTESTED — added `aria-label="Jump to original message"` |
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ function IncomingCall({ dm, info, onIgnore, onAnswer, onReject }: IncomingCallPr
|
|||||||
const { room } = info;
|
const { room } = info;
|
||||||
|
|
||||||
const audioRef = useRef<HTMLAudioElement>(null);
|
const audioRef = useRef<HTMLAudioElement>(null);
|
||||||
|
const [ringtoneVolume] = useSetting(settingsAtom, 'ringtoneVolume');
|
||||||
|
|
||||||
const roomName = useRoomName(room);
|
const roomName = useRoomName(room);
|
||||||
const roomAvatar = useRoomAvatar(room, dm);
|
const roomAvatar = useRoomAvatar(room, dm);
|
||||||
@@ -126,8 +127,10 @@ function IncomingCall({ dm, info, onIgnore, onAnswer, onReject }: IncomingCallPr
|
|||||||
|
|
||||||
const playSound = useCallback(() => {
|
const playSound = useCallback(() => {
|
||||||
const audioElement = audioRef.current;
|
const audioElement = audioRef.current;
|
||||||
audioElement?.play().catch(() => undefined);
|
if (!audioElement) return;
|
||||||
}, []);
|
audioElement.volume = Math.max(0, Math.min(1, ringtoneVolume / 100));
|
||||||
|
audioElement.play().catch(() => undefined);
|
||||||
|
}, [ringtoneVolume]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const audioEl = audioRef.current;
|
const audioEl = audioRef.current;
|
||||||
|
|||||||
@@ -252,6 +252,7 @@ export function ExitFormatting({ tooltip }: ExitFormattingProps) {
|
|||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
size="400"
|
size="400"
|
||||||
radii="300"
|
radii="300"
|
||||||
|
aria-label="Exit formatting"
|
||||||
>
|
>
|
||||||
<Text size="B400">{`Exit ${KeySymbol.Hyper}`}</Text>
|
<Text size="B400">{`Exit ${KeySymbol.Hyper}`}</Text>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|||||||
@@ -15,34 +15,42 @@ export const Reaction = as<
|
|||||||
reaction: string;
|
reaction: string;
|
||||||
useAuthentication?: boolean;
|
useAuthentication?: boolean;
|
||||||
}
|
}
|
||||||
>(({ className, mx, count, reaction, useAuthentication, ...props }, ref) => (
|
>(({ className, mx, count, reaction, useAuthentication, ...props }, ref) => {
|
||||||
<Box
|
const shortcode = reaction.startsWith('mxc://')
|
||||||
as="button"
|
? 'custom emoji'
|
||||||
className={classNames(css.Reaction, className)}
|
: (getShortcodeFor(getHexcodeForEmoji(reaction)) ?? reaction);
|
||||||
alignItems="Center"
|
const label = `${shortcode} reaction, ${count} ${count === 1 ? 'person' : 'people'}`;
|
||||||
shrink="No"
|
|
||||||
gap="200"
|
return (
|
||||||
{...props}
|
<Box
|
||||||
ref={ref}
|
as="button"
|
||||||
>
|
className={classNames(css.Reaction, className)}
|
||||||
<Text className={css.ReactionText} as="span" size="T400">
|
alignItems="Center"
|
||||||
{reaction.startsWith('mxc://') ? (
|
shrink="No"
|
||||||
<img
|
gap="200"
|
||||||
className={css.ReactionImg}
|
aria-label={label}
|
||||||
src={mxcUrlToHttp(mx, reaction, useAuthentication) ?? reaction}
|
{...props}
|
||||||
alt={reaction}
|
ref={ref}
|
||||||
/>
|
>
|
||||||
) : (
|
<Text className={css.ReactionText} as="span" size="T400">
|
||||||
<Text as="span" size="Inherit" truncate>
|
{reaction.startsWith('mxc://') ? (
|
||||||
{reaction}
|
<img
|
||||||
</Text>
|
className={css.ReactionImg}
|
||||||
)}
|
src={mxcUrlToHttp(mx, reaction, useAuthentication) ?? reaction}
|
||||||
</Text>
|
alt={reaction}
|
||||||
<Text as="span" size="T300">
|
/>
|
||||||
{count}
|
) : (
|
||||||
</Text>
|
<Text as="span" size="Inherit" truncate>
|
||||||
</Box>
|
{reaction}
|
||||||
));
|
</Text>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
<Text as="span" size="T300">
|
||||||
|
{count}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
type ReactionTooltipMsgProps = {
|
type ReactionTooltipMsgProps = {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
|||||||
@@ -2102,6 +2102,9 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
|
|||||||
<Box
|
<Box
|
||||||
direction="Column"
|
direction="Column"
|
||||||
justifyContent="End"
|
justifyContent="End"
|
||||||
|
role="log"
|
||||||
|
aria-label="Message timeline"
|
||||||
|
aria-live="polite"
|
||||||
style={{ minHeight: '100%', padding: `${config.space.S600} 0` }}
|
style={{ minHeight: '100%', padding: `${config.space.S600} 0` }}
|
||||||
>
|
>
|
||||||
{!canPaginateBack && rangeAtStart && getItems().length > 0 && (
|
{!canPaginateBack && rangeAtStart && getItems().length > 0 && (
|
||||||
|
|||||||
@@ -524,6 +524,7 @@ export function RoomViewHeader({ callView }: { callView?: boolean }) {
|
|||||||
<PageHeader
|
<PageHeader
|
||||||
className={ContainerColor({ variant: 'Surface' })}
|
className={ContainerColor({ variant: 'Surface' })}
|
||||||
balance={screenSize === ScreenSize.Mobile}
|
balance={screenSize === ScreenSize.Mobile}
|
||||||
|
aria-label={`${name} room header`}
|
||||||
>
|
>
|
||||||
<Box grow="Yes" gap="300">
|
<Box grow="Yes" gap="300">
|
||||||
{screenSize === ScreenSize.Mobile && (
|
{screenSize === ScreenSize.Mobile && (
|
||||||
@@ -652,6 +653,7 @@ export function RoomViewHeader({ callView }: { callView?: boolean }) {
|
|||||||
style={{ position: 'relative' }}
|
style={{ position: 'relative' }}
|
||||||
onClick={handleOpenPinMenu}
|
onClick={handleOpenPinMenu}
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
|
aria-label="Pinned messages"
|
||||||
aria-pressed={!!pinMenuAnchor}
|
aria-pressed={!!pinMenuAnchor}
|
||||||
>
|
>
|
||||||
{pinnedEvents.length > 0 && (
|
{pinnedEvents.length > 0 && (
|
||||||
|
|||||||
@@ -1234,6 +1234,7 @@ function Calls() {
|
|||||||
settingsAtom,
|
settingsAtom,
|
||||||
'callJoinLeaveSound',
|
'callJoinLeaveSound',
|
||||||
);
|
);
|
||||||
|
const [ringtoneVolume, setRingtoneVolume] = useSetting(settingsAtom, 'ringtoneVolume');
|
||||||
|
|
||||||
const handleJoinLeaveSoundChange = (value: 'off' | 'chime' | 'soft' | 'retro') => {
|
const handleJoinLeaveSoundChange = (value: 'off' | 'chime' | 'soft' | 'retro') => {
|
||||||
setCallJoinLeaveSound(value);
|
setCallJoinLeaveSound(value);
|
||||||
@@ -1565,6 +1566,29 @@ function Calls() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
|
<SettingTile
|
||||||
|
title="Ringtone Volume"
|
||||||
|
description="Volume of the incoming call ringtone."
|
||||||
|
after={
|
||||||
|
<Box direction="Row" alignItems="Center" gap="200" style={{ minWidth: '160px' }}>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
step="5"
|
||||||
|
value={ringtoneVolume}
|
||||||
|
onChange={(e) => setRingtoneVolume(parseInt(e.target.value, 10))}
|
||||||
|
aria-label="Ringtone volume"
|
||||||
|
style={{ flex: 1, accentColor: 'var(--accent-orange)' }}
|
||||||
|
/>
|
||||||
|
<Text size="T200" style={{ minWidth: '32px', textAlign: 'right' }}>
|
||||||
|
{ringtoneVolume}%
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SequenceCard>
|
||||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<SettingTile
|
<SettingTile
|
||||||
title="Join & Leave Sounds"
|
title="Join & Leave Sounds"
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ export interface Settings {
|
|||||||
afkTimeoutMinutes: number;
|
afkTimeoutMinutes: number;
|
||||||
|
|
||||||
callJoinLeaveSound: 'off' | 'chime' | 'soft' | 'retro';
|
callJoinLeaveSound: 'off' | 'chime' | 'soft' | 'retro';
|
||||||
|
ringtoneVolume: number; // 0–100
|
||||||
|
|
||||||
seasonalThemeOverride:
|
seasonalThemeOverride:
|
||||||
| 'auto'
|
| 'auto'
|
||||||
@@ -242,6 +243,7 @@ const defaultSettings: Settings = {
|
|||||||
afkTimeoutMinutes: 10,
|
afkTimeoutMinutes: 10,
|
||||||
|
|
||||||
callJoinLeaveSound: 'chime',
|
callJoinLeaveSound: 'chime',
|
||||||
|
ringtoneVolume: 70,
|
||||||
|
|
||||||
seasonalThemeOverride: 'auto',
|
seasonalThemeOverride: 'auto',
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user