feat: presence avatar border rings (P5-18) + room emoji prefix support (P5-6)
P5-18: PresenceRingAvatar wrapper component applies a 2px box-shadow ring to user avatars — green (online), yellow (idle/unavailable), red (DND via status_msg='dnd'), no ring (offline). Applied to: message timeline sender avatars, members drawer (members + knock requests), @mention autocomplete, and inbox notification senders. P5-6: Leading emoji in room names renders at 1.15× in the sidebar via Unicode emoji regex detection in RoomNavItem. Emoji picker (EmojiBoard in PopOut) added to all three room-name inputs: Create Room dialog (converted to controlled input), Room Settings name field (shown only when canEditName), and the "Rename for me" local rename dialog. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,13 +7,17 @@ import {
|
||||
color,
|
||||
config,
|
||||
Icon,
|
||||
IconButton,
|
||||
Icons,
|
||||
Input,
|
||||
PopOut,
|
||||
RectCords,
|
||||
Spinner,
|
||||
Switch,
|
||||
Text,
|
||||
TextArea,
|
||||
} from 'folds';
|
||||
import { EmojiBoard } from '../../components/emoji-board';
|
||||
import { SettingTile } from '../../components/setting-tile';
|
||||
import { SequenceCard } from '../../components/sequence-card';
|
||||
import {
|
||||
@@ -94,6 +98,13 @@ export function CreateRoomForm({
|
||||
const [encryption, setEncryption] = useState(false);
|
||||
const [knock, setKnock] = useState(false);
|
||||
const [advance, setAdvance] = useState(false);
|
||||
const [nameValue, setNameValue] = useState('');
|
||||
const [emojiAnchor, setEmojiAnchor] = useState<RectCords>();
|
||||
|
||||
const handleEmojiSelect = useCallback((unicode: string) => {
|
||||
setNameValue((prev) => unicode + prev);
|
||||
setEmojiAnchor(undefined);
|
||||
}, []);
|
||||
|
||||
const allowKnock = access === CreateRoomAccess.Private && knockSupported(selectedRoomVersion);
|
||||
const allowKnockRestricted =
|
||||
@@ -183,17 +194,56 @@ export function CreateRoomForm({
|
||||
</Box>
|
||||
<Box shrink="No" direction="Column" gap="100">
|
||||
<Text size="L400">Name</Text>
|
||||
<Input
|
||||
required
|
||||
before={<Icon size="100" src={getCreateRoomAccessToIcon(access, type)} />}
|
||||
name="nameInput"
|
||||
autoFocus
|
||||
size="500"
|
||||
variant="SurfaceVariant"
|
||||
radii="400"
|
||||
autoComplete="off"
|
||||
disabled={disabled}
|
||||
/>
|
||||
<Box direction="Row" gap="100" alignItems="Center">
|
||||
<PopOut
|
||||
anchor={emojiAnchor}
|
||||
position="Top"
|
||||
align="End"
|
||||
content={
|
||||
<EmojiBoard
|
||||
imagePackRooms={[]}
|
||||
returnFocusOnDeactivate={false}
|
||||
onEmojiSelect={handleEmojiSelect}
|
||||
requestClose={() => setEmojiAnchor(undefined)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
type="button"
|
||||
size="400"
|
||||
radii="400"
|
||||
variant="Surface"
|
||||
fill="Soft"
|
||||
outlined
|
||||
disabled={disabled}
|
||||
aria-label="Insert emoji"
|
||||
aria-expanded={!!emojiAnchor}
|
||||
aria-haspopup="dialog"
|
||||
onClick={(evt: React.MouseEvent<HTMLButtonElement>) => {
|
||||
const rect = evt.currentTarget.getBoundingClientRect();
|
||||
setEmojiAnchor((prev) => (prev ? undefined : rect));
|
||||
}}
|
||||
>
|
||||
<Icon src={Icons.Smile} size="400" />
|
||||
</IconButton>
|
||||
</PopOut>
|
||||
<Box grow="Yes">
|
||||
<Input
|
||||
required
|
||||
before={<Icon size="100" src={getCreateRoomAccessToIcon(access, type)} />}
|
||||
name="nameInput"
|
||||
autoFocus
|
||||
size="500"
|
||||
variant="SurfaceVariant"
|
||||
radii="400"
|
||||
autoComplete="off"
|
||||
disabled={disabled}
|
||||
value={nameValue}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setNameValue(e.target.value)}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box shrink="No" direction="Column" gap="100">
|
||||
<Text size="L400">Topic (Optional)</Text>
|
||||
|
||||
Reference in New Issue
Block a user