fix: use vue-query; make less useless api call
This commit is contained in:
@@ -3,4 +3,5 @@ import { defineConfig } from '@hey-api/openapi-ts';
|
||||
export default defineConfig({
|
||||
input: `http://localhost:7777/api/openapi.json`,
|
||||
output: 'src/api/',
|
||||
plugins: ['@hey-api/typescript', '@hey-api/sdk', '@tanstack/vue-query'],
|
||||
});
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.1.16",
|
||||
"@tanstack/query-persist-client-core": "^5.104.3",
|
||||
"@tanstack/query-sync-storage-persister": "^5.104.3",
|
||||
"@tanstack/vue-query": "^5.104.3",
|
||||
"@tanstack/vue-table": "^8.21.3",
|
||||
"@vueuse/core": "^14.0.0",
|
||||
"dayjs": "^1.11.20",
|
||||
|
||||
Generated
+74
@@ -11,6 +11,15 @@ importers:
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.1.16
|
||||
version: 4.1.16(vite@7.1.12(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.4))
|
||||
'@tanstack/query-persist-client-core':
|
||||
specifier: ^5.104.3
|
||||
version: 5.104.3
|
||||
'@tanstack/query-sync-storage-persister':
|
||||
specifier: ^5.104.3
|
||||
version: 5.104.3
|
||||
'@tanstack/vue-query':
|
||||
specifier: ^5.104.3
|
||||
version: 5.104.3(vue@3.5.22(typescript@5.9.3))
|
||||
'@tanstack/vue-table':
|
||||
specifier: ^8.21.3
|
||||
version: 8.21.3(vue@3.5.22(typescript@5.9.3))
|
||||
@@ -515,10 +524,32 @@ packages:
|
||||
peerDependencies:
|
||||
vite: ^5.2.0 || ^6 || ^7
|
||||
|
||||
'@tanstack/match-sorter-utils@9.2.6':
|
||||
resolution: {integrity: sha512-DIpUSOhPfQ7u/qfUi/Wgm0wBO56MyBYXGvnlI0NcauDRPqdBUlAZZjW2Dxm92m+k1UWVhKw8YJ4f47+75Aujyw==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
'@tanstack/query-core@5.104.3':
|
||||
resolution: {integrity: sha512-DZY9TuJAOgU3UyHUhXb4CcTYEHNZszlwkPg4VViIwTxOKzLprPUU+rbaPZQd3srqkGOtpaDdKpW972UV/LKlcg==}
|
||||
|
||||
'@tanstack/query-persist-client-core@5.104.3':
|
||||
resolution: {integrity: sha512-6nhc/QbMpkKeuAM81SFSdcsJ9B8dm3fGHPODbau2cRSDyqcXiSl7sXQM7POVktch/YdgIGYPIQyZOQQaRarHMA==}
|
||||
|
||||
'@tanstack/query-sync-storage-persister@5.104.3':
|
||||
resolution: {integrity: sha512-sNM9X7QZdTHBlzHuNxuHQjECn3/zncuMgw5pesOFXu8XQ01UXBGASdMKZSRaxfNOhigRjm5souHhfmByN97NEg==}
|
||||
|
||||
'@tanstack/table-core@8.21.3':
|
||||
resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@tanstack/vue-query@5.104.3':
|
||||
resolution: {integrity: sha512-bdVlhyEFPllLfHioHx8kwtCAQ1YJQrvYczlsNzyiYFsp6n5yz9isNXiy6MIu+QGmG+RyghVdnp1UmiNDqLS22g==}
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.1.2
|
||||
vue: ^2.6.0 || ^3.3.0
|
||||
peerDependenciesMeta:
|
||||
'@vue/composition-api':
|
||||
optional: true
|
||||
|
||||
'@tanstack/vue-table@8.21.3':
|
||||
resolution: {integrity: sha512-rusRyd77c5tDPloPskctMyPLFEQUeBzxdQ+2Eow4F7gDPlPOB1UnnhzfpdvqZ8ZyX2rRNGmqNnQWm87OI2OQPw==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -961,6 +992,9 @@ packages:
|
||||
regenerator-runtime@0.13.11:
|
||||
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
|
||||
|
||||
remove-accents@0.5.0:
|
||||
resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==}
|
||||
|
||||
resolve-pkg-maps@1.0.0:
|
||||
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
|
||||
|
||||
@@ -1072,6 +1106,17 @@ packages:
|
||||
vscode-uri@3.1.0:
|
||||
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
|
||||
|
||||
vue-demi@0.14.10:
|
||||
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.0.0-rc.1
|
||||
vue: ^3.0.0-0 || ^2.6.0
|
||||
peerDependenciesMeta:
|
||||
'@vue/composition-api':
|
||||
optional: true
|
||||
|
||||
vue-i18n@11.4.12:
|
||||
resolution: {integrity: sha512-yvArvvo3ErLKFNlQrZUGmRkxL/ctM5xFYtE9FxTKiE/PalWP1bnTopBgbSRGLluYEyYSNl1Pt8l2J7zVavbXww==}
|
||||
engines: {node: '>= 22'}
|
||||
@@ -1445,8 +1490,31 @@ snapshots:
|
||||
tailwindcss: 4.1.16
|
||||
vite: 7.1.12(@types/node@24.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.4)
|
||||
|
||||
'@tanstack/match-sorter-utils@9.2.6':
|
||||
dependencies:
|
||||
remove-accents: 0.5.0
|
||||
|
||||
'@tanstack/query-core@5.104.3': {}
|
||||
|
||||
'@tanstack/query-persist-client-core@5.104.3':
|
||||
dependencies:
|
||||
'@tanstack/query-core': 5.104.3
|
||||
|
||||
'@tanstack/query-sync-storage-persister@5.104.3':
|
||||
dependencies:
|
||||
'@tanstack/query-core': 5.104.3
|
||||
'@tanstack/query-persist-client-core': 5.104.3
|
||||
|
||||
'@tanstack/table-core@8.21.3': {}
|
||||
|
||||
'@tanstack/vue-query@5.104.3(vue@3.5.22(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@tanstack/match-sorter-utils': 9.2.6
|
||||
'@tanstack/query-core': 5.104.3
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.22(typescript@5.9.3)
|
||||
vue-demi: 0.14.10(vue@3.5.22(typescript@5.9.3))
|
||||
|
||||
'@tanstack/vue-table@8.21.3(vue@3.5.22(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@tanstack/table-core': 8.21.3
|
||||
@@ -1863,6 +1931,8 @@ snapshots:
|
||||
|
||||
regenerator-runtime@0.13.11: {}
|
||||
|
||||
remove-accents@0.5.0: {}
|
||||
|
||||
resolve-pkg-maps@1.0.0: {}
|
||||
|
||||
rfdc@1.4.1: {}
|
||||
@@ -1962,6 +2032,10 @@ snapshots:
|
||||
|
||||
vscode-uri@3.1.0: {}
|
||||
|
||||
vue-demi@0.14.10(vue@3.5.22(typescript@5.9.3)):
|
||||
dependencies:
|
||||
vue: 3.5.22(typescript@5.9.3)
|
||||
|
||||
vue-i18n@11.4.12(vue@3.5.22(typescript@5.9.3)):
|
||||
dependencies:
|
||||
'@intlify/core-base': 11.4.12
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from "vue";
|
||||
import { computed } from "vue";
|
||||
import { RouterView, useRoute } from "vue-router";
|
||||
import AppHeader from "@/layout/AppHeader.vue";
|
||||
import AppFooter from "@/layout/AppFooter.vue";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
|
||||
const userStore = useUserStore();
|
||||
const route = useRoute();
|
||||
// Embed routes (e.g. place/embed) are meant to be dropped into an <iframe>
|
||||
// as-is - the header/footer chrome would just eat into the fixed space a
|
||||
// stream overlay or OBS browser source gives it.
|
||||
const isEmbed = computed(() => route.meta.embed === true);
|
||||
|
||||
onMounted(() => {
|
||||
userStore.loadUserData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import { ref, computed } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useTitle } from "@vueuse/core";
|
||||
import { gamesApiListEventGames } from "./api";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import { eventGamesQuery } from "@/queries";
|
||||
import type { GamesApiListEventGamesResponse } from "./api/types.gen";
|
||||
|
||||
interface Props {
|
||||
@@ -16,8 +17,7 @@ const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
useTitle(computed(() => t("home.title")));
|
||||
|
||||
const games = ref<GamesApiListEventGamesResponse | undefined>();
|
||||
const loading = ref(true);
|
||||
const { data: games, isPending: loading } = useQuery(eventGamesQuery(props.eventPath));
|
||||
const hasSteamGames = computed(() => games.value?.some((game) => game.steam_app_id) ?? false);
|
||||
const imageErrors = ref<Set<number>>(new Set());
|
||||
|
||||
@@ -40,15 +40,6 @@ const goToGame = (game: GamesApiListEventGamesResponse[number]) => {
|
||||
router.push(`/${props.eventPath}${path}`);
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
const gamesResponse = await gamesApiListEventGames({
|
||||
path: { event_path: props.eventPath },
|
||||
});
|
||||
if (gamesResponse.data) {
|
||||
games.value = gamesResponse.data;
|
||||
}
|
||||
loading.value = false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -9,10 +9,11 @@ import Results from "@/components/Results.vue";
|
||||
import Winners from "@/components/Winners.vue";
|
||||
import PuzzleResults from "@/components/PuzzleResults.vue";
|
||||
import TopUsersLeaderboard from "@/components/TopUsersLeaderboard.vue";
|
||||
import { polylanSubmitterApiGetUserInfo, opusMagnumApiGetCollection } from "@/api";
|
||||
import { opusMagnumApiGetCollection } from "@/api";
|
||||
import { apiService, errorHelpers } from "@/services/apiService";
|
||||
import { usePuzzlesStore } from "@/stores/puzzles";
|
||||
import { useSubmissionsStore } from "@/stores/submissions";
|
||||
import { queryClient, userQuery } from "@/queries";
|
||||
import { provideEventPath } from "@/composables/useEventPath";
|
||||
import type { PuzzleResponse, UserInfo, SteamCollection } from "@/types";
|
||||
import { useCountdown } from "@vueuse/core";
|
||||
@@ -74,13 +75,7 @@ async function initialize() {
|
||||
|
||||
// Load user info
|
||||
console.log("Loading user info...");
|
||||
const userResponse = await polylanSubmitterApiGetUserInfo();
|
||||
if (userResponse.data) {
|
||||
userInfo.value = userResponse.data as UserInfo;
|
||||
console.log("User info loaded:", userResponse.data);
|
||||
} else if (userResponse.error) {
|
||||
console.warn("User info error:", userResponse.error);
|
||||
}
|
||||
userInfo.value = (await queryClient.fetchQuery(userQuery()).catch(() => null)) as UserInfo | null;
|
||||
|
||||
// Load collection data
|
||||
console.log("Loading collection...");
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, computed } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useTitle } from "@vueuse/core";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { useQueryClient } from "@tanstack/vue-query";
|
||||
import { useUser } from "@/composables/useUser";
|
||||
import { eventGamesQuery } from "@/queries";
|
||||
import { usePageHeaderStore } from "@/stores/pageHeader";
|
||||
import { gamesApiListEventGames, submissionsApiListAvailableScopes, submissionsApiCreateSubmission, submissionsApiListUserSubmissions, submissionsApiCreateImageCrop, submissionsApiDeleteImageCrop, submissionsApiProcessFileOcr, submissionsApiProcessCropOcr } from "@/api";
|
||||
import { submissionsApiListAvailableScopes, submissionsApiCreateSubmission, submissionsApiListUserSubmissions, submissionsApiCreateImageCrop, submissionsApiDeleteImageCrop, submissionsApiProcessFileOcr, submissionsApiProcessCropOcr } from "@/api";
|
||||
import { useSubmissionsWebSocket } from "@/composables/useSubmissionsWebSocket";
|
||||
import { provideEventPath } from "@/composables/useEventPath";
|
||||
|
||||
@@ -31,8 +32,8 @@ const gameSteamId = ref("");
|
||||
|
||||
useTitle(computed(() => t("submissions.pageTitle", { game: gameName.value, scope: scopeName.value })));
|
||||
|
||||
const userStore = useUserStore();
|
||||
const { userInfo } = storeToRefs(userStore);
|
||||
const queryClient = useQueryClient();
|
||||
const userInfo = useUser();
|
||||
|
||||
const uploadedFiles = ref<File[]>([]);
|
||||
const isDragover = ref(false);
|
||||
@@ -503,17 +504,16 @@ const handlePasteOnUploadArea = (event: ClipboardEvent) => {
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
userStore.loadUserData();
|
||||
loadUserSubmissions();
|
||||
|
||||
const [gamesResponse, scopesResponse] = await Promise.all([
|
||||
gamesApiListEventGames({ path: { event_path: props.eventPath } }),
|
||||
const [games, scopesResponse] = await Promise.all([
|
||||
queryClient.ensureQueryData(eventGamesQuery(props.eventPath)),
|
||||
submissionsApiListAvailableScopes({
|
||||
path: { event_path: props.eventPath, game_path: props.gamePath },
|
||||
}),
|
||||
]);
|
||||
|
||||
const game = gamesResponse.data?.find((g) => g.path === props.gamePath);
|
||||
const game = games.find((g) => g.path === props.gamePath);
|
||||
if (game) {
|
||||
gameName.value = game.name;
|
||||
gameImage.value = game.image ?? "";
|
||||
|
||||
@@ -3,7 +3,9 @@ import { computed, onMounted, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useTitle } from "@vueuse/core";
|
||||
import { gamesApiListEventGames, submissionsApiListAvailableScopes } from "@/api";
|
||||
import { submissionsApiListAvailableScopes } from "@/api";
|
||||
import { useQueryClient } from "@tanstack/vue-query";
|
||||
import { eventGamesQuery } from "@/queries";
|
||||
import type { ScopeOut } from "@/api/types.gen";
|
||||
|
||||
interface Props {
|
||||
@@ -14,6 +16,7 @@ interface Props {
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
const { t } = useI18n();
|
||||
useTitle(computed(() => t("submissionsSelect.title")));
|
||||
|
||||
@@ -32,14 +35,14 @@ const goToScope = (scope: ScopeOut) => {
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
const [gamesResponse, scopesResponse] = await Promise.all([
|
||||
gamesApiListEventGames({ path: { event_path: props.eventPath } }),
|
||||
const [games, scopesResponse] = await Promise.all([
|
||||
queryClient.ensureQueryData(eventGamesQuery(props.eventPath)),
|
||||
submissionsApiListAvailableScopes({
|
||||
path: { event_path: props.eventPath, game_path: props.gamePath },
|
||||
}),
|
||||
]);
|
||||
|
||||
gameName.value = gamesResponse.data?.find((game) => game.path === props.gamePath)?.name ?? "";
|
||||
gameName.value = games.find((game) => game.path === props.gamePath)?.name ?? "";
|
||||
|
||||
const availableScopes = scopesResponse.data ?? [];
|
||||
|
||||
|
||||
@@ -0,0 +1,947 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
import { type DefaultError, type InfiniteData, infiniteQueryOptions, queryOptions, type UseMutationOptions } from '@tanstack/vue-query';
|
||||
|
||||
import { client } from '../client.gen';
|
||||
import { animationsApiPuzzleResults, animationsApiResults, animationsApiTopSubmissions, clockApiCurrentState, clockApiMyStats, gamesApiListEventGames, gamesApiListEventSteamGames, gamesApiListGames, marketApiBetHistory, marketApiCloseMarket, marketApiCreateBet, marketApiGetUserPoints, marketApiListMarkets, marketApiListUserBets, marketApiResolveMarket, noitaApiGetLeaderboard, noitaApiGetResults, noitaApiSubmitLogFile, type Options, opusMagnumApiCreateSubmission, opusMagnumApiDeleteSubmission, opusMagnumApiGetCollection, opusMagnumApiGetStats, opusMagnumApiGetSubmission, opusMagnumApiListPuzzles, opusMagnumApiListResponsesNeedingValidation, opusMagnumApiListSubmissions, opusMagnumApiValidateAuto, opusMagnumApiValidateResponse, opusMagnumApiValidateSubmission, placeApiCanvas, placeApiEvolutionGif, polylanSubmitterApiClearCache, polylanSubmitterApiGetUserInfo, polylanSubmitterApiHealthCheck, submissionsApiCreateImageCrop, submissionsApiCreateSubmission, submissionsApiDeleteImageCrop, submissionsApiGetSubmission, submissionsApiListAvailableScopes, submissionsApiListMyScopes, submissionsApiListSubmissionsByGame, submissionsApiListUserSubmissions, submissionsApiProcessCropOcr, submissionsApiProcessFileOcr } from '../sdk.gen';
|
||||
import type { AnimationsApiPuzzleResultsData, AnimationsApiPuzzleResultsResponse, AnimationsApiResultsData, AnimationsApiResultsResponse, AnimationsApiTopSubmissionsData, AnimationsApiTopSubmissionsResponse, ClockApiCurrentStateData, ClockApiCurrentStateResponse, ClockApiMyStatsData, ClockApiMyStatsResponse, GamesApiListEventGamesData, GamesApiListEventGamesResponse, GamesApiListEventSteamGamesData, GamesApiListEventSteamGamesResponse, GamesApiListGamesData, GamesApiListGamesResponse, MarketApiBetHistoryData, MarketApiBetHistoryResponse, MarketApiCloseMarketData, MarketApiCreateBetData, MarketApiCreateBetResponse, MarketApiGetUserPointsData, MarketApiGetUserPointsResponse, MarketApiListMarketsData, MarketApiListMarketsResponse, MarketApiListUserBetsData, MarketApiListUserBetsResponse, MarketApiResolveMarketData, MarketApiResolveMarketResponse, NoitaApiGetLeaderboardData, NoitaApiGetLeaderboardResponse, NoitaApiGetResultsData, NoitaApiGetResultsResponse, NoitaApiSubmitLogFileData, NoitaApiSubmitLogFileError, NoitaApiSubmitLogFileResponse, OpusMagnumApiCreateSubmissionData, OpusMagnumApiCreateSubmissionResponse, OpusMagnumApiDeleteSubmissionData, OpusMagnumApiGetCollectionData, OpusMagnumApiGetCollectionResponse, OpusMagnumApiGetStatsData, OpusMagnumApiGetSubmissionData, OpusMagnumApiGetSubmissionResponse, OpusMagnumApiListPuzzlesData, OpusMagnumApiListPuzzlesResponse, OpusMagnumApiListResponsesNeedingValidationData, OpusMagnumApiListResponsesNeedingValidationResponse, OpusMagnumApiListSubmissionsData, OpusMagnumApiListSubmissionsResponse, OpusMagnumApiValidateAutoData, OpusMagnumApiValidateAutoResponse, OpusMagnumApiValidateResponseData, OpusMagnumApiValidateResponseResponse, OpusMagnumApiValidateSubmissionData, OpusMagnumApiValidateSubmissionResponse, PlaceApiCanvasData, PlaceApiEvolutionGifData, PolylanSubmitterApiClearCacheData, PolylanSubmitterApiGetUserInfoData, PolylanSubmitterApiGetUserInfoResponse, PolylanSubmitterApiHealthCheckData, SubmissionsApiCreateImageCropData, SubmissionsApiCreateImageCropResponse, SubmissionsApiCreateSubmissionData, SubmissionsApiCreateSubmissionError, SubmissionsApiCreateSubmissionResponse, SubmissionsApiDeleteImageCropData, SubmissionsApiDeleteImageCropResponse, SubmissionsApiGetSubmissionData, SubmissionsApiGetSubmissionResponse, SubmissionsApiListAvailableScopesData, SubmissionsApiListAvailableScopesError, SubmissionsApiListAvailableScopesResponse, SubmissionsApiListMyScopesData, SubmissionsApiListMyScopesError, SubmissionsApiListMyScopesResponse, SubmissionsApiListSubmissionsByGameData, SubmissionsApiListSubmissionsByGameResponse, SubmissionsApiListUserSubmissionsData, SubmissionsApiListUserSubmissionsError, SubmissionsApiListUserSubmissionsResponse, SubmissionsApiProcessCropOcrData, SubmissionsApiProcessCropOcrResponse, SubmissionsApiProcessFileOcrData, SubmissionsApiProcessFileOcrResponse } from '../types.gen';
|
||||
|
||||
export type QueryKey<TOptions extends Options> = [
|
||||
Pick<TOptions, 'baseUrl' | 'body' | 'headers' | 'path' | 'query'> & {
|
||||
_id: string;
|
||||
_infinite?: boolean;
|
||||
tags?: ReadonlyArray<string>;
|
||||
}
|
||||
];
|
||||
|
||||
const createQueryKey = <TOptions extends Options>(id: string, options?: TOptions, infinite?: boolean, tags?: ReadonlyArray<string>): [
|
||||
QueryKey<TOptions>[0]
|
||||
] => {
|
||||
const params: QueryKey<TOptions>[0] = { _id: id, baseUrl: options?.baseUrl || (options?.client ?? client).getConfig().baseUrl } as QueryKey<TOptions>[0];
|
||||
if (infinite) {
|
||||
params._infinite = infinite;
|
||||
}
|
||||
if (tags) {
|
||||
params.tags = tags;
|
||||
}
|
||||
if (options?.body) {
|
||||
params.body = options.body;
|
||||
}
|
||||
if (options?.headers) {
|
||||
params.headers = options.headers;
|
||||
}
|
||||
if (options?.path) {
|
||||
params.path = options.path;
|
||||
}
|
||||
if (options?.query) {
|
||||
params.query = options.query;
|
||||
}
|
||||
return [params];
|
||||
};
|
||||
|
||||
export const polylanSubmitterApiHealthCheckQueryKey = (options?: Options<PolylanSubmitterApiHealthCheckData>) => createQueryKey('polylanSubmitterApiHealthCheck', options);
|
||||
|
||||
/**
|
||||
* Health Check
|
||||
*
|
||||
* Health check endpoint
|
||||
*/
|
||||
export const polylanSubmitterApiHealthCheckOptions = (options?: Options<PolylanSubmitterApiHealthCheckData>) => queryOptions<unknown, DefaultError, unknown, ReturnType<typeof polylanSubmitterApiHealthCheckQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await polylanSubmitterApiHealthCheck({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: polylanSubmitterApiHealthCheckQueryKey(options)
|
||||
});
|
||||
|
||||
/**
|
||||
* Clear Cache
|
||||
*
|
||||
* Clear API caches (admin only). Pass event_path to clear just that event's keys.
|
||||
*/
|
||||
export const polylanSubmitterApiClearCacheMutation = (options?: Partial<Options<PolylanSubmitterApiClearCacheData>>): UseMutationOptions<unknown, DefaultError, Options<PolylanSubmitterApiClearCacheData>> => {
|
||||
const mutationOptions: UseMutationOptions<unknown, DefaultError, Options<PolylanSubmitterApiClearCacheData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await polylanSubmitterApiClearCache({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const polylanSubmitterApiGetUserInfoQueryKey = (options?: Options<PolylanSubmitterApiGetUserInfoData>) => createQueryKey('polylanSubmitterApiGetUserInfo', options);
|
||||
|
||||
/**
|
||||
* Get User Info
|
||||
*
|
||||
* Get current user information
|
||||
*/
|
||||
export const polylanSubmitterApiGetUserInfoOptions = (options?: Options<PolylanSubmitterApiGetUserInfoData>) => queryOptions<PolylanSubmitterApiGetUserInfoResponse, DefaultError, PolylanSubmitterApiGetUserInfoResponse, ReturnType<typeof polylanSubmitterApiGetUserInfoQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await polylanSubmitterApiGetUserInfo({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: polylanSubmitterApiGetUserInfoQueryKey(options)
|
||||
});
|
||||
|
||||
export const gamesApiListGamesQueryKey = (options?: Options<GamesApiListGamesData>) => createQueryKey('gamesApiListGames', options);
|
||||
|
||||
/**
|
||||
* List Games
|
||||
*/
|
||||
export const gamesApiListGamesOptions = (options?: Options<GamesApiListGamesData>) => queryOptions<GamesApiListGamesResponse, DefaultError, GamesApiListGamesResponse, ReturnType<typeof gamesApiListGamesQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await gamesApiListGames({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: gamesApiListGamesQueryKey(options)
|
||||
});
|
||||
|
||||
export const opusMagnumApiListPuzzlesQueryKey = (options: Options<OpusMagnumApiListPuzzlesData>) => createQueryKey('opusMagnumApiListPuzzles', options);
|
||||
|
||||
/**
|
||||
* List Puzzles
|
||||
*
|
||||
* Get list of available puzzles
|
||||
*/
|
||||
export const opusMagnumApiListPuzzlesOptions = (options: Options<OpusMagnumApiListPuzzlesData>) => queryOptions<OpusMagnumApiListPuzzlesResponse, DefaultError, OpusMagnumApiListPuzzlesResponse, ReturnType<typeof opusMagnumApiListPuzzlesQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await opusMagnumApiListPuzzles({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: opusMagnumApiListPuzzlesQueryKey(options)
|
||||
});
|
||||
|
||||
export const opusMagnumApiGetCollectionQueryKey = (options: Options<OpusMagnumApiGetCollectionData>) => createQueryKey('opusMagnumApiGetCollection', options);
|
||||
|
||||
/**
|
||||
* Get Collection
|
||||
*
|
||||
* Get the active collection details
|
||||
*/
|
||||
export const opusMagnumApiGetCollectionOptions = (options: Options<OpusMagnumApiGetCollectionData>) => queryOptions<OpusMagnumApiGetCollectionResponse, DefaultError, OpusMagnumApiGetCollectionResponse, ReturnType<typeof opusMagnumApiGetCollectionQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await opusMagnumApiGetCollection({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: opusMagnumApiGetCollectionQueryKey(options)
|
||||
});
|
||||
|
||||
export const opusMagnumApiListSubmissionsQueryKey = (options: Options<OpusMagnumApiListSubmissionsData>) => createQueryKey('opusMagnumApiListSubmissions', options);
|
||||
|
||||
/**
|
||||
* List Submissions
|
||||
*
|
||||
* Get paginated list of submissions
|
||||
*/
|
||||
export const opusMagnumApiListSubmissionsOptions = (options: Options<OpusMagnumApiListSubmissionsData>) => queryOptions<OpusMagnumApiListSubmissionsResponse, DefaultError, OpusMagnumApiListSubmissionsResponse, ReturnType<typeof opusMagnumApiListSubmissionsQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await opusMagnumApiListSubmissions({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: opusMagnumApiListSubmissionsQueryKey(options)
|
||||
});
|
||||
|
||||
const createInfiniteParams = <K extends Pick<QueryKey<Options>[0], 'body' | 'headers' | 'path' | 'query'>>(queryKey: QueryKey<Options>, page: K) => {
|
||||
const params = { ...queryKey[0] };
|
||||
if (page.body) {
|
||||
params.body = {
|
||||
...queryKey[0].body as any,
|
||||
...page.body as any
|
||||
};
|
||||
}
|
||||
if (page.headers) {
|
||||
params.headers = {
|
||||
...queryKey[0].headers,
|
||||
...page.headers
|
||||
};
|
||||
}
|
||||
if (page.path) {
|
||||
params.path = {
|
||||
...queryKey[0].path as any,
|
||||
...page.path as any
|
||||
};
|
||||
}
|
||||
if (page.query) {
|
||||
params.query = {
|
||||
...queryKey[0].query as any,
|
||||
...page.query as any
|
||||
};
|
||||
}
|
||||
return params as unknown as typeof page;
|
||||
};
|
||||
|
||||
export const opusMagnumApiListSubmissionsInfiniteQueryKey = (options: Options<OpusMagnumApiListSubmissionsData>): QueryKey<Options<OpusMagnumApiListSubmissionsData>> => createQueryKey('opusMagnumApiListSubmissions', options, true);
|
||||
|
||||
/**
|
||||
* List Submissions
|
||||
*
|
||||
* Get paginated list of submissions
|
||||
*/
|
||||
export const opusMagnumApiListSubmissionsInfiniteOptions = (options: Options<OpusMagnumApiListSubmissionsData>) => infiniteQueryOptions<OpusMagnumApiListSubmissionsResponse, DefaultError, InfiniteData<OpusMagnumApiListSubmissionsResponse>, QueryKey<Options<OpusMagnumApiListSubmissionsData>>, number | Pick<QueryKey<Options<OpusMagnumApiListSubmissionsData>>[0], 'body' | 'headers' | 'path' | 'query'>>(
|
||||
// @ts-ignore
|
||||
{
|
||||
queryFn: async ({ pageParam, queryKey, signal }) => {
|
||||
// @ts-ignore
|
||||
const page: Pick<QueryKey<Options<OpusMagnumApiListSubmissionsData>>[0], 'body' | 'headers' | 'path' | 'query'> = typeof pageParam === 'object' ? pageParam : {
|
||||
query: {
|
||||
offset: pageParam
|
||||
}
|
||||
};
|
||||
const params = createInfiniteParams(queryKey, page);
|
||||
const { data } = await opusMagnumApiListSubmissions({
|
||||
...options,
|
||||
...params,
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: opusMagnumApiListSubmissionsInfiniteQueryKey(options)
|
||||
});
|
||||
|
||||
/**
|
||||
* Create Submission
|
||||
*
|
||||
* Create a new submission with multiple puzzle responses
|
||||
*/
|
||||
export const opusMagnumApiCreateSubmissionMutation = (options?: Partial<Options<OpusMagnumApiCreateSubmissionData>>): UseMutationOptions<OpusMagnumApiCreateSubmissionResponse, DefaultError, Options<OpusMagnumApiCreateSubmissionData>> => {
|
||||
const mutationOptions: UseMutationOptions<OpusMagnumApiCreateSubmissionResponse, DefaultError, Options<OpusMagnumApiCreateSubmissionData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await opusMagnumApiCreateSubmission({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete Submission
|
||||
*
|
||||
* Delete a submission (admin only)
|
||||
*/
|
||||
export const opusMagnumApiDeleteSubmissionMutation = (options?: Partial<Options<OpusMagnumApiDeleteSubmissionData>>): UseMutationOptions<unknown, DefaultError, Options<OpusMagnumApiDeleteSubmissionData>> => {
|
||||
const mutationOptions: UseMutationOptions<unknown, DefaultError, Options<OpusMagnumApiDeleteSubmissionData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await opusMagnumApiDeleteSubmission({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const opusMagnumApiGetSubmissionQueryKey = (options: Options<OpusMagnumApiGetSubmissionData>) => createQueryKey('opusMagnumApiGetSubmission', options);
|
||||
|
||||
/**
|
||||
* Get Submission
|
||||
*
|
||||
* Get detailed submission by ID
|
||||
*/
|
||||
export const opusMagnumApiGetSubmissionOptions = (options: Options<OpusMagnumApiGetSubmissionData>) => queryOptions<OpusMagnumApiGetSubmissionResponse, DefaultError, OpusMagnumApiGetSubmissionResponse, ReturnType<typeof opusMagnumApiGetSubmissionQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await opusMagnumApiGetSubmission({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: opusMagnumApiGetSubmissionQueryKey(options)
|
||||
});
|
||||
|
||||
/**
|
||||
* Validate Response
|
||||
*
|
||||
* Manually validate a puzzle response
|
||||
*/
|
||||
export const opusMagnumApiValidateResponseMutation = (options?: Partial<Options<OpusMagnumApiValidateResponseData>>): UseMutationOptions<OpusMagnumApiValidateResponseResponse, DefaultError, Options<OpusMagnumApiValidateResponseData>> => {
|
||||
const mutationOptions: UseMutationOptions<OpusMagnumApiValidateResponseResponse, DefaultError, Options<OpusMagnumApiValidateResponseData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await opusMagnumApiValidateResponse({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate Auto
|
||||
*
|
||||
* Try to auto validate a puzzle response
|
||||
*/
|
||||
export const opusMagnumApiValidateAutoMutation = (options?: Partial<Options<OpusMagnumApiValidateAutoData>>): UseMutationOptions<OpusMagnumApiValidateAutoResponse, DefaultError, Options<OpusMagnumApiValidateAutoData>> => {
|
||||
const mutationOptions: UseMutationOptions<OpusMagnumApiValidateAutoResponse, DefaultError, Options<OpusMagnumApiValidateAutoData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await opusMagnumApiValidateAuto({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const opusMagnumApiListResponsesNeedingValidationQueryKey = (options: Options<OpusMagnumApiListResponsesNeedingValidationData>) => createQueryKey('opusMagnumApiListResponsesNeedingValidation', options);
|
||||
|
||||
/**
|
||||
* List Responses Needing Validation
|
||||
*
|
||||
* Get all responses that need manual validation
|
||||
*/
|
||||
export const opusMagnumApiListResponsesNeedingValidationOptions = (options: Options<OpusMagnumApiListResponsesNeedingValidationData>) => queryOptions<OpusMagnumApiListResponsesNeedingValidationResponse, DefaultError, OpusMagnumApiListResponsesNeedingValidationResponse, ReturnType<typeof opusMagnumApiListResponsesNeedingValidationQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await opusMagnumApiListResponsesNeedingValidation({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: opusMagnumApiListResponsesNeedingValidationQueryKey(options)
|
||||
});
|
||||
|
||||
/**
|
||||
* Validate Submission
|
||||
*
|
||||
* Mark entire submission as validated
|
||||
*/
|
||||
export const opusMagnumApiValidateSubmissionMutation = (options?: Partial<Options<OpusMagnumApiValidateSubmissionData>>): UseMutationOptions<OpusMagnumApiValidateSubmissionResponse, DefaultError, Options<OpusMagnumApiValidateSubmissionData>> => {
|
||||
const mutationOptions: UseMutationOptions<OpusMagnumApiValidateSubmissionResponse, DefaultError, Options<OpusMagnumApiValidateSubmissionData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await opusMagnumApiValidateSubmission({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const opusMagnumApiGetStatsQueryKey = (options: Options<OpusMagnumApiGetStatsData>) => createQueryKey('opusMagnumApiGetStats', options);
|
||||
|
||||
/**
|
||||
* Get Stats
|
||||
*
|
||||
* Get submission statistics
|
||||
*/
|
||||
export const opusMagnumApiGetStatsOptions = (options: Options<OpusMagnumApiGetStatsData>) => queryOptions<unknown, DefaultError, unknown, ReturnType<typeof opusMagnumApiGetStatsQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await opusMagnumApiGetStats({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: opusMagnumApiGetStatsQueryKey(options)
|
||||
});
|
||||
|
||||
export const animationsApiResultsQueryKey = (options: Options<AnimationsApiResultsData>) => createQueryKey('animationsApiResults', options);
|
||||
|
||||
/**
|
||||
* Results
|
||||
*/
|
||||
export const animationsApiResultsOptions = (options: Options<AnimationsApiResultsData>) => queryOptions<AnimationsApiResultsResponse, DefaultError, AnimationsApiResultsResponse, ReturnType<typeof animationsApiResultsQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await animationsApiResults({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: animationsApiResultsQueryKey(options)
|
||||
});
|
||||
|
||||
export const animationsApiTopSubmissionsQueryKey = (options: Options<AnimationsApiTopSubmissionsData>) => createQueryKey('animationsApiTopSubmissions', options);
|
||||
|
||||
/**
|
||||
* Top Submissions
|
||||
*
|
||||
* Get tournament top submissions for each puzzle. Only available when tournament is closed.
|
||||
*/
|
||||
export const animationsApiTopSubmissionsOptions = (options: Options<AnimationsApiTopSubmissionsData>) => queryOptions<AnimationsApiTopSubmissionsResponse, DefaultError, AnimationsApiTopSubmissionsResponse, ReturnType<typeof animationsApiTopSubmissionsQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await animationsApiTopSubmissions({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: animationsApiTopSubmissionsQueryKey(options)
|
||||
});
|
||||
|
||||
export const animationsApiPuzzleResultsQueryKey = (options: Options<AnimationsApiPuzzleResultsData>) => createQueryKey('animationsApiPuzzleResults', options);
|
||||
|
||||
/**
|
||||
* Puzzle Results
|
||||
*
|
||||
* Get tournament results organized by puzzle with coefficients. Only available when tournament is closed.
|
||||
*/
|
||||
export const animationsApiPuzzleResultsOptions = (options: Options<AnimationsApiPuzzleResultsData>) => queryOptions<AnimationsApiPuzzleResultsResponse, DefaultError, AnimationsApiPuzzleResultsResponse, ReturnType<typeof animationsApiPuzzleResultsQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await animationsApiPuzzleResults({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: animationsApiPuzzleResultsQueryKey(options)
|
||||
});
|
||||
|
||||
export const noitaApiGetResultsQueryKey = (options: Options<NoitaApiGetResultsData>) => createQueryKey('noitaApiGetResults', options);
|
||||
|
||||
/**
|
||||
* Get Results
|
||||
*/
|
||||
export const noitaApiGetResultsOptions = (options: Options<NoitaApiGetResultsData>) => queryOptions<NoitaApiGetResultsResponse, DefaultError, NoitaApiGetResultsResponse, ReturnType<typeof noitaApiGetResultsQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await noitaApiGetResults({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: noitaApiGetResultsQueryKey(options)
|
||||
});
|
||||
|
||||
export const noitaApiGetLeaderboardQueryKey = (options: Options<NoitaApiGetLeaderboardData>) => createQueryKey('noitaApiGetLeaderboard', options);
|
||||
|
||||
/**
|
||||
* Get Leaderboard
|
||||
*
|
||||
* Get the global leaderboard for all users ranked by total score.
|
||||
*
|
||||
* Uses Window functions to rank users by their total score in descending order.
|
||||
*/
|
||||
export const noitaApiGetLeaderboardOptions = (options: Options<NoitaApiGetLeaderboardData>) => queryOptions<NoitaApiGetLeaderboardResponse, DefaultError, NoitaApiGetLeaderboardResponse, ReturnType<typeof noitaApiGetLeaderboardQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await noitaApiGetLeaderboard({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: noitaApiGetLeaderboardQueryKey(options)
|
||||
});
|
||||
|
||||
/**
|
||||
* Submit Log File
|
||||
*
|
||||
* Submit a Noita run file (log file, screenshot, or video).
|
||||
*
|
||||
* Accepts:
|
||||
* - Text files (.txt) for polylan_mod_log.txt
|
||||
* - Images (.png, .jpg, .gif)
|
||||
* - Videos (.mp4, .webm)
|
||||
*
|
||||
* Max file size: 256 MB
|
||||
*/
|
||||
export const noitaApiSubmitLogFileMutation = (options?: Partial<Options<NoitaApiSubmitLogFileData>>): UseMutationOptions<NoitaApiSubmitLogFileResponse, NoitaApiSubmitLogFileError, Options<NoitaApiSubmitLogFileData>> => {
|
||||
const mutationOptions: UseMutationOptions<NoitaApiSubmitLogFileResponse, NoitaApiSubmitLogFileError, Options<NoitaApiSubmitLogFileData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await noitaApiSubmitLogFile({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const gamesApiListEventGamesQueryKey = (options: Options<GamesApiListEventGamesData>) => createQueryKey('gamesApiListEventGames', options);
|
||||
|
||||
/**
|
||||
* List Event Games
|
||||
*/
|
||||
export const gamesApiListEventGamesOptions = (options: Options<GamesApiListEventGamesData>) => queryOptions<GamesApiListEventGamesResponse, DefaultError, GamesApiListEventGamesResponse, ReturnType<typeof gamesApiListEventGamesQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await gamesApiListEventGames({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: gamesApiListEventGamesQueryKey(options)
|
||||
});
|
||||
|
||||
export const gamesApiListEventSteamGamesQueryKey = (options: Options<GamesApiListEventSteamGamesData>) => createQueryKey('gamesApiListEventSteamGames', options);
|
||||
|
||||
/**
|
||||
* List Event Steam Games
|
||||
*
|
||||
* The event's Steam games with their store info (price, discount, blurb).
|
||||
*/
|
||||
export const gamesApiListEventSteamGamesOptions = (options: Options<GamesApiListEventSteamGamesData>) => queryOptions<GamesApiListEventSteamGamesResponse, DefaultError, GamesApiListEventSteamGamesResponse, ReturnType<typeof gamesApiListEventSteamGamesQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await gamesApiListEventSteamGames({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: gamesApiListEventSteamGamesQueryKey(options)
|
||||
});
|
||||
|
||||
export const submissionsApiListMyScopesQueryKey = (options: Options<SubmissionsApiListMyScopesData>) => createQueryKey('submissionsApiListMyScopes', options);
|
||||
|
||||
/**
|
||||
* List My Scopes
|
||||
*/
|
||||
export const submissionsApiListMyScopesOptions = (options: Options<SubmissionsApiListMyScopesData>) => queryOptions<SubmissionsApiListMyScopesResponse, SubmissionsApiListMyScopesError, SubmissionsApiListMyScopesResponse, ReturnType<typeof submissionsApiListMyScopesQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await submissionsApiListMyScopes({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: submissionsApiListMyScopesQueryKey(options)
|
||||
});
|
||||
|
||||
export const submissionsApiListAvailableScopesQueryKey = (options: Options<SubmissionsApiListAvailableScopesData>) => createQueryKey('submissionsApiListAvailableScopes', options);
|
||||
|
||||
/**
|
||||
* List Available Scopes
|
||||
*
|
||||
* Scopes the current user may submit to for this game - superusers see
|
||||
* every active scope, everyone else only the ones assigned to them. Used
|
||||
* by the submissions-select and submissions-detail pages, both to list
|
||||
* scopes and (for the detail page) to confirm the requested scope_id is
|
||||
* one the current user is actually allowed to see.
|
||||
*/
|
||||
export const submissionsApiListAvailableScopesOptions = (options: Options<SubmissionsApiListAvailableScopesData>) => queryOptions<SubmissionsApiListAvailableScopesResponse, SubmissionsApiListAvailableScopesError, SubmissionsApiListAvailableScopesResponse, ReturnType<typeof submissionsApiListAvailableScopesQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await submissionsApiListAvailableScopes({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: submissionsApiListAvailableScopesQueryKey(options)
|
||||
});
|
||||
|
||||
/**
|
||||
* Create Submission
|
||||
*/
|
||||
export const submissionsApiCreateSubmissionMutation = (options?: Partial<Options<SubmissionsApiCreateSubmissionData>>): UseMutationOptions<SubmissionsApiCreateSubmissionResponse, SubmissionsApiCreateSubmissionError, Options<SubmissionsApiCreateSubmissionData>> => {
|
||||
const mutationOptions: UseMutationOptions<SubmissionsApiCreateSubmissionResponse, SubmissionsApiCreateSubmissionError, Options<SubmissionsApiCreateSubmissionData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await submissionsApiCreateSubmission({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const submissionsApiListUserSubmissionsQueryKey = (options: Options<SubmissionsApiListUserSubmissionsData>) => createQueryKey('submissionsApiListUserSubmissions', options);
|
||||
|
||||
/**
|
||||
* List User Submissions
|
||||
*/
|
||||
export const submissionsApiListUserSubmissionsOptions = (options: Options<SubmissionsApiListUserSubmissionsData>) => queryOptions<SubmissionsApiListUserSubmissionsResponse, SubmissionsApiListUserSubmissionsError, SubmissionsApiListUserSubmissionsResponse, ReturnType<typeof submissionsApiListUserSubmissionsQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await submissionsApiListUserSubmissions({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: submissionsApiListUserSubmissionsQueryKey(options)
|
||||
});
|
||||
|
||||
export const submissionsApiListSubmissionsByGameQueryKey = (options: Options<SubmissionsApiListSubmissionsByGameData>) => createQueryKey('submissionsApiListSubmissionsByGame', options);
|
||||
|
||||
/**
|
||||
* List Submissions By Game
|
||||
*/
|
||||
export const submissionsApiListSubmissionsByGameOptions = (options: Options<SubmissionsApiListSubmissionsByGameData>) => queryOptions<SubmissionsApiListSubmissionsByGameResponse, DefaultError, SubmissionsApiListSubmissionsByGameResponse, ReturnType<typeof submissionsApiListSubmissionsByGameQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await submissionsApiListSubmissionsByGame({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: submissionsApiListSubmissionsByGameQueryKey(options)
|
||||
});
|
||||
|
||||
export const submissionsApiGetSubmissionQueryKey = (options: Options<SubmissionsApiGetSubmissionData>) => createQueryKey('submissionsApiGetSubmission', options);
|
||||
|
||||
/**
|
||||
* Get Submission
|
||||
*/
|
||||
export const submissionsApiGetSubmissionOptions = (options: Options<SubmissionsApiGetSubmissionData>) => queryOptions<SubmissionsApiGetSubmissionResponse, DefaultError, SubmissionsApiGetSubmissionResponse, ReturnType<typeof submissionsApiGetSubmissionQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await submissionsApiGetSubmission({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: submissionsApiGetSubmissionQueryKey(options)
|
||||
});
|
||||
|
||||
/**
|
||||
* Create Image Crop
|
||||
*/
|
||||
export const submissionsApiCreateImageCropMutation = (options?: Partial<Options<SubmissionsApiCreateImageCropData>>): UseMutationOptions<SubmissionsApiCreateImageCropResponse, DefaultError, Options<SubmissionsApiCreateImageCropData>> => {
|
||||
const mutationOptions: UseMutationOptions<SubmissionsApiCreateImageCropResponse, DefaultError, Options<SubmissionsApiCreateImageCropData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await submissionsApiCreateImageCrop({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete Image Crop
|
||||
*/
|
||||
export const submissionsApiDeleteImageCropMutation = (options?: Partial<Options<SubmissionsApiDeleteImageCropData>>): UseMutationOptions<SubmissionsApiDeleteImageCropResponse, DefaultError, Options<SubmissionsApiDeleteImageCropData>> => {
|
||||
const mutationOptions: UseMutationOptions<SubmissionsApiDeleteImageCropResponse, DefaultError, Options<SubmissionsApiDeleteImageCropData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await submissionsApiDeleteImageCrop({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Process File Ocr
|
||||
*/
|
||||
export const submissionsApiProcessFileOcrMutation = (options?: Partial<Options<SubmissionsApiProcessFileOcrData>>): UseMutationOptions<SubmissionsApiProcessFileOcrResponse, DefaultError, Options<SubmissionsApiProcessFileOcrData>> => {
|
||||
const mutationOptions: UseMutationOptions<SubmissionsApiProcessFileOcrResponse, DefaultError, Options<SubmissionsApiProcessFileOcrData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await submissionsApiProcessFileOcr({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Process Crop Ocr
|
||||
*/
|
||||
export const submissionsApiProcessCropOcrMutation = (options?: Partial<Options<SubmissionsApiProcessCropOcrData>>): UseMutationOptions<SubmissionsApiProcessCropOcrResponse, DefaultError, Options<SubmissionsApiProcessCropOcrData>> => {
|
||||
const mutationOptions: UseMutationOptions<SubmissionsApiProcessCropOcrResponse, DefaultError, Options<SubmissionsApiProcessCropOcrData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await submissionsApiProcessCropOcr({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const clockApiCurrentStateQueryKey = (options: Options<ClockApiCurrentStateData>) => createQueryKey('clockApiCurrentState', options);
|
||||
|
||||
/**
|
||||
* Current State
|
||||
*/
|
||||
export const clockApiCurrentStateOptions = (options: Options<ClockApiCurrentStateData>) => queryOptions<ClockApiCurrentStateResponse, DefaultError, ClockApiCurrentStateResponse, ReturnType<typeof clockApiCurrentStateQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await clockApiCurrentState({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: clockApiCurrentStateQueryKey(options)
|
||||
});
|
||||
|
||||
export const clockApiMyStatsQueryKey = (options: Options<ClockApiMyStatsData>) => createQueryKey('clockApiMyStats', options);
|
||||
|
||||
/**
|
||||
* My Stats
|
||||
*/
|
||||
export const clockApiMyStatsOptions = (options: Options<ClockApiMyStatsData>) => queryOptions<ClockApiMyStatsResponse, DefaultError, ClockApiMyStatsResponse, ReturnType<typeof clockApiMyStatsQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await clockApiMyStats({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: clockApiMyStatsQueryKey(options)
|
||||
});
|
||||
|
||||
export const placeApiCanvasQueryKey = (options: Options<PlaceApiCanvasData>) => createQueryKey('placeApiCanvas', options);
|
||||
|
||||
/**
|
||||
* Canvas
|
||||
*
|
||||
* The one-time full-grid load. Every placement after that arrives as a
|
||||
* single websocket message, never a re-fetch.
|
||||
*/
|
||||
export const placeApiCanvasOptions = (options: Options<PlaceApiCanvasData>) => queryOptions<unknown, DefaultError, unknown, ReturnType<typeof placeApiCanvasQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await placeApiCanvas({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: placeApiCanvasQueryKey(options)
|
||||
});
|
||||
|
||||
export const placeApiEvolutionGifQueryKey = (options: Options<PlaceApiEvolutionGifData>) => createQueryKey('placeApiEvolutionGif', options);
|
||||
|
||||
/**
|
||||
* Evolution Gif
|
||||
*
|
||||
* Admin-only: replays the full placement history as an animated GIF.
|
||||
*/
|
||||
export const placeApiEvolutionGifOptions = (options: Options<PlaceApiEvolutionGifData>) => queryOptions<unknown, DefaultError, unknown, ReturnType<typeof placeApiEvolutionGifQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await placeApiEvolutionGif({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: placeApiEvolutionGifQueryKey(options)
|
||||
});
|
||||
|
||||
export const marketApiListMarketsQueryKey = (options: Options<MarketApiListMarketsData>) => createQueryKey('marketApiListMarkets', options);
|
||||
|
||||
/**
|
||||
* List Markets
|
||||
*
|
||||
* List all markets (excludes draft markets).
|
||||
*/
|
||||
export const marketApiListMarketsOptions = (options: Options<MarketApiListMarketsData>) => queryOptions<MarketApiListMarketsResponse, DefaultError, MarketApiListMarketsResponse, ReturnType<typeof marketApiListMarketsQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await marketApiListMarkets({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: marketApiListMarketsQueryKey(options)
|
||||
});
|
||||
|
||||
export const marketApiListUserBetsQueryKey = (options: Options<MarketApiListUserBetsData>) => createQueryKey('marketApiListUserBets', options);
|
||||
|
||||
/**
|
||||
* List User Bets
|
||||
*
|
||||
* List all bets placed by the current user.
|
||||
*/
|
||||
export const marketApiListUserBetsOptions = (options: Options<MarketApiListUserBetsData>) => queryOptions<MarketApiListUserBetsResponse, DefaultError, MarketApiListUserBetsResponse, ReturnType<typeof marketApiListUserBetsQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await marketApiListUserBets({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: marketApiListUserBetsQueryKey(options)
|
||||
});
|
||||
|
||||
export const marketApiGetUserPointsQueryKey = (options: Options<MarketApiGetUserPointsData>) => createQueryKey('marketApiGetUserPoints', options);
|
||||
|
||||
/**
|
||||
* Get User Points
|
||||
*
|
||||
* Get the current user's points balance for this event.
|
||||
*/
|
||||
export const marketApiGetUserPointsOptions = (options: Options<MarketApiGetUserPointsData>) => queryOptions<MarketApiGetUserPointsResponse, DefaultError, MarketApiGetUserPointsResponse, ReturnType<typeof marketApiGetUserPointsQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await marketApiGetUserPoints({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: marketApiGetUserPointsQueryKey(options)
|
||||
});
|
||||
|
||||
export const marketApiBetHistoryQueryKey = (options: Options<MarketApiBetHistoryData>) => createQueryKey('marketApiBetHistory', options);
|
||||
|
||||
/**
|
||||
* Bet History
|
||||
*
|
||||
* Points added to each option over time (anonymous, one entry per bet placed/increased).
|
||||
*/
|
||||
export const marketApiBetHistoryOptions = (options: Options<MarketApiBetHistoryData>) => queryOptions<MarketApiBetHistoryResponse, DefaultError, MarketApiBetHistoryResponse, ReturnType<typeof marketApiBetHistoryQueryKey>>({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await marketApiBetHistory({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: marketApiBetHistoryQueryKey(options)
|
||||
});
|
||||
|
||||
/**
|
||||
* Close Market
|
||||
*
|
||||
* Close a market. Admin only.
|
||||
*/
|
||||
export const marketApiCloseMarketMutation = (options?: Partial<Options<MarketApiCloseMarketData>>): UseMutationOptions<unknown, DefaultError, Options<MarketApiCloseMarketData>> => {
|
||||
const mutationOptions: UseMutationOptions<unknown, DefaultError, Options<MarketApiCloseMarketData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await marketApiCloseMarket({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve Market
|
||||
*
|
||||
* Resolve a market with a winning option. Admin only.
|
||||
*/
|
||||
export const marketApiResolveMarketMutation = (options?: Partial<Options<MarketApiResolveMarketData>>): UseMutationOptions<MarketApiResolveMarketResponse, DefaultError, Options<MarketApiResolveMarketData>> => {
|
||||
const mutationOptions: UseMutationOptions<MarketApiResolveMarketResponse, DefaultError, Options<MarketApiResolveMarketData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await marketApiResolveMarket({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create Bet
|
||||
*
|
||||
* Place a bet on a market option.
|
||||
*/
|
||||
export const marketApiCreateBetMutation = (options?: Partial<Options<MarketApiCreateBetData>>): UseMutationOptions<MarketApiCreateBetResponse, DefaultError, Options<MarketApiCreateBetData>> => {
|
||||
const mutationOptions: UseMutationOptions<MarketApiCreateBetResponse, DefaultError, Options<MarketApiCreateBetData>> = {
|
||||
mutationFn: async (fnOptions) => {
|
||||
const { data } = await marketApiCreateBet({
|
||||
...options,
|
||||
...fnOptions,
|
||||
throwOnError: true
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
@@ -2,7 +2,8 @@
|
||||
import { ref, onMounted } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import RankBadge from "./RankBadge.vue";
|
||||
import { animationsApiResults, polylanSubmitterApiClearCache, polylanSubmitterApiGetUserInfo } from "@/api";
|
||||
import { animationsApiResults, polylanSubmitterApiClearCache } from "@/api";
|
||||
import { queryClient, userQuery } from "@/queries";
|
||||
import { useEventPath } from "@/composables/useEventPath";
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -142,8 +143,7 @@ const clearCache = async () => {
|
||||
|
||||
const loadUserData = async () => {
|
||||
try {
|
||||
const response = await polylanSubmitterApiGetUserInfo();
|
||||
const user = response.data;
|
||||
const user = await queryClient.fetchQuery(userQuery());
|
||||
if (user?.is_authenticated) {
|
||||
userInfo.value.username = user.username ?? "";
|
||||
userInfo.value.isStaff = user.is_staff || false;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { computed } from "vue";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import { userQuery } from "@/queries";
|
||||
|
||||
export const useUser = () => {
|
||||
const { data } = useQuery(userQuery());
|
||||
|
||||
return computed(() => ({
|
||||
username: data.value?.username ?? "",
|
||||
isAuthenticated: data.value?.is_authenticated ?? false,
|
||||
isStaff: data.value?.is_staff ?? false,
|
||||
}));
|
||||
};
|
||||
@@ -3,14 +3,13 @@ import { computed } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { useUser } from "@/composables/useUser";
|
||||
import { usePageHeaderStore } from "@/stores/pageHeader";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const userStore = useUserStore();
|
||||
const { userInfo } = storeToRefs(userStore);
|
||||
const userInfo = useUser();
|
||||
const pageHeaderStore = usePageHeaderStore();
|
||||
const { title: dynamicTitle, backRoute: dynamicBackRoute } = storeToRefs(pageHeaderStore);
|
||||
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import { createApp } from "vue";
|
||||
import { VueQueryPlugin } from "@tanstack/vue-query";
|
||||
import App from "@/App.vue";
|
||||
import { router } from "@/router";
|
||||
import { pinia } from "@/stores";
|
||||
import { i18n } from "@/i18n";
|
||||
import { onQueryCacheRestored, queryClient, restoreQueryCache } from "@/queries";
|
||||
import "@/style.css";
|
||||
|
||||
const app = createApp(App);
|
||||
app.use(pinia);
|
||||
app.use(VueQueryPlugin, {
|
||||
queryClient,
|
||||
clientPersister: restoreQueryCache,
|
||||
clientPersisterOnSuccess: onQueryCacheRestored,
|
||||
});
|
||||
app.use(router);
|
||||
app.use(i18n);
|
||||
app.mount("#app");
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { QueryClient } from "@tanstack/vue-query";
|
||||
import { persistQueryClient } from "@tanstack/query-persist-client-core";
|
||||
import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
|
||||
import {
|
||||
gamesApiListEventGamesOptions,
|
||||
polylanSubmitterApiGetUserInfoOptions,
|
||||
polylanSubmitterApiGetUserInfoQueryKey,
|
||||
} from "@/api/@tanstack/vue-query.gen";
|
||||
|
||||
// Exported (not just provided by VueQueryPlugin) so Pinia stores can
|
||||
// fetchQuery() through the same cache.
|
||||
export const queryClient = new QueryClient();
|
||||
|
||||
// Login/logout are full-page redirects, so the user can't change within a
|
||||
// page load - only refetched once after a reload (see restoreQueryCache).
|
||||
export const userQuery = () => ({
|
||||
...polylanSubmitterApiGetUserInfoOptions(),
|
||||
staleTime: Infinity,
|
||||
});
|
||||
|
||||
// Games only change when staff enable/disable one, a few minutes of
|
||||
// staleness is fine.
|
||||
export const eventGamesQuery = (eventPath: string) => ({
|
||||
...gamesApiListEventGamesOptions({ path: { event_path: eventPath } }),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
|
||||
const PERSISTED_QUERIES = new Set(["polylanSubmitterApiGetUserInfo", "gamesApiListEventGames"]);
|
||||
|
||||
// Restores user + games from localStorage before the app mounts, so pages
|
||||
// render right away on reload instead of waiting on the API.
|
||||
export const restoreQueryCache = (client: QueryClient) =>
|
||||
persistQueryClient({
|
||||
queryClient: client,
|
||||
persister: createSyncStoragePersister({ storage: window.localStorage }),
|
||||
dehydrateOptions: {
|
||||
shouldDehydrateQuery: (query) =>
|
||||
query.state.status === "success" &&
|
||||
PERSISTED_QUERIES.has((query.queryKey[0] as { _id: string })._id),
|
||||
},
|
||||
});
|
||||
|
||||
// The restored user is only a placeholder until the real one comes back.
|
||||
export const onQueryCacheRestored = (client: QueryClient) =>
|
||||
client.invalidateQueries({ queryKey: polylanSubmitterApiGetUserInfoQueryKey() });
|
||||
@@ -1,7 +1,8 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { polylanSubmitterApiGetUserInfo, marketApiListMarkets, marketApiListUserBets, marketApiGetUserPoints } from '@/api'
|
||||
import { marketApiListMarkets, marketApiListUserBets, marketApiGetUserPoints } from '@/api'
|
||||
import type { Market } from '@/types'
|
||||
import { queryClient, userQuery } from '@/queries'
|
||||
import type { UserInfoOut, UserBetSchema } from '@/api/types.gen'
|
||||
|
||||
export const useMarketStore = defineStore('market', () => {
|
||||
@@ -15,15 +16,7 @@ export const useMarketStore = defineStore('market', () => {
|
||||
|
||||
// Actions
|
||||
const loadUserInfo = async () => {
|
||||
try {
|
||||
const response = await polylanSubmitterApiGetUserInfo()
|
||||
if (response.data) {
|
||||
userInfo.value = response.data
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = 'Failed to load user info'
|
||||
console.error('Error loading user info:', err)
|
||||
}
|
||||
userInfo.value = await queryClient.fetchQuery(userQuery()).catch(() => undefined)
|
||||
}
|
||||
|
||||
const loadMarkets = async (eventPath: string) => {
|
||||
|
||||
@@ -4,10 +4,10 @@ import {
|
||||
noitaApiGetResults,
|
||||
noitaApiGetLeaderboard,
|
||||
noitaApiSubmitLogFile,
|
||||
polylanSubmitterApiGetUserInfo,
|
||||
polylanSubmitterApiClearCache,
|
||||
} from '@/api'
|
||||
import type { ObjectivResultOut, LeaderboardEntryOut } from '@/api/types.gen'
|
||||
import { queryClient, userQuery } from '@/queries'
|
||||
|
||||
export type Objective = ObjectivResultOut
|
||||
|
||||
@@ -80,14 +80,10 @@ export const useNoitaStore = defineStore('noita', () => {
|
||||
}
|
||||
|
||||
const loadUserData = async (eventPath: string) => {
|
||||
try {
|
||||
const response = await polylanSubmitterApiGetUserInfo()
|
||||
if (response.data?.is_authenticated) {
|
||||
userInfo.value.username = response.data.username ?? ''
|
||||
userInfo.value.isStaff = response.data.is_staff || false
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching user info:', err)
|
||||
const user = await queryClient.fetchQuery(userQuery()).catch(() => undefined)
|
||||
if (user?.is_authenticated) {
|
||||
userInfo.value.username = user.username ?? ''
|
||||
userInfo.value.isStaff = user.is_staff || false
|
||||
}
|
||||
|
||||
await Promise.all([fetchUserResults(eventPath), fetchLeaderboard(eventPath)])
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { polylanSubmitterApiGetUserInfo } from '@/api'
|
||||
|
||||
interface UserInfo {
|
||||
username: string
|
||||
isAuthenticated: boolean
|
||||
isStaff: boolean
|
||||
}
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
const userInfo = ref<UserInfo>({
|
||||
username: '',
|
||||
isAuthenticated: false,
|
||||
isStaff: false,
|
||||
})
|
||||
const isLoading = ref(false)
|
||||
const error = ref<string>('')
|
||||
|
||||
const loadUserData = async () => {
|
||||
isLoading.value = true
|
||||
try {
|
||||
const response = await polylanSubmitterApiGetUserInfo()
|
||||
if (response.data) {
|
||||
const user = response.data
|
||||
userInfo.value.isAuthenticated = user.is_authenticated || false
|
||||
userInfo.value.username = user.username || ''
|
||||
userInfo.value.isStaff = user.is_staff || false
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = 'Failed to fetch user info'
|
||||
console.error('Error fetching user info:', err)
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
userInfo,
|
||||
isLoading,
|
||||
error,
|
||||
loadUserData,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user