feat(call): add sound effects and incoming call notification UI
- Implemented sound management for call events including mute, unmute, deafen, undeafen, call joined, call depart, and dialing sounds. - Created a new CallSounds class to handle sound playback and state management. - Added IncomingCallNotification component to display incoming call alerts with caller information and action buttons. - Styled the incoming call notification using vanilla-extract CSS for animations and layout. - Integrated sound playback for incoming calls with a 15-second threshold for dialing sound.
This commit is contained in:
@@ -32,7 +32,7 @@ import FocusTrap from 'focus-trap-react';
|
||||
import { Page, PageContent, PageHeader } from '../../../components/page';
|
||||
import { SequenceCard } from '../../../components/sequence-card';
|
||||
import { useSetting } from '../../../state/hooks/settings';
|
||||
import { DateFormat, MessageLayout, MessageSpacing, settingsAtom } from '../../../state/settings';
|
||||
import { DateFormat, EmojiStyle, MessageLayout, MessageSpacing, settingsAtom } from '../../../state/settings';
|
||||
import { SettingTile } from '../../../components/setting-tile';
|
||||
import { KeySymbol } from '../../../utils/key-symbol';
|
||||
import { isMacOS } from '../../../utils/user-agent';
|
||||
@@ -303,10 +303,50 @@ function PageZoomInput() {
|
||||
);
|
||||
}
|
||||
|
||||
const emojiStyleNames: Record<EmojiStyle, string> = {
|
||||
[EmojiStyle.System]: 'System',
|
||||
[EmojiStyle.Apple]: 'Apple',
|
||||
[EmojiStyle.Twemoji]: 'Twemoji',
|
||||
};
|
||||
|
||||
type EmojiStyleSelectorProps = {
|
||||
selected: EmojiStyle;
|
||||
onSelect: (style: EmojiStyle) => void;
|
||||
};
|
||||
const EmojiStyleSelector = as<'div', EmojiStyleSelectorProps>(
|
||||
({ selected, onSelect, ...props }, ref) => (
|
||||
<Menu {...props} ref={ref}>
|
||||
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
|
||||
{Object.values(EmojiStyle).map((style) => (
|
||||
<MenuItem
|
||||
key={style}
|
||||
size="300"
|
||||
variant={style === selected ? 'Primary' : 'Surface'}
|
||||
radii="300"
|
||||
onClick={() => onSelect(style)}
|
||||
>
|
||||
<Text size="T300">{emojiStyleNames[style]}</Text>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Box>
|
||||
</Menu>
|
||||
)
|
||||
);
|
||||
|
||||
function Appearance() {
|
||||
const [systemTheme, setSystemTheme] = useSetting(settingsAtom, 'useSystemTheme');
|
||||
const [monochromeMode, setMonochromeMode] = useSetting(settingsAtom, 'monochromeMode');
|
||||
const [twitterEmoji, setTwitterEmoji] = useSetting(settingsAtom, 'twitterEmoji');
|
||||
const [emojiStyle, setEmojiStyle] = useSetting(settingsAtom, 'emojiStyle');
|
||||
const [emojiMenuCords, setEmojiMenuCords] = useState<RectCords>();
|
||||
|
||||
const handleEmojiMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||
setEmojiMenuCords(evt.currentTarget.getBoundingClientRect());
|
||||
};
|
||||
|
||||
const handleEmojiStyleSelect = (style: EmojiStyle) => {
|
||||
setEmojiStyle(style);
|
||||
setEmojiMenuCords(undefined);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box direction="Column" gap="100">
|
||||
@@ -342,8 +382,45 @@ function Appearance() {
|
||||
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Twitter Emoji"
|
||||
after={<Switch variant="Primary" value={twitterEmoji} onChange={setTwitterEmoji} />}
|
||||
title="Emoji Style"
|
||||
description="Choose which emoji set to display."
|
||||
after={
|
||||
<>
|
||||
<Button
|
||||
size="300"
|
||||
variant="Primary"
|
||||
outlined
|
||||
fill="Soft"
|
||||
radii="300"
|
||||
after={<Icon size="300" src={Icons.ChevronBottom} />}
|
||||
onClick={handleEmojiMenu}
|
||||
>
|
||||
<Text size="T300">{emojiStyleNames[emojiStyle]}</Text>
|
||||
</Button>
|
||||
<PopOut
|
||||
anchor={emojiMenuCords}
|
||||
offset={5}
|
||||
position="Bottom"
|
||||
align="End"
|
||||
content={
|
||||
<FocusTrap
|
||||
focusTrapOptions={{
|
||||
initialFocus: false,
|
||||
onDeactivate: () => setEmojiMenuCords(undefined),
|
||||
clickOutsideDeactivates: true,
|
||||
isKeyForward: (evt: KeyboardEvent) =>
|
||||
evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
|
||||
isKeyBackward: (evt: KeyboardEvent) =>
|
||||
evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
|
||||
escapeDeactivates: stopPropagation,
|
||||
}}
|
||||
>
|
||||
<EmojiStyleSelector selected={emojiStyle} onSelect={handleEmojiStyleSelect} />
|
||||
</FocusTrap>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</SequenceCard>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user