Skip to content

Commit a36c855

Browse files
committed
click-app-event: instrument LocalizedLink to fire Addressable Pixel on app.shapeshift.com clicks
1 parent 29665de commit a36c855

File tree

2 files changed

+90
-56
lines changed

2 files changed

+90
-56
lines changed
Lines changed: 89 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,108 @@
1-
'use client';
1+
'use client'
22

3-
import Link from 'next/link';
4-
import {usePathname} from 'next/navigation';
5-
import {forwardRef} from 'react';
3+
import Link from 'next/link'
4+
import {usePathname} from 'next/navigation'
5+
import {forwardRef} from 'react'
66

7-
import {DEFAULT_LANGUAGE, getLanguageFromPath} from '@/app/[lang]/_utils/i18nconfig';
7+
import {DEFAULT_LANGUAGE, getLanguageFromPath} from '@/app/[lang]/_utils/i18nconfig'
88

9-
import type {LinkProps} from 'next/link';
10-
import type {AnchorHTMLAttributes, ReactNode} from 'react';
11-
import type {UrlObject} from 'url';
9+
import type {LinkProps} from 'next/link'
10+
import type {AnchorHTMLAttributes, ReactNode} from 'react'
11+
import type {UrlObject} from 'url'
12+
13+
/* eslint-disable @typescript-eslint/naming-convention */
14+
declare const __adrsbl: {run: (event: string, conversion: boolean) => void} | undefined
15+
/* eslint-enable @typescript-eslint/naming-convention */
1216

1317
type TLocalizedLinkProps = LinkProps &
1418
AnchorHTMLAttributes<HTMLAnchorElement> & {
15-
children: ReactNode;
16-
};
19+
children: ReactNode
20+
}
1721

1822
/**
1923
* A localized version of Next.js Link that automatically prepends the current language
20-
* to internal links when needed.
2124
*/
22-
export const LocalizedLink = forwardRef<HTMLAnchorElement, TLocalizedLinkProps>(({href, children, ...props}, ref) => {
23-
const pathname = usePathname();
24-
const currentLanguage = getLanguageFromPath(pathname) || DEFAULT_LANGUAGE;
25+
export const LocalizedLink = forwardRef<HTMLAnchorElement, TLocalizedLinkProps>(
26+
({href, children, onClick, ...props}, ref) => {
27+
const pathname = usePathname()
28+
const currentLanguage = getLanguageFromPath(pathname) || DEFAULT_LANGUAGE
29+
30+
// Convert href to string for processing
31+
const hrefString =
32+
typeof href === 'string'
33+
? href
34+
: typeof href === 'object' &&
35+
href !== null &&
36+
'pathname' in href &&
37+
typeof (href as UrlObject).pathname === 'string'
38+
? (href as UrlObject).pathname
39+
: ''
40+
41+
// External app.shapeshift.com link detection
42+
const isAppLink = typeof hrefString === 'string' && /^https?:\/\/app\.shapeshift\.com(\/|$)/i.test(hrefString)
43+
44+
// Compose click handler for external app links
45+
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
46+
if (isAppLink) {
47+
try {
48+
if (__adrsbl?.run) {
49+
__adrsbl.run('app_click', true)
50+
}
51+
} catch {
52+
// ignore
53+
}
54+
e.preventDefault()
55+
try {
56+
window.open(hrefString || (typeof href === 'string' ? href : ''), '_blank', 'noopener,noreferrer')
57+
} catch {
58+
if (hrefString) {
59+
window.location.assign(hrefString)
60+
}
61+
}
62+
}
63+
if (onClick) {
64+
onClick(e)
65+
}
66+
}
67+
68+
// Don't modify external links, anchors, or already localized paths
69+
if (
70+
hrefString?.startsWith('http') ||
71+
hrefString?.startsWith('#') ||
72+
hrefString?.startsWith('mailto:') ||
73+
hrefString?.startsWith('tel:') ||
74+
!hrefString?.startsWith('/')
75+
) {
76+
return (
77+
<Link
78+
href={href}
79+
ref={ref}
80+
{...props}
81+
onClick={isAppLink ? handleClick : onClick}>
82+
{children}
83+
</Link>
84+
)
85+
}
2586

26-
// Convert href to string for processing
27-
const hrefString =
28-
typeof href === 'string'
29-
? href
30-
: typeof href === 'object' &&
31-
href !== null &&
32-
'pathname' in href &&
33-
typeof (href as UrlObject).pathname === 'string'
34-
? (href as UrlObject).pathname
35-
: '';
87+
// Check if the href already has a language prefix
88+
const hasLanguagePrefix = hrefString.match(/^\/([a-z]{2})(\/|$)/)
89+
90+
// Build the localized href
91+
let localizedHref = hrefString
92+
if (!hasLanguagePrefix && currentLanguage !== DEFAULT_LANGUAGE) {
93+
localizedHref = `/${currentLanguage}${hrefString}`
94+
}
3695

37-
// Don't modify external links, anchors, or already localized paths
38-
if (
39-
hrefString?.startsWith('http') ||
40-
hrefString?.startsWith('#') ||
41-
hrefString?.startsWith('mailto:') ||
42-
hrefString?.startsWith('tel:') ||
43-
!hrefString?.startsWith('/')
44-
) {
4596
return (
4697
<Link
47-
href={href}
98+
href={localizedHref}
4899
ref={ref}
49-
{...props}>
100+
{...props}
101+
onClick={onClick}>
50102
{children}
51103
</Link>
52-
);
104+
)
53105
}
106+
)
54107

55-
// Check if the href already has a language prefix
56-
const hasLanguagePrefix = hrefString.match(/^\/[a-z]{2}(\/|$)/);
57-
58-
// Build the localized href
59-
let localizedHref = hrefString;
60-
if (!hasLanguagePrefix && currentLanguage !== DEFAULT_LANGUAGE) {
61-
localizedHref = `/${currentLanguage}${hrefString}`;
62-
}
63-
64-
return (
65-
<Link
66-
href={localizedHref}
67-
ref={ref}
68-
{...props}>
69-
{children}
70-
</Link>
71-
);
72-
});
73-
74-
LocalizedLink.displayName = 'LocalizedLink';
108+
LocalizedLink.displayName = 'LocalizedLink'

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,5 @@
3333
"eslint.config.mjs",
3434
"postcss.config.mjs"
3535
],
36-
"exclude": ["node_modules", ".next"]
36+
"exclude": ["node_modules", ".next", "scripts"]
3737
}

0 commit comments

Comments
 (0)