diff --git a/apps/web/app/[language]/achievement/[id]/tier-table.tsx b/apps/web/app/[language]/achievement/[id]/tier-table.tsx index e03614a8d..39fcbb000 100644 --- a/apps/web/app/[language]/achievement/[id]/tier-table.tsx +++ b/apps/web/app/[language]/achievement/[id]/tier-table.tsx @@ -98,9 +98,9 @@ const TierTableAccountRow: FC = ({ achievement, accoun <> {tiers.map((tier, index) => { const previousTier = index === 0 ? { points: 0, count: 0 } : tiers[index - 1]; - const isDone = progress && (progress.done || progress.current >= tier.count); - const isCurrent = progress && !isDone && progress.current >= previousTier.count; - const percentage = isDone ? 1 : isCurrent ? ((progress.current - previousTier.count) / (tier.count - previousTier.count)) : 0; + const isDone = progress && (progress.done || (progress.current ?? 0) >= tier.count); + const isCurrent = progress && !isDone && (progress.current ?? 0) >= previousTier.count; + const percentage = isDone ? 1 : isCurrent ? (((progress.current ?? 0) - previousTier.count) / (tier.count - previousTier.count)) : 0; return ( diff --git a/apps/web/app/[language]/wizards-vault/objectives.tsx b/apps/web/app/[language]/wizards-vault/objectives.tsx index 5c97cd816..772496092 100644 --- a/apps/web/app/[language]/wizards-vault/objectives.tsx +++ b/apps/web/app/[language]/wizards-vault/objectives.tsx @@ -17,9 +17,6 @@ import { Scope } from '@gw2me/client'; import { useSubscription } from '@/components/Gw2Api/Gw2AccountSubscriptionProvider'; import { useGw2Accounts } from '@/components/Gw2Api/use-gw2-accounts'; import type { Gw2Account } from '@/components/Gw2Api/types'; -import { FlexRow } from '@gw2treasures/ui/components/Layout/FlexRow'; -import { SubmitButton } from '@gw2treasures/ui/components/Form/Buttons/SubmitButton'; -import { reauthorize } from '@/components/Gw2Api/reauthorize'; import { Waypoint } from '@/components/Waypoint/Waypoint'; import { Gw2AccountName } from '@/components/Gw2Api/Gw2AccountName'; import { Gw2AccountAuthorizationNotice } from '@/components/Gw2Api/Gw2AccountAuthorizationNotice'; @@ -194,20 +191,3 @@ const AccountObjectiveDetails: FC = ({ account, objectiv ); }; - -interface WizardsProgress { - meta_progress_current: number, - meta_progress_complete: number, - meta_reward_item_id: number, - meta_reward_astral: number, - meta_reward_claimed: number, - objectives: { - id: number, - title: string, - track: string, - acclaim: number, - progress_current: number, - progress_complete: number, - claimed: boolean, - }[] -} diff --git a/apps/web/components/Achievement/AccountAchievementProgress.tsx b/apps/web/components/Achievement/AccountAchievementProgress.tsx index 08e860867..19ec41b89 100644 --- a/apps/web/components/Achievement/AccountAchievementProgress.tsx +++ b/apps/web/components/Achievement/AccountAchievementProgress.tsx @@ -79,8 +79,8 @@ export const AccountAchievementProgressCell: FC - {progress.done ? : `${progress.current} / ${progress.max}`} + + {progress.done ? : `${(progress.current ?? 0)} / ${(progress.max ?? 1)}`} {progress.repeated && ` (↻ ${progress.repeated})`} ); diff --git a/apps/web/components/Gw2Api/Gw2AccountSubscriptionProvider.tsx b/apps/web/components/Gw2Api/Gw2AccountSubscriptionProvider.tsx index 452d45b86..4baf964a3 100644 --- a/apps/web/components/Gw2Api/Gw2AccountSubscriptionProvider.tsx +++ b/apps/web/components/Gw2Api/Gw2AccountSubscriptionProvider.tsx @@ -6,6 +6,9 @@ import { fetchAccessTokens } from './fetch-accounts-action'; import type { Language } from '@gw2treasures/database'; import { getResetDate } from '../Reset/ResetTimer'; import { useVisibilityState } from '@/lib/useVisibilityState'; +import { fetchGw2Api } from '@gw2api/fetch'; +import type { AccountAchievement } from '@gw2api/types/data/account-achievements'; +import type { AccountWallet } from '@gw2api/types/data/account-wallet'; export interface Gw2AccountSubscriptionProviderProps { children: ReactNode @@ -14,10 +17,10 @@ export interface Gw2AccountSubscriptionProviderProps { type SubscriptionType = 'achievements' | 'skins' | 'wallet' | 'wizards-vault'; type SubscriptionData = - T extends 'achievements' ? Gw2ApiAccountProgression : + T extends 'achievements' ? AccountAchievement[] : T extends 'skins' ? number[] : - T extends 'wallet' ? AccountWallet : - T extends 'wizards-vault' ? AccountWizardsVaultData : + T extends 'wallet' ? AccountWallet[] : + T extends 'wizards-vault' ? Awaited> : never type SubscriptionResponse = { @@ -223,82 +226,28 @@ function useRefTo(to: T): MutableRefObject { return ref; } -const fetchJson = async (url: string): Promise => { - const response = await fetch(url, { redirect: 'manual', cache: 'no-cache' }); - if(!response.ok) { - throw new Error('Bad response'); - } - - return response.json(); -}; - -const achievementFetch = (accessToken: string) => fetchJson(`https://api.guildwars2.com/v2/account/achievements?access_token=${accessToken}`) as Promise; -const skinsFetch = (accessToken: string) => fetchJson(`https://api.guildwars2.com/v2/account/skins?access_token=${accessToken}`) as Promise; -const walletFetch = (accessToken: string) => fetchJson(`https://api.guildwars2.com/v2/account/wallet?access_token=${accessToken}`) as Promise; +const achievementFetch = (accessToken: string) => fetchGw2Api('/v2/account/achievements', { accessToken, cache: 'no-cache' }); +const skinsFetch = (accessToken: string) => fetchGw2Api('/v2/account/skins', { accessToken, cache: 'no-cache' }); +const walletFetch = (accessToken: string) => fetchGw2Api('/v2/account/wallet', { accessToken, cache: 'no-cache' }); const wizardsVaultFetch = (accessToken: string) => loadAccountsWizardsVault(accessToken, 'en'); -type Gw2ApiAccountProgression = { - id: number, - current: number, - max: number, - done: boolean, - bits?: number[], - repeated?: number, - unlocked?: boolean, -}[]; - -type AccountWallet = { - id: number, - value: number, -}[]; - -interface AccountWizardsVaultData { - account: { id: string, last_modified: string, name: string }, - lastModifiedToday: boolean, - lastModifiedThisWeek: boolean, - daily: WizardsProgress | undefined, - weekly: WizardsProgress | undefined, - special: WizardsProgress | undefined, - acclaim: number, -} - -interface WizardsProgress { - meta_progress_current: number, - meta_progress_complete: number, - meta_reward_item_id: number, - meta_reward_astral: number, - meta_reward_claimed: number, - objectives: { - id: number, - title: string, - track: string, - acclaim: number, - progress_current: number, - progress_complete: number, - claimed: boolean, - }[] -} - -async function loadAccountsWizardsVault(subtoken: string, lang: Language): Promise { - const account = await fetchJson(`https://api.guildwars2.com/v2/account?v=2019-02-21T00:00:00.000Z&access_token=${subtoken}`); +async function loadAccountsWizardsVault(accessToken: string, lang: Language) { + const account = await fetchGw2Api('/v2/account', { accessToken, schema: '2019-02-21T00:00:00.000Z', cache: 'no-cache' }); const lastModified = new Date(account.last_modified); const lastModifiedToday = lastModified > getResetDate('last-daily'); const lastModifiedThisWeek = lastModified > getResetDate('last-weekly'); - const [daily, weekly, special, acclaim] = await Promise.all([ - lastModifiedToday ? fetchJson(`https://api.guildwars2.com/v2/account/wizardsvault/daily?v=2019-02-21T00:00:00.000Z&lang=${lang}&access_token=${subtoken}`) : undefined, - lastModifiedThisWeek ? fetchJson(`https://api.guildwars2.com/v2/account/wizardsvault/weekly?v=2019-02-21T00:00:00.000Z&lang=${lang}&access_token=${subtoken}`) : undefined, - fetchJson(`https://api.guildwars2.com/v2/account/wizardsvault/special?v=2019-02-21T00:00:00.000Z&lang=${lang}&access_token=${subtoken}`), - // TODO: needs wallet permission - // fetch(`https://api.guildwars2.com/v2/account/wallet?v=2019-02-21T00:00:00.000Z&access_token=${subtoken}`).then((r) => r.json()).then((wallet) => wallet.find((currency: any) => currency.id === 63)?.value) as Promise, - 0 + const [daily, weekly, special] = await Promise.all([ + lastModifiedToday ? fetchGw2Api('/v2/account/wizardsvault/daily', { accessToken, cache: 'no-cache' }) : undefined, + lastModifiedThisWeek ? fetchGw2Api('/v2/account/wizardsvault/weekly', { accessToken, cache: 'no-cache' }) : undefined, + fetchGw2Api('/v2/account/wizardsvault/special', { accessToken, cache: 'no-cache' }), ]); return { account, lastModifiedToday, lastModifiedThisWeek, - daily, weekly, special, acclaim + daily, weekly, special }; } diff --git a/apps/web/components/Item/TradingPost.tsx b/apps/web/components/Item/TradingPost.tsx index eada8b510..920f2d778 100644 --- a/apps/web/components/Item/TradingPost.tsx +++ b/apps/web/components/Item/TradingPost.tsx @@ -1,22 +1,21 @@ 'use client'; -import type { Gw2Api } from 'gw2-api-types'; import { type FC, useEffect, useState } from 'react'; import { Coins } from '../Format/Coins'; import { FormatNumber } from '../Format/FormatNumber'; +import { fetchGw2Api } from '@gw2api/fetch'; import styles from './TradingPost.module.css'; +import type { Price } from '@gw2api/types/data/commerce'; export interface TradingPostProps { itemId: number; } export const TradingPost: FC = ({ itemId }) => { - const [tpData, setTpData] = useState(); + const [tpData, setTpData] = useState(); useEffect(() => { - fetch(`https://api.guildwars2.com/v2/commerce/prices/${itemId}`).then((r) => r.json()).then((prices) => { - setTpData(prices); - }); + fetchGw2Api(`/v2/commerce/prices/${itemId}`, {}).then(setTpData); }, [itemId]); return ( diff --git a/apps/web/package.json b/apps/web/package.json index 18171f3af..bf6c2cae8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -15,6 +15,8 @@ "@floating-ui/react": "0.26.19", "@floating-ui/react-dom": "2.1.1", "@formatjs/intl-localematcher": "0.5.4", + "@gw2api/fetch": "0.1.0", + "@gw2api/types": "0.0.8", "@gw2me/client": "0.2.5", "@gw2treasures/database": "*", "@gw2treasures/eslint-plugin-nextjs": "*", diff --git a/package-lock.json b/package-lock.json index e93c1697f..485c1cb7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,8 @@ "@floating-ui/react": "0.26.19", "@floating-ui/react-dom": "2.1.1", "@formatjs/intl-localematcher": "0.5.4", + "@gw2api/fetch": "0.1.0", + "@gw2api/types": "0.0.8", "@gw2me/client": "0.2.5", "@gw2treasures/database": "*", "@gw2treasures/eslint-plugin-nextjs": "*", @@ -1310,6 +1312,19 @@ "tslib": "^2.4.0" } }, + "node_modules/@gw2api/fetch": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@gw2api/fetch/-/fetch-0.1.0.tgz", + "integrity": "sha512-YstOGUs6J7p+4o932zrKnKiOcxMaKy4zpneHIxP8nDb7nwcqD2LVJHLCAli5tgX/ftrs7/JdFKNOnJOiq+3Waw==", + "peerDependencies": { + "@gw2api/types": "~0.0.5" + } + }, + "node_modules/@gw2api/types": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@gw2api/types/-/types-0.0.8.tgz", + "integrity": "sha512-QXFAoxViFA5bo3NlgWkG0F799TmDdNNvZlEiDofRJ5Uh0RPeMxWX7xkRXhO2fTasrm6RzF3qayuQJbpgPqrHMg==" + }, "node_modules/@gw2me/client": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/@gw2me/client/-/client-0.2.5.tgz",