feat: just a bunch of cleanup

- no deduping, ui improvements, general fixups
This commit is contained in:
zkldi
2022-12-22 17:23:37 +00:00
parent 3b37e45335
commit c2bccd64ae
16 changed files with 128 additions and 67 deletions
@@ -98,9 +98,7 @@ function FervidexForm({
<br />
<span className="text-warning">
Warning: You should always import from your network first. Statically
imported scores have the bare minimum data (Lamp + EX Score + BP), but due
to score de-duplication rules, future network imports cannot append more
data (Timestamps, Graphs, etc.) to these scores.
imported scores have the bare minimum data (No timestamps!).
</span>
</Form.Text>
</Form.Group>
@@ -88,9 +88,7 @@ function KsHookSV6CForm({
<br />
<span className="text-warning">
Warning: You should always import from your network first. Statically
imported scores have the bare minimum data (Lamp + EX Score + BP), but due
to score de-duplication rules, future network imports cannot append more
data (Timestamps, Graphs, etc.) to these scores.
imported scores have the bare minimum data (No timestamps!).
</span>
</Form.Text>
</Form.Group>
@@ -259,8 +259,8 @@ function ImageForm({ reqUser }: { reqUser: UserDocument }) {
return (
<div>
<Alert variant="danger">
Remember Rule 6! Do not set inappropriate stuff as your avatar/banner. If you have
to ask, the answer is probably no.
Do not set inappropriate stuff as your avatar/banner. If you have to ask, the answer
is probably no.
</Alert>
<Form.Group>
<Form.Label>Profile Picture</Form.Label>
@@ -211,38 +211,37 @@ function PreferencesForm({ reqUser, game, playtype }: UGPT) {
</Form.Group>
)}
<Form.Group>
<Form.Label>Preferred Score Info</Form.Label>
<Form.Label>Preferred Folder Info</Form.Label>
<Form.Control
as="select"
id="scoreBucket"
value={formik.values.scoreBucket}
onChange={formik.handleChange}
>
<option value="grade">Score</option>
<option value="lamp">Lamp</option>
<option value="grade">Grades</option>
<option value="lamp">Lamps</option>
</Form.Control>
<Form.Text className="text-muted">
What should {TachiConfig.name} prefer to show you about scores?
<br />
Note: This will only affect defaults, such as what graph is shown in the folder
breakdown. You can still view all the same stats!
</Form.Text>
</Form.Group>
<Form.Group>
<Form.Label>Preferred Ranking</Form.Label>
<Form.Control
as="select"
id="preferredRanking"
value={formik.values.preferredRanking}
onChange={formik.handleChange}
>
<option value="global">Global Rankings</option>
<option value="rival">Rival Rankings</option>
</Form.Control>
<Form.Text className="text-muted">
What should {TachiConfig.name} default to when showing your score rankings?
What should {TachiConfig.name} default to showing you about folders?
</Form.Text>
</Form.Group>
{settings.rivals.length !== 0 && (
<Form.Group>
<Form.Label>Preferred Ranking</Form.Label>
<Form.Control
as="select"
id="preferredRanking"
value={formik.values.preferredRanking}
onChange={formik.handleChange}
>
<option value="global">Global Rankings</option>
<option value="rival">Rival Rankings</option>
</Form.Control>
<Form.Text className="text-muted">
What should {TachiConfig.name} default to when showing your score rankings?
</Form.Text>
</Form.Group>
)}
<Form.Group>
<Form.Label>Preferred Table</Form.Label>
<Form.Control
@@ -262,7 +261,7 @@ function PreferencesForm({ reqUser, game, playtype }: UGPT) {
))}
</Form.Control>
<Form.Text className="text-muted">
What folders would you like to see when you go to the folders page by default?
What folders would you like to see by default?
</Form.Text>
</Form.Group>
{game === "iidx" && (
@@ -572,13 +571,13 @@ function ManageAccount({ reqUser, game, playtype }: UGPT) {
<Col xs={12}>
<h4>Delete Score</h4>
If you have an invalid score, you can delete it by going to that score and clicking
"Manage Score".
"Delete Score".
</Col>
<Col xs={12} className="mt-8">
<h4>Undo Import</h4>
If you messed up an import, you can undo it by going to{" "}
<Link to={`/u/${reqUser.username}/imports`}>your imports page</Link> and click
"Manage Import".
"Revert Import".
</Col>
<Col xs={12} className="mt-8">
<h3>Completely Wipe Profile</h3>
@@ -1,5 +1,6 @@
import React, { Fragment } from "react";
import { Link } from "react-router-dom";
import { GetGameConfig } from "tachi-common";
export function Breadcrumbs({ items }: { items: string[] }) {
const url = location.pathname;
@@ -12,17 +13,28 @@ export function Breadcrumbs({ items }: { items: string[] }) {
<Link className="opacity-75 hover-opacity-100" to="/">
<i className="flaticon2-shelter text-white icon-1x" />
</Link>
{items.map((name, index) => (
<Fragment key={index}>
<span className="label label-dot label-sm bg-white opacity-75 mx-3" />
<Link
className="text-white text-hover-white opacity-75 hover-opacity-100"
to={`/${parts.slice(0, index + 1).join("/")}`}
>
{name}
</Link>
</Fragment>
))}
{items.map((name, index) => {
// Skip playtype in the breadcrumbs if the game only has one playtype.
// @hack
// this only works if the game has one playtype called "Single".
// Some games (like pop'n) have one playtype, but it's called
// 9B. They'll just have to cope.
if (items[index - 2] === "Games" && name === "Single") {
return;
}
return (
<Fragment key={index}>
<span className="label label-dot label-sm bg-white opacity-75 mx-3" />
<Link
className="text-white text-hover-white opacity-75 hover-opacity-100"
to={`/${parts.slice(0, index + 1).join("/")}`}
>
{name}
</Link>
</Fragment>
);
})}
</div>
);
}
@@ -129,6 +129,10 @@ function SessionScoreStatBreakdown({
Record<Grades[IDStrings], { score: ScoreDocument; scoreInfo: SessionScoreInfo }[]>
> = {};
// dedupe grades/lamps for repeat plays
const isBestGradeThisSession = new Map();
const isBestLampThisSession = new Map();
for (const scoreInfo of sessionData.session.scoreInfo) {
const score = scoreMap.get(scoreInfo.scoreID);
@@ -139,15 +143,51 @@ function SessionScoreStatBreakdown({
continue;
}
const g = isBestGradeThisSession.get(score.chartID);
const l = isBestLampThisSession.get(score.chartID);
// if we haven't saw this chart before
// or the score we have saw for this chart before had a worse grade
if (!g || (g.scoreData.gradeIndex && g < score.scoreData.gradeIndex)) {
isBestGradeThisSession.set(score.chartID, score);
}
// same but for lamp
if (!l || (l.scoreData.lampIndex && l < score.scoreData.lampIndex)) {
isBestLampThisSession.set(score.chartID, score);
}
}
for (const scoreInfo of sessionData.session.scoreInfo) {
const score = scoreMap.get(scoreInfo.scoreID);
if (!score) {
console.error(
`Session score info contains scoreID ${scoreInfo.scoreID}, but no score exists?`
);
continue;
}
const wasBestLamp = isBestLampThisSession.get(score.chartID)?.scoreID === score.scoreID;
const wasBestGrade =
isBestGradeThisSession.get(score.chartID)?.scoreID === score.scoreID;
if (scoreInfo.isNewScore) {
PartialArrayRecordAssign(newLamps, score.scoreData.lamp, { score, scoreInfo });
PartialArrayRecordAssign(newGrades, score.scoreData.grade, { score, scoreInfo });
if (wasBestLamp) {
PartialArrayRecordAssign(newLamps, score.scoreData.lamp, { score, scoreInfo });
}
if (wasBestGrade) {
PartialArrayRecordAssign(newGrades, score.scoreData.grade, {
score,
scoreInfo,
});
}
} else {
if (scoreInfo.lampDelta > 0) {
if (scoreInfo.lampDelta > 0 && wasBestLamp) {
PartialArrayRecordAssign(newLamps, score.scoreData.lamp, { score, scoreInfo });
}
if (scoreInfo.gradeDelta > 0) {
if (scoreInfo.gradeDelta > 0 && wasBestLamp) {
PartialArrayRecordAssign(newGrades, score.scoreData.grade, {
score,
scoreInfo,
@@ -17,7 +17,12 @@ export default function SelectableRanking({
} & ZTableTHProps) {
const { settings } = useLUGPTSettings();
if (rankingViewMode === "both-if-self" || rankingViewMode === "global-no-switch" || !settings) {
if (
rankingViewMode === "both-if-self" ||
rankingViewMode === "global-no-switch" ||
!settings ||
settings.rivals.length === 0
) {
return (
<SortableTH
changeSort={changeSort}
@@ -53,7 +53,7 @@ export default function ImportDropdown({ data }: { data: ImportDataset[0] }) {
{currentUser?.id === data.userID && (
<SelectButton setValue={setView} value={view} id="manage">
<Icon type="trash" />
Manage Import
Revert Import
</SelectButton>
)}
<HasDevModeOn>
@@ -166,18 +166,18 @@ export default function ScoreDropdown<I extends IDStrings = IDStrings>({
Goals & Quests{targetData && ` (${targetData.goals.length})`}
</SelectButton>
)}
{currentUser?.id === user.id && (
<SelectButton setValue={setView} value={view} id="manage">
<Icon type="trash" />
Manage Score
</SelectButton>
)}
{currentUser && (
<SelectButton setValue={setView} value={view} id="rivals">
<Icon type="users" />
Rivals
</SelectButton>
)}
{currentUser?.id === user.id && (
<SelectButton setValue={setView} value={view} id="manage">
<Icon type="trash" />
Delete Score
</SelectButton>
)}
<HasDevModeOn>
<SelectButton setValue={setView} value={view} id="debug">
<Icon type="bug" />
@@ -41,10 +41,7 @@ function Row({ data }: { data: ImportDataset[0] }) {
return (
<DropdownRow dropdown={<ImportDropdown data={data} />}>
<td>
<Link
className="gentle-link"
to={`/u/${data.__related.user.username}`}
>
<Link className="gentle-link" to={`/u/${data.__related.user.username}`}>
{data.__related.user.username}
</Link>
</td>
@@ -19,7 +19,7 @@ export default function ImportViewerOptions({
<>
<Card header="Options">
<Select
name="Made with intent (i.e. not by an automated hook or IR)"
name="Made with user intent (i.e. not an automatic upload by an IR or hook)"
allowNull
unselectedName="Either"
setValue={setUserIntent}
+1 -2
View File
@@ -1,5 +1,4 @@
import { Game, GetGamePTConfig } from "tachi-common";
import { Playtype } from "tachi-common";
import { Game, GetGamePTConfig, Playtype } from "tachi-common";
import useLUGPTSettings from "./useLUGPTSettings";
export function useBucket(game: Game, playtype: Playtype) {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "tachi",
"version": "2.2.0",
"version": "2.3.0-dev",
"private": true,
"description": "The root of the Tachi monorepo. Contains common utils like linters.",
"scripts": {
+1 -1
View File
@@ -18,7 +18,7 @@
REDIRECT_URI: "https://example.com",
},
ARC_AUTH_TOKEN: "unused",
OUR_URL: "https://127.0.0.1:8080/",
OUR_URL: "https://127.0.0.1:8080",
CLIENT_DEV_SERVER: "http://127.0.0.1:3000",
ENABLE_SERVER_HTTPS: true,
OPTIONS_ALWAYS_SUCCEEDS: true,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "tachi-server",
"version": "2.2.0",
"version": "2.3.0-dev",
"description": "A score tracking server.",
"main": "./js/main.js",
"files": [
+14 -1
View File
@@ -190,7 +190,20 @@ const err = p(config, {
MAX_QUEST_SUBSCRIPTIONS: p.optional(p.isPositiveInteger),
MAX_FOLLOWING_AMOUNT: p.optional(p.isPositiveInteger),
MAX_RIVALS: p.optional(p.isPositiveInteger),
OUR_URL: "string",
OUR_URL: (self) => {
if (typeof self !== "string") {
return "Expected a string.";
}
if (self.endsWith("/")) {
return `OUR_URL should not end with a trailing slash. Use ${self.substring(
0,
self.length - 1
)} instead.`;
}
return true;
},
INVITE_CODE_CONFIG: p.optional({
BATCH_SIZE: p.isPositiveInteger,
INVITE_CAP: p.isPositiveInteger,