feat: extend verification badge to user profile and settings members list
Extract MemberVerificationBadge into a shared component and render it in: - UserRoomProfile: shield badge beside the display name on the profile card - common-settings Members: badge next to each member in the room/space settings members page (accessible from the lobby) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { Icon, Icons, Text, Tooltip, TooltipProvider } from 'folds';
|
||||
import { useUserVerifiedStatus } from '../hooks/useUserVerifiedStatus';
|
||||
|
||||
type MemberVerificationBadgeProps = {
|
||||
userId: string;
|
||||
};
|
||||
|
||||
export function MemberVerificationBadge({ userId }: MemberVerificationBadgeProps) {
|
||||
const vs = useUserVerifiedStatus(userId);
|
||||
if (vs === 'unknown') return null;
|
||||
const color =
|
||||
vs === 'verified' ? 'var(--tc-positive-normal, #5effc4)' : 'var(--tc-warning-normal, #ffcc55)';
|
||||
const label = vs === 'verified' ? 'Identity verified' : 'Not verified';
|
||||
return (
|
||||
<TooltipProvider
|
||||
position="Top"
|
||||
tooltip={
|
||||
<Tooltip>
|
||||
<Text size="T200">{label}</Text>
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
{(ref) => (
|
||||
<span
|
||||
ref={ref}
|
||||
title={label}
|
||||
style={{ display: 'inline-flex', alignItems: 'center', flexShrink: 0 }}
|
||||
>
|
||||
<Icon size="100" src={Icons.ShieldUser} style={{ color }} />
|
||||
</span>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import { usePowerLevels } from '../../hooks/usePowerLevels';
|
||||
import { useRoom } from '../../hooks/useRoom';
|
||||
import { useUserPresence } from '../../hooks/useUserPresence';
|
||||
import { IgnoredUserAlert, MutualRoomsChip, OptionsChip, ServerChip, ShareChip } from './UserChips';
|
||||
import { useCrossSigningActive } from '../../hooks/useCrossSigning';
|
||||
import { MemberVerificationBadge } from '../MemberVerificationBadge';
|
||||
import { useCloseUserRoomProfile } from '../../state/hooks/userRoomProfile';
|
||||
import { PowerChip } from './PowerChip';
|
||||
import { UserInviteAlert, UserBanAlert, UserModeration, UserKickAlert } from './UserModeration';
|
||||
@@ -28,6 +30,7 @@ type UserRoomProfileProps = {
|
||||
};
|
||||
export function UserRoomProfile({ userId }: UserRoomProfileProps) {
|
||||
const mx = useMatrixClient();
|
||||
const crossSigningActive = useCrossSigningActive();
|
||||
const useAuthentication = useMediaAuthentication();
|
||||
const navigate = useNavigate();
|
||||
const closeUserRoomProfile = useCloseUserRoomProfile();
|
||||
@@ -35,6 +38,7 @@ export function UserRoomProfile({ userId }: UserRoomProfileProps) {
|
||||
const ignored = ignoredUsers.includes(userId);
|
||||
|
||||
const room = useRoom();
|
||||
const showEncryption = room.hasEncryptionStateEvent() && crossSigningActive;
|
||||
const powerLevels = usePowerLevels(room);
|
||||
const creators = useRoomCreators(room);
|
||||
|
||||
@@ -76,8 +80,9 @@ export function UserRoomProfile({ userId }: UserRoomProfileProps) {
|
||||
/>
|
||||
<Box direction="Column" gap="500" style={{ padding: config.space.S400 }}>
|
||||
<Box direction="Column" gap="400">
|
||||
<Box gap="400" alignItems="Start">
|
||||
<Box gap="400" alignItems="Center">
|
||||
<UserHeroName displayName={displayName} userId={userId} />
|
||||
{showEncryption && <MemberVerificationBadge userId={userId} />}
|
||||
{userId !== myUserId && (
|
||||
<Box shrink="No">
|
||||
<Button
|
||||
|
||||
@@ -56,6 +56,8 @@ import { useSpaceOptionally } from '../../../hooks/useSpace';
|
||||
import { useFlattenPowerTagMembers, useGetMemberPowerTag } from '../../../hooks/useMemberPowerTag';
|
||||
import { useRoomCreators } from '../../../hooks/useRoomCreators';
|
||||
import { getMouseEventCords } from '../../../utils/dom';
|
||||
import { useCrossSigningActive } from '../../../hooks/useCrossSigning';
|
||||
import { MemberVerificationBadge } from '../../../components/MemberVerificationBadge';
|
||||
|
||||
const SEARCH_OPTIONS: UseAsyncSearchOptions = {
|
||||
limit: 1000,
|
||||
@@ -79,6 +81,8 @@ export function Members({ requestClose }: MembersProps) {
|
||||
const useAuthentication = useMediaAuthentication();
|
||||
const room = useRoom();
|
||||
const members = useRoomMembers(mx, room.roomId);
|
||||
const crossSigningActive = useCrossSigningActive();
|
||||
const showEncryption = room.hasEncryptionStateEvent() && crossSigningActive;
|
||||
const fetchingMembers = members.length < room.getJoinedMemberCount();
|
||||
const openProfile = useOpenUserRoomProfile();
|
||||
const profileUser = useUserRoomProfileState();
|
||||
@@ -328,11 +332,16 @@ export function Members({ requestClose }: MembersProps) {
|
||||
member={tagOrMember}
|
||||
useAuthentication={useAuthentication}
|
||||
after={
|
||||
server && (
|
||||
<Box as="span" shrink="No" alignSelf="End">
|
||||
<ServerBadge server={server} fill="None" />
|
||||
</Box>
|
||||
)
|
||||
<>
|
||||
{showEncryption && (
|
||||
<MemberVerificationBadge userId={tagOrMember.userId} />
|
||||
)}
|
||||
{server && (
|
||||
<Box as="span" shrink="No" alignSelf="End">
|
||||
<ServerBadge server={server} fill="None" />
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -60,7 +60,7 @@ import { ContainerColor } from '../../styles/ContainerColor.css';
|
||||
import { useFlattenPowerTagMembers, useGetMemberPowerTag } from '../../hooks/useMemberPowerTag';
|
||||
import { useRoomCreators } from '../../hooks/useRoomCreators';
|
||||
import { useCrossSigningActive } from '../../hooks/useCrossSigning';
|
||||
import { useUserVerifiedStatus } from '../../hooks/useUserVerifiedStatus';
|
||||
import { MemberVerificationBadge } from '../../components/MemberVerificationBadge';
|
||||
|
||||
type MemberDrawerHeaderProps = {
|
||||
room: Room;
|
||||
@@ -115,34 +115,6 @@ type MemberItemProps = {
|
||||
showEncryption?: boolean;
|
||||
};
|
||||
|
||||
function MemberVerificationBadge({ userId }: { userId: string }) {
|
||||
const vs = useUserVerifiedStatus(userId);
|
||||
if (vs === 'unknown') return null;
|
||||
const color =
|
||||
vs === 'verified' ? 'var(--tc-positive-normal, #5effc4)' : 'var(--tc-warning-normal, #ffcc55)';
|
||||
const label = vs === 'verified' ? 'Identity verified' : 'Not verified';
|
||||
return (
|
||||
<TooltipProvider
|
||||
position="Top"
|
||||
tooltip={
|
||||
<Tooltip>
|
||||
<Text size="T200">{label}</Text>
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
{(ref) => (
|
||||
<span
|
||||
ref={ref}
|
||||
title={label}
|
||||
style={{ display: 'inline-flex', alignItems: 'center', flexShrink: 0 }}
|
||||
>
|
||||
<Icon size="50" src={Icons.ShieldUser} style={{ color }} />
|
||||
</span>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function MemberItem({
|
||||
mx,
|
||||
useAuthentication,
|
||||
|
||||
Reference in New Issue
Block a user