From d2b18360ed781546497c9b70a3fff78928e702f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com>
Date: Sun, 28 Apr 2024 18:43:12 -0500
Subject: [PATCH] feat: cc account tracking
---
src/app/callback/[provider]/page.tsx | 62 +++++
src/app/tools/session/page.tsx | 401 +++++++++++----------------
src/constants/regions.ts | 9 +
src/stores/app.ts | 12 +
src/stores/session.ts | 58 ++--
5 files changed, 260 insertions(+), 282 deletions(-)
create mode 100644 src/app/callback/[provider]/page.tsx
diff --git a/src/app/callback/[provider]/page.tsx b/src/app/callback/[provider]/page.tsx
new file mode 100644
index 000000000..1c21aac6c
--- /dev/null
+++ b/src/app/callback/[provider]/page.tsx
@@ -0,0 +1,62 @@
+'use client';
+
+import { Flex, Heading, Text } from '@radix-ui/themes';
+import { useRouter } from 'next/navigation';
+import { useEffect } from 'react';
+import PageWrapper from '../../../components/PageWrapper';
+import { mutateApp } from '../../../stores/app';
+
+export default function Page({
+ params,
+ searchParams,
+}: {
+ params: { provider: string };
+ searchParams: { [key: string]: string };
+}) {
+ const router = useRouter();
+
+ useEffect(() => {
+ switch (params.provider) {
+ case 'wargaming': {
+ if (
+ !searchParams.expires_at ||
+ !searchParams.access_token ||
+ !searchParams.account_id
+ ) {
+ break;
+ }
+
+ mutateApp((draft) => {
+ draft.login = {
+ expiresAt: Number(searchParams.expires_at),
+ id: Number(searchParams.account_id),
+ token: searchParams.access_token,
+ };
+ });
+
+ break;
+ }
+
+ default: {
+ console.error(`Unknown provider: ${params.provider}`);
+ }
+ }
+
+ router.push(searchParams.return ? searchParams.return : '/');
+ });
+
+ return (
+
+
+ Verifying you...
+ You'll be on your way shortly.
+
+
+ );
+}
diff --git a/src/app/tools/session/page.tsx b/src/app/tools/session/page.tsx
index 5a7b346e0..9db52d701 100644
--- a/src/app/tools/session/page.tsx
+++ b/src/app/tools/session/page.tsx
@@ -1,269 +1,190 @@
'use client';
-import {
- CaretDownIcon,
- CopyIcon,
- PersonIcon,
- ReloadIcon,
-} from '@radix-ui/react-icons';
+import { MagnifyingGlassIcon } from '@radix-ui/react-icons';
import {
AlertDialog,
Button,
- Checkbox,
- DropdownMenu,
+ Card,
Flex,
- Heading,
+ Link,
Text,
TextField,
} from '@radix-ui/themes';
import { debounce } from 'lodash';
-import { ChangeEvent, useEffect, useRef, useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
import PageWrapper from '../../../components/PageWrapper';
-import { UNLOCALIZED_REGION_NAMES } from '../../../constants/regions';
+import { UNLOCALIZED_REGION_NAMES_SHORT } from '../../../constants/regions';
+import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
+import { getAccountInfo } from '../../../core/blitz/getAccountInfo';
+import { idToRegion } from '../../../core/blitz/idToRegion';
import searchPlayersAcrossRegions, {
AccountListWithServer,
} from '../../../core/blitz/searchPlayersAcrossRegions';
-import { resetSession } from '../../../core/blitzkit/resetSession';
-import { setSession } from '../../../core/blitzkit/setSession';
-import { useSession } from '../../../stores/session';
-import SessionPage from '../../embeds/session/page';
-import { Menu } from './components/Menu';
-import * as styles from './page.css';
+import { useApp } from '../../../stores/app';
+import mutateSession, {
+ SessionTracking,
+ useSession,
+} from '../../../stores/session';
export default function Page() {
+ const [showSearch, setShowSearch] = useState(false);
+ const [searching, setSearching] = useState(false);
+ const [searchResults, setSearchResults] = useState([]);
+ const [showFurtherVerification, setShowFurtherVerification] = useState(false);
const input = useRef(null);
- const [searchResults, setSearchResults] = useState<
- AccountListWithServer | undefined
- >(undefined);
- const [showSearchResults, setShowSearchResults] = useState(false);
- const handleChange = debounce(
- async (event: ChangeEvent) => {
- if (event.target.value) {
- setSearchResults(await searchPlayersAcrossRegions(event.target.value));
- } else {
- setSearchResults(undefined);
- setShowSearchResults(false);
- }
- },
- 500,
- );
const session = useSession();
+ const login = useApp((state) => state.login);
+
+ const search = debounce(async () => {
+ if (!input.current) return;
+
+ setSearchResults(await searchPlayersAcrossRegions(input.current.value, 9));
+ setSearching(false);
+ }, 500);
useEffect(() => {
- if (session.isTracking) input.current!.value = session.nickname;
- }, [session]);
+ (async () => {
+ if (!session.tracking) return;
+
+ const accountInfo = await getAccountInfo(
+ idToRegion(session.player.id),
+ session.player.id,
+ );
+
+ if (accountInfo === null) {
+ if (login?.id === session.player.id) {
+ } else {
+ setShowFurtherVerification(true);
+ }
+ }
+ })();
+ }, [session.tracking && session.player.id]);
return (
-
-
-
-
{
- event.stopPropagation();
+
+ {/* omit totally to avoid accessing undefined player */}
+ {showFurtherVerification && (
+
+
+ We need further verification
+
+ You're trying to track a CC account with private statistics.
+ Please sign in to verify the ownership of this account.
+
- if (event.target.value) {
- setShowSearchResults(true);
- setSearchResults(undefined);
- } else {
- setShowSearchResults(false);
- }
-
- handleChange(event);
- }}
- onBlur={(event) => {
- // TODO: remove this hack when https://github.com/radix-ui/primitives/issues/2193 (https://github.com/radix-ui/primitives/issues/1969) is fixed
- if (showSearchResults) event.target.focus();
- }}
- onKeyDown={(event) => {
- if (event.key === 'Escape') {
- setShowSearchResults(false);
- input.current?.blur();
- }
- }}
- placeholder="Search for a player..."
- >
-
-
-
-
-
-
-
-
-
-
-
- {searchResults === undefined ? (
- Searching...
- ) : searchResults.length === 0 ? (
- No results
- ) : (
- searchResults?.map(
- ({ account_id: id, nickname, region }, index) => (
- {
- setShowSearchResults(false);
- setSession(region, id, nickname);
- }}
- shortcut={UNLOCALIZED_REGION_NAMES[region]}
- >
- {nickname}
-
- ),
- )
- )}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Are you sure you want to reset?
-
-
- This'll discard your current session and start tracking from
- scratch again.
-
-
-
-
-
- useSession.setState({
- resetPrompt: !checked,
- })
- }
- />
- Don't ask me again
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Copied link to your clipboard
-
-
- Instructions for streamers:
-
+ {login && (
+
+ You are currently signed with a different account.
+ )}
-
-
- useSession.setState({
- embedPrompt: !checked,
- })
- }
- />
- Don't show this again
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {!session.isTracking && (
-
- No player selected
-
- Search for a Player to get started
-
-
+
+
+
+
+
+
+
+
+
+
+
+
)}
+
+ {
+ if (!input.current) return;
+
+ const sanitized = input.current.value.trim();
+
+ if (sanitized.length > 0) {
+ setShowSearch(true);
+ setSearching(true);
+ search();
+ } else {
+ setShowSearch(false);
+ }
+ }}
+ >
+
+
+
+
+ {showSearch && (
+
+ {searching && (
+
+ Searching...
+
+ )}
+
+ {!searching && searchResults.length === 0 && (
+
+ No players found
+
+ )}
+
+ {!searching && searchResults.length > 0 && (
+
+ {searchResults.map((player) => (
+
+ ))}
+
+ )}
+
+ )}
+
);
}
diff --git a/src/constants/regions.ts b/src/constants/regions.ts
index 85e84a7d0..7351792f5 100644
--- a/src/constants/regions.ts
+++ b/src/constants/regions.ts
@@ -11,3 +11,12 @@ export const UNLOCALIZED_REGION_NAMES: Record = {
asia: 'Asia',
eu: 'Europe',
};
+
+/**
+ * @deprecated
+ */
+export const UNLOCALIZED_REGION_NAMES_SHORT: Record = {
+ com: 'NA',
+ asia: 'APAC',
+ eu: 'EU',
+};
diff --git a/src/stores/app.ts b/src/stores/app.ts
index 227ace7d2..38fbdff15 100644
--- a/src/stores/app.ts
+++ b/src/stores/app.ts
@@ -1,11 +1,19 @@
+import { produce } from 'immer';
import { merge } from 'lodash';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
+interface Login {
+ id: number;
+ token: string;
+ expiresAt: number;
+}
+
export interface App {
devBuildAgreementTime: number;
developerMode: boolean;
darkMode: boolean;
+ login?: Login;
}
export const useApp = create()(
@@ -18,3 +26,7 @@ export const useApp = create()(
{ name: 'app', merge: (a, b) => merge(b, a) },
),
);
+
+export function mutateApp(recipe: (draft: App) => void) {
+ useApp.setState(produce(recipe));
+}
diff --git a/src/stores/session.ts b/src/stores/session.ts
index cf4cfe48c..98854518c 100644
--- a/src/stores/session.ts
+++ b/src/stores/session.ts
@@ -2,56 +2,30 @@ import { produce } from 'immer';
import { merge } from 'lodash';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
-import { CustomColumnDisplay } from '../app/tools/session/components/CustomColumn';
-import { AccentColor, GrayColor } from '../constants/radixColors';
import { Region } from '../constants/regions';
-import { NormalizedTankStats } from '../types/tanksStats';
-type Session = (
- | {
- isTracking: false;
- }
- | {
- isTracking: true;
+interface SessionBase {}
- region: Region;
- id: number;
- nickname: string;
- tankStats: NormalizedTankStats;
- time: number;
- }
-) & {
- title: string;
- resetPrompt: boolean;
- embedPrompt: boolean;
- customColumns: {
- display: CustomColumnDisplay;
- showDelta: boolean;
- }[] & { length: 4 };
- showTotal: boolean;
- showCareer: boolean;
- color: AccentColor | GrayColor;
-};
+export interface SessionTracking extends SessionBase {
+ tracking: true;
+ player: {
+ id: number;
+ region: Region;
+ };
+}
+
+interface SessionNotTracking extends SessionBase {
+ tracking: false;
+}
+
+type Session = SessionTracking | SessionNotTracking;
export const useSession = create()(
persist(
(set) => ({
- title: 'Untitled session',
- isTracking: false,
- resetPrompt: true,
- showTotal: true,
- showCareer: true,
- embedPrompt: true,
-
- customColumns: [
- { display: 'battles', showDelta: false },
- { display: 'winrate', showDelta: true },
- { display: 'wn8', showDelta: false },
- { display: 'damage', showDelta: true },
- ],
- color: 'slate',
+ tracking: false,
}),
- { name: 'session', merge: (a, b) => merge(b, a) },
+ { name: 'session-2', merge: (a, b) => merge(b, a) },
),
);