diff --git a/src/components/item-image/index.js b/src/components/item-image/index.js index 1c358465e..69318faca 100644 --- a/src/components/item-image/index.js +++ b/src/components/item-image/index.js @@ -1,32 +1,33 @@ import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react'; -import {renderToStaticMarkup} from "react-dom/server"; -import { Link, useNavigate } from "react-router-dom"; +import { renderToStaticMarkup } from 'react-dom/server'; +import { Link, useNavigate } from 'react-router-dom'; import ImageViewer from 'react-simple-image-viewer'; import { useTranslation } from 'react-i18next'; import Tippy from '@tippyjs/react'; import 'tippy.js/dist/tippy.css'; import ResizeObserver from 'resize-observer-polyfill'; +import useResponsiveScaling from '../../hooks/useResponsiveScaling.jsx'; import './index.css'; const colors = { - black: {r: 0, g: 0, b: 0, alpha: 77/255}, - blue: {r: 28, g: 65, b: 86, alpha: 77/255}, - default: {r: 127, g: 127, b: 127, alpha: 77/255}, - green: {r: 21, g: 45, b: 0, alpha: 77/255}, - grey: {r: 29, g: 29, b: 29, alpha: 77/255}, - orange: {r: 60, g: 25, b: 0, alpha: 77/255}, - red: {r: 109, g: 36, b: 24, alpha: 77/255}, - violet: {r: 76, g: 42, b: 85, alpha: 77/255}, - yellow: {r: 104, g: 102, b: 40, alpha: 77/255}, + black: { r: 0, g: 0, b: 0, alpha: 77 / 255 }, + blue: { r: 28, g: 65, b: 86, alpha: 77 / 255 }, + default: { r: 127, g: 127, b: 127, alpha: 77 / 255 }, + green: { r: 21, g: 45, b: 0, alpha: 77 / 255 }, + grey: { r: 29, g: 29, b: 29, alpha: 77 / 255 }, + orange: { r: 60, g: 25, b: 0, alpha: 77 / 255 }, + red: { r: 109, g: 36, b: 24, alpha: 77 / 255 }, + violet: { r: 76, g: 42, b: 85, alpha: 77 / 255 }, + yellow: { r: 104, g: 102, b: 40, alpha: 77 / 255 }, }; -function ItemImage({ - item, - backgroundScale = 1, - imageField = 'baseImageLink', - nonFunctionalOverlay = false, - imageViewer = false, +function ItemImage({ + item, + backgroundScale = 1, + imageField = 'baseImageLink', + nonFunctionalOverlay = false, + imageViewer = false, children = '', attributes = [], count, @@ -57,7 +58,8 @@ function ItemImage({ }, []);*/ const refImage = useRef(); - const [imageDimensions, setImageDimensions] = useState({ width: 0, height: 0}); + const scaler = useResponsiveScaling(); + const [imageDimensions, setImageDimensions] = useState({ width: 0, height: 0 }); useEffect(() => { if (!refImage.current) { return; @@ -66,7 +68,10 @@ function ItemImage({ if (!refImage.current) { return; } - if (refImage.current.width === imageDimensions.width && refImage.current.height === imageDimensions.height) { + if ( + refImage.current.width === imageDimensions.width && + refImage.current.height === imageDimensions.height + ) { return; } setImageDimensions({ @@ -74,7 +79,7 @@ function ItemImage({ height: refImage.current.height, }); }); - const intersectionObserver = new IntersectionObserver(entries => { + const intersectionObserver = new IntersectionObserver((entries) => { if (!refImage.current) { return; } @@ -95,13 +100,11 @@ function ItemImage({ return <>; } const loadingStyle = { - WebkitMask:`url(${item[imageField]}) center/cover`, - mask:`url(${item[imageField]}) center/cover`, + WebkitMask: `url(${item[imageField]}) center/cover`, + mask: `url(${item[imageField]}) center/cover`, }; - - return ( -
- ); + + return
; }, [item, imageField]); const [isViewerOpen, setIsViewerOpen] = useState(false); @@ -110,7 +113,7 @@ function ItemImage({ return; } setIsViewerOpen(true); - }, [imageViewer]); + }, [imageViewer]); const closeImageViewer = () => { setIsViewerOpen(false); }; @@ -130,20 +133,32 @@ function ItemImage({ imageStyle.cursor = 'zoom-in'; } //console.log(dimensions); - const img = {item.name}; + if (imageField === 'inspectImageLink') { + imageStyle.maxWidth = `100%`; + imageStyle.maxHeight = `100%`; + } + + const img = ( + {item.name} + ); if (linkToItem && !item.types.includes('quest')) { - return - {img} - ; + return {img}; } return img; }, [item, refImage, imageField, openImageViewer, imageViewer, linkToItem]); const imageScale = useMemo(() => { - const w = imageDimensions.width || (item.width * 63) + 1 - return w / ((item.width * 63) + 1); + const w = imageDimensions.width || item.width * 63 + 1; + return w / (item.width * 63 + 1); }, [imageDimensions, item]); - + const textSize = useMemo(() => { return Math.min(12 * imageScale, 16); }, [imageScale]); @@ -158,32 +173,42 @@ function ItemImage({ }, [item]); const nonFunctionalElement = useMemo(() => { - if (!nonFunctionalOverlay || !item.types.includes('gun') || !item.properties?.defaultPreset) { + if ( + !nonFunctionalOverlay || + !item.types.includes('gun') || + !item.properties?.defaultPreset + ) { return <>; } const nonFunctionalStyle = { position: 'absolute', boxSizing: 'border-box', - top: `${1*backgroundScale}px`, - left: `${1*backgroundScale}px`, - height: `calc(100% - ${2*backgroundScale}px)`, - width: `calc(100% - ${2*backgroundScale}px)`, + top: `${1 * backgroundScale}px`, + left: `${1 * backgroundScale}px`, + height: `calc(100% - ${2 * backgroundScale}px)`, + width: `calc(100% - ${2 * backgroundScale}px)`, fallbacks: [ - {width: `-webkit-calc(100% - ${2*backgroundScale}px)`}, - {width: `-moz-calc(100% - ${2*backgroundScale}px)`}, - {height: `-webkit-calc(100% - ${2*backgroundScale}px)`}, - {height: `-moz-calc(100% - ${2*backgroundScale}px)`}, + { width: `-webkit-calc(100% - ${2 * backgroundScale}px)` }, + { width: `-moz-calc(100% - ${2 * backgroundScale}px)` }, + { height: `-webkit-calc(100% - ${2 * backgroundScale}px)` }, + { height: `-moz-calc(100% - ${2 * backgroundScale}px)` }, ], backgroundColor: '#4400004f', }; if (imageViewer) { nonFunctionalStyle.cursor = 'zoom-in'; } - return
; + return ( +
+ ); }, [item, nonFunctionalOverlay, backgroundScale, openImageViewer, imageViewer]); const toolOverride = useMemo(() => { - return isTool || attributes?.some(att => att.name === 'tool'); + return isTool || attributes?.some((att) => att.name === 'tool'); }, [attributes, isTool]); const borderColor = useMemo(() => { @@ -198,14 +223,12 @@ function ItemImage({ } return color; }, [item, toolOverride, nonFunctional]); - - const backgroundStyle = useMemo(() => { if (imageField === 'iconLink') { const iconStyle = { position: 'relative', maxHeight: `${imageDimensions.height || 64}px`, - maxWidth: `${imageDimensions.width || 64}px`, + maxWidth: `${imageDimensions.width || 64}px`, }; if (toolOverride || nonFunctional) { iconStyle.outline = `1px solid ${borderColor}`; @@ -215,55 +238,125 @@ function ItemImage({ } let sizeFactor = 1; if (imageField === 'image512pxLink') { - sizeFactor = 512 / ((item.width * 63) + 1); + sizeFactor = 512 / (item.width * 63 + 1); if (item.height > item.width) { - sizeFactor = 512 / ((item.height * 63) + 1); + sizeFactor = 512 / (item.height * 63 + 1); } } if (imageField === 'image8xLink') { sizeFactor = 8; } - let width = imageDimensions.width || (((item.width * 63) + 1) * sizeFactor); - let height = imageDimensions.height || (((item.height * 63) + 1) * sizeFactor); - const gridSvg = () => + if (imageField === 'inspectImageLink') { + const loadOutImgStyle = { + maxHeight: `${175 / scaler}px`, + maxWidth: `${256 / scaler}px`, + position: 'relative', + }; + return loadOutImgStyle; + } + let width = imageDimensions.width || (item.width * 63 + 1) * sizeFactor; + let height = imageDimensions.height || (item.height * 63 + 1) * sizeFactor; + const gridSvg = () => ( - - - - - + + + + + - - - - + + + + - - - ; + + + + ); const backgroundStyle = { backgroundImage: `url('data:image/svg+xml,${encodeURIComponent(renderToStaticMarkup(gridSvg()))}')`, backgroundSize: `${gridPercentX}% ${gridPercentY}%`, position: 'relative', - outline: `${1*backgroundScale}px solid ${borderColor}`, - outlineOffset: `-${1*backgroundScale}px`, + outline: `${1 * backgroundScale}px solid ${borderColor}`, + outlineOffset: `-${1 * backgroundScale}px`, maxHeight: `${height}px`, - maxWidth: `${width}px`, + maxWidth: `${width}px`, }; return backgroundStyle; - }, [backgroundScale, borderColor, colorString, imageField, gridPercentX, gridPercentY, item, imageDimensions, toolOverride, nonFunctional]); + }, [ + backgroundScale, + borderColor, + colorString, + imageField, + gridPercentX, + gridPercentY, + item, + imageDimensions, + toolOverride, + nonFunctional, + scaler, + ]); const imageTextStyle = useMemo(() => { if (imageField === 'iconLink' || item.types.includes('loading')) { - return {display: 'none'}; + return { display: 'none' }; } const style = { position: 'absolute', top: `${Math.min(backgroundScale + imageScale, 4)}px`, - right: `${Math.min(backgroundScale + (1.5*imageScale), 7)}px`, + right: `${Math.min(backgroundScale + 1.5 * imageScale, 7)}px`, cursor: 'default', color: '#a4aeb4', fontWeight: 'bold', @@ -274,8 +367,11 @@ function ItemImage({ if (linkToItem) { style.cursor = 'pointer'; } + if (imageField === 'inspectImageLink') { + style.fontSize = `${Math.min(26 / scaler, 20)}px`; + } return style; - }, [imageField, imageScale, textSize, backgroundScale, item, linkToItem]); + }, [imageField, imageScale, textSize, backgroundScale, item, linkToItem, scaler]); const imageTextClick = useMemo(() => { if (!linkToItem) { @@ -287,11 +383,13 @@ function ItemImage({ }, [item, linkToItem, navigate]); const imageText = useMemo(() => { - let element =
{item.shortName}
; - if (fullNameTooltip && imageTextStyle.dispolay !== 'none') { - element = - {element} - ; + let element = ( +
+ {item.shortName} +
+ ); + if (fullNameTooltip && imageTextStyle.display !== 'none') { + element = {element}; } return element; }, [fullNameTooltip, imageTextClick, imageTextStyle, item]); @@ -334,40 +432,52 @@ function ItemImage({ }, [imageScale, item]); return ( -
+
{loadingImage} {imageElement} {nonFunctionalElement} {imageText}
- {isFIR && - - } + {isFIR && ( + + + + )} {count && {count}}
- {trader &&
- - - {trader.name} - - -
} - {station &&
- - - {station.name} - - -
} + {trader && ( +
+ + + {trader.name} + + +
+ )} + {station && ( +
+ + + {station.name} + + +
+ )} {children} {isViewerOpen && ( { + const QueryBody = (offset) => { return `query TarkovDevItems { items(lang: ${language}, gameMode: ${gameMode}, limit: ${itemLimit}, offset: ${offset}) { id @@ -42,6 +42,7 @@ class ItemsQuery extends APIQuery { baseImageLink image512pxLink image8xLink + inspectImageLink updated sellFor { ...ItemPriceFragment @@ -411,13 +412,19 @@ class ItemsQuery extends APIQuery { if (itemBatch.data.items.length === 0) { break; } - retrievedItems.data.items = retrievedItems.data.items.concat(itemBatch.data.items); + retrievedItems.data.items = retrievedItems.data.items.concat( + itemBatch.data.items, + ); if (itemBatch.data.items.length < itemLimit) { break; } } if (!itemBatch.errors) { - if (!itemBatch.data || !itemBatch.data.items || !itemBatch.data.items.length) { + if ( + !itemBatch.data || + !itemBatch.data.items || + !itemBatch.data.items.length + ) { break; } } @@ -428,23 +435,27 @@ class ItemsQuery extends APIQuery { } resolve(retrievedItems); }), - this.graphqlRequest(`{ + this.graphqlRequest( + `{ fleaMarket { sellOfferFeeRate sellRequirementFeeRate } - }`.replace(/\s{2,}/g, ' ')), - new Promise(resolve => { + }`.replace(/\s{2,}/g, ' '), + ), + new Promise((resolve) => { if (prebuild) { return resolve({}); } - return resolve(fetch(`${process.env.PUBLIC_URL}/data/item-grids.min.json`).then( - (response) => response.json(), - )).catch(error => { + return resolve( + fetch(`${process.env.PUBLIC_URL}/data/item-grids.min.json`).then((response) => + response.json(), + ), + ).catch((error) => { console.log('Error retrieving item grids', error); return {}; }); - }) + }), ]); //console.timeEnd('items query'); if (itemData.errors) { @@ -463,15 +474,12 @@ class ItemsQuery extends APIQuery { } console.log(`Error in items API query: ${error.message}`, error.path); if (badItem) { - console.log(badItem) + console.log(badItem); } } } // only throw error if this is for prebuild or data wasn't returned - if ( - prebuild || !itemData.data || - !itemData.data.items || !itemData.data.items.length - ) { + if (prebuild || !itemData.data || !itemData.data.items || !itemData.data.items.length) { return Promise.reject(new Error(itemData.errors[0].message)); } } @@ -492,15 +500,12 @@ class ItemsQuery extends APIQuery { } console.log(`Error in items API query: ${error.message}`, error.path); if (badItem) { - console.log(badItem) + console.log(badItem); } } } // only throw error if this is for prebuild or data wasn't returned - if ( - prebuild || !miscData.data || - !miscData.data.fleaMarket - ) { + if (prebuild || !miscData.data || !miscData.data.fleaMarket) { return Promise.reject(new Error(miscData.errors[0].message)); } } @@ -516,14 +521,15 @@ class ItemsQuery extends APIQuery { let gridPockets = []; if (itemGrids[rawItem.id]) { gridPockets = itemGrids[rawItem.id]; - } - else if (rawItem.properties.grids.length === 1) { - gridPockets = [{ - row: 0, - col: 0, - width: rawItem.properties.grids[0].width, - height: rawItem.properties.grids[0].height, - }]; + } else if (rawItem.properties.grids.length === 1) { + gridPockets = [ + { + row: 0, + col: 0, + width: rawItem.properties.grids[0].width, + height: rawItem.properties.grids[0].height, + }, + ]; } if (gridPockets.length > 1) { @@ -533,12 +539,10 @@ class ItemsQuery extends APIQuery { grid.width = Math.max( ...gridPockets.map((pocket) => pocket.col + pocket.width), ); - } - else if (rawItem.properties.grids.length >= 1) { + } else if (rawItem.properties.grids.length >= 1) { grid.height = rawItem.properties.grids[0].height; grid.width = rawItem.properties.grids[0].width; - } - else { + } else { grid.height = rawItem.height; grid.width = rawItem.width; } @@ -547,20 +551,30 @@ class ItemsQuery extends APIQuery { rawItem.grid = grid; rawItem.properties = { - ...rawItem.properties + ...rawItem.properties, }; // calculate flea market fee - const fee = fleaMarketFee(rawItem.basePrice, rawItem.lastLowPrice, 1, flea.sellOfferFeeRate, flea.sellRequirementFeeRate); + const fee = fleaMarketFee( + rawItem.basePrice, + rawItem.lastLowPrice, + 1, + flea.sellOfferFeeRate, + flea.sellRequirementFeeRate, + ); rawItem.fee = fee; const container = rawItem.properties?.slots || rawItem.properties?.grids; if (container) { for (const slot of container) { - slot.filters.allowedCategories = slot.filters.allowedCategories.map(cat => cat.id); - slot.filters.allowedItems = slot.filters.allowedItems.map(it => it.id); - slot.filters.excludedCategories = slot.filters.excludedCategories.map(cat => cat.id); - slot.filters.excludedItems = slot.filters.excludedItems.map(it => it.id); + slot.filters.allowedCategories = slot.filters.allowedCategories.map( + (cat) => cat.id, + ); + slot.filters.allowedItems = slot.filters.allowedItems.map((it) => it.id); + slot.filters.excludedCategories = slot.filters.excludedCategories.map( + (cat) => cat.id, + ); + slot.filters.excludedItems = slot.filters.excludedItems.map((it) => it.id); } } diff --git a/src/hooks/useResponsiveScaling.jsx b/src/hooks/useResponsiveScaling.jsx new file mode 100644 index 000000000..6e85229bb --- /dev/null +++ b/src/hooks/useResponsiveScaling.jsx @@ -0,0 +1,25 @@ +import { useState, useEffect } from 'react'; + +const useResponsiveScaling = () => { + const [scaler, setScaler] = useState(1); + useEffect(() => { + const adjustScaler = () => { + const width = window.innerWidth; + if (width >= 1000) { + setScaler(1); + } else if (width <= 500) { + setScaler(2); + } else setScaler(1.5); + }; + + adjustScaler(); + + window.addEventListener('resize', adjustScaler, false); + return () => { + window.removeEventListener('resize', adjustScaler); + }; + }, []); + return scaler; +}; + +export default useResponsiveScaling; diff --git a/src/pages/player/index.css b/src/pages/player/index.css index 32335702e..3fef70381 100644 --- a/src/pages/player/index.css +++ b/src/pages/player/index.css @@ -1,41 +1,82 @@ +.css-53rj3a-MuiTreeItem-content { + justify-content: center; +} + .MuiTreeItem-iconContainer { width: auto !important; justify-content: normal !important; margin-right: 0px !important; + align-self: center; +} + +.MuiTreeItem-label { + width: auto !important; + font-size: 16px !important; + font-weight: bold !important; + word-wrap: break-word; } .inventory { - display: flow-root; + display: flex; + gap: 10px; + overflow-y: hidden; + overflow-x: scroll; + justify-content: center; + padding-bottom: 12px; } .grid-container { display: grid; - float: left; gap: 10px; } .grid-container.main { - grid-template-columns: auto auto auto; + grid-template-columns: repeat(3, minmax(280px, auto)); + grid-template-rows: repeat(4, minmax(183px, auto)); } .grid-container.side { - margin-left: 10px; - grid-template-columns: auto; + grid-template-columns: minmax(280px, auto); + grid-template-rows: minmax(183px, auto) 32px repeat(2, minmax(183px, auto)); + align-self: flex-start; } -.grid-container>div { - background-color: rgba(0, 0, 0, 0.4); +.grid-container > div { + background-color: rgba(0, 0, 0); text-align: center; font-size: 20px; + clip-path: polygon(0 0, 0 100%, 100% 100%, 100% 24px, 96% 0); + font-size: 16px; + font-weight: bold; +} + +.gear-slot-label { + background-image: linear-gradient(#474747, #000); + background-size: 101% 101%; + color: #fff; + padding: 0 0 5px 5px; + width: 100%; + text-align: left; + font-size: 16px; + font-weight: bold; } .weapon { grid-column: 1 / span 2; + justify-items: center; + align-items: center; + clip-path: polygon(0 0, 0 100%, 100% 100%, 100% 24px, 98% 0) !important; +} + +.pockets_and_special_slots { + clip-path: none !important; } ul.favorite-item-list { list-style: none; padding: 0px; + display: flex; + flex-wrap: wrap; } ul.favorite-item-list li { @@ -51,7 +92,8 @@ ul.favorite-item-list li { color: #ffe084; } -.banned, .not-banned { +.banned, +.not-banned { margin-left: 1em; } @@ -68,7 +110,7 @@ ul.favorite-item-list li { } .profile-button { - padding: .2rem; + padding: 0.2rem; border-radius: 4px; vertical-align: middle; margin-left: 20px; @@ -77,3 +119,40 @@ ul.favorite-item-list li { .current-wipe-achievement { font-weight: bold; } + +@media screen and (max-width: 1000px) { + .grid-container.main { + grid-template-columns: repeat(3, minmax(187.7px, auto)); + grid-template-rows: repeat(4, minmax(122px, auto)); + } + + .grid-container.side { + grid-template-columns: minmax(187.7px, auto); + grid-template-rows: minmax(122px, auto) 32px repeat(2, minmax(122px, auto)); + } + + .inventory { + justify-content: flex-start; + } + + .gear-slot-label { + padding-bottom: 1px; + font-size: 14px; + } +} + +@media screen and (max-width: 500px) { + .grid-container.main { + grid-template-columns: repeat(3, minmax(140px, auto)); + grid-template-rows: repeat(4, minmax(91.5px, auto)); + } + + .grid-container.side { + grid-template-columns: minmax(140px, auto); + grid-template-rows: minmax(91.5px, auto) 32px repeat(2, minmax(91.5px, auto)); + } + + .gear-slot-label { + font-size: 12px; + } +} diff --git a/src/pages/player/index.js b/src/pages/player/index.js index 7a19c8091..d634bd36b 100644 --- a/src/pages/player/index.js +++ b/src/pages/player/index.js @@ -1,7 +1,7 @@ import { useState, useMemo, useEffect, useCallback, useRef } from 'react'; import { useParams, Link, useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { Turnstile } from '@marsidev/react-turnstile' +//import { Turnstile } from '@marsidev/react-turnstile'; --------------------COMMENTED OUT CLOUDFLARE IS ANNOYING DURING DEV ISSUE #1000--------------------- import { Icon } from '@mdi/react'; import { mdiAccountDetails, @@ -39,30 +39,30 @@ function getDHMS(seconds) { // calculate (and subtract) whole days const secondsPerDay = 24 * 60 * 60; const days = Math.floor(seconds / secondsPerDay); - seconds -= (days * secondsPerDay); + seconds -= days * secondsPerDay; // calculate (and subtract) whole hours const secondsPerHour = secondsPerDay / 24; const hours = Math.floor(seconds / secondsPerHour) % 24; - seconds -= (hours * secondsPerHour); + seconds -= hours * secondsPerHour; // calculate (and subtract) whole minutes const minutes = Math.floor(seconds / 60) % 60; - seconds -= (minutes * 60); + seconds -= minutes * 60; return { days, hours, minutes, seconds, - } + }; } const raritySort = { - "common": 0, - "rare": 1, - "legendary": 2 -} + common: 0, + rare: 1, + legendary: 2, +}; const memberFlags = { Developer: 1, @@ -71,11 +71,11 @@ const memberFlags = { Sherpa: 256, Emissary: 512, Unheard: 1024, -} +}; function Player() { const turnstileRef = useRef(); - const inputFile = useRef() + const inputFile = useRef(); const { t } = useTranslation(); const params = useParams(); const navigate = useNavigate(); @@ -112,7 +112,7 @@ function Player() { const { data: metaData } = useMetaData(); const { data: achievements } = useAchievementsData(); const [turnstileToken, setTurnstileToken] = useState(); - const [ playerBanned, setPlayerBanned ] = useState(); + const [playerBanned, setPlayerBanned] = useState(); const bannedButtonRef = useRef(); const fetchProfile = useCallback(async () => { @@ -125,7 +125,11 @@ function Player() { } if (isNaN(accountId)) { try { - const searchResponse = await playerStats.searchPlayers(accountId, gameMode, turnstileToken); + const searchResponse = await playerStats.searchPlayers( + accountId, + gameMode, + turnstileToken, + ); if (turnstileRef.current?.reset) { turnstileRef.current.reset(); } @@ -149,7 +153,15 @@ function Player() { } catch (error) { setProfileError(error.message); } - }, [accountId, setPlayerData, setProfileError, navigate, turnstileToken, turnstileRef, gameMode]); + }, [ + accountId, + setPlayerData, + setProfileError, + navigate, + turnstileToken, + turnstileRef, + gameMode, + ]); const checkBanned = useCallback(async () => { const token = turnstileRef?.current?.getResponse(); @@ -160,7 +172,11 @@ function Player() { return; } try { - const searchResponse = await playerStats.searchPlayers(playerData.info.nickname, gameMode, turnstileToken); + const searchResponse = await playerStats.searchPlayers( + playerData.info.nickname, + gameMode, + turnstileToken, + ); if (turnstileRef.current?.reset) { turnstileRef.current.reset(); } @@ -172,7 +188,9 @@ function Player() { } setPlayerBanned(true); } catch (error) { - console.log(`Error checking banned status for ${playerData.info.nickname}: ${error.message}`); + console.log( + `Error checking banned status for ${playerData.info.nickname}: ${error.message}`, + ); } return false; }, [playerData, setPlayerBanned, turnstileToken, turnstileRef, gameMode]); @@ -182,29 +200,32 @@ function Player() { return; } const element = document.createElement('a'); - const file = new Blob([JSON.stringify(playerData, null, 4)], {type: 'application/json'}); + const file = new Blob([JSON.stringify(playerData, null, 4)], { type: 'application/json' }); element.href = URL.createObjectURL(file); element.download = `${playerData.aid}.json`; document.body.appendChild(element); // Required for this to work in FireFox element.click(); }, [playerData]); - const loadProfile = useCallback((e) => { - e.preventDefault() - const reader = new FileReader(); - reader.onload = async (e) => { - const text = (e.target.result); - try { - const data = JSON.parse(text); - data.saved = true; - setPlayerData(data); - window.history.replaceState(null, null, `/players/${gameMode}/${data.aid}`); - } catch(error) { - setProfileError('Error reading profile'); - } - }; - reader.readAsText(e.target.files[0]); - }, [setPlayerData, setProfileError, gameMode]); + const loadProfile = useCallback( + (e) => { + e.preventDefault(); + const reader = new FileReader(); + reader.onload = async (e) => { + const text = e.target.result; + try { + const data = JSON.parse(text); + data.saved = true; + setPlayerData(data); + window.history.replaceState(null, null, `/players/${gameMode}/${data.aid}`); + } catch (error) { + setProfileError('Error reading profile'); + } + }; + reader.readAsText(e.target.files[0]); + }, + [setPlayerData, setProfileError, gameMode], + ); const currentWipe = wipeDetails()[0]; @@ -222,7 +243,6 @@ function Player() { if (expTotal > playerData.info.experience) { return metaData.playerLevels[i - 1].level; } - } return metaData.playerLevels[metaData.playerLevels.length - 1].level; }, [playerData, metaData]); @@ -270,24 +290,26 @@ function Player() { if (latest === 0) { return ''; } - return
{t('Last active: {{date}}', {date: new Date(latest * 1000).toLocaleString()})}
; + return ( +
+ {t('Last active: {{date}}', { date: new Date(latest * 1000).toLocaleString() })} +
+ ); }, [playerData, t]); const achievementColumns = useMemo( () => [ { Header: () => ( -
- {t('Name')} -
), +
{t('Name')}
+ ), id: 'name', accessor: 'name', }, { Header: () => ( -
- {t('Description')} -
), +
{t('Description')}
+ ), id: 'description', accessor: 'description', }, @@ -296,11 +318,7 @@ function Player() { id: 'playersCompletedPercent', accessor: 'adjustedPlayersCompletedPercent', Cell: (props) => { - return ( -
- {props.value}% -
- ); + return
{props.value}%
; }, }, { @@ -309,7 +327,9 @@ function Player() { accessor: 'completionDate', Cell: (props) => { return ( -
currentWipe.start ? ' current-wipe-achievement' : ''}`}> +
currentWipe.start ? ' current-wipe-achievement' : ''}`} + > {new Date(props.value * 1000).toLocaleString()}
); @@ -330,7 +350,10 @@ function Player() { let rowAr = raritySort[rowA.original.normalizedRarity]; let rowBr = raritySort[rowB.original.normalizedRarity]; if (rowAr === rowBr) { - return rowB.original.playersCompletedPercent - rowA.original.playersCompletedPercent + return ( + rowB.original.playersCompletedPercent - + rowA.original.playersCompletedPercent + ); } return rowAr - rowBr; }, @@ -340,25 +363,25 @@ function Player() { ); const achievementsData = useMemo(() => { - return achievements?.map(a => { - if (!playerData.achievements[a.id]) { - return false; - } - return { - ...a, - completionDate: playerData.achievements[a.id], - } - }).filter(Boolean) || []; + return ( + achievements + ?.map((a) => { + if (!playerData.achievements[a.id]) { + return false; + } + return { + ...a, + completionDate: playerData.achievements[a.id], + }; + }) + .filter(Boolean) || [] + ); }, [achievements, playerData]); const raidsColumns = useMemo( () => [ { - Header: ( -
- {t('Side')} -
- ), + Header:
{t('Side')}
, id: 'side', accessor: 'side', Cell: (props) => { @@ -366,28 +389,20 @@ function Player() { }, }, { - Header: ( -
- {t('Raids')} -
- ), + Header:
{t('Raids')}
, id: 'raids', accessor: 'raids', }, { Header: ( -
- {t('Survived')} -
+
{t('Survived')}
), id: 'survived', accessor: 'survived', }, { Header: ( -
- {t('Runthrough')} -
+
{t('Runthrough')}
), id: 'runthrough', accessor: 'runthrough', @@ -396,11 +411,7 @@ function Player() { }, }, { - Header: ( -
- {t('MIA')} -
- ), + Header:
{t('MIA')}
, id: 'mia', accessor: 'mia', Cell: (props) => { @@ -408,11 +419,7 @@ function Player() { }, }, { - Header: ( -
- {t('KIA')} -
- ), + Header:
{t('KIA')}
, id: 'kia', accessor: 'kia', Cell: (props) => { @@ -420,11 +427,7 @@ function Player() { }, }, { - Header: ( -
- {t('Kills')} -
- ), + Header:
{t('Kills')}
, id: 'kills', accessor: 'kills', Cell: (props) => { @@ -445,9 +448,7 @@ function Player() { }, { Header: ( -
- {t('Win Streak')} -
+
{t('Win Streak')}
), id: 'streak', accessor: 'streak', @@ -463,7 +464,7 @@ function Player() { if (!playerData.pmcStats?.eft) { return []; } - const statSides = { 'scavStats': 'Scav', 'pmcStats': 'PMC' }; + const statSides = { scavStats: 'Scav', pmcStats: 'PMC' }; const statTypes = [ { name: 'raids', @@ -491,8 +492,8 @@ function Player() { }, { name: 'streak', - key: ['LongestWinStreak'] - } + key: ['LongestWinStreak'], + }, ]; const getStats = (side) => { return { @@ -500,7 +501,7 @@ function Player() { ...statTypes.reduce((all, s) => { all[s.name] = 0; return all; - }, {}) + }, {}), }; }; const totalStats = getStats('Total'); @@ -510,7 +511,9 @@ function Player() { const stats = playerData[sideKey].eft.overAllCounters.Items; const currentData = getStats(sideLabel); for (const st of statTypes) { - const foundStat = stats.find(s => !st.key.some(keyPart => !s.Key.includes(keyPart))); + const foundStat = stats.find( + (s) => !st.key.some((keyPart) => !s.Key.includes(keyPart)), + ); currentData[st.name] = foundStat?.Value || 0; totalStats[st.name] += currentData[st.name]; } @@ -522,11 +525,7 @@ function Player() { const skillsColumns = useMemo( () => [ { - Header: ( -
- {t('Skill')} -
- ), + Header:
{t('Skill')}
, id: 'skill', accessor: 'skill', Cell: (props) => { @@ -534,11 +533,7 @@ function Player() { }, }, { - Header: ( -
- {t('Level')} -
- ), + Header:
{t('Level')}
, id: 'progress', accessor: 'progress', Cell: (props) => { @@ -547,9 +542,7 @@ function Player() { }, { Header: ( -
- {t('Last Access')} -
+
{t('Last Access')}
), id: 'lastAccess', accessor: 'lastAccess', @@ -562,27 +555,26 @@ function Player() { ); const skillsData = useMemo(() => { - return playerData.skills?.Common?.map(s => { - if (!s.Progress || s.LastAccess <= 0) { - return false; - } - const skill = metaData.skills.find(skill => skill.id === s.Id); - return { - skill: skill?.name || s.Id, - progress: s.Progress, - lastAccess: s.LastAccess, - } - }).filter(Boolean) || []; + return ( + playerData.skills?.Common?.map((s) => { + if (!s.Progress || s.LastAccess <= 0) { + return false; + } + const skill = metaData.skills.find((skill) => skill.id === s.Id); + return { + skill: skill?.name || s.Id, + progress: s.Progress, + lastAccess: s.LastAccess, + }; + }).filter(Boolean) || [] + ); }, [playerData, metaData]); const masteringColumns = useMemo( () => [ { id: 'expander', - Header: ({ - getToggleAllRowsExpandedProps, - isAllRowsExpanded, - }) => + Header: ({ getToggleAllRowsExpandedProps, isAllRowsExpanded }) => // // {isAllRowsExpanded ? 'v' : '>'} // @@ -610,30 +602,19 @@ function Player() { ) : null, }, { - Header: ( -
- {t('Weapon')} -
- ), + Header:
{t('Weapon')}
, id: 'name', accessor: 'name', Cell: (props) => { if (props.row.original.shortName) { - return ( - - ); + return ; } return props.value; }, }, { Header: ( -
- {t('Progress')} -
+
{t('Progress')}
), id: 'Progress', accessor: 'Progress', @@ -658,37 +639,45 @@ function Player() { ); const masteringData = useMemo(() => { - return playerData.skills?.Mastering?.map(masteringProgress => { - const mastering = metaData.mastering.find(m => m.id === String(masteringProgress.Id)); - if (!mastering) { - return false; - } - let level = 1; - if (masteringProgress.Progress > mastering.level3) { - level = 3; - } else if (masteringProgress.Progress > mastering.level2) { - level = 2; - } - return { - ...masteringProgress, - name: masteringProgress.Id, - level, - subRows: mastering.weapons.map(w => { - const baseItem = items.find(i => i.id === w.id); - if (!baseItem) { - return false; - } - const preset = items.find (i => i.id === baseItem.properties.defaultPreset?.id); - return { - ...baseItem, - itemLink: `/item/${baseItem.normalizedName}`, - iconLink: preset ? preset.iconLink : baseItem.iconLink, - Progress: masteringProgress.Progress, - level, - }; - }).filter(Boolean), - }; - }).filter(Boolean) || []; + return ( + playerData.skills?.Mastering?.map((masteringProgress) => { + const mastering = metaData.mastering.find( + (m) => m.id === String(masteringProgress.Id), + ); + if (!mastering) { + return false; + } + let level = 1; + if (masteringProgress.Progress > mastering.level3) { + level = 3; + } else if (masteringProgress.Progress > mastering.level2) { + level = 2; + } + return { + ...masteringProgress, + name: masteringProgress.Id, + level, + subRows: mastering.weapons + .map((w) => { + const baseItem = items.find((i) => i.id === w.id); + if (!baseItem) { + return false; + } + const preset = items.find( + (i) => i.id === baseItem.properties.defaultPreset?.id, + ); + return { + ...baseItem, + itemLink: `/item/${baseItem.normalizedName}`, + iconLink: preset ? preset.iconLink : baseItem.iconLink, + Progress: masteringProgress.Progress, + level, + }; + }) + .filter(Boolean), + }; + }).filter(Boolean) || [] + ); }, [playerData, metaData, items]); const totalTimeInGame = useMemo(() => { @@ -701,175 +690,224 @@ function Player() { days, hours, minutes, - seconds + seconds, }); - return (

{`${t('Total account time in game')}: ${formattedTime}`}

); + return

{`${t('Total account time in game')}: ${formattedTime}`}

; }, [playerData, t]); - const getItemDisplay = useCallback((loadoutItem, imageOptions = {}) => { - let item = items.find(i => i.id === loadoutItem._tpl); - if (!item) { - return undefined; - } - if (item.properties?.defaultPreset) { - const preset = items.find(i => i.id === item.properties.defaultPreset.id); - item = { - ...item, - width: preset.width, - height: preset.height, - baseImageLink: preset.baseImageLink, - }; - } - let countLabel; + const getItemDisplay = useCallback( + (loadoutItem, imageOptions = {}) => { + let item = items.find((i) => i.id === loadoutItem._tpl); + if (!item) { + return undefined; + } + if (item.properties?.defaultPreset) { + const preset = items.find((i) => i.id === item.properties.defaultPreset.id); + item = { + ...item, + width: preset.width, + height: preset.height, + inspectImageLink: preset.inspectImageLink, + }; + } + let countLabel; - let label = ''; - if (loadoutItem.upd?.StackObjectsCount > 1) { - countLabel = loadoutItem.upd?.StackObjectsCount; - } - if (loadoutItem.upd?.Dogtag) { - const tag = loadoutItem.upd.Dogtag; - const weapon = items.find(i => i.id === tag.WeaponName?.split(' ')[0]); - countLabel = tag.Level; - let killerInfo = {tag.KillerName}; - if (tag.KillerAccountId) { - killerInfo = {tag.KillerName}; + let label = ''; + if (loadoutItem.upd?.StackObjectsCount > 1) { + countLabel = loadoutItem.upd?.StackObjectsCount; } - let victimInfo = ( - {tag.Nickname} - ); - if (tag.AccountId !== '0') { - victimInfo = ( - {tag.Nickname} + if (loadoutItem.upd?.Dogtag) { + const tag = loadoutItem.upd.Dogtag; + const weapon = items.find((i) => i.id === tag.WeaponName?.split(' ')[0]); + countLabel = tag.Level; + let killerInfo = {tag.KillerName}; + if (tag.KillerAccountId) { + killerInfo = ( + + {tag.KillerName} + + ); + } + let victimInfo = {tag.Nickname}; + if (tag.AccountId !== '0') { + victimInfo = ( + {tag.Nickname} + ); + } + label = ( + + {victimInfo} + {` ${t(tag.Status)} `} + {killerInfo} + {weapon !== undefined && [ + {` ${t('using')} `}, + + {weapon.shortName} + , + ]} +
{` ${new Date(tag.Time).toLocaleString()}`}
+
); } - label = ( - - {victimInfo} - {` ${t(tag.Status)} `} - {killerInfo} - {weapon !== undefined && [ - {` ${t('using')} `}, - {weapon.shortName} - ]} -
{` ${new Date(tag.Time).toLocaleString()}`}
-
- ); - } - if (loadoutItem.upd?.Key) { - const key = items.find(i => i.id === loadoutItem._tpl); - if (key) { - if (key.properties.uses) { - countLabel = `${key.properties.uses - loadoutItem.upd.Key.NumberOfUsages}/${key.properties.uses}`; - } else { - countLabel = loadoutItem.upd.Key.NumberOfUsages; + if (loadoutItem.upd?.Key) { + const key = items.find((i) => i.id === loadoutItem._tpl); + if (key) { + if (key.properties.uses) { + countLabel = `${key.properties.uses - loadoutItem.upd.Key.NumberOfUsages}/${key.properties.uses}`; + } else { + countLabel = loadoutItem.upd.Key.NumberOfUsages; + } } - } - } - if (loadoutItem.upd?.Repairable) { - countLabel = `${loadoutItem.upd.Repairable.Durability.toFixed(2)}/${loadoutItem.upd.Repairable.MaxDurability}` - } - if (loadoutItem.upd?.MedKit) { - const item = items.find(i => i.id === loadoutItem._tpl); - if (item?.properties?.uses || item?.properties?.hitpoints) { - countLabel = `${loadoutItem.upd.MedKit.HpResource}/${item.properties?.uses || item.properties?.hitpoints}`; + if (loadoutItem.upd?.Repairable) { + countLabel = `${loadoutItem.upd.Repairable.Durability.toFixed(2)}/${loadoutItem.upd.Repairable.MaxDurability}`; + } + if (loadoutItem.upd?.MedKit) { + const item = items.find((i) => i.id === loadoutItem._tpl); + if (item?.properties?.uses || item?.properties?.hitpoints) { + countLabel = `${loadoutItem.upd.MedKit.HpResource}/${item.properties?.uses || item.properties?.hitpoints}`; + } } - } - const itemImage = ( - - ); - return { image: itemImage, label }; - }, [items, t, gameMode]); + const itemImage = ( + + ); + return { image: itemImage, label }; + }, + [items, t, gameMode], + ); - const getLoadoutContents = useCallback((parentItem, itemType = 'loadout') => { - const itemSource = itemType === 'loadout' ? playerData?.equipment?.Items : playerData?.favoriteItems; - return itemSource?.reduce((contents, loadoutItem) => { - if (loadoutItem.parentId !== parentItem._id) { - return contents; - } - const itemDisplay = getItemDisplay(loadoutItem); - if (!itemDisplay) { + const getLoadoutContents = useCallback( + (parentItem, itemType = 'loadout') => { + const itemSource = + itemType === 'loadout' ? playerData?.equipment?.Items : playerData?.favoriteItems; + return itemSource?.reduce((contents, loadoutItem) => { + if (loadoutItem.parentId !== parentItem._id) { + return contents; + } + const itemDisplay = getItemDisplay(loadoutItem); + if (!itemDisplay) { + return contents; + } + contents.push( + { + return itemDisplay.image; + }, + }} + label={itemDisplay.label} + > + {getLoadoutContents(loadoutItem, itemType)} + , + ); return contents; - } - contents.push(( - { return itemDisplay.image}}} label={itemDisplay.label}> - {getLoadoutContents(loadoutItem, itemType)} - - )); - return contents; - }, []); - }, [playerData, getItemDisplay]); + }, []); + }, + [playerData, getItemDisplay], + ); - const getLoadoutInSlot = useCallback((slot) => { - if (playerData?.equipment?.Id === undefined) { - return "None"; - } + const getLoadoutInSlot = useCallback( + (slot) => { + if (playerData?.equipment?.Id === undefined) { + return 'None'; + } - let loadoutRoot = playerData.equipment.Items.find(i => i._id === playerData.equipment.Id); - let loadoutItem = playerData.equipment.Items.find(i => i.slotId === slot && i.parentId === loadoutRoot._id); + let loadoutRoot = playerData.equipment.Items.find( + (i) => i._id === playerData.equipment.Id, + ); + let loadoutItem = playerData.equipment.Items.find( + (i) => i.slotId === slot && i.parentId === loadoutRoot._id, + ); - if (loadoutItem === undefined) { - return "None"; - } + if (loadoutItem === undefined) { + return 'None'; + } - let itemImage = undefined; - let itemLabel = ''; - let contents = []; - let itemDisplay = getItemDisplay(loadoutItem); - if (itemDisplay) { - itemImage = itemDisplay.image; - } - else { - itemLabel = slot; - } - contents.push(( - { return itemImage}}} label={itemLabel}> - {getLoadoutContents(loadoutItem)} - - )); + let itemImage = undefined; + let itemLabel = ''; + let contents = []; + let itemDisplay = getItemDisplay(loadoutItem); + if (itemDisplay) { + itemImage = itemDisplay.image; + } else { + itemLabel = slot; + } + contents.push( + { + return itemImage; + }, + }} + label={itemLabel} + > + {getLoadoutContents(loadoutItem)} + , + ); - return - {contents} - - }, [playerData, getItemDisplay, getLoadoutContents]); + return {contents}; + }, + [playerData, getItemDisplay, getLoadoutContents], + ); const favoriteItemsContent = useMemo(() => { if (!playerData?.favoriteItems?.length) { return ''; } - return ([ -

{t('Favorite Items')}

, + return [ +

+ + {t('Favorite Items')} +

,
    - {playerData.favoriteItems.map(itemData => { - if (itemData.parentId) { - return false; - } + {playerData.favoriteItems + .map((itemData) => { + if (itemData.parentId) { + return false; + } - let itemImage = undefined; - let itemLabel = ''; - let itemDisplay = getItemDisplay(itemData); - if (itemDisplay) { - itemImage = itemDisplay.image; - itemLabel = itemDisplay.label; - } - return ( -
  • - - { return itemImage}}} label={itemLabel}> - {getLoadoutContents(itemData, 'favorite')} - - -
  • - ); - }).filter(Boolean)} -
- ]) + let itemImage = undefined; + let itemLabel = ''; + let itemDisplay = getItemDisplay(itemData); + if (itemDisplay) { + itemImage = itemDisplay.image; + itemLabel = itemDisplay.label; + } + return ( +
  • + + { + return itemImage; + }, + }} + label={itemLabel} + > + {getLoadoutContents(itemData, 'favorite')} + + +
  • + ); + }) + .filter(Boolean)} + , + ]; }, [playerData, getItemDisplay, getLoadoutContents, t]); useEffect(() => { @@ -882,19 +920,36 @@ function Player() { if (playerData.saved) { return; } - fetchProfile(); - }, [playerData, accountId, turnstileToken, fetchProfile]) + /* + *-----Commented out to prevent any unneccesary fetches to CloudFlare workers and/or Tarkov scanners, using the load profile feature to populate page ISSUE #1000 ------ + *fetchProfile(); + */ + }, [playerData, accountId, turnstileToken, fetchProfile]); const playerSearchDiv = (

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

    @@ -923,10 +978,17 @@ function Player() { {pageTitle} {playerData.aid !== 0 && ( - - + + )} @@ -934,10 +996,16 @@ function Player() { {typeof playerBanned === 'undefined' && ( - + )} {playerBanned === false && ( @@ -949,42 +1017,49 @@ function Player() { )} - { // https://developers.cloudflare.com/turnstile/reference/client-side-errors#error-codes if (errorCode === '110200') { - setProfileError(`Turnstile error: ${window.location.hostname} is not a valid hostname`); + setProfileError( + `Turnstile error: ${window.location.hostname} is not a valid hostname`, + ); } else { setProfileError(`Turnstile error code ${errorCode}`); } }} - options={{appearance: 'interaction-only'}} - /> + options={{ appearance: 'interaction-only' }} + /> */}
    {accountCategories} {!!playerData.saved && ( -

    {t('Warning: Profiles loaded from files may contain edited information')}

    +

    + {t('Warning: Profiles loaded from files may contain edited information')} +

    )} {totalTimeInGame} {lastActiveDate} - {raidsData?.length > 0 && ( + {raidsData?.length > 0 && ( <> -

    {t('Raid Stats')}

    - +

    + + {t('Raid Stats')} +

    + )} - {achievementsData?.length > 0 && ( + {achievementsData?.length > 0 && ( <> -

    {t('Achievements')}

    +

    + + {t('Achievements')} +

    0 && ( <> -

    {t('Loadout')}

    +

    + + {t('Loadout')} +

    -
    {getLoadoutInSlot('Earpiece')}
    -
    {getLoadoutInSlot('Headwear')}
    -
    {getLoadoutInSlot('FaceCover')}
    -
    {getLoadoutInSlot('ArmBand')}
    -
    {getLoadoutInSlot('ArmorVest')}
    -
    {getLoadoutInSlot('Eyewear')}
    -
    {getLoadoutInSlot('FirstPrimaryWeapon')}
    -
    {getLoadoutInSlot('Holster')}
    -
    {getLoadoutInSlot('SecondPrimaryWeapon')}
    -
    {getLoadoutInSlot('Scabbard')}
    +
    +
    {'EARPIECE'}
    + {getLoadoutInSlot('Earpiece')} +
    +
    +
    {'HEADWEAR'}
    + {getLoadoutInSlot('Headwear')} +
    +
    +
    {'FACE COVER'}
    + {getLoadoutInSlot('FaceCover')} +
    +
    +
    {'ARMBAND'}
    + {getLoadoutInSlot('ArmBand')} +
    +
    +
    {'BODY ARMOR'}
    + {getLoadoutInSlot('ArmorVest')} +
    +
    +
    {'EYEWEAR'}
    + {getLoadoutInSlot('Eyewear')} +
    +
    +
    {'ON SLING'}
    + {getLoadoutInSlot('FirstPrimaryWeapon')} +
    +
    +
    {'HOLSTER'}
    + {getLoadoutInSlot('Holster')} +
    +
    +
    {'ON BACK'}
    + {getLoadoutInSlot('SecondPrimaryWeapon')} +
    +
    +
    {'SHEATH'}
    + {getLoadoutInSlot('Scabbard')} +
    -
    {getLoadoutInSlot('TacticalVest')}
    -
    {getLoadoutInSlot('Pockets')}
    -
    {getLoadoutInSlot('Backpack')}
    -
    {getLoadoutInSlot('SecuredContainer')}
    +
    +
    {'TATICAL RIG'}
    + {getLoadoutInSlot('TacticalVest')} +
    +
    + {getLoadoutInSlot('Pockets')} +
    +
    +
    {'BACKPACK'}
    + {getLoadoutInSlot('Backpack')} +
    +
    +
    {'POUCH'}
    + {getLoadoutInSlot('SecuredContainer')} +
    @@ -1021,7 +1140,10 @@ function Player() { {favoriteItemsContent} {skillsData?.length > 0 && ( <> -

    {t('Skills')}

    +

    + + {t('Skills')} +

    0 && ( <> -

    {t('Mastering')}

    +

    + + {t('Mastering')} +