diff --git a/src/components/footer/index.js b/src/components/footer/index.js index 57e22b2fc..1ecfbe333 100644 --- a/src/components/footer/index.js +++ b/src/components/footer/index.js @@ -61,7 +61,7 @@ function Footer() {

{t('Item Data')}

{t( - 'Fresh EFT data and player name searching courtesy of', + 'Fresh EFT data courtesy of', )}{' '} Tarkov-Changes diff --git a/src/pages/player/index.css b/src/pages/player/index.css index bb7dd2686..f244a3f54 100644 --- a/src/pages/player/index.css +++ b/src/pages/player/index.css @@ -50,3 +50,7 @@ ul.favorite-item-list li { .legendary { color: #ffe084; } + +.banned { + color: #cd1e2f; +} diff --git a/src/pages/player/index.js b/src/pages/player/index.js index defb08bf9..213b8a077 100644 --- a/src/pages/player/index.js +++ b/src/pages/player/index.js @@ -12,7 +12,8 @@ import { mdiBagPersonal, mdiArmFlex, mdiStarBox, - mdiTrophyAward + mdiTrophyAward, + mdiAccountSearch, } from '@mdi/js'; import { TreeView, TreeItem } from '@mui/x-tree-view'; @@ -114,12 +115,18 @@ function Player() { } return; } catch (error) { + if (error.message.includes('NetworkError')) { + setProfileError('Rate limited exceeded. Wait one minute to send another request.'); + } console.log('Error retrieving player profile', error); } } try { const response = await fetch('https://player.tarkov.dev/account/'+accountId); if (response.status !== 200) { + if (response.status === 429) { + setProfileError(`Rate limited exceeded. Wait ${response.headers.get('Retry-After')} seconds to send another request`); + } return; } const profileResponse = await response.json(); @@ -127,8 +134,14 @@ function Player() { setProfileError(profileResponse.errmsg); return; } + if (response.status !== 200) { + return; + } setPlayerData(profileResponse); } catch (error) { + if (error.message.includes('NetworkError')) { + setProfileError('Rate limited exceeded. Wait one minute to send another request.'); + } console.log('Error retrieving player profile', error); } } @@ -165,6 +178,16 @@ function Player() { }); }, [playerData, playerLevel, t]); + const bannedMessage = useMemo(() => { + if (!playerData?.info?.bannedState) { + return false; + } + if (playerData.info.bannedUntil < 0) { + return t('Banned Permanently'); + } + return t('Banned until {{banLiftDate}}', {banLiftDate: new Date(playerData.info.bannedUntil * 1000).toLocaleString()}); + }, [playerData, t]); + const achievementColumns = useMemo( () => [ { @@ -645,6 +668,23 @@ function Player() { ]) }, [playerData, getItemDisplay, getLoadoutContents, t]); + const playerSearchDiv = ( +

+

+ {t('Search different player')} +

+
+ ); + + if (profileError) { + return ( +
+

{profileError}

+ {playerSearchDiv} +
+ ); + } + return [ ,
+ {playerSearchDiv}

@@ -659,14 +700,11 @@ function Player() {

- {profileError && ( -

{profileError}

- )} {playerData.info.registrationDate !== 0 && (

{`${t('Started current wipe')}: ${new Date(playerData.info.registrationDate * 1000).toLocaleString()}`}

)} - {playerData.info.bannedState && ( -

{t('Banned')}

+ {!!bannedMessage && ( +

{bannedMessage}

)} {totalSecondsInGame > 0 && (

{`${t('Total account time in game')}: ${(() => { diff --git a/src/pages/players/index.js b/src/pages/players/index.js index 9867f2f8a..bb49fe89c 100644 --- a/src/pages/players/index.js +++ b/src/pages/players/index.js @@ -1,10 +1,12 @@ -import { useState, useCallback, useMemo } from 'react'; +import { useState, useCallback, useMemo, useEffect } from 'react'; import { Link } from 'react-router-dom'; import { Trans, useTranslation } from 'react-i18next'; import { Icon } from '@mdi/react'; import { mdiAccountSearch } from '@mdi/js'; +import useKeyPress from '../../hooks/useKeyPress.jsx'; + import SEO from '../../components/SEO.jsx'; import { InputFilter } from '../../components/filter/index.js'; @@ -13,11 +15,14 @@ import './index.css'; function Players() { const { t } = useTranslation(); + const enterPress = useKeyPress('Enter'); + const defaultQuery = new URLSearchParams(window.location.search).get( 'search', ); const [nameFilter, setNameFilter] = useState(defaultQuery || ''); const [nameResults, setNameResults] = useState([]); + const [nameResultsError, setNameResultsError] = useState(false); const [isButtonDisabled, setButtonDisabled] = useState(true); const [searched, setSearched] = useState(false); @@ -27,25 +32,40 @@ function Players() { return; } try { + setNameResultsError(false); setButtonDisabled(true); const response = await fetch('https://player.tarkov.dev/name/'+nameFilter); if (response.status !== 200) { - return; + let errorMessage = await response.text(); + try { + const json = JSON.parse(errorMessage); + errorMessage = json.errmsg; + } catch {} + throw new Error(errorMessage); } - setButtonDisabled(false); setSearched(true); setNameResults(await response.json()); } catch (error) { - setNameResults(['Error searching player profile: ' + error]); + let message = error.message; + if (message.includes('NetworkError')) { + message = 'Rate limited exceeded. Wait one minute to send another request.'; + } + if (message.includes('Malformed')) { + message = 'Error searching player profile; try removing one character from the end until the search works.' + } + setSearched(false); + setNameResults([]); + setNameResultsError(message); } - }, [nameFilter, setNameResults]); + setButtonDisabled(false); + }, [nameFilter, setNameResults, setNameResultsError]); const searchResults = useMemo(() => { if (!searched) { return ''; } if (nameResults.length < 1) { - return 'No players with this name'; + return 'No players with this name. Note: banned players do not show up in name searches.'; } let morePlayers = ''; if (nameResults.length >= 5) { @@ -71,6 +91,12 @@ function Players() { searchForName(); } + useEffect(() => { + if (enterPress) { + searchForName(); + } + }, [enterPress, searchForName]); + return [

- {searchResults} + {!!nameResultsError && ( +
+

{`${t('Search error')}: ${nameResultsError}`}

+
+ )} + {!nameResultsError && searchResults}
, ]; }