feat: trusted bots badge & fix verified account checker

pull/27073/head
Badi Ifaoui 2024-01-25 08:32:00 +01:00
parent a8f96c5f51
commit 615ce96d58
7 changed files with 89 additions and 14 deletions

View File

@ -11,6 +11,7 @@ export type BareUser = {
};
export const verifiedAccountsAtom = atomWithStorage<Record<string, string>>("VERIFIED_ACCOUNTS", {});
export const verifiedBotsAtom = atomWithStorage<Record<string, string>>("VERIFIED_BOTS", {});
export const minimumTokenThresholdAtom = atomWithStorage<Record<string, TokenThreshold>>("TOKEN_THRESHOLD", {});
export const communityBotAtom = atomWithStorage<BareUser>("COMMUNITY_BOT", {
userId: "",

View File

@ -81,6 +81,7 @@ import { SdkContextClass } from "matrix-react-sdk/src/contexts/SDKContext";
import { UserProfilesStore } from "matrix-react-sdk/src/stores/UserProfilesStore";
import { UserVerifiedBadge } from "../elements/UserVerifiedBadge";
import { BotVerifiedBadge } from "../elements/BotVerifiedBadge";
// we have a number of types defined from the Matrix spec which can't reasonably be altered here.
/* eslint-disable camelcase */
@ -288,6 +289,7 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
<div className="mx_InviteDialog_tile_nameStack_name">
{this.highlightName(this.props.member.name)}
<UserVerifiedBadge userId={this.props.member.userId} />
<BotVerifiedBadge userId={this.props.member.userId} />
</div>
<div className="mx_InviteDialog_tile_nameStack_userId">{caption}</div>
</span>

View File

@ -0,0 +1,31 @@
import React from "react";
import { useVerifiedBot } from "../../../hooks/useVerifiedBot";
export interface UserVerifiedBadgeProps {
userId: string;
}
export const BotVerifiedBadge = ({ userId }: UserVerifiedBadgeProps): JSX.Element => {
const isVerifiedBot = useVerifiedBot(userId);
return (
<>
{isVerifiedBot && (
<div
style={{
color: "rgba(30, 203, 172, 1)",
fontWeight: 700,
fontSize: "10px",
backgroundColor: "rgba(30, 203, 172, 0.2)",
padding: "2px 4px",
borderRadius: "15px",
marginLeft: "15px",
}}
>
Trusted Bot
</div>
)}
</>
);
};

View File

@ -22,6 +22,7 @@ import { Icon as CommunityRoomIcon } from "../../../../res/themes/superhero/img/
import { useRoomName } from "../../../hooks/useRoomName";
import { useVerifiedRoom } from "../../../hooks/useVerifiedRoom";
import { UserVerifiedBadge } from "./UserVerifiedBadge";
import { BotVerifiedBadge } from "./BotVerifiedBadge";
interface IProps {
room?: Room | IPublicRoomsChunkRoom;
@ -33,16 +34,18 @@ export const RoomName = ({ room, children, maxLength }: IProps): JSX.Element =>
const roomName = useRoomName(room);
const { isTokenGatedRoom, isCommunityRoom } = useVerifiedRoom(room);
const roomUsers: string[] = useMemo(() => {
if ((room as Room).getJoinedMemberCount?.() > 2 || (room as IPublicRoomsChunkRoom).num_joined_members > 2) {
return [];
const roomUser: string | undefined = useMemo(() => {
// check if this is a DM room and if so, return the other user's ID
const dmUserId = (room as Room)?.guessDMUserId?.();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// need to access the private summaryHeroes property, to know if it's a DM room
if (!(room as Room)?.summaryHeroes) {
return undefined;
}
return (
(room as Room)
?.getMembers?.()
.map((m: { userId: string }) => m.userId)
.filter((userId: string) => !!userId && userId !== (room as Room)?.myUserId) || []
);
return dmUserId && dmUserId !== (room as Room).myUserId ? dmUserId : undefined;
}, [room]);
const truncatedRoomName = useMemo(() => {
@ -63,12 +66,11 @@ export const RoomName = ({ room, children, maxLength }: IProps): JSX.Element =>
)}
{isTokenGatedRoom && <TokenGatedRoomIcon className="sh_RoomTokenGatedRoomIcon" />}
<span dir="auto">{truncatedRoomName}</span>
{roomUsers?.length && !isTokenGatedRoom && !isCommunityRoom ? (
<UserVerifiedBadge userId={roomUsers[0]} />
) : null}
{roomUser && !isTokenGatedRoom && !isCommunityRoom ? <UserVerifiedBadge userId={roomUser} /> : null}
{roomUser && !isTokenGatedRoom && !isCommunityRoom ? <BotVerifiedBadge userId={roomUser} /> : null}
</span>
),
[truncatedRoomName, isCommunityRoom, isTokenGatedRoom, roomUsers],
[truncatedRoomName, isCommunityRoom, isTokenGatedRoom, roomUser],
);
if (children) return children(renderRoomName());

View File

@ -84,6 +84,7 @@ import UIStore from "matrix-react-sdk/src/stores/UIStore";
import { UserVerifiedBadge } from "../elements/UserVerifiedBadge";
import { MessageButton } from "../elements/MessageButton";
import { BotVerifiedBadge } from "../elements/BotVerifiedBadge";
export interface IDevice extends Device {
ambiguous?: boolean;
@ -1617,6 +1618,7 @@ export const UserInfoHeader: React.FC<{
<div>
<h2>
<UserVerifiedBadge userId={member.userId} />
<BotVerifiedBadge userId={member.userId} />
<span title={displayName} aria-label={displayName} dir="auto">
{displayName}
</span>

View File

@ -1,7 +1,7 @@
import { useAtom } from "jotai";
import React, { useCallback, useEffect } from "react";
import { communityBotAtom, minimumTokenThresholdAtom, verifiedAccountsAtom } from "../atoms";
import { communityBotAtom, minimumTokenThresholdAtom, verifiedAccountsAtom, verifiedBotsAtom } from "../atoms";
const useMinimumTokenThreshold = (config: any): void => {
const [, setMinimumTokenThreshold] = useAtom(minimumTokenThresholdAtom);
@ -43,6 +43,7 @@ const useMinimumTokenThreshold = (config: any): void => {
*/
export const SuperheroProvider = ({ children, config }: any): any => {
const [verifiedAccounts, setVerifiedAccounts] = useAtom(verifiedAccountsAtom);
const [, setVerifiedBots] = useAtom(verifiedBotsAtom);
const [, setCommunityBot] = useAtom(communityBotAtom);
useEffect(() => {
@ -65,7 +66,24 @@ export const SuperheroProvider = ({ children, config }: any): any => {
}
}
function loadVerifiedBots(): void {
if (config.bots_backend_url) {
fetch(`${config.bots_backend_url}/ui/get-verified-bots`, {
method: "POST",
})
.then((res) => res.json())
.then(setVerifiedBots)
.catch(() => {
setVerifiedBots({
"@walletbot:superhero.chat": "true",
"@communitybot:superhero.chat": "true",
});
});
}
}
useEffect(() => {
loadVerifiedBots();
if (!verifiedAccounts?.length) {
loadVerifiedAccounts();
}

View File

@ -0,0 +1,19 @@
import { useMemo } from "react";
import { useAtom } from "jotai";
import { verifiedBotsAtom } from "../atoms";
/**
* Custom hook to check if a bot is verified.
* @param botId - The ID of the bot to check.
* @returns A boolean indicating whether the bot is verified or not.
*/
export function useVerifiedBot(botId?: string): boolean {
const [verifiedBots] = useAtom(verifiedBotsAtom);
const isVerifiedBot: boolean = useMemo(() => {
return !!(botId && !!verifiedBots[botId]);
}, [botId, verifiedBots]);
return isVerifiedBot;
}