Use the join address selection in Player Retention

This commit is contained in:
Aurora Lahtela 2024-04-06 11:01:27 +03:00
parent 69a3dd96f2
commit 1af5f470ec
5 changed files with 54 additions and 39 deletions

View File

@ -0,0 +1,29 @@
import LoadIn from "../../animation/LoadIn.jsx";
import ExtendableRow from "../../layout/extension/ExtendableRow.jsx";
import {Col} from "react-bootstrap";
import PlayerRetentionGraphCard from "./PlayerRetentionGraphCard.jsx";
import React, {useState} from "react";
import {JoinAddressListContextProvider} from "../../../hooks/context/joinAddressListContextHook.jsx";
import AddressGroupSelectorRow from "./AddressGroupSelectorRow.jsx";
const PlayerRetention = ({id, seeRetention, identifier}) => {
const [selectedGroupBy, setSelectedGroupBy] = useState('none');
return (
<LoadIn>
{seeRetention && <section className={id}>
<JoinAddressListContextProvider identifier={identifier} loadIndividualAddresses>
<ExtendableRow id={`row-${id}-0`}>
<Col lg={12}>
<PlayerRetentionGraphCard identifier={identifier}
selectedGroupBy={selectedGroupBy}
setSelectedGroupBy={setSelectedGroupBy}/>
</Col>
</ExtendableRow>
{selectedGroupBy === 'joinAddress' && <AddressGroupSelectorRow/>}
</JoinAddressListContextProvider>
</section>}
</LoadIn>
)
};
export default PlayerRetention

View File

@ -17,6 +17,7 @@ import {useTheme} from "../../../hooks/themeHook";
import {useNavigation} from "../../../hooks/navigationHook";
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
import {faQuestionCircle} from "@fortawesome/free-regular-svg-icons";
import {useJoinAddressListContext} from "../../../hooks/context/joinAddressListContextHook.jsx";
const dayMs = 24 * 3600000;
const getWeek = (date) => {
@ -26,7 +27,7 @@ const getWeek = (date) => {
return Math.ceil(dayOfYear / 7)
};
const PlayerRetentionGraphCard = ({identifier}) => {
const PlayerRetentionGraphCard = ({identifier, selectedGroupBy, setSelectedGroupBy}) => {
const {t} = useTranslation();
const {nightModeEnabled} = useTheme();
const {setHelpModalTopic} = useNavigation();
@ -40,6 +41,8 @@ const PlayerRetentionGraphCard = ({identifier}) => {
loadingError: joinAddressLoadingError
} = useDataRequest(fetchPlayerJoinAddresses, [identifier]);
const {list, playerAddresses} = useJoinAddressListContext();
const [selectedWindow, setSelectedWindow] = useState('days');
const windowOptions = useMemo(() => [
{name: 'hours', displayName: t('html.label.time.hours'), increment: 3600000},
@ -57,7 +60,7 @@ const PlayerRetentionGraphCard = ({identifier}) => {
{name: 'registered-2y', displayName: t('html.label.retention.inLast730d'), start: time - 2 * 365 * dayMs},
{name: 'registered-ever', displayName: t('html.label.retention.inAnytime'), start: 0},
], [t, time]);
const [selectedGroupBy, setSelectedGroupBy] = useState('none');
// State moved to higher level for join address group selection
const groupByOptions = useMemo(() => [
{name: 'none', displayName: t('html.label.retention.groupByNone')},
{name: 'days', displayName: t('html.label.time.day')},
@ -165,8 +168,12 @@ const PlayerRetentionGraphCard = ({identifier}) => {
break;
case 'joinAddress':
const joinAddress = joinAddressData[point.playerUUID];
if (!grouped[joinAddress]) grouped[joinAddress] = [];
grouped[joinAddress].push(point);
const joinAddressGroups = list.filter(g => g.addresses.includes(joinAddress)).map(g => g.name);
for (const joinAddressGroup of joinAddressGroups) {
if (!grouped[joinAddressGroup]) grouped[joinAddressGroup] = [];
grouped[joinAddressGroup].push(point);
}
break;
case 'none':
default:
@ -175,7 +182,7 @@ const PlayerRetentionGraphCard = ({identifier}) => {
}
}
return grouped;
}, [groupByOptions, selectedGroupBy]);
}, [groupByOptions, selectedGroupBy, list]);
const createSeries = useCallback(async (retentionData, joinAddressData) => {
@ -207,10 +214,10 @@ const PlayerRetentionGraphCard = ({identifier}) => {
}, [nightModeEnabled, mapToData, groupOptions, selectedGroup, selectedYAxis, group]);
useEffect(() => {
if (!data || !joinAddressData) return;
if (!data || !playerAddresses) return;
createSeries(data.player_retention, joinAddressData.joinAddressByPlayer).then(series => setSeries(series.flat()));
}, [data, joinAddressData, createSeries, setSeries]);
createSeries(data.player_retention, playerAddresses).then(series => setSeries(series.flat()));
}, [data, playerAddresses, createSeries, setSeries]);
useEffect(() => {
const windowName = windowOptions.find(option => option.name === selectedWindow).displayName;

View File

@ -7,7 +7,7 @@ import {useTranslation} from "react-i18next";
const JoinAddressListContext = createContext({});
export const JoinAddressListContextProvider = ({identifier, children}) => {
export const JoinAddressListContextProvider = ({identifier, children, loadIndividualAddresses}) => {
const {t} = useTranslation();
const {updateRequested} = useNavigation();
const {preferencesLoaded, getKeyedPreference, setSomePreferences} = usePreferences();
@ -46,17 +46,19 @@ export const JoinAddressListContextProvider = ({identifier, children}) => {
}, [updateList, list]);
const [allAddresses, setAllAddresses] = useState([]);
const [playerAddresses, setPlayerAddresses] = useState([]);
const loadAddresses = useCallback(async () => {
const {data, error} = await fetchPlayerJoinAddresses(updateRequested, identifier, true);
const {data, error} = await fetchPlayerJoinAddresses(updateRequested, identifier, !loadIndividualAddresses);
setAllAddresses(data?.joinAddresses || [error]);
setPlayerAddresses(data?.joinAddressByPlayer || {});
}, [setAllAddresses, identifier, updateRequested]);
useEffect(() => {
loadAddresses();
}, [loadAddresses]);
const sharedState = useMemo(() => {
return {list, add, remove, replace, allAddresses};
}, [list, add, remove, replace, allAddresses]);
return {list, add, remove, replace, allAddresses, playerAddresses};
}, [list, add, remove, replace, allAddresses, playerAddresses]);
return (<JoinAddressListContext.Provider value={sharedState}>
{children}
</JoinAddressListContext.Provider>

View File

@ -1,24 +1,12 @@
import React from 'react';
import ExtendableRow from "../../components/layout/extension/ExtendableRow";
import {Col} from "react-bootstrap";
import LoadIn from "../../components/animation/LoadIn";
import PlayerRetentionGraphCard from "../../components/cards/common/PlayerRetentionGraphCard";
import {useAuth} from "../../hooks/authenticationHook";
import PlayerRetention from "../../components/cards/common/PlayerRetention.jsx";
const NetworkPlayerRetention = () => {
const {hasPermission} = useAuth();
const seeRetention = hasPermission('page.network.retention');
return (
<LoadIn>
{seeRetention && <section className="network-retention">
<ExtendableRow id={'row-network-retention-0'}>
<Col lg={12}>
<PlayerRetentionGraphCard identifier={null}/>
</Col>
</ExtendableRow>
</section>}
</LoadIn>
<PlayerRetention id={"network-retention"} identifier={null} seeRetention={seeRetention}/>
)
};

View File

@ -1,10 +1,7 @@
import React from 'react';
import ExtendableRow from "../../components/layout/extension/ExtendableRow";
import {Col} from "react-bootstrap";
import LoadIn from "../../components/animation/LoadIn";
import PlayerRetentionGraphCard from "../../components/cards/common/PlayerRetentionGraphCard";
import {useParams} from "react-router-dom";
import {useAuth} from "../../hooks/authenticationHook";
import PlayerRetention from "../../components/cards/common/PlayerRetention.jsx";
const ServerPlayerRetention = () => {
const {hasPermission} = useAuth();
@ -12,15 +9,7 @@ const ServerPlayerRetention = () => {
const seeRetention = hasPermission('page.server.retention');
return (
<LoadIn>
<section className="server-retention">
{seeRetention && <ExtendableRow id={'row-server-retention-0'}>
<Col lg={12}>
<PlayerRetentionGraphCard identifier={identifier}/>
</Col>
</ExtendableRow>}
</section>
</LoadIn>
<PlayerRetention id={"server-retention"} identifier={identifier} seeRetention={seeRetention}/>
)
};