nitro-v36-with-renderer-v36
/
src /
components /
inventory /
views /
furniture /
InventoryTradeNameScamWarningView.tsx
InventoryTradeNameScamWarningView.tsx 6.6 Ko
Télécharger le fichier
import { IRoomUserData, RoomObjectType } from '@octane/renderer';
import { FC, useEffect, useState } from 'react';
import { findLookalikeNames, GetUserProfile, localizeWithFallback, MessengerFriend } from '../../../../api';
import { LayoutAvatarImageView, OctaneCardHeaderView, OctaneCardView } from '../../../../common';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';
import { useFriendsState, useInventoryTrade, useRoomUserListSnapshot } from '../../../../hooks';
import { OctaneButton } from '../../../../layout';
/** Seconds the warning stays up before it can be closed (official TradingNameScamWarningView). */
const CLOSE_LOCK_SECONDS = 6;
interface NameScamWarning {
userId: number;
userName: string;
figure: string;
similarInRoom: string[];
similarInFriends: string[];
}
const findNameScamWarning = (
userId: number,
userName: string,
roomUsers: ReadonlyArray<IRoomUserData>,
friends: ReadonlyArray<MessengerFriend>
): NameScamWarning => {
if (!userId || !userName) return null;
const roomNames = roomUsers.filter((user) => user.type === RoomObjectType.USER && user.webID !== userId).map((user) => user.name);
const friendNames = friends.filter((friend) => friend.id !== userId).map((friend) => friend.name);
const similarInRoom = findLookalikeNames(userName, roomNames);
const similarInFriends = findLookalikeNames(userName, friendNames);
if (!similarInRoom.length && !similarInFriends.length) return null;
const figure = roomUsers.find((user) => user.webID === userId)?.figure ?? '';
return { userId, userName, figure, similarInRoom, similarInFriends };
};
/** Warns when the trade partner's name could pass for someone in the room or a friend ("B0b" for "Bob"). */
export const InventoryTradeNameScamWarningView: FC<{}> = () => {
const [warning, setWarning] = useState<NameScamWarning>(null);
const [lockSeconds, setLockSeconds] = useState(0);
const { isTrading = false, otherUser = null } = useInventoryTrade();
const { friends = [] } = useFriendsState();
const roomUsers = useRoomUserListSnapshot();
const otherUserId = isTrading ? (otherUser?.userId ?? 0) : 0;
const [checkedUserId, setCheckedUserId] = useState(0);
// Checked once when a trade opens, with the room and friends as they are at that moment.
if (otherUserId !== checkedUserId) {
const found = findNameScamWarning(otherUserId, otherUser?.userName, roomUsers, friends);
setCheckedUserId(otherUserId);
setWarning(found);
setLockSeconds(found ? CLOSE_LOCK_SECONDS : 0);
}
useEffect(() => {
if (lockSeconds <= 0) return;
const timeout = setTimeout(() => setLockSeconds((seconds) => seconds - 1), 1000);
return () => clearTimeout(timeout);
}, [lockSeconds]);
if (!warning) return null;
const close = () => {
if (lockSeconds <= 0) setWarning(null);
};
return (
<NativeTextTree>
<OctaneCardView className="min-w-0 w-[min(356px,calc(100vw-16px))] max-w-[calc(100vw-16px)] max-h-[calc(100vh-16px)]" uniqueKey="trade-name-scam">
<OctaneCardHeaderView headerText={localizeWithFallback('inventory.trading.namescam.title', 'Check who you are trading with')} onCloseClick={close} />
<div className="octane-trade-name-scam-body flex flex-col gap-2 bg-[#DFDFDF] p-2">
<p className="m-0">
{localizeWithFallback(
'inventory.trading.namescam.warning',
`The name ${warning.userName} looks a lot like someone you know. Make sure this is the person you meant to trade with.`,
['trader_name'],
[warning.userName]
)}
</p>
<div className="flex items-center gap-2 rounded bg-white p-1">
<div className="h-[64px] w-[48px] shrink-0 overflow-hidden">
{warning.figure && <LayoutAvatarImageView direction={2} figure={warning.figure} headOnly />}
</div>
<div className="flex min-w-0 flex-1 flex-col">
<span className="octane-trade-name-scam-muted">{localizeWithFallback('inventory.trading.namescam.trader', 'Trading with')}</span>
<span className="octane-trade-name-scam-trader truncate">{warning.userName}</span>
</div>
<OctaneButton className="octane-trade-button shrink-0" onClick={() => GetUserProfile(warning.userId)}>
{localizeWithFallback('inventory.trading.namescam.open_profile', 'Profile')}
</OctaneButton>
</div>
{warning.similarInRoom.length > 0 && (
<div>
<span className="font-bold">{localizeWithFallback('inventory.trading.namescam.similar_in_room', 'Similar names in this room')}</span>
<ul className="m-0 pl-4">
{warning.similarInRoom.map((name) => (
<li key={name}>{name}</li>
))}
</ul>
</div>
)}
{warning.similarInFriends.length > 0 && (
<div>
<span className="font-bold">{localizeWithFallback('inventory.trading.namescam.similar_in_friends', 'Similar names in your friends')}</span>
<ul className="m-0 pl-4">
{warning.similarInFriends.map((name) => (
<li key={name}>{name}</li>
))}
</ul>
</div>
)}
<div className="flex items-center justify-end gap-2">
{lockSeconds > 0 && (
<span className="octane-trade-name-scam-muted">
{localizeWithFallback('inventory.trading.namescam.close_countdown', `You can close this in ${lockSeconds} s`, ['seconds'], [String(lockSeconds)])}
</span>
)}
<OctaneButton className="octane-trade-button" disabled={lockSeconds > 0} onClick={close}>
{localizeWithFallback('inventory.trading.namescam.close', 'OK')}
</OctaneButton>
</div>
</div>
</OctaneCardView>
</NativeTextTree>
);
};
