mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 02:37:05 +02:00
feat: remove patreon
This commit is contained in:
1 parent
da8025a0ad
commit
0df58d2d2c
13 files changed
+21
-326
No files matched your search
@@ -3,7 +3,6 @@
|
||||
import { idToRegion } from '@blitzkit/core';
|
||||
import { Button, Flex, Heading, Link, Text } from '@radix-ui/themes';
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { PatreonAuthResponse } from '../pages/auth/[provider]/_index';
|
||||
import { App } from '../stores/app';
|
||||
import { CURRENT_POLICIES_AGREEMENT_INDEX } from '../stores/app/constants';
|
||||
|
||||
@@ -30,7 +29,6 @@ export function Checks() {
|
||||
|
||||
/**
|
||||
* Wargaming: 14 (refresh: 7)
|
||||
* Patreon: 31 (refresh: 15)
|
||||
*/
|
||||
function Content() {
|
||||
const logins = App.use((state) => state.logins);
|
||||
@@ -103,33 +101,6 @@ function Content() {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (logins.patreon) {
|
||||
const expiresIn = logins.patreon.expires - Date.now();
|
||||
const expiresInDays = expiresIn / 1000 / 60 / 60 / 24;
|
||||
|
||||
if (expiresInDays < 0) {
|
||||
mutateApp((draft) => {
|
||||
draft.logins.patreon = undefined;
|
||||
});
|
||||
} else if (expiresInDays < 15) {
|
||||
const { patreon } = appStore.getState().logins;
|
||||
|
||||
if (!patreon) return;
|
||||
|
||||
fetch(`/api/patreon/refresh/${patreon.refreshToken}`)
|
||||
.then((response) => response.json() as Promise<PatreonAuthResponse>)
|
||||
.then((data) => {
|
||||
mutateApp((draft) => {
|
||||
draft.logins.patreon = {
|
||||
token: data.access_token,
|
||||
refreshToken: data.refresh_token,
|
||||
expires: Date.now() + data.expires_in * 1000,
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!showPoliciesAgreement) return null;
|
||||
|
||||
@@ -84,8 +84,6 @@ const gameDefinitions = await fetchGameDefinitions();
|
||||
GitHub
|
||||
</Link>
|
||||
<Link href="/docs/patches" color="gray"> Patches </Link>
|
||||
|
||||
{/* patreon supporters? */}
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import {
|
||||
} from '@radix-ui/themes';
|
||||
import { resolveBranchName } from '../core/blitzkit/branches';
|
||||
import { homeTool, TOOLS } from '../constants/tools';
|
||||
import { PatreonIcon } from './PatreonIcon';
|
||||
import { NavbarAccount } from './NavbarAccount';
|
||||
|
||||
const toolsFiltered = TOOLS.filter((tool) => !('href' in tool));
|
||||
@@ -176,15 +175,6 @@ const isBranchNamed = branchName !== undefined;
|
||||
>
|
||||
<DiscordLogoIcon />
|
||||
</Link>
|
||||
<Link
|
||||
style={{ display: 'flex', alignContent: 'center' }}
|
||||
color="gray"
|
||||
href="https://www.patreon.com/tresabhi"
|
||||
underline="none"
|
||||
target="_blank"
|
||||
>
|
||||
<PatreonIcon width="0.75em" height="0.75em" />
|
||||
</Link>
|
||||
<Link
|
||||
style={{ display: 'flex', alignContent: 'center' }}
|
||||
color="gray"
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
import { patreonLoginUrl } from '@blitzkit/core';
|
||||
import { PersonIcon } from '@radix-ui/react-icons';
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
IconButton,
|
||||
Inset,
|
||||
Link,
|
||||
Popover,
|
||||
Separator,
|
||||
Skeleton,
|
||||
Text,
|
||||
} from '@radix-ui/themes';
|
||||
import { Suspense } from 'react';
|
||||
import { App } from '../../stores/app';
|
||||
import { BlitzKitTheme } from '../BlitzKitTheme';
|
||||
import { PatreonIcon } from '../PatreonIcon';
|
||||
import { WargamingAccountName } from '../WargamingAccountName';
|
||||
import { WargamingIcon } from '../WargamingIcon';
|
||||
import { WargamingLoginButton } from '../WargamingLoginButton';
|
||||
@@ -46,30 +41,12 @@ function Content() {
|
||||
width="320px"
|
||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
||||
>
|
||||
{(logins.patreon || logins.wargaming) && (
|
||||
{logins.wargaming && (
|
||||
<Flex direction="column" gap="2">
|
||||
<Text align="center" color="gray">
|
||||
Logged in accounts
|
||||
</Text>
|
||||
|
||||
{logins.patreon && (
|
||||
<Flex align="center" gap="2" justify="center">
|
||||
<PatreonIcon width={15} height={15} />
|
||||
<Text>Patreon</Text>
|
||||
<Button
|
||||
color="red"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
mutateApp((draft) => {
|
||||
draft.logins.patreon = undefined;
|
||||
});
|
||||
}}
|
||||
>
|
||||
Logout
|
||||
</Button>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{logins.wargaming && (
|
||||
<Flex align="center" gap="2" justify="center">
|
||||
<WargamingIcon width={15} height={15} />
|
||||
@@ -96,26 +73,18 @@ function Content() {
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{!logins.patreon !== !logins.wargaming && (
|
||||
{/* {!logins. !== !logins.wargaming && (
|
||||
<Inset side="x">
|
||||
<Separator my="4" size="4" />
|
||||
</Inset>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
{(!logins.patreon || !logins.wargaming) && (
|
||||
{!logins.wargaming && (
|
||||
<Flex direction="column" gap="2">
|
||||
<Text align="center" color="gray">
|
||||
Log in with...
|
||||
</Text>
|
||||
|
||||
{!logins.patreon && (
|
||||
<Link style={{ width: '100%' }} href={patreonLoginUrl()}>
|
||||
<Button style={{ width: '100%' }} color="tomato">
|
||||
<PatreonIcon width={15} height={15} /> Patreon
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{!logins.wargaming && <WargamingLoginButton />}
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { ComponentProps } from 'react';
|
||||
|
||||
export function PatreonIcon(props: ComponentProps<'svg'>) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 436 476" {...props}>
|
||||
<path
|
||||
fill="currentColor"
|
||||
data-fill="1"
|
||||
d="M436 143c-.084-60.778-47.57-110.591-103.285-128.565C263.528-7.884 172.279-4.649 106.214 26.424 26.142 64.089.988 146.596.051 228.883c-.77 67.653 6.004 245.841 106.83 247.11 74.917.948 86.072-95.279 120.737-141.623 24.662-32.972 56.417-42.285 95.507-51.929C390.309 265.865 436.097 213.011 436 143Z"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
import { assertSecret, patreonLoginUrl } from '@blitzkit/core';
|
||||
import { assertSecret } from '@blitzkit/core';
|
||||
import { ArrowRightIcon } from '@radix-ui/react-icons';
|
||||
import { Button, Code, Flex, Link, Separator, Text } from '@radix-ui/themes';
|
||||
import { useAdExempt } from '../hooks/useAdExempt';
|
||||
import { Button, Code, Flex, Link, Text } from '@radix-ui/themes';
|
||||
import { App } from '../stores/app';
|
||||
import { PatreonIcon } from './PatreonIcon';
|
||||
|
||||
export function Plugs() {
|
||||
return (
|
||||
@@ -14,9 +12,6 @@ export function Plugs() {
|
||||
}
|
||||
|
||||
function Content() {
|
||||
const exempt = useAdExempt();
|
||||
const hasPatreon = App.use((state) => state.logins.patreon !== undefined);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
align="center"
|
||||
@@ -28,50 +23,22 @@ function Content() {
|
||||
>
|
||||
{assertSecret(import.meta.env.PUBLIC_PROMOTE_OPENTEST) === 'true' &&
|
||||
assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH) !== 'opentest' && (
|
||||
<>
|
||||
<Flex direction="column" gap="3" align="center">
|
||||
<Text size="5">
|
||||
BlitzKit{' '}
|
||||
<Code variant="outline" size="4" color="gray" highContrast>
|
||||
opentest
|
||||
</Code>{' '}
|
||||
is available now{' '}
|
||||
</Text>
|
||||
<Flex direction="column" gap="3" align="center">
|
||||
<Text size="5">
|
||||
BlitzKit{' '}
|
||||
<Code variant="outline" size="4" color="gray" highContrast>
|
||||
opentest
|
||||
</Code>{' '}
|
||||
is available now{' '}
|
||||
</Text>
|
||||
|
||||
<Link href="https://opentest.blitzkit.app/" target="_blank">
|
||||
<Button color="green">
|
||||
BlitzKit opentest <ArrowRightIcon />
|
||||
</Button>
|
||||
</Link>
|
||||
</Flex>
|
||||
|
||||
<Separator size="3" />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Flex direction="column" gap="3" align="center">
|
||||
<Flex gap="1" align="center">
|
||||
<Text size="4" align="center">
|
||||
{exempt
|
||||
? 'You are supporting BlitzKit, thank you!'
|
||||
: 'Active Patreon supports go ad-free'}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
<Flex gap="2" wrap="wrap" justify="center">
|
||||
<Link href="https://www.patreon.com/tresabhi" target="_blank">
|
||||
<Button>
|
||||
<PatreonIcon width={15} height={15} />{' '}
|
||||
{exempt ? 'Manage membership' : 'Support on Patreon'}
|
||||
</Button>
|
||||
</Link>
|
||||
{!hasPatreon && !exempt && (
|
||||
<Link href={patreonLoginUrl()}>
|
||||
<Button variant="outline">Log in with Patreon</Button>
|
||||
<Link href="https://opentest.blitzkit.app/" target="_blank">
|
||||
<Button color="green">
|
||||
BlitzKit opentest <ArrowRightIcon />
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import { times } from 'lodash-es';
|
||||
import { memo, useEffect, useMemo, useState } from 'react';
|
||||
import { filterTank } from '../../core/blitzkit/filterTank';
|
||||
import { resolveReload } from '../../core/blitzkit/resolveReload';
|
||||
import { useAdExempt } from '../../hooks/useAdExempt';
|
||||
import { $tankFilters } from '../../stores/tankFilters';
|
||||
import {
|
||||
SORT_NAMES,
|
||||
@@ -49,7 +48,6 @@ const tankNames = await fetchTankNames();
|
||||
export const TankSearch = memo<TankSearchProps>(
|
||||
({ compact, onSelect, onSelectAll, ...props }) => {
|
||||
const awaitedTanksDefinitionsArray = Object.values(tankDefinitions.tanks);
|
||||
const exempt = useAdExempt();
|
||||
const tankFilters = useStore($tankFilters);
|
||||
const tankopediaSort = useStore($tankopediaSort);
|
||||
|
||||
|
||||
-2
@@ -26,7 +26,6 @@ import { Fragment } from 'react/jsx-runtime';
|
||||
import { lerp } from 'three/src/math/MathUtils.js';
|
||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
|
||||
import { useAdExempt } from '../../../../../hooks/useAdExempt';
|
||||
import { useEquipment } from '../../../../../hooks/useEquipment';
|
||||
import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition';
|
||||
import { Duel } from '../../../../../stores/duel';
|
||||
@@ -39,7 +38,6 @@ const provisionDefinitions = await fetchProvisionDefinitions();
|
||||
|
||||
export function Characteristics() {
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const exempt = useAdExempt();
|
||||
const crewSkills = TankopediaEphemeral.use((state) => state.skills);
|
||||
const penetrationDistanceInput = useRef<HTMLInputElement>(null);
|
||||
const hasImprovedVentilation = useEquipment(102);
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { App } from '../stores/app';
|
||||
|
||||
let cache: Record<string, boolean | Promise<boolean>> = {};
|
||||
|
||||
export function useAdExempt() {
|
||||
const patreon = App.use((state) => state.logins.patreon);
|
||||
const [exempt, setExempt] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (!patreon) return setExempt(false);
|
||||
|
||||
if (!(patreon.token in cache)) {
|
||||
cache[patreon.token] = new Promise<boolean>(async (resolve) => {
|
||||
const response = await fetch(`/api/patreon/membership`, {
|
||||
cache: 'default',
|
||||
headers: { token: patreon.token },
|
||||
});
|
||||
|
||||
if (!response.ok) return resolve(false);
|
||||
|
||||
const json = await response.json();
|
||||
const exempt = typeof json === 'boolean' && json;
|
||||
|
||||
cache[patreon.token] = exempt;
|
||||
resolve(exempt);
|
||||
});
|
||||
}
|
||||
|
||||
setExempt(await cache[patreon.token]);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return exempt;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import { assertSecret, type BlitzkitResponse } from '@blitzkit/core';
|
||||
import type { APIContext } from 'astro';
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
export async function GET({ request, params }: APIContext) {
|
||||
const code = request.headers.get('code');
|
||||
|
||||
if (!code) {
|
||||
return Response.json({
|
||||
status: 'error',
|
||||
error: 'UNDEFINED_CODE',
|
||||
} satisfies BlitzkitResponse);
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`https://www.patreon.com/api/oauth2/token?code=${
|
||||
code
|
||||
}&grant_type=authorization_code&client_id=${assertSecret(
|
||||
import.meta.env.PUBLIC_PATREON_CLIENT_ID,
|
||||
)}&client_secret=${assertSecret(
|
||||
import.meta.env.PATREON_CLIENT_SECRET,
|
||||
)}&redirect_uri=${assertSecret(
|
||||
import.meta.env.PUBLIC_PATREON_REDIRECT_URI,
|
||||
)}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
},
|
||||
);
|
||||
const json = await response.json();
|
||||
|
||||
return Response.json(json);
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
import { assertSecret } from '@blitzkit/core';
|
||||
import type { APIContext } from 'astro';
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
interface PatreonMembershipResponse {
|
||||
data: {
|
||||
attributes: unknown;
|
||||
id: `${number}`;
|
||||
relationships: {
|
||||
memberships: {
|
||||
data: {
|
||||
id: string; // uuid
|
||||
type: 'member';
|
||||
}[];
|
||||
};
|
||||
};
|
||||
type: 'user';
|
||||
};
|
||||
included?: (
|
||||
| {
|
||||
attributes: {
|
||||
patron_status:
|
||||
| 'active_patron'
|
||||
| 'declined_patron'
|
||||
| 'former_patron'
|
||||
| null; // null = never a paid member
|
||||
};
|
||||
id: string; // uuid
|
||||
relationships: {
|
||||
campaign: {
|
||||
data: {
|
||||
id: `${number}`; // id of my campaign to check against
|
||||
type: 'campaign';
|
||||
};
|
||||
links: {
|
||||
related: string; // url
|
||||
};
|
||||
};
|
||||
};
|
||||
type: 'member';
|
||||
}
|
||||
| {
|
||||
attributes: {};
|
||||
id: `${number}`;
|
||||
type: 'campaign'; // ignore this discriminator
|
||||
}
|
||||
)[];
|
||||
links: {
|
||||
self: 'https://www.patreon.com/api/oauth2/v2/user/124201389';
|
||||
};
|
||||
}
|
||||
|
||||
export async function GET({ request }: APIContext) {
|
||||
const token = request.headers.get('token');
|
||||
|
||||
if (!token) return Response.json(false);
|
||||
|
||||
const patreonResponse = await fetch(
|
||||
encodeURI(
|
||||
'https://www.patreon.com/api/oauth2/v2/identity?include=memberships.campaign&fields[member]=patron_status',
|
||||
),
|
||||
{ headers: { Authorization: `Bearer ${token}` } },
|
||||
);
|
||||
|
||||
if (!patreonResponse.ok) return Response.json(false);
|
||||
|
||||
const json = (await patreonResponse.json()) as PatreonMembershipResponse;
|
||||
|
||||
const response = Response.json(
|
||||
json.included?.some(
|
||||
(item) =>
|
||||
item.type === 'member' &&
|
||||
item.relationships.campaign.data.id ===
|
||||
assertSecret(process.env.PATREON_CAMPAIGN_ID) &&
|
||||
item.attributes.patron_status === 'active_patron',
|
||||
) ?? false,
|
||||
);
|
||||
|
||||
response.headers.set('Cache-Control', 'max-age=86400'); // a day
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -1,24 +1,14 @@
|
||||
import { useEffect } from 'react';
|
||||
import { App } from '../../../stores/app';
|
||||
|
||||
export const AUTH_PROVIDERS = ['wargaming', 'patreon'] as const;
|
||||
export const AUTH_PROVIDERS = ['wargaming'] as const;
|
||||
|
||||
type AuthProvider = (typeof AUTH_PROVIDERS)[number];
|
||||
|
||||
export const AUTH_PROVIDER_NAMES: Record<AuthProvider, string> = {
|
||||
patreon: 'Patreon',
|
||||
wargaming: 'Wargaming',
|
||||
};
|
||||
|
||||
export interface PatreonAuthResponse {
|
||||
access_token: string;
|
||||
expires_in: number;
|
||||
token_type: 'Bearer';
|
||||
scope: string;
|
||||
refresh_token: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
interface AuthorizeProps {
|
||||
provider: AuthProvider;
|
||||
}
|
||||
@@ -59,25 +49,6 @@ function Content({ provider }: AuthorizeProps) {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'patreon': {
|
||||
if (!searchParams.get('code')) break;
|
||||
|
||||
const response = await fetch('/api/patreon/auth', {
|
||||
headers: { code: searchParams.get('code')! },
|
||||
});
|
||||
const data = (await response.json()) as PatreonAuthResponse;
|
||||
|
||||
mutateApp((draft) => {
|
||||
draft.logins.patreon = {
|
||||
token: data.access_token,
|
||||
refreshToken: data.refresh_token,
|
||||
expires: Date.now() + data.expires_in * 1000,
|
||||
};
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown provider: ${provider}`);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ interface AppStore {
|
||||
policiesAgreementIndex: number;
|
||||
logins: {
|
||||
wargaming?: { token: string; id: number; expires: number };
|
||||
patreon?: { token: string; refreshToken: string; expires: number };
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user