feat: clump successive goals

This commit is contained in:
zkldi
2022-11-26 19:01:46 +00:00
parent 2c3c363fe8
commit bc092b0a11
4 changed files with 107 additions and 28 deletions
+57 -12
View File
@@ -9,7 +9,6 @@ import SessionRaiseBreakdown from "components/sessions/SessionRaiseBreakdown";
import ScoreTable from "components/tables/scores/ScoreTable";
import ApiError from "components/util/ApiError";
import Divider from "components/util/Divider";
import GoalLink from "components/util/GoalLink";
import Icon from "components/util/Icon";
import LinkButton from "components/util/LinkButton";
import Loading from "components/util/Loading";
@@ -31,6 +30,7 @@ import {
ClumpedActivityScores,
ClumpedActivitySession,
} from "types/tachi";
import { InnerQuestSectionGoal } from "components/targets/quests/Quest";
// Records activity for a group of users on a GPT. Also used for single users.
export default function Activity({
@@ -161,7 +161,7 @@ function ActivityInner({
user={user}
/>
);
case "GOAL_ACHIEVEMENT":
case "GOAL_ACHIEVEMENTS":
return (
<GoalActivity
shouldShowGame={shouldShowGame}
@@ -209,9 +209,11 @@ function ActivityInner({
case "SESSION":
lastTimestamp = lastThing.timeStarted;
break;
case "GOAL_ACHIEVEMENT":
case "GOAL_ACHIEVEMENTS":
lastTimestamp = lastThing.goals[0]?.timeAchieved;
break;
case "QUEST_ACHIEVEMENT":
lastTimestamp = lastThing.sub.timeAchieved!;
lastTimestamp = lastThing.sub.timeAchieved;
}
if (!lastTimestamp) {
@@ -342,31 +344,74 @@ function GoalActivity({
user: UserDocument;
shouldShowGame: boolean;
}) {
const { game, playtype } = data.goal;
const { game, playtype } = data.goals[0];
const prettyGame = shouldShowGame ? FormatGame(game, playtype) : "";
const prettyGame = shouldShowGame ? `${FormatGame(game, playtype)} ` : "";
const [show, setShow] = useState(false);
let subMessage;
let mutedText: string | null | undefined;
if (data.goals.length === 1) {
const goal0 = data.goals[0];
subMessage = `${goal0.__related.goal.name}${
shouldShowGame ? `in ${FormatGame(game, playtype)}` : ""
}!`;
} else {
subMessage = `${data.goals.length} ${prettyGame}goals`;
mutedText = TruncateString(data.goals.map((e) => e.__related.goal.name).join(", "), 100);
}
return (
<div className="timeline-item timeline-hover my-4">
<div className="timeline-badge bg-warning"></div>
<div className="timeline-content">
<div className="timeline-content-inner">
<div className="timeline-content-inner" onClick={() => setShow(!show)}>
<div className="timeline-content-title">
<span style={{ fontSize: "1.15rem" }}>
<Icon
type={`chevron-${show ? "down" : "right"}`}
style={{
fontSize: "0.75rem",
}}
/>
<span style={{ fontSize: "1.15rem" }} className="ml-2">
<UGPTLink reqUser={user} game={game} playtype={playtype} /> achieved{" "}
<GoalLink noPad goal={data.goal} />
{prettyGame && ` in ${prettyGame}`}!
{subMessage}!
</span>
{mutedText && (
<>
<br />
<Muted>{mutedText}</Muted>
</>
)}
</div>
<div className="timeline-content-timestamp">
{MillisToSince(data.sub.timeAchieved ?? 0)}
{MillisToSince(data.goals[0]?.timeAchieved ?? 0)}
<br />
<span className="text-muted font-italic text-right">
{FormatTime(data.sub.timeAchieved ?? 0)}
{FormatTime(data.goals[0]?.timeAchieved ?? 0)}
</span>
</div>
</div>
{show && (
<>
<Divider />
<div className="pl-4">
{data.goals.map((e) => (
<InnerQuestSectionGoal
goal={e.__related.goal}
goalSubOverride={e}
key={e.goalID}
/>
))}
</div>
</>
)}
</div>
</div>
);
+10 -2
View File
@@ -9,7 +9,13 @@ import Muted from "components/util/Muted";
import { UserContext } from "context/UserContext";
import React, { useContext, useState } from "react";
import { Badge, Button } from "react-bootstrap";
import { FormatGame, GoalDocument, QuestDocument, QuestSection } from "tachi-common";
import {
FormatGame,
GoalDocument,
GoalSubscriptionDocument,
QuestDocument,
QuestSection,
} from "tachi-common";
import { GamePT } from "types/react";
import { TargetsContext } from "context/TargetsContext";
import QuickTooltip from "components/layout/misc/QuickTooltip";
@@ -195,14 +201,16 @@ export function InnerQuestSectionGoal({
goal,
note,
dependencies,
goalSubOverride,
}: {
goal: GoalDocument;
note?: string;
dependencies?: string[];
goalSubOverride?: GoalSubscriptionDocument;
}) {
const { goalSubs } = useContext(TargetsContext);
const goalSub = goalSubs.get(goal.goalID);
const goalSub = goalSubOverride ?? goalSubs.get(goal.goalID);
if (!goalSub) {
return (
+2 -3
View File
@@ -25,11 +25,10 @@ export type ClumpedActivityClassAchievement = {
} & ClassAchievementDocument;
export type ClumpedActivityGoalAchievement = {
type: "GOAL_ACHIEVEMENT";
type: "GOAL_ACHIEVEMENTS";
// redundant, but convenient.
userID: integer;
sub: GoalSubscriptionDocument;
goal: GoalDocument;
goals: Array<GoalSubscriptionDocument & { __related: { goal: GoalDocument } }>;
};
export type ClumpedActivityQuestAchievement = {
+38 -11
View File
@@ -1,5 +1,9 @@
import { ActivityReturn, RecordActivityReturn } from "types/api-returns";
import { ClumpedActivity, ClumpedActivityScores } from "types/tachi";
import {
ClumpedActivity,
ClumpedActivityGoalAchievement,
ClumpedActivityScores,
} from "types/tachi";
import { ONE_HOUR } from "./constants/time";
import { CreateSongMap, CreateChartMap, CreateGoalMap } from "./data";
import { CreateQuestMap } from "./misc";
@@ -13,7 +17,8 @@ const SORT_ACTIVITY = NumericSOV((x: ClumpedActivity[0]) => {
return x.scores[0]?.timeAchieved ?? -Infinity;
case "CLASS_ACHIEVEMENT":
return x.timeAchieved;
case "GOAL_ACHIEVEMENT":
case "GOAL_ACHIEVEMENTS":
return x.goals[0]?.timeAchieved ?? -Infinity;
case "QUEST_ACHIEVEMENT":
return x.sub.timeAchieved ?? -Infinity;
}
@@ -95,24 +100,46 @@ export function ClumpActivity(data: ActivityReturn | RecordActivityReturn): Clum
}
const goalMap = CreateGoalMap(data.goals);
for (const sub of data.goalSubs) {
let goalClump: ClumpedActivityGoalAchievement["goals"] = [];
// reset last clumps. I realise this is super copypasta, but we'll live.
curUserID = null;
lastTime = null;
for (const sub of data.goalSubs
.slice(0)
.sort(NumericSOV((x) => x.timeAchieved ?? -Infinity, true))) {
const goal = goalMap.get(sub.goalID);
if (!goal) {
console.warn(
`Couldn't find parent goal for recently-achieved-sub '${sub.goalID}. Skipping.'`
);
console.warn(`Failed to resolve goal ${sub.goalID}. Skipping.`);
continue;
}
clumped.push({
type: "GOAL_ACHIEVEMENT",
userID: sub.userID,
sub,
goal,
if (
(curUserID !== sub.userID && curUserID !== null) ||
(lastTime !== null && lastTime - (sub.timeAchieved ?? 0) > ONE_HOUR * 8)
) {
clumped.push({ type: "GOAL_ACHIEVEMENTS", userID: sub.userID, goals: goalClump });
goalClump = [];
}
curUserID = sub.userID;
lastTime = sub.timeAchieved;
goalClump.push({
...sub,
__related: {
goal,
},
});
}
if (goalClump.length !== 0) {
clumped.push({ type: "GOAL_ACHIEVEMENTS", userID: goalClump[0].userID, goals: goalClump });
}
const questMap = CreateQuestMap(data.quests);
for (const sub of data.questSubs) {