Skip to content

Commit 77c14a0

Browse files
committed
refactor: generate urls with a tag
1 parent d13b981 commit 77c14a0

File tree

6 files changed

+73
-48
lines changed

6 files changed

+73
-48
lines changed

app/[lang]/(home)/Hero/StatsUrlCards.tsx

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import type {ReactElement} from 'react';
44
import {CopyToClipboard} from 'react-copy-to-clipboard';
5-
import {useSnackbar} from 'react-simple-snackbar';
5+
import toast from 'react-hot-toast';
66
import {track} from '@amplitude/analytics-browser';
77
import {CopyIcon} from '@primer/octicons-react';
88
import {useRouter} from 'next/navigation';
@@ -13,36 +13,44 @@ import Button from '../../(common)/Button';
1313
import type {PluginType} from '../Home';
1414

1515
const rootUrl = `${process.env.NEXT_PUBLIC_ROOT_URL}/api`;
16+
const baseUrl = process.env.NEXT_PUBLIC_ROOT_URL;
1617

1718
type Props = {
1819
t: Translates['home'];
1920
login: string;
20-
svgStatsURL: string;
21+
svgStatsURLDisplay: string;
22+
svgStatsURLCopy: string;
2123
svgTrophiesURL: string;
2224
selectedPluginType: PluginType;
2325
};
2426

2527
export default function StatsUrlCard({
2628
t,
2729
login,
28-
svgStatsURL,
30+
svgStatsURLDisplay,
31+
svgStatsURLCopy,
2932
svgTrophiesURL,
3033
}: Props): ReactElement {
3134
const router = useRouter();
32-
const [openSnackbar] = useSnackbar();
3335
const {login: authLogin} = useAuthContext();
3436

3537
const copyURLToClipboard = (type: 'stats' | 'trophies'): void => {
36-
openSnackbar(`${t.urlCopiedToClipboard}`);
37-
track('Copy URL to clipboard', {login, type});
38+
toast.success(t.urlCopiedToClipboard);
39+
40+
if (process.env.NEXT_PUBLIC_AMPLITUDE_KEY) {
41+
track('Copy URL to clipboard', {login, type});
42+
}
3843
};
3944

40-
const trophiesURL = `![${login} github-trophies](${rootUrl}/github-trophies?login=${login})`;
45+
// For display (markdown)
46+
const trophiesURLDisplay = `![${login} github-trophies](${rootUrl}/github-trophies?login=${login})`;
47+
// For copying (HTML)
48+
const trophiesURLCopy = `<a href="${baseUrl}/stats/${login}"><img src="${rootUrl}/github-trophies?login=${login}" width="600" /></a>`;
4149

4250
return (
4351
<div className="mb-[32px] self-stretch relative flex flex-col">
4452
{/* Stats URL */}
45-
<CopyToClipboard text={svgStatsURL}>
53+
<CopyToClipboard text={svgStatsURLCopy}>
4654
{/* @ts-ignore */}
4755
<div className="bg-basic relative flex-1 p-4 flex flex-col flex-wrap mt-2 mb-3 rounded-md">
4856
<div
@@ -53,14 +61,14 @@ export default function StatsUrlCard({
5361
>
5462
<div className="px-[18px] py-[8px] flex flex-row items-center">
5563
<CopyIcon className="text-contrast" />
56-
<p className="body3 text-contrast pl-3">{svgStatsURL}</p>
64+
<p className="body3 text-contrast pl-3">{svgStatsURLDisplay}</p>
5765
</div>
5866
</div>
5967
</div>
6068
</CopyToClipboard>
6169
{/* Trophies URL */}
6270
{svgTrophiesURL ? (
63-
<CopyToClipboard text={trophiesURL}>
71+
<CopyToClipboard text={trophiesURLCopy}>
6472
{/* @ts-ignore */}
6573
<div className="bg-basic relative flex-1 p-4 flex flex-col flex-wrap rounded-md">
6674
<div
@@ -71,7 +79,7 @@ export default function StatsUrlCard({
7179
>
7280
<div className="px-[18px] py-[8px] flex flex-row items-center">
7381
<CopyIcon className="text-contrast" />
74-
<p className="body3 text-contrast pl-3">{trophiesURL}</p>
82+
<p className="body3 text-contrast pl-3">{trophiesURLDisplay}</p>
7583
</div>
7684
</div>
7785
</div>

app/[lang]/(home)/Hero/index.tsx

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,18 @@ import {useMediaQuery} from 'usehooks-ts';
2121
import GreatFrontEnd from '../../(common)/GreatFrontEnd';
2222

2323
const rootUrl = `${process.env.NEXT_PUBLIC_ROOT_URL}/api`;
24+
const baseUrl = process.env.NEXT_PUBLIC_ROOT_URL;
2425

25-
const generateSvgUrlToCopy = (login: string, isBasic?: boolean): string => {
26-
if (isBasic) {
27-
return `![${login} github-stats](${rootUrl}/github-stats?login=${login})`;
28-
}
26+
// For display (markdown format)
27+
const generateSvgUrlToDisplay = (login: string, isBasic?: boolean): string => {
28+
const apiEndpoint = isBasic ? 'github-stats' : 'github-stats-advanced';
29+
return `![${login} github-stats](${rootUrl}/${apiEndpoint}?login=${login})`;
30+
};
2931

30-
return `![${login} github-stats](${rootUrl}/github-stats-advanced?login=${login})`;
32+
// For copying (HTML format)
33+
const generateSvgUrlToCopy = (login: string, isBasic?: boolean): string => {
34+
const apiEndpoint = isBasic ? 'github-stats' : 'github-stats-advanced';
35+
return `<a href="${baseUrl}/stats/${login}"><img src="${rootUrl}/${apiEndpoint}?login=${login}" width="600" /></a>`;
3136
};
3237

3338
type SvgType = 'basic' | 'advanced' | 'trophies';
@@ -64,7 +69,8 @@ function Hero({t, statsInfo}: Props): ReactElement {
6469
const [searchLogin, setSearchedUID] = useState('');
6570
const [githubSVG, setGithubSVG] = useState<string | null>(null);
6671
const [noTrophies, setNoTrophies] = useState(false);
67-
const [svgStatsURL, setSvgStatsURL] = useState<string>('');
72+
const [svgStatsURLDisplay, setSvgStatsURLDisplay] = useState<string>('');
73+
const [svgStatsURLCopy, setSvgStatsURLCopy] = useState<string>('');
6874
const [isBasic, setIsBasic] = useState<boolean | undefined>(undefined);
6975
const {register, formState, handleSubmit} = useForm();
7076

@@ -80,13 +86,15 @@ function Hero({t, statsInfo}: Props): ReactElement {
8086
const base64dataStats = btoa(unescape(encodeURIComponent(svgStats)));
8187
setGithubSVG(base64dataStats);
8288

83-
setSvgStatsURL(generateSvgUrlToCopy(login, false));
89+
setSvgStatsURLDisplay(generateSvgUrlToDisplay(login, false));
90+
setSvgStatsURLCopy(generateSvgUrlToCopy(login, false));
8491
setSearchedUID(login);
8592
setIsBasic(false);
8693
setNoTrophies(false);
8794
} catch (err) {
8895
setGithubSVG(null);
89-
setSvgStatsURL('');
96+
setSvgStatsURLDisplay('');
97+
setSvgStatsURLCopy('');
9098
setIsBasic(undefined);
9199
}
92100
}
@@ -202,7 +210,8 @@ function Hero({t, statsInfo}: Props): ReactElement {
202210
buttons={[{label: t.basic}, {label: t.advanced}]}
203211
onClick={(index) => {
204212
const shouldBeBasic = index === 0;
205-
setSvgStatsURL(generateSvgUrlToCopy(login, shouldBeBasic));
213+
setSvgStatsURLDisplay(generateSvgUrlToDisplay(login, shouldBeBasic));
214+
setSvgStatsURLCopy(generateSvgUrlToCopy(login, shouldBeBasic));
206215
setIsBasic(shouldBeBasic);
207216

208217
track('Press Stat Tab', {login, basic: shouldBeBasic});
@@ -243,11 +252,12 @@ function Hero({t, statsInfo}: Props): ReactElement {
243252
/>
244253
</div>
245254
) : null}
246-
{svgStatsURL ? (
255+
{svgStatsURLDisplay ? (
247256
<StatsUrlCard
248257
t={t}
249258
selectedPluginType={selectedPluginType}
250-
svgStatsURL={svgStatsURL}
259+
svgStatsURLDisplay={svgStatsURLDisplay}
260+
svgStatsURLCopy={svgStatsURLCopy}
251261
svgTrophiesURL={
252262
!noTrophies ? getSVGUrl('trophies', searchLogin) : ''
253263
}

bun.lock

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"react-dom": "^19.2.0",
2626
"react-hamburger-menu": "^1.2.1",
2727
"react-hook-form": "^7.66.1",
28-
"react-simple-snackbar": "^1.1.11",
28+
"react-hot-toast": "^2.6.0",
2929
"server-only": "^0.0.1",
3030
"tiny-invariant": "^1.3.3",
3131
"typescript": "5.9.3",
@@ -1094,8 +1094,6 @@
10941094

10951095
"dom-accessibility-api": ["[email protected]", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="],
10961096

1097-
"dom-helpers": ["[email protected]", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
1098-
10991097
"dom-serializer": ["[email protected]", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
11001098

11011099
"domelementtype": ["[email protected]", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
@@ -1290,6 +1288,8 @@
12901288

12911289
"globby": ["[email protected]", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="],
12921290

1291+
"goober": ["[email protected]", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw=="],
1292+
12931293
"gopd": ["[email protected]", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
12941294

12951295
"graceful-fs": ["[email protected]", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
@@ -1740,14 +1740,12 @@
17401740

17411741
"react-hook-form": ["[email protected]", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-2KnjpgG2Rhbi+CIiIBQQ9Df6sMGH5ExNyFl4Hw9qO7pIqMBR8Bvu9RQyjl3JM4vehzCh9soiNUM/xYMswb2EiA=="],
17421742

1743+
"react-hot-toast": ["[email protected]", "", { "dependencies": { "csstype": "^3.1.3", "goober": "^2.1.16" }, "peerDependencies": { "react": ">=16", "react-dom": ">=16" } }, "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg=="],
1744+
17431745
"react-is": ["[email protected]", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="],
17441746

17451747
"react-refresh": ["[email protected]", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
17461748

1747-
"react-simple-snackbar": ["[email protected]", "", { "dependencies": { "@babel/runtime": "^7.13.10", "react-transition-group": "^4.4.1" }, "peerDependencies": { "react": "16.x", "react-dom": "16.x" } }, "sha512-l+RMkIoqnOXBQaQbirk4yGaEH5Jeynup/AQBGaq7hCPIIraOQi2RFVthZIM4Ms6sFtdSYxQ8emN4/jI30l7gXw=="],
1748-
1749-
"react-transition-group": ["[email protected]", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="],
1750-
17511749
"read-cache": ["[email protected]", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="],
17521750

17531751
"read-cmd-shim": ["[email protected]", "", {}, "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A=="],

environment.d.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,3 @@ declare module '*.svg?component' {
2222
const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
2323
export default content;
2424
}
25-
26-
declare module 'react-simple-snackbar' {
27-
import type React from 'react';
28-
29-
interface SnackbarProps {}
30-
31-
const Snackbar: React.FC<SnackbarProps>;
32-
33-
export const useSnackbar = (): any => {
34-
return [openSnackbar, closeSnackbar];
35-
};
36-
37-
export default Snackbar;
38-
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"react-dom": "^19.2.0",
4343
"react-hamburger-menu": "^1.2.1",
4444
"react-hook-form": "^7.66.1",
45-
"react-simple-snackbar": "^1.1.11",
45+
"react-hot-toast": "^2.6.0",
4646
"server-only": "^0.0.1",
4747
"tiny-invariant": "^1.3.3",
4848
"typescript": "5.9.3",

src/components/RootProvider.tsx

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

33
import type {ReactElement, ReactNode} from 'react';
4-
import SnackbarProvider from 'react-simple-snackbar';
4+
import {Toaster} from 'react-hot-toast';
55

66
import {AuthProvider} from './AuthProvider';
77

@@ -19,9 +19,32 @@ export default function RootProvider({
1919
}): ReactElement {
2020
return (
2121
<LocaleProvider initialLocale={initialLocale}>
22-
<SnackbarProvider>
23-
<AuthProvider>{children}</AuthProvider>
24-
</SnackbarProvider>
22+
<AuthProvider>
23+
{children}
24+
<Toaster
25+
position="bottom-center"
26+
toastOptions={{
27+
duration: 2000,
28+
success: {
29+
style: {
30+
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
31+
color: '#fff',
32+
padding: '12px 20px',
33+
borderRadius: '12px',
34+
fontSize: '14px',
35+
fontWeight: '500',
36+
boxShadow: '0 10px 25px rgba(102, 126, 234, 0.3)',
37+
maxWidth: 'fit-content',
38+
minWidth: 'auto',
39+
},
40+
iconTheme: {
41+
primary: '#fff',
42+
secondary: '#667eea',
43+
},
44+
},
45+
}}
46+
/>
47+
</AuthProvider>
2548
</LocaleProvider>
2649
);
2750
}

0 commit comments

Comments
 (0)