Skip to content

Commit 77c14a0

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

6 files changed

Lines changed: 73 additions & 48 deletions

File tree

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
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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)