Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perf/improve game console performance #314

Merged
merged 5 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ import { formatBudget } from "../../../../../lib/formatter";
import { useTranslation } from "../../../../translations";
import { synthesisConstants } from "../../../playerActions/constants/synthesis";
import { Icon } from "../../../../common/components/Icon";
import { usePlay, useTeamValues } from "../../../context/playContext";
import { usePlay } from "../../../context/playContext";
import { ITeam } from "../../../../../utils/types";
import { getDaysToEnergyShiftTargetYear } from "../../../../../lib/time";
import { Typography } from "../../../../common/components/Typography";
import { Tag } from "../../../../common/components/Tag";
import { ENERGY_SHIFT_TARGET_YEAR } from "../../../../common/constants";
import { CardStyled } from "../Synthesis.common.styles";
import { useTeamValuesForTeam } from "../../../context/hooks/shared";

export default SynthesisBudget;

function SynthesisBudget({ team }: { team: ITeam | null }) {
const { t } = useTranslation(["common", "countries"]);
const { game } = usePlay();
const daysTo2050 = getDaysToEnergyShiftTargetYear(new Date(game.date));
const { getTeamById } = useTeamValues();
const teamValues = getTeamById(team?.id);
const { teamValues } = useTeamValuesForTeam({ teamId: team?.id });

const budget = teamValues?.budgetSpent || 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,20 @@ import {
import { useTranslation } from "../../../../translations";
import { synthesisConstants } from "../../../playerActions/constants/synthesis";
import { Icon } from "../../../../common/components/Icon";
import { useTeamValues } from "../../../context/playContext";
import { ITeam } from "../../../../../utils/types";
import { Typography } from "../../../../common/components/Typography";
import { TagNumber } from "../../../../common/components/TagNumber";
import { Tag } from "../../../../common/components/Tag";
import { CardStyled } from "../Synthesis.common.styles";
import { useTeamValuesForTeam } from "../../../context/hooks/shared";

const CARBON_FOOTPRINT_TONS_THRESHOLD = 2;

export default SynthesisCarbon;

function SynthesisCarbon({ team }: { team: ITeam | null }) {
const { t } = useTranslation();
const { getTeamById } = useTeamValues();
const teamValues = getTeamById(team?.id);
const { teamValues } = useTeamValuesForTeam({ teamId: team?.id });
const teamCarbonFootprintInKgPerDay = teamValues?.carbonFootprint || 0;
const carbonFootprintReduction = teamValues?.carbonFootprintReduction || 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from "../../../../../lib/formatter";
import { useTranslation } from "../../../../translations";
import { Icon } from "../../../../common/components/Icon";
import { usePlay, useTeamValues } from "../../../context/playContext";
import { usePlay } from "../../../context/playContext";
import { ITeam } from "../../../../../utils/types";
import { Typography } from "../../../../common/components/Typography";
import { TagNumber } from "../../../../common/components/TagNumber";
Expand All @@ -15,14 +15,14 @@ import { ProductionDatum } from "../../../../persona/production";
import { isDecarbonatedEnergyProduction } from "../../../utils/production";
import { TagEnergy } from "../../../../common/components/TagEnergy";
import { CardStyled } from "../Synthesis.common.styles";
import { useTeamValuesForTeam } from "../../../context/hooks/shared";

export default SynthesisProduction;

function SynthesisProduction({ team }: { team: ITeam | null }) {
const { t } = useTranslation();
const { productionOfCountryToday } = usePlay();
const { getTeamById } = useTeamValues();
const teamValues = getTeamById(team?.id);
const { teamValues } = useTeamValuesForTeam({ teamId: team?.id });

const computeRenewableEnergyProduction = useCallback(
(production: ProductionDatum[] = []) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Box, Grid, useTheme } from "@mui/material";
import { PlayBox } from "../Components";
import { TeamIdToValues, usePlay, useTeamValues } from "../context/playContext";
import { TeamIdToValues, usePlay } from "../context/playContext";
import { ConsumptionStats, ProductionStats } from "./ProdStats";
import { Typography } from "../../common/components/Typography";
import { Icon } from "../../common/components/Icon";
Expand All @@ -13,6 +13,7 @@ import {
import { useTranslation } from "../../translations/useTranslation";
import { I18nTranslateFunction } from "../../translations";
import { IEnrichedGame, ITeam } from "../../../utils/types";
import { useTeamValues } from "../context/hooks/shared";

export { StatsConsole };

Expand Down
167 changes: 121 additions & 46 deletions packages/client/src/modules/play/GameConsole/TeamConsoleContent.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Box, Rating, useTheme } from "@mui/material";
import { ReactNode, useMemo } from "react";
import { PlayerChart } from "./PlayerChart";
import { PlayBox } from "../Components";
import { Icon } from "../../common/components/Icon";
Expand All @@ -17,29 +18,142 @@ import {
import { TeamActionsRecap } from "../Components/TeamActionsRecap";
import { getTeamActionsAtCurrentStep } from "../utils/teamActions";
import { sumAllValues } from "../../persona";
import { useMemo } from "react";
import SynthesisRecapForTeacher from "../Components/Synthesis/SynthesisRecapForTeacher";

export { TeamConsoleContent };

function TeamConsoleContent({ team }: { team: ITeam }) {
const { game, players, productionActionById } = usePlay();
const currentStep = useCurrentStep();

const isProductionStep = currentStep?.type === "production";
const isSynthesisStep = currentStep?.id === "final-situation";

if (isSynthesisStep) {
return <TeamConsoleContentSynthesis team={team} />;
} else if (isProductionStep) {
return <TeamConsoleContentProduction team={team} />;
} else {
return <TeamConsoleContentConsumption team={team} />;
}
}

function TeamConsoleContentConsumption({ team }: { team: ITeam }) {
const { players } = usePlay();
const playersInTeam = useMemo(
() => players.filter((p) => p.teamId === team.id),
[players, team]
);

const isProductionStep = currentStep?.type === "production";
const isSynthesisStep = currentStep?.id === "final-situation";
return (
<TeamConsoleContentLayout
team={team}
content={
<>
<Box display="grid" gap={2} gridTemplateColumns="1fr 2fr">
<Box display="flex" flexDirection="column" gap={2}>
{playersInTeam.map((player) => (
<PlayerConsumption key={player.user.id} player={player} />
))}
</Box>
<Box display="flex" flexDirection="column" gap={2}>
<Box>
<PlayerChart team={team} />
</Box>
</Box>
</Box>
</>
}
/>
);
}

function TeamConsoleContentProduction({ team }: { team: ITeam }) {
const { game, players, productionActionById } = usePlay();
const playersInTeam = useMemo(
() => players.filter((p) => p.teamId === team.id),
[players, team]
);

const teamActionsAtCurrentStep = getTeamActionsAtCurrentStep(
game.step,
team.actions,
productionActionById
);
const PlayerComponent = getPlayerComponent(isProductionStep, isSynthesisStep);

return (
<TeamConsoleContentLayout
team={team}
content={
<>
<Box display="grid" gap={2} gridTemplateColumns="1fr 2fr">
<Box display="flex" flexDirection="column" gap={2}>
{playersInTeam.map((player) => (
<PlayerProduction key={player.user.id} player={player} />
))}
</Box>
<Box display="flex" flexDirection="column" gap={2}>
<Box>
<PlayBox>
<TeamActionsRecap
title="Actions Production"
teamActions={teamActionsAtCurrentStep}
showCredibility
showProductionValue
/>
</PlayBox>
</Box>
<Box>
<PlayerChart team={team} />
</Box>
</Box>
</Box>
</>
}
/>
);
}

function TeamConsoleContentSynthesis({ team }: { team: ITeam }) {
const { players } = usePlay();
const playersInTeam = useMemo(
() => players.filter((p) => p.teamId === team.id),
[players, team]
);

return (
<TeamConsoleContentLayout
team={team}
content={
<>
<PlayBox>
<SynthesisRecapForTeacher team={team} />
</PlayBox>

<Box display="grid" gap={2} gridTemplateColumns="1fr 2fr">
<Box display="flex" flexDirection="column" gap={2}>
{playersInTeam.map((player) => (
<PlayerSynthesis key={player.user.id} player={player} />
))}
</Box>
<Box display="flex" flexDirection="column" gap={2}>
<Box>
<PlayerChart team={team} />
</Box>
</Box>
</Box>
</>
}
/>
);
}

function TeamConsoleContentLayout({
team,
content,
}: {
team: ITeam;
content: ReactNode;
}) {
return (
<PlayBox display="flex" flexDirection="column" gap={3} mt={2} pt={3}>
<Typography
Expand All @@ -52,52 +166,13 @@ function TeamConsoleContent({ team }: { team: ITeam }) {
{team.scenarioName ? ` - ${team.scenarioName}` : ""}
</Typography>

{isSynthesisStep && (
<PlayBox>
<SynthesisRecapForTeacher team={team} />
</PlayBox>
)}

<Box display="grid" gap={2} gridTemplateColumns="1fr 2fr">
<Box display="flex" flexDirection="column" gap={2}>
{playersInTeam.map((player) => (
<PlayerComponent key={player.user.id} player={player} />
))}
</Box>
<Box display="flex" flexDirection="column" gap={2}>
{isProductionStep && (
<Box>
<PlayBox>
<TeamActionsRecap
title="Actions Production"
teamActions={teamActionsAtCurrentStep}
showCredibility
showProductionValue
/>
</PlayBox>
</Box>
)}
<Box>
<PlayerChart team={team} />
</Box>
</Box>
<Box display="flex" flexDirection="column" gap={3}>
{content}
</Box>
</PlayBox>
);
}

function getPlayerComponent(
isProductionStep: boolean,
isSynthesisStep: boolean
) {
if (isSynthesisStep) {
return PlayerSynthesis;
} else if (isProductionStep) {
return PlayerProduction;
}
return PlayerConsumption;
}

function PlayerSynthesis({ player }: { player: Player }) {
const { latestPersona } = usePersonaByUserId(player.userId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Link } from "react-router-dom";
import { useAuth } from "../../auth/authProvider";
import GameStepper from "../../common/components/Stepper";
import { PlayBox } from "../Components";
import { useCurrentStep, usePlay, useTeamValues } from "../context/playContext";
import { useCurrentStep, usePlay } from "../context/playContext";
import { Icon } from "../../common/components/Icon";
import {
formatPoints,
Expand All @@ -26,6 +26,7 @@ import { useTranslation } from "../../translations/useTranslation";
import { useCurrentPlayer, usePersona } from "../context/hooks/player";
import { Button } from "../../common/components/Button";
import { useMemo } from "react";
import { useTeamValues } from "../context/hooks/shared";

export { PlayerHeader };

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { useTeamValues } from "./useTeamValues";
export { useTeamValuesForTeam } from "./useTeamValuesForTeam";
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useMemo } from "react";
import { TeamValues, usePersonaByUserId, usePlay } from "../../playContext";
import { buildTeamValues } from "./utils";

export { useTeamValues };

function useTeamValues(): {
teamValues: TeamValues[];
getTeamById: (id: number | undefined) => TeamValues | undefined;
} {
const { game, players, teams } = usePlay();

const userIds: number[] = useMemo(
() => players.map((p) => p.userId),
[players]
);
const personaByUserId = usePersonaByUserId(userIds);

const teamValues = useMemo(() => {
return teams.map((team) => {
return buildTeamValues({
game,
personaByUserId,
players,
team,
});
});
// TODO: check `personaByUserId` in deps doesn't trigger infinite renders.
}, [game, personaByUserId, players, teams]);

const getTeamById = (id: number | undefined) => {
return teamValues.find((t) => t.id === id);
};

return {
teamValues,
getTeamById,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useMemo } from "react";
import { TeamValues, usePersonaByUserId, usePlay } from "../../playContext";
import { buildTeamValues } from "./utils";

export { useTeamValuesForTeam };

function useTeamValuesForTeam({ teamId }: { teamId?: number }): {
teamValues: TeamValues;
} {
const { game, players, teams } = usePlay();

const userIds: number[] = useMemo(
() => players.filter((p) => p.teamId === teamId).map((p) => p.userId),
[players, teamId]
);
const personaByUserId = usePersonaByUserId(userIds);

const teamValues = useMemo(() => {
const team = teams.find((t) => t.id === teamId);
return buildTeamValues({ game, personaByUserId, players, team });
}, [game, personaByUserId, players, teamId, teams]);

return {
teamValues,
};
}
Loading
Loading