Skip to content

Commit 79f0928

Browse files
committed
[ux] Experiment network selection as global state
1 parent c5635d5 commit 79f0928

File tree

9 files changed

+175
-1
lines changed

9 files changed

+175
-1
lines changed

astro.config.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import sitemap from "@astrojs/sitemap";
1313
import partytown from "@astrojs/partytown";
1414
import node from "@astrojs/node";
1515
import react from "@astrojs/react";
16+
// Removed astro-iconify import
1617
import Icons from "unplugin-icons/vite";
1718
import starlightLlmsTxt from "starlight-llms-txt";
1819
import { sidebar } from "./astro.sidebar.ts";
@@ -95,7 +96,7 @@ export default defineConfig({
9596
},
9697
components: {
9798
Head: "./src/starlight-overrides/Head.astro",
98-
// Header: "./src/starlight-overrides/Header.astro",
99+
Header: "./src/starlight-overrides/Header.astro",
99100
LanguageSelect: "./src/starlight-overrides/LanguageSelect.astro",
100101
MobileMenuToggle: "./src/starlight-overrides/MobileMenuToggle.astro",
101102
PageFrame: "./src/starlight-overrides/PageFrame.astro",
@@ -161,6 +162,7 @@ export default defineConfig({
161162
experimentalReactChildren: true,
162163
include: ["**/GraphQLEditor.tsx"],
163164
}),
165+
// Removed astro-iconify integration
164166
],
165167
adapter: process.env.VERCEL
166168
? vercel({
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
---
3+
4+
{/* This is intentionally inlined to avoid FOUC. */}
5+
<script is:inline>
6+
window.StarlightNetworkProvider = (() => {
7+
function setNetworkPreference() {
8+
// Check for cookie
9+
const cookieMatch = document.cookie.match(/preferred_network=([^;]+)/);
10+
const preferredNetwork = cookieMatch ? cookieMatch[1] : "mainnet";
11+
12+
// Store the preference in a data attribute on the document
13+
document.documentElement.dataset.preferredNetwork = preferredNetwork;
14+
15+
return preferredNetwork;
16+
}
17+
18+
// Set the network preference immediately
19+
const networkPreference = setNetworkPreference();
20+
21+
return {
22+
updatePickers: function (network = networkPreference) {
23+
document.documentElement.dataset.preferredNetwork = network;
24+
document.querySelectorAll("starlight-network-select").forEach((picker) => {
25+
const select = picker.querySelector("select");
26+
if (select) select.value = network;
27+
});
28+
},
29+
};
30+
})();
31+
</script>

src/components/NetworkSelect.astro

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
import Select from "@astrojs/starlight/components/Select.astro";
3+
import { MOVE_REFERENCE_BRANCHES } from "../content.config";
4+
5+
const networks = MOVE_REFERENCE_BRANCHES.map(network => ({
6+
label: network.label,
7+
value: network.name,
8+
selected: network.name === "mainnet" // Default to mainnet
9+
}));
10+
---
11+
12+
<starlight-network-select>
13+
<Select
14+
icon="setting"
15+
label="Network"
16+
value="mainnet"
17+
options={networks}
18+
width="7.5em"
19+
/>
20+
</starlight-network-select>
21+
22+
{/* Inlined to avoid FOUC. Uses global scope from `NetworkProvider.astro` */}
23+
<script is:inline>
24+
window.StarlightNetworkProvider.updatePickers();
25+
</script>
26+
27+
<script>
28+
declare global {
29+
interface Window {
30+
StarlightNetworkProvider: {
31+
updatePickers: (network?: string) => void;
32+
};
33+
}
34+
}
35+
36+
class StarlightNetworkSelect extends HTMLElement {
37+
constructor() {
38+
super();
39+
const select = this.querySelector('select');
40+
if (!select) return;
41+
42+
select.addEventListener('change', (e) => {
43+
if (e.currentTarget instanceof HTMLSelectElement) {
44+
// Get the selected network
45+
const network = e.currentTarget.value;
46+
47+
// Set the cookie with a 1-year expiration
48+
const expiryDate = new Date();
49+
expiryDate.setFullYear(expiryDate.getFullYear() + 1);
50+
document.cookie = `preferred_network=${network}; expires=${expiryDate.toUTCString()}; path=/; SameSite=Lax`;
51+
52+
console.log(`Set network preference cookie: preferred_network=${network}`);
53+
54+
// Update all pickers
55+
window.StarlightNetworkProvider.updatePickers(network);
56+
57+
// Reload the page to apply the network change
58+
// This will trigger the middleware to redirect to the correct network
59+
window.location.reload();
60+
}
61+
});
62+
}
63+
}
64+
customElements.define('starlight-network-select', StarlightNetworkSelect);
65+
</script>

src/globals.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@import "tailwindcss";
22
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
33
@plugin "@astrojs/starlight-tailwind";
4+
@import "./styles/network-select.css";
45

56
/* Register your custom font family and tell the browser where to find it. */
67
@font-face {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { MOVE_REFERENCE_BRANCHES, type MoveNetwork } from "../content.config";
2+
3+
const NETWORK_NAMES = MOVE_REFERENCE_BRANCHES.map((branch) => branch.name);
4+
const DEFAULT_NETWORK: MoveNetwork = "mainnet";
5+
6+
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
7+
export default function middleware(request: Request): Response | void {
8+
const url = new URL(request.url);
9+
const pathname = url.pathname;
10+
11+
// Check for cookie
12+
const cookies = request.headers.get("cookie") ?? "";
13+
const networkCookieMatch = /preferred_network=([a-z-]+)/.exec(cookies);
14+
let preferredNetwork: MoveNetwork = DEFAULT_NETWORK;
15+
16+
// Validate that the cookie value is a valid network
17+
if (networkCookieMatch && NETWORK_NAMES.includes(networkCookieMatch[1] as MoveNetwork)) {
18+
preferredNetwork = networkCookieMatch[1] as MoveNetwork;
19+
}
20+
21+
// Only process move-reference paths
22+
if (!pathname.startsWith("/move-reference")) {
23+
return undefined;
24+
}
25+
26+
// Check if the path already includes a network
27+
const networkPathMatch = /^\/move-reference\/([a-z-]+)(\/.*|$)/.exec(pathname);
28+
29+
if (networkPathMatch) {
30+
const pathNetwork = networkPathMatch[1];
31+
const remainingPath = networkPathMatch[2] ?? "/";
32+
33+
// If the network in the path is valid but different from the preferred one, redirect
34+
if (NETWORK_NAMES.includes(pathNetwork as MoveNetwork) && pathNetwork !== preferredNetwork) {
35+
url.pathname = `/move-reference/${preferredNetwork}${remainingPath}`;
36+
return Response.redirect(url);
37+
}
38+
} else if (pathname === "/move-reference" || pathname === "/move-reference/") {
39+
// If no network is specified in the path, redirect to the preferred network
40+
url.pathname = `/move-reference/${preferredNetwork}/`;
41+
return Response.redirect(url);
42+
}
43+
44+
return undefined;
45+
}

src/starlight-overrides/Head.astro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Default from "@astrojs/starlight/components/Head.astro";
33
// import { ClientRouter } from "astro:transitions";
44
import { Schema } from "astro-seo-schema";
55
import TextSizeProvider from "../components/TextSizeProvider.astro";
6+
import NetworkProvider from "../components/NetworkProvider.astro";
67
import { SUPPORTED_LANGUAGES } from "../config/locales";
78
import { getImageUrl } from "~/lib/og-image/getImageUrl";
89
@@ -167,6 +168,7 @@ const lastUpdatedDate = lastUpdated ? lastUpdated.toISOString() : new Date().toI
167168
/>
168169

169170
<TextSizeProvider />
171+
<NetworkProvider />
170172
<Default {...Astro.props}><slot /></Default>
171173

172174
<!-- <meta property="article:published_time" content={entryData.publishDate} /> -->

src/starlight-overrides/Header.astro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import SiteTitle from "@astrojs/starlight/components/SiteTitle.astro";
44
import SocialIcons from "@astrojs/starlight/components/SocialIcons.astro";
55
import ThemeSelect from "@astrojs/starlight/components/ThemeSelect.astro";
66
// import TextSizeToggle from "../components/TextSizeToggle.astro";
7+
import NetworkSelect from "../components/NetworkSelect.astro";
78
import LanguageSelect from "./LanguageSelect.astro";
89
---
910

@@ -21,6 +22,7 @@ import LanguageSelect from "./LanguageSelect.astro";
2122
<!-- <TextSizeToggle /> -->
2223
<ThemeSelect />
2324
<LanguageSelect />
25+
<NetworkSelect />
2426
</div>
2527
</div>
2628

src/styles/network-select.css

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* Network selector styles */
2+
:root[data-preferred-network="mainnet"] {
3+
--network-color: var(--sl-color-green-high);
4+
}
5+
6+
:root[data-preferred-network="testnet"] {
7+
--network-color: var(--sl-color-orange-high);
8+
}
9+
10+
:root[data-preferred-network="devnet"] {
11+
--network-color: var(--sl-color-blue-high);
12+
}
13+
14+
/* Add a subtle indicator to the network select */
15+
starlight-network-select::after {
16+
content: "";
17+
position: absolute;
18+
bottom: -2px;
19+
left: 0;
20+
width: 100%;
21+
height: 2px;
22+
background-color: var(--network-color, transparent);
23+
border-radius: 1px;
24+
}

src/vercel-middleware.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-invalid-void-type */
22
// Edge-compatible middleware that implements a middleware chain pattern
33
import i18nRedirect from "./middlewares/i18n-redirect";
4+
import networkRedirect from "./middlewares/network-redirect";
45
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
56
// @ts-ignore - auto-generated import
67
import { matcher } from "./middlewares/matcher-routes-dynamic";
@@ -30,6 +31,7 @@ async function applyMiddleware(
3031
export default async function middleware(req: Request) {
3132
return await applyMiddleware(req, [
3233
i18nRedirect,
34+
networkRedirect,
3335
// Add more middleware functions here as needed
3436
]);
3537
}

0 commit comments

Comments
 (0)