Skip to content

Commit 5f77152

Browse files
authored
Merge pull request #18067 from ethereum/fix/find-wallet-ssr-17717
fix(seo): ssr the wallet list on /wallets/find-wallet
2 parents 871c743 + 99ecb53 commit 5f77152

15 files changed

Lines changed: 221 additions & 437 deletions

File tree

app/[locale]/layer-2/networks/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import type { ExtendedRollup, Lang, PageParams } from "@/lib/types"
1010
import CalloutSSR from "@/components/CalloutSSR"
1111
import { ContentHero, type ContentHeroProps } from "@/components/Hero"
1212
import I18nProvider from "@/components/I18nProvider"
13-
import Layer2NetworksTable from "@/components/Layer2NetworksTable/lazy"
13+
import Layer2NetworksTable from "@/components/Layer2NetworksTable"
1414
import MainArticle from "@/components/MainArticle"
1515
import NetworkMaturity from "@/components/NetworkMaturity"
1616
import { ButtonLink } from "@/components/ui/buttons/Button"

app/[locale]/wallets/find-wallet/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
import type { Lang, PageParams, WalletData } from "@/lib/types"
99

1010
import Breadcrumbs from "@/components/Breadcrumbs"
11-
import FindWalletProductTable from "@/components/FindWalletProductTable/lazy"
11+
import FindWalletProductTable from "@/components/FindWalletProductTable"
1212
import I18nProvider from "@/components/I18nProvider"
1313
import ListingMethodology from "@/components/ListingMethodology"
1414
import MainArticle from "@/components/MainArticle"
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { memo, useCallback, useRef, useState } from "react"
2+
3+
import type { WalletRow } from "@/lib/types"
4+
5+
import { cn } from "@/lib/utils/cn"
6+
import { trackCustomEvent } from "@/lib/utils/matomo"
7+
8+
import WalletInfo from "./WalletInfo"
9+
10+
type RowProps = {
11+
wallet: WalletRow
12+
index: number
13+
open: boolean
14+
visible: boolean
15+
onOpenChange: (open: boolean, wallet: WalletRow) => void
16+
children?: React.ReactNode
17+
}
18+
19+
const Row = memo(function Row({
20+
wallet,
21+
index,
22+
open,
23+
visible,
24+
onOpenChange,
25+
children,
26+
}: RowProps) {
27+
const handleToggle = useCallback(
28+
(e: React.SyntheticEvent<HTMLDetailsElement>) => {
29+
onOpenChange(e.currentTarget.open, wallet)
30+
},
31+
[onOpenChange, wallet]
32+
)
33+
return (
34+
<details
35+
data-index={index}
36+
open={open}
37+
onToggle={handleToggle}
38+
className={cn(
39+
"group/collapsible flex w-full flex-col border-b open:bg-background-highlight hover:bg-background-highlight",
40+
!visible && "hidden"
41+
)}
42+
>
43+
<summary className="cursor-pointer list-none p-4 focus-visible:outline focus-visible:-outline-offset-1 focus-visible:outline-primary-hover [&::-webkit-details-marker]:hidden">
44+
<WalletInfo wallet={wallet} />
45+
</summary>
46+
{open && <div className="p-4">{children}</div>}
47+
</details>
48+
)
49+
})
50+
51+
type ListProps = {
52+
data: WalletRow[]
53+
// Rows whose id is not in `matchedIds` are display:none. Avoids
54+
// mount/unmount churn on filter changes.
55+
matchedIds: Set<string>
56+
renderSubComponent: (wallet: WalletRow, listIdx: number) => React.ReactNode
57+
matomoEventCategory: string
58+
}
59+
60+
const List = ({
61+
data,
62+
matchedIds,
63+
renderSubComponent,
64+
matomoEventCategory,
65+
...rest
66+
}: ListProps) => {
67+
const [expanded, setExpanded] = useState<Record<string, boolean>>({})
68+
const previousExpandedRef = useRef<Record<string, boolean>>({})
69+
70+
const handleOpenChange = useCallback(
71+
(open: boolean, wallet: WalletRow) => {
72+
setExpanded((prev) => ({ ...prev, [wallet.id]: open }))
73+
74+
if (!open) return
75+
76+
const expandedOnce = previousExpandedRef.current[wallet.id]
77+
if (!expandedOnce) {
78+
trackCustomEvent({
79+
eventCategory: matomoEventCategory,
80+
eventAction: "expanded",
81+
eventName: wallet.id,
82+
})
83+
}
84+
previousExpandedRef.current = {
85+
...previousExpandedRef.current,
86+
[wallet.id]: true,
87+
}
88+
},
89+
[matomoEventCategory]
90+
)
91+
92+
return (
93+
<div {...rest}>
94+
{data.map((wallet, index) => {
95+
const open = !!expanded[wallet.id]
96+
return (
97+
<Row
98+
key={wallet.id}
99+
wallet={wallet}
100+
index={index}
101+
open={open}
102+
visible={matchedIds.has(wallet.id)}
103+
onOpenChange={handleOpenChange}
104+
>
105+
{open && renderSubComponent(wallet, index)}
106+
</Row>
107+
)
108+
})}
109+
</div>
110+
)
111+
}
112+
113+
export default List

src/components/FindWalletProductTable/WalletInfo.tsx

Lines changed: 65 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@ import { DevicesIcon, LanguagesIcon } from "@/components/icons/wallets"
88
import { Image } from "@/components/Image"
99
import { SupportedLanguagesTooltip } from "@/components/SupportedLanguagesTooltip"
1010

11-
import { breakpointAsNumber } from "@/lib/utils/screen"
1211
import { formatStringList, getWalletPersonas } from "@/lib/utils/wallets"
1312

1413
import { NUMBER_OF_SUPPORTED_LANGUAGES_SHOWN } from "@/lib/constants"
1514

16-
import MediaQuery from "../MediaQuery"
1715
import { ButtonLink } from "../ui/buttons/Button"
1816
import { TagsInlineText } from "../ui/tag"
1917

@@ -59,120 +57,79 @@ const WalletInfo = ({ wallet }: WalletInfoProps) => {
5957
}, [wallet.supportedLanguages])
6058

6159
return (
62-
<div className="flex flex-col gap-4">
60+
<div className="relative flex flex-col gap-4">
61+
{/* Open-state stripe (desktop only), sits in the image-column gutter. */}
62+
<div
63+
aria-hidden
64+
className={`pointer-events-none absolute inset-s-7 top-16 -bottom-9 hidden w-1 -translate-x-1/2 lg:group-[[open]]/collapsible:block rtl:translate-x-1/2 ${wallet.twBackgroundColor}`}
65+
/>
66+
6367
<div className="flex flex-row items-center justify-between gap-4">
64-
<div className="flex flex-col gap-4">
65-
{/* Desktop layout */}
66-
<MediaQuery queries={[`(min-width: ${breakpointAsNumber.lg}px)`]}>
67-
<div className="hidden flex-row gap-4 lg:flex">
68-
<Image
69-
src={wallet.image}
70-
alt=""
71-
style={{ objectFit: "contain", width: "56px", height: "56px" }}
72-
/>
73-
<div className="flex flex-col gap-2">
74-
<p className="text-xl font-bold">{wallet.name}</p>
75-
76-
<PersonaTags walletPersonas={walletPersonas} />
77-
78-
<div
79-
className={`ms-2 ${walletPersonas.length === 0 ? "mb-4" : ""} mt-1`}
80-
>
81-
<ChainImages
82-
chains={wallet.supported_chains as ChainName[]}
83-
className={`ms-2 ${walletPersonas.length === 0 ? "mb-4" : ""}`}
84-
/>
85-
</div>
86-
</div>
87-
</div>
88-
</MediaQuery>
89-
90-
{/* Mobile layout */}
91-
<MediaQuery queries={[`(max-width: ${breakpointAsNumber.lg - 1}px)`]}>
92-
<div className="flex flex-col gap-4 lg:hidden">
93-
<div className="flex flex-row items-center gap-4">
94-
<Image
95-
src={wallet.image}
96-
alt=""
97-
style={{
98-
objectFit: "contain",
99-
width: "24px",
100-
height: "24px",
101-
}}
102-
/>
103-
<p className="text-xl font-bold">{wallet.name}</p>
104-
</div>
105-
<div>
106-
<PersonaTags walletPersonas={walletPersonas} />
107-
</div>
108-
<ChainImages
109-
chains={wallet.supported_chains as ChainName[]}
110-
className={walletPersonas.length === 0 ? "mb-4" : ""}
111-
/>
68+
<div className="grid flex-1 grid-cols-[auto_1fr] items-start gap-x-4 gap-y-2">
69+
<Image
70+
src={wallet.image}
71+
alt=""
72+
className="size-6 self-center object-contain lg:row-span-full lg:size-14 lg:self-start"
73+
/>
74+
75+
<p className="self-center text-xl font-bold lg:self-start">
76+
{wallet.name}
77+
</p>
78+
79+
{walletPersonas.length > 0 && (
80+
<div className="col-span-2 lg:col-span-1 lg:col-start-2">
81+
<PersonaTags walletPersonas={walletPersonas} />
11282
</div>
113-
</MediaQuery>
83+
)}
84+
85+
<div className="col-span-2 lg:col-span-1 lg:col-start-2">
86+
<ChainImages chains={wallet.supported_chains as ChainName[]} />
87+
</div>
11488

115-
<div className="flex flex-row gap-4">
116-
<div className="relative hidden w-14 lg:block">
117-
<div
118-
className={`absolute -top-0 -bottom-9 left-1/2 hidden w-1 -translate-x-1/2 transform group-data-[state=open]/collapsible:block ${wallet.twBackgroundColor}`}
119-
/>
89+
{deviceLabels.length > 0 && (
90+
<div className="col-span-2 flex flex-row gap-2 lg:col-span-1 lg:col-start-2">
91+
<DevicesIcon className="size-6" />
92+
<TagsInlineText list={deviceLabels} />
12093
</div>
121-
<div
122-
className={`flex flex-col gap-2 ${walletPersonas.length === 0 ? "-mt-4" : ""}`}
123-
>
124-
{deviceLabels.length > 0 && (
125-
<div className="flex flex-row gap-2">
126-
<DevicesIcon className="size-6" />
127-
<TagsInlineText list={deviceLabels} />
128-
</div>
94+
)}
95+
96+
<div className="col-span-2 flex flex-row gap-2 lg:col-span-1 lg:col-start-2">
97+
<LanguagesIcon className="size-6" />
98+
<p className="text-md">
99+
{formattedLanguages}{" "}
100+
{hasExtraLanguages && (
101+
<SupportedLanguagesTooltip
102+
supportedLanguages={wallet.supportedLanguages}
103+
/>
129104
)}
130-
<div className="flex flex-row gap-2">
131-
<LanguagesIcon className="size-6" />
132-
<p className="text-md">
133-
{formattedLanguages}{" "}
134-
{hasExtraLanguages && (
135-
<SupportedLanguagesTooltip
136-
supportedLanguages={wallet.supportedLanguages}
137-
/>
138-
)}
139-
</p>
140-
</div>
141-
</div>
105+
</p>
142106
</div>
143107
</div>
144-
<div>
145-
<button className="text-primary">
146-
<ChevronUp className="text-2xl group-data-[state=closed]/collapsible:hidden" />
147-
<ChevronDown className="text-2xl group-data-[state=open]/collapsible:hidden" />
148-
</button>
149-
</div>
108+
109+
<span className="text-primary">
110+
<ChevronUp className="text-2xl group-[&:not([open])]/collapsible:hidden" />
111+
<ChevronDown className="text-2xl group-[[open]]/collapsible:hidden" />
112+
</span>
150113
</div>
151-
<div className="flex flex-row gap-4">
152-
<div className="relative hidden w-14 lg:block">
153-
<div
154-
className={`absolute -top-0 -bottom-9 left-1/2 hidden w-1 -translate-x-1/2 transform group-data-[state=open]/collapsible:block ${wallet.twBackgroundColor}`}
155-
/>
156-
</div>
157-
<div className="flex flex-1">
158-
<ButtonLink
159-
href={wallet.url}
160-
variant="outline"
161-
className="p-2 max-sm:w-full"
162-
size="sm"
163-
customEventOptions={{
164-
eventCategory: "WalletExternalLinkList",
165-
eventAction: "Tap main button",
166-
eventName: `${wallet.name}`,
167-
}}
168-
onClick={(e) => {
169-
// Prevent expanding the wallet more info section when clicking on the "Visit website" button
170-
e.stopPropagation()
171-
}}
172-
>
173-
{t("page-find-wallet-visit-website")}
174-
</ButtonLink>
175-
</div>
114+
115+
<div className="lg:ps-18">
116+
<ButtonLink
117+
href={wallet.url}
118+
variant="outline"
119+
className="p-2 max-sm:w-full"
120+
size="sm"
121+
customEventOptions={{
122+
eventCategory: "WalletExternalLinkList",
123+
eventAction: "Tap main button",
124+
eventName: `${wallet.name}`,
125+
}}
126+
onClick={(e) => {
127+
// Prevent expanding the wallet more info section when clicking on the "Visit website" button
128+
e.stopPropagation()
129+
}}
130+
>
131+
{t("page-find-wallet-visit-website")}
132+
</ButtonLink>
176133
</div>
177134
</div>
178135
)

src/components/FindWalletProductTable/WalletSubComponent.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import InlineLink from "@/components/ui/Link"
1515

1616
import { cn } from "@/lib/utils/cn"
1717
import { getLocaleFormattedDate } from "@/lib/utils/date"
18-
import { trackCustomEvent } from "@/lib/utils/matomo"
1918

2019
import { useTranslation } from "@/hooks/useTranslation"
2120

@@ -55,12 +54,6 @@ const WalletSubComponent = ({
5554
wallet.last_updated
5655
)
5756

58-
trackCustomEvent({
59-
eventCategory: "WalletMoreInfo",
60-
eventAction: "More info wallet",
61-
eventName: `More info ${wallet.name}`,
62-
})
63-
6457
return (
6558
<div className="flex flex-row gap-2">
6659
<div className="w-1 md:w-14">

0 commit comments

Comments
 (0)