Fix some sonar smells in frontend code

This commit is contained in:
Aurora Lahtela 2023-04-08 09:13:38 +03:00
parent 58eae50428
commit f43d8f89fb
21 changed files with 109 additions and 70 deletions

View File

@ -49,7 +49,6 @@ const Accordion = ({headers, slices, open, style}) => {
} }
const width = headers.length; const width = headers.length;
return ( return (
<table className={"table accordion-striped" + (nightModeEnabled ? " table-dark" : '')} id="tableAccordion" <table className={"table accordion-striped" + (nightModeEnabled ? " table-dark" : '')} id="tableAccordion"
style={style}> style={style}>

View File

@ -48,7 +48,8 @@ const QueryPath = () => {
return ( return (
<aside id={"result-path"} className={"alert shadow " + (hasResults ? "alert-success" : "alert-warning")}> <aside id={"result-path"} className={"alert shadow " + (hasResults ? "alert-success" : "alert-warning")}>
{path.map((step, i) => <p key={i} style={{marginBottom: 0, marginLeft: i * 0.7 + "rem"}}> {path.map((step, i) => <p key={step.kind + step.size}
style={{marginBottom: 0, marginLeft: i * 0.7 + "rem"}}>
<FontAwesomeIcon <FontAwesomeIcon
icon={faFilter}/> '{getReadableFilterName(step.kind)}' matched {step.size} players icon={faFilter}/> '{getReadableFilterName(step.kind)}' matched {step.size} players
</p>)} </p>)}

View File

@ -1,11 +1,11 @@
import React, {useEffect, useState} from 'react'; import React, {useEffect, useState} from 'react';
import {Transition} from 'react-transition-group'; import {Transition} from 'react-transition-group';
const reduceAnimations = window.matchMedia(`(prefers-reduced-motion: reduce)`) === true;
const defaultDuration = 250; const defaultDuration = 250;
const LoadIn = ({children, duration}) => { const LoadIn = ({children, duration}) => {
if (!duration) duration = defaultDuration; if (!duration) duration = defaultDuration;
const reduceAnimations = window.matchMedia(`(prefers-reduced-motion: reduce)`).matches;
const defaultStyle = reduceAnimations ? { const defaultStyle = reduceAnimations ? {
transition: `opacity ${duration}ms ease-in-out`, transition: `opacity ${duration}ms ease-in-out`,

View File

@ -22,8 +22,8 @@ const ProjectionDropDown = ({projection, setProjection}) => {
<Dropdown.Menu> <Dropdown.Menu>
<h6 className="dropdown-header">{t('html.label.geoProjection.dropdown')}</h6> <h6 className="dropdown-header">{t('html.label.geoProjection.dropdown')}</h6>
{projectionOptions.map((option, i) => ( {projectionOptions.map(option => (
<Dropdown.Item key={i} onClick={() => setProjection(option)}> <Dropdown.Item key={option} onClick={() => setProjection(option)}>
{t(option)} {t(option)}
</Dropdown.Item> </Dropdown.Item>
))} ))}

View File

@ -23,8 +23,8 @@ const SortDropDown = ({sortBy, sortReversed, setSortBy}) => {
<Dropdown.Menu> <Dropdown.Menu>
<h6 className="dropdown-header">{t('html.label.sortBy')}</h6> <h6 className="dropdown-header">{t('html.label.sortBy')}</h6>
{sortOptions.map((option, i) => ( {sortOptions.map(option => (
<Dropdown.Item key={i} onClick={() => setSortBy(option)}> <Dropdown.Item key={option} onClick={() => setSortBy(option)}>
{t(option.label)} {t(option.label)}
</Dropdown.Item> </Dropdown.Item>
))} ))}

View File

@ -27,7 +27,7 @@ const NicknamesCard = ({player}) => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{player.nicknames.map((nickname, i) => (<tr key={'nick-' + i}> {player.nicknames.map(nickname => (<tr key={JSON.stringify(nickname)}>
<td dangerouslySetInnerHTML={{__html: nickname.nickname}}/> <td dangerouslySetInnerHTML={{__html: nickname.nickname}}/>
<td>{nickname.server}</td> <td>{nickname.server}</td>
<td>{nickname.date}</td> <td>{nickname.date}</td>

View File

@ -51,8 +51,8 @@ const FilterDropdown = ({filterOptions, filters, setFilters}) => {
<Dropdown.Menu popperConfig={{strategy: "absolute"}}> <Dropdown.Menu popperConfig={{strategy: "absolute"}}>
<h6 className="dropdown-header">{t('html.query.filters.add')}</h6> <h6 className="dropdown-header">{t('html.query.filters.add')}</h6>
<Scrollable> <Scrollable>
{filterOptions.map((option, i) => ( {filterOptions.map(option => (
<Dropdown.Item key={i} onClick={() => addFilter(option)}> <Dropdown.Item key={option} onClick={() => addFilter(option)}>
{getReadableFilterName(option)} {getReadableFilterName(option)}
</Dropdown.Item> </Dropdown.Item>
))} ))}

View File

@ -29,7 +29,7 @@ const FilterList = ({filters, setFilters, setAsInvalid, setAsValid}) => {
return ( return (
<ul id={"filters"} className={"filters"}> <ul id={"filters"} className={"filters"}>
{filters.map((filter, i) => <li key={i} className={"filter"}> {filters.map((filter, i) => <li key={JSON.stringify(filter)} className={"filter"}>
<Filter filter={filter} index={i} <Filter filter={filter} index={i}
setFilterOptions={newOptions => updateFilterOptions(i, newOptions)} setFilterOptions={newOptions => updateFilterOptions(i, newOptions)}
removeFilter={() => removeFilter(i)} removeFilter={() => removeFilter(i)}

View File

@ -23,28 +23,36 @@ const PlayerbaseTrendsCard = ({data}) => {
headers={[t('html.label.thirtyDaysAgo'), t('html.label.now'), t('html.label.trend')]}> headers={[t('html.label.thirtyDaysAgo'), t('html.label.now'), t('html.label.trend')]}>
<TableRow icon={faUsers} color="black" text={t('html.label.totalPlayers')} <TableRow icon={faUsers} color="black" text={t('html.label.totalPlayers')}
values={[data.total_players_then, data.total_players_now, values={[data.total_players_then, data.total_players_now,
<BigTrend trend={data.total_players_trend}/>]}/> <BigTrend key={JSON.stringify(data.total_players_trend)}
trend={data.total_players_trend}/>]}/>
<TableRow icon={faUsers} color="lime" text={t('html.label.regularPlayers')} <TableRow icon={faUsers} color="lime" text={t('html.label.regularPlayers')}
values={[data.regular_players_then, data.regular_players_now, values={[data.regular_players_then, data.regular_players_now,
<BigTrend trend={data.regular_players_trend}/>]}/> <BigTrend key={JSON.stringify(data.regular_players_trend)}
trend={data.regular_players_trend}/>]}/>
<TableRow icon={faClock} color="green" <TableRow icon={faClock} color="green"
text={t('html.label.averagePlaytime') + ' ' + t('html.label.perPlayer')} text={t('html.label.averagePlaytime') + ' ' + t('html.label.perPlayer')}
values={[data.playtime_avg_then, data.playtime_avg_now, values={[data.playtime_avg_then, data.playtime_avg_now,
<BigTrend trend={data.playtime_avg_trend}/>]}/> <BigTrend key={JSON.stringify(data.total_players_trend)}
trend={data.playtime_avg_trend}/>]}/>
<TableRow icon={faClock} color="gray" text={t('html.label.afk') + ' ' + t('html.label.perPlayer')} <TableRow icon={faClock} color="gray" text={t('html.label.afk') + ' ' + t('html.label.perPlayer')}
values={[data.afk_then, data.afk_now, <BigTrend trend={data.afk_trend}/>]}/> values={[data.afk_then, data.afk_now,
<BigTrend key={JSON.stringify(data.afk_trend)}
trend={data.afk_trend}/>]}/>
<TableRow icon={faClock} color="green" <TableRow icon={faClock} color="green"
text={t('html.label.averagePlaytime') + ' ' + t('html.label.perRegularPlayer')} text={t('html.label.averagePlaytime') + ' ' + t('html.label.perRegularPlayer')}
values={[data.regular_playtime_avg_then, data.regular_playtime_avg_now, values={[data.regular_playtime_avg_then, data.regular_playtime_avg_now,
<BigTrend trend={data.regular_playtime_avg_trend}/>]}/> <BigTrend key={JSON.stringify(data.regular_playtime_avg_trend)}
trend={data.regular_playtime_avg_trend}/>]}/>
<TableRow icon={faClock} color="teal" <TableRow icon={faClock} color="teal"
text={t('html.label.averageSessionLength') + ' ' + t('html.label.perRegularPlayer')} text={t('html.label.averageSessionLength') + ' ' + t('html.label.perRegularPlayer')}
values={[data.regular_session_avg_then, data.regular_session_avg_now, values={[data.regular_session_avg_then, data.regular_session_avg_now,
<BigTrend trend={data.regular_session_avg_trend}/>]}/> <BigTrend key={JSON.stringify(data.regular_session_avg_trend)}
trend={data.regular_session_avg_trend}/>]}/>
<TableRow icon={faClock} color="gray" <TableRow icon={faClock} color="gray"
text={t('html.label.afk') + ' ' + t('html.label.perRegularPlayer')} text={t('html.label.afk') + ' ' + t('html.label.perRegularPlayer')}
values={[data.regular_afk_avg_then, data.regular_afk_avg_now, values={[data.regular_afk_avg_then, data.regular_afk_avg_now,
<BigTrend trend={data.regular_afk_avg_trend}/>]}/> <BigTrend key={JSON.stringify(data.regular_afk_avg_trend)}
trend={data.regular_afk_avg_trend}/>]}/>
</ComparisonTable> </ComparisonTable>
</Card> </Card>
) )

View File

@ -22,30 +22,43 @@ const ServerWeekComparisonCard = ({data}) => {
<ComparisonTable comparisonHeader={t('html.label.comparing7days')} <ComparisonTable comparisonHeader={t('html.label.comparing7days')}
headers={[data.start + ' - ' + data.midpoint, data.midpoint + ' - ' + data.end, t('html.label.trend')]}> headers={[data.start + ' - ' + data.midpoint, data.midpoint + ' - ' + data.end, t('html.label.trend')]}>
<TableRow icon={faUsers} color="blue" text={t('html.label.uniquePlayers')} <TableRow icon={faUsers} color="blue" text={t('html.label.uniquePlayers')}
values={[data.unique_before, data.unique_after, <BigTrend trend={data.unique_trend}/>]}/> values={[data.unique_before, data.unique_after,
<BigTrend key={JSON.stringify(data.unique_trend)}
trend={data.unique_trend}/>]}/>
<TableRow icon={faUsers} color="light-green" text={t('html.label.newPlayers')} <TableRow icon={faUsers} color="light-green" text={t('html.label.newPlayers')}
values={[data.new_before, data.new_after, <BigTrend trend={data.new_trend}/>]}/> values={[data.new_before, data.new_after,
<BigTrend key={JSON.stringify(data.new_trend)}
trend={data.new_trend}/>]}/>
<TableRow icon={faUsers} color="lime" text={t('html.label.regularPlayers')} <TableRow icon={faUsers} color="lime" text={t('html.label.regularPlayers')}
values={[data.regular_before, data.regular_after, <BigTrend trend={data.regular_trend}/>]}/> values={[data.regular_before, data.regular_after,
<BigTrend key={JSON.stringify(data.regular_trend)}
trend={data.regular_trend}/>]}/>
<TableRow icon={faClock} color="green" <TableRow icon={faClock} color="green"
text={t('html.label.averagePlaytime') + ' ' + t('html.label.perPlayer')} text={t('html.label.averagePlaytime') + ' ' + t('html.label.perPlayer')}
values={[data.average_playtime_before, data.average_playtime_after, values={[data.average_playtime_before, data.average_playtime_after,
<BigTrend trend={data.average_playtime_trend}/>]}/> <BigTrend key={JSON.stringify(data.average_playtime_trend)}
trend={data.average_playtime_trend}/>]}/>
<TableRow icon={faClock} color="teal" <TableRow icon={faClock} color="teal"
text={t('html.label.averageSessionLength')} text={t('html.label.averageSessionLength')}
values={[data.session_length_average_before, data.session_length_average_after, values={[data.session_length_average_before, data.session_length_average_after,
<BigTrend trend={data.session_length_average_trend}/>]}/> <BigTrend key={JSON.stringify(data.session_length_average_trend)}
trend={data.session_length_average_trend}/>]}/>
<TableRow icon={faCalendarCheck} color="teal" text={t('html.label.sessions')} <TableRow icon={faCalendarCheck} color="teal" text={t('html.label.sessions')}
values={[data.sessions_before, data.sessions_after, values={[data.sessions_before, data.sessions_after,
<BigTrend trend={data.sessions_trend}/>]}/> <BigTrend key={JSON.stringify(data.sessions_trend)}
trend={data.sessions_trend}/>]}/>
<TableRow icon={faCrosshairs} color="red" text={t('html.label.playerKills')} <TableRow icon={faCrosshairs} color="red" text={t('html.label.playerKills')}
values={[data.player_kills_before, data.player_kills_after, values={[data.player_kills_before, data.player_kills_after,
<BigTrend trend={data.player_kills_trend}/>]}/> <BigTrend key={JSON.stringify(data.player_kills_trend)}
trend={data.player_kills_trend}/>]}/>
<TableRow icon={faCrosshairs} color="green" text={t('html.label.mobKills')} <TableRow icon={faCrosshairs} color="green" text={t('html.label.mobKills')}
values={[data.mob_kills_before, data.mob_kills_after, values={[data.mob_kills_before, data.mob_kills_after,
<BigTrend trend={data.mob_kills_trend}/>]}/> <BigTrend key={JSON.stringify(data.mob_kills_trend)}
trend={data.mob_kills_trend}/>]}/>
<TableRow icon={faSkull} color="black" text={t('html.label.deaths')} <TableRow icon={faSkull} color="black" text={t('html.label.deaths')}
values={[data.deaths_before, data.deaths_after, <BigTrend trend={data.deaths_trend}/>]}/> values={[data.deaths_before, data.deaths_after,
<BigTrend key={JSON.stringify(data.deaths_trend)}
trend={data.deaths_trend}/>]}/>
</ComparisonTable> </ComparisonTable>
</Card> </Card>
) )

View File

@ -96,7 +96,8 @@ const ExtensionCard = ({extension}) => {
</Card.Header> </Card.Header>
<ul className="nav nav-tabs tab-nav-right" role="tablist"> <ul className="nav nav-tabs tab-nav-right" role="tablist">
{extension.onlyGenericTab ? '' : {extension.onlyGenericTab ? '' :
extension.tabs.map((tab, i) => <li key={i} role="presentation" className="nav-item col-black"> extension.tabs.map((tab, i) => <li key={JSON.stringify(tab)} role="presentation"
className="nav-item col-black">
<button className={"nav-link col-black" <button className={"nav-link col-black"
+ (openTabIndex === i ? ' active' : '')} onClick={() => toggleTabIndex(i)}> + (openTabIndex === i ? ' active' : '')} onClick={() => toggleTabIndex(i)}>
<ExtensionIcon icon={tab.tabInformation.icon}/> {tab.tabInformation.tabName} <ExtensionIcon icon={tab.tabInformation.icon}/> {tab.tabInformation.tabName}

View File

@ -13,7 +13,7 @@ const MultiSelect = ({options, selectedIndexes, setSelectedIndexes}) => {
onChange={handleChange}> onChange={handleChange}>
{options.map((option, i) => { {options.map((option, i) => {
return ( return (
<option key={i} value={selectedIndexes.includes(i)} <option key={JSON.stringify(option)} value={selectedIndexes.includes(i)}
selected={selectedIndexes.includes(i)}>{option}</option> selected={selectedIndexes.includes(i)}>{option}</option>
) )
})} })}

View File

@ -58,7 +58,7 @@ const getContributionIcon = (type) => {
const Contributor = ({contributor}) => { const Contributor = ({contributor}) => {
const icons = contributor.contributed.map( const icons = contributor.contributed.map(
(type, i) => <Fa key={i} icon={["fa", getContributionIcon(type)]}/>); type => <Fa key={"" + type} icon={["fa", getContributionIcon(type)]}/>);
return ( return (
<li className="contributor">{contributor.name} {icons} </li> <li className="contributor">{contributor.name} {icons} </li>
) )
@ -78,7 +78,7 @@ const Contributions = () => {
<p>In addition following <span className="col-theme">awesome people</span> have <p>In addition following <span className="col-theme">awesome people</span> have
contributed:</p> contributed:</p>
<ul className="row contributors"> <ul className="row contributors">
{contributors.map((contributor, i) => <Contributor key={i} contributor={contributor}/>)} {contributors.map((contributor, i) => <Contributor key={contributor.name} contributor={contributor}/>)}
<li>{t('html.modal.info.contributors.bugreporters')}</li> <li>{t('html.modal.info.contributors.bugreporters')}</li>
</ul> </ul>
<small> <small>

View File

@ -22,8 +22,8 @@ const LanguageSelector = () => {
className="form-select form-select-sm" className="form-select form-select-sm"
id="langSelector" id="langSelector"
defaultValue={localeService.clientLocale}> defaultValue={localeService.clientLocale}>
{languages.map((lang, i) => {languages.map(lang =>
<option key={i} value={lang.name}>{lang.displayName}</option>)} <option key={lang.name} value={lang.name}>{lang.displayName}</option>)}
</select> </select>
) )
} }

View File

@ -150,8 +150,8 @@ const SidebarCollapse = ({item, open, setOpen, collapseSidebar}) => {
<div className="bg-white py-2 collapse-inner rounded"> <div className="bg-white py-2 collapse-inner rounded">
{item.contents {item.contents
.filter(content => content !== undefined) .filter(content => content !== undefined)
.map((content, i) => .map(content =>
<Item key={i} <Item key={JSON.stringify(content)}
inner inner
active={false} active={false}
item={content} item={content}

View File

@ -30,7 +30,7 @@ const GroupTable = ({groups, colors}) => {
<table className={"table mb-0" + (nightModeEnabled ? " table-dark" : '')}> <table className={"table mb-0" + (nightModeEnabled ? " table-dark" : '')}>
<tbody> <tbody>
{groups.length ? groups.map((group, i) => {groups.length ? groups.map((group, i) =>
<GroupRow key={i} <GroupRow key={group.name}
group={group} group={group}
color={getColor(i)}/>) : color={getColor(i)}/>) :
<tr> <tr>

View File

@ -1,4 +1,4 @@
import {createContext, useCallback, useContext, useEffect, useState} from "react"; import {createContext, useCallback, useContext, useEffect, useMemo, useState} from "react";
import {fetchWhoAmI} from "../service/authenticationService"; import {fetchWhoAmI} from "../service/authenticationService";
const AuthenticationContext = createContext({}); const AuthenticationContext = createContext({});
@ -35,7 +35,18 @@ export const AuthenticationContextProvider = ({children}) => {
updateLoginDetails(); updateLoginDetails();
}, [updateLoginDetails]); }, [updateLoginDetails]);
const sharedState = { const sharedState = useMemo(() => {
return {
authLoaded,
authRequired,
loggedIn,
user,
loginError,
hasPermission,
hasPermissionOtherThan,
updateLoginDetails
}
}, [
authLoaded, authLoaded,
authRequired, authRequired,
loggedIn, loggedIn,
@ -44,7 +55,7 @@ export const AuthenticationContextProvider = ({children}) => {
hasPermission, hasPermission,
hasPermissionOtherThan, hasPermissionOtherThan,
updateLoginDetails updateLoginDetails
} ])
return (<AuthenticationContext.Provider value={sharedState}> return (<AuthenticationContext.Provider value={sharedState}>
{children} {children}
</AuthenticationContext.Provider> </AuthenticationContext.Provider>

View File

@ -1,4 +1,4 @@
import {createContext, useContext, useEffect, useState} from "react"; import {createContext, useContext, useEffect, useMemo, useState} from "react";
import {useDataRequest} from "./dataFetchHook"; import {useDataRequest} from "./dataFetchHook";
import {fetchExtensionData} from "../service/serverService"; import {fetchExtensionData} from "../service/serverService";
@ -15,7 +15,9 @@ export const ServerExtensionContextProvider = ({identifier, children}) => {
setExtensionDataLoadingError(loadingError); setExtensionDataLoadingError(loadingError);
}, [data, loadingError, setExtensionData, setExtensionDataLoadingError]) }, [data, loadingError, setExtensionData, setExtensionDataLoadingError])
const sharedState = {extensionData, extensionDataLoadingError} const sharedState = useMemo(() => {
return {extensionData, extensionDataLoadingError};
}, [extensionData, extensionDataLoadingError]);
return (<ServerExtensionContext.Provider value={sharedState}> return (<ServerExtensionContext.Provider value={sharedState}>
{children} {children}
</ServerExtensionContext.Provider> </ServerExtensionContext.Provider>

View File

@ -2187,7 +2187,7 @@ a.text-dark:hover, a.text-dark:focus {
border: 0; border: 0;
font-weight: 900; font-weight: 900;
content: '\f105'; content: '\f105';
font-family: 'Font Awesome 5 Free'; font-family: 'Font Awesome 5 Free', sans-serif;
} }
.sidebar .nav-item.dropdown.show .dropdown-toggle::after, .sidebar .nav-item.dropdown.show .dropdown-toggle::after,
@ -2471,7 +2471,7 @@ a.text-dark:hover, a.text-dark:focus {
.sidebar #sidebarToggle::after { .sidebar #sidebarToggle::after {
font-weight: 900; font-weight: 900;
content: '\f104'; content: '\f104';
font-family: 'Font Awesome 5 Free'; font-family: 'Font Awesome 5 Free', sans-serif;
margin-right: 0.1rem; margin-right: 0.1rem;
} }
@ -2490,7 +2490,7 @@ a.text-dark:hover, a.text-dark:focus {
.sidebar.toggled #sidebarToggle::after { .sidebar.toggled #sidebarToggle::after {
content: '\f105'; content: '\f105';
font-family: 'Font Awesome 5 Free'; font-family: 'Font Awesome 5 Free', sans-serif;
margin-left: 0.25rem; margin-left: 0.25rem;
} }
@ -2611,7 +2611,7 @@ a.text-dark:hover, a.text-dark:focus {
border: 0; border: 0;
font-weight: 900; font-weight: 900;
content: '\f107'; content: '\f107';
font-family: 'Font Awesome 5 Free'; font-family: 'Font Awesome 5 Free', sans-serif;
} }
.sidebar .nav-item .nav-link[data-bs-toggle="collapse"].collapsed::after { .sidebar .nav-item .nav-link[data-bs-toggle="collapse"].collapsed::after {
@ -2892,7 +2892,7 @@ a.text-dark:hover, a.text-dark:focus {
line-height: 51px; line-height: 51px;
font-weight: 900; font-weight: 900;
content: '\f107'; content: '\f107';
font-family: 'Font Awesome 5 Free'; font-family: 'Font Awesome 5 Free', sans-serif;
color: #d1d3e2; color: #d1d3e2;
} }

View File

@ -146,6 +146,32 @@ const LoginPage = () => {
} }
}, [setRedirectTo, setSuccessMessage, t]) }, [setRedirectTo, setSuccessMessage, t])
const redirectAfterLogin = () => {
if (redirectTo && !redirectTo.startsWith('http') && !redirectTo.startsWith('file') && !redirectTo.startsWith('javascript')) {
// Normalize the URL so that it can't redirect to different domain.
try {
const redirectUrl = new URL(
redirectTo.substring(redirectTo.indexOf('/')) + (window.location.hash ? window.location.hash : ''),
window.location.protocol + '//' + window.location.host
);
if (redirectUrl.pathname.includes("//")) {
// Invalid redirect URL, something fishy might be going on, redirect to /
navigate('/');
} else {
navigate(
redirectUrl.pathname + redirectUrl.search + redirectUrl.hash
);
}
} catch (e) {
console.warn(e);
// Invalid redirect URL, something fishy might be going on, redirect to /
navigate('/');
}
} else {
navigate('/');
}
};
const login = async (username, password) => { const login = async (username, password) => {
if (!username || username.length < 1) { if (!username || username.length < 1) {
return setFailMessage(t('html.register.error.noUsername')); return setFailMessage(t('html.register.error.noUsername'));
@ -168,29 +194,7 @@ const LoginPage = () => {
} }
} else if (data && data.success) { } else if (data && data.success) {
await updateLoginDetails(); await updateLoginDetails();
if (redirectTo && !redirectTo.startsWith('http') && !redirectTo.startsWith('file') && !redirectTo.startsWith('javascript')) { redirectAfterLogin();
// Normalize the URL so that it can't redirect to different domain.
try {
const redirectUrl = new URL(
redirectTo.substring(redirectTo.indexOf('/')) + (window.location.hash ? window.location.hash : ''),
window.location.protocol + '//' + window.location.host
);
if (redirectUrl.pathname.includes("//")) {
// Invalid redirect URL, something fishy might be going on, redirect to /
navigate('/');
} else {
navigate(
redirectUrl.pathname + redirectUrl.search + redirectUrl.hash
);
}
} catch (e) {
console.warn(e);
// Invalid redirect URL, something fishy might be going on, redirect to /
navigate('/');
}
} else {
navigate('/');
}
} else { } else {
setFailMessage(t('html.login.failed') + data ? data.error : t('generic.noData')); setFailMessage(t('html.login.failed') + data ? data.error : t('generic.noData'));
} }

View File

@ -34,7 +34,7 @@ const ConnectionsCard = ({player}) => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{player.connections.map((connection, i) => (<tr key={'connection-' + i}> {player.connections.map((connection, i) => (<tr key={JSON.stringify(connection)}>
<td>{connection.geolocation}</td> <td>{connection.geolocation}</td>
<td>{connection.date}</td> <td>{connection.date}</td>
</tr>))} </tr>))}