-
-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement WACCA import from MYT (#1039)
* add inGameID to WACCA charts * add WACCA import support from MYT * add client support for api/myt-wacca * add bot support for api/myt-wacca * remove artistJP and titleJP from songs-wacca Turns out these aren't actually in the game data or anything, and we won't be able to get their values for omni songs or future songs added in plus. * add some missing omni WACCA songs * add test * fix tabs in test.conf.json5 * revamp grpc call handling a bit and add more error handling * feat: drag the protofiles in rather than alter build --------- Co-authored-by: zkldi <[email protected]>
- Loading branch information
Showing
55 changed files
with
7,182 additions
and
1,192 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
import { APIFetchV1 } from "util/api"; | ||
import { ErrorPage } from "app/pages/ErrorPage"; | ||
import useSetSubheader from "components/layout/header/useSetSubheader"; | ||
import ApiError from "components/util/ApiError"; | ||
import Divider from "components/util/Divider"; | ||
import FormInput from "components/util/FormInput"; | ||
import Loading from "components/util/Loading"; | ||
import useImport from "components/util/import/useImport"; | ||
import useApiQuery from "components/util/query/useApiQuery"; | ||
import { UserContext } from "context/UserContext"; | ||
import React, { useContext, useMemo, useReducer, useState } from "react"; | ||
import { Button, Form } from "react-bootstrap"; | ||
import { APIImportTypes, GetGameConfig, MytCardInfo } from "tachi-common"; | ||
import { SetState } from "types/react"; | ||
import Icon from "components/util/Icon"; | ||
import ImportStateRenderer from "./ImportStateRenderer"; | ||
|
||
interface Props { | ||
// Other games will be added in the future. | ||
game: "wacca"; | ||
} | ||
|
||
export default function MytIntegrationPage({ game }: Props) { | ||
const gameConfig = GetGameConfig(game); | ||
|
||
const [reload, shouldReloadCardInfo] = useReducer((x) => x + 1, 0); | ||
const [showEdit, setShowEdit] = useState(false); | ||
|
||
useSetSubheader(["Import Scores", `${gameConfig.name} Sync (MYT)`]); | ||
|
||
const { user } = useContext(UserContext); | ||
|
||
if (!user) { | ||
return <ErrorPage statusCode={401} />; | ||
} | ||
|
||
const { data, error } = useApiQuery<MytCardInfo | null>( | ||
`/users/${user.id}/integrations/myt`, | ||
undefined, | ||
[reload] | ||
); | ||
|
||
if (error) { | ||
return <ApiError error={error} />; | ||
} | ||
|
||
// null is a valid response for this call, so be explicit with going to loading | ||
if (data === undefined) { | ||
return <Loading />; | ||
} | ||
|
||
return ( | ||
<div> | ||
{(showEdit || !data) && ( | ||
<> | ||
<MytNeedsIntegrate | ||
onSubmit={async (cardAccessCode) => { | ||
const res = await APIFetchV1( | ||
`/users/${user.id}/integrations/myt`, | ||
{ | ||
method: "PUT", | ||
body: JSON.stringify({ cardAccessCode }), | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
}, | ||
true, | ||
true | ||
); | ||
|
||
if (res.success) { | ||
shouldReloadCardInfo(); | ||
} | ||
}} | ||
initialCardAccessCode={data?.cardAccessCode ?? undefined} | ||
/> | ||
<Divider /> | ||
</> | ||
)} | ||
{data && ( | ||
<MytImporter | ||
game={game} | ||
cardAccessCode={data.cardAccessCode} | ||
setShowEdit={setShowEdit} | ||
showEdit={showEdit} | ||
/> | ||
)} | ||
</div> | ||
); | ||
} | ||
|
||
function MytImporter({ | ||
game, | ||
cardAccessCode, | ||
showEdit, | ||
setShowEdit, | ||
}: Pick<Props, "game"> & { | ||
cardAccessCode: string; | ||
showEdit: boolean; | ||
setShowEdit: SetState<boolean>; | ||
}) { | ||
const importType: APIImportTypes = `api/myt-${game}`; | ||
|
||
const { importState, runImport } = useImport("/import/from-api", { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify({ | ||
importType, | ||
}), | ||
}); | ||
|
||
return ( | ||
<div> | ||
<h2 className="text-center mb-4"> | ||
Importing scores from MYT card{" "} | ||
<code>{cardAccessCode.match(/.{1,4}/gu)?.join(" ")}</code>{" "} | ||
<Icon | ||
onClick={() => setShowEdit(!showEdit)} | ||
type={showEdit ? "times" : "pencil-alt"} | ||
noPad | ||
/> | ||
. | ||
</h2> | ||
<Divider /> | ||
<div className="d-flex w-100 justify-content-center"> | ||
<Button | ||
className="mx-auto" | ||
variant="primary" | ||
onClick={() => runImport()} | ||
disabled={ | ||
importState.state === "waiting_init" || | ||
importState.state === "waiting_processing" | ||
} | ||
> | ||
{importState.state === "waiting_init" || | ||
importState.state === "waiting_processing" | ||
? "Syncing..." | ||
: "Click to Sync!"} | ||
</Button> | ||
</div> | ||
<Divider /> | ||
<div> | ||
Play on MYT a lot? You can synchronise your scores straight from the discord by | ||
typing <code>/sync</code>! | ||
</div> | ||
<Divider /> | ||
<ImportStateRenderer state={importState} /> | ||
</div> | ||
); | ||
} | ||
|
||
export function MytNeedsIntegrate({ | ||
initialCardAccessCode, | ||
onSubmit, | ||
}: { | ||
initialCardAccessCode?: string; | ||
onSubmit: (cardAccessCode: string) => Promise<void>; | ||
}) { | ||
const [cardAccessCode, setCardAccessCode] = useState(initialCardAccessCode ?? ""); | ||
|
||
// strip any whitespace the user feels like entering | ||
const realCardAccessCode = useMemo(() => cardAccessCode.replace(/\s+/gu, ""), [cardAccessCode]); | ||
|
||
const validCardAccessCode = useMemo( | ||
// Note: valid Myt cards must start with 0 or 3 as 0008, sega, or aime cards. | ||
// See https://sega.bsnk.me/misc/card_convert/#lookup-a-card / https://sega.bsnk.me/allnet/access_codes/ | ||
// We only implement this in client-side because it's just meant to be a | ||
// user hint, in case someone tries to enter an AIC code or something. | ||
() => /^[03][0-9]{19}$/u.exec(realCardAccessCode), | ||
[realCardAccessCode] | ||
); | ||
|
||
return ( | ||
<div> | ||
<h3 className="text-center mb-4">Set your MYT card.</h3> | ||
|
||
<FormInput | ||
fieldName="Card Access Code" | ||
setValue={setCardAccessCode} | ||
value={cardAccessCode} | ||
/> | ||
<Form.Label> | ||
This is the card access code that's displayed in game. It should be 20 digits. | ||
<br /> | ||
{cardAccessCode.length > 0 && !validCardAccessCode ? ( | ||
<span className="text-danger"> | ||
Invalid card access code. This should be exactly 20 digits as displayed in | ||
game. It may not be the same as the code on the back of your card. | ||
</span> | ||
) : ( | ||
cardAccessCode.length > 0 && <span className="text-success">Looking good!</span> | ||
)} | ||
</Form.Label> | ||
|
||
<Divider /> | ||
<div className="w-100 d-flex justify-content-center"> | ||
<Button | ||
disabled={!validCardAccessCode} | ||
onClick={() => onSubmit(realCardAccessCode)} | ||
> | ||
Submit Card Access Code | ||
</Button> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.