Skip to content

Commit b3d0b00

Browse files
committed
Persist server settings in localStorage
1 parent 2c6192f commit b3d0b00

File tree

4 files changed

+137
-13
lines changed

4 files changed

+137
-13
lines changed

app/components/PageLayout.tsx

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type { ReactNode } from "react";
22
import { useChains } from "../contexts/ChainsContext";
33
import { Tooltip } from "react-tooltip";
4+
import { useServerConfig } from "~/contexts/ServerConfigContext";
5+
import { removeCurrentServerUrl } from "../utils/serverStorage";
46

57
interface PageLayoutProps {
68
children: ReactNode;
@@ -11,6 +13,22 @@ interface PageLayoutProps {
1113

1214
export default function PageLayout({ children, maxWidth = "max-w-4xl", title, subtitle }: PageLayoutProps) {
1315
const { loading, error, refetch } = useChains();
16+
const { serverUrl, setServerUrl, getDefaultServerUrls, setCustomServerUrls } = useServerConfig();
17+
18+
const handleResetServerSettings = () => {
19+
// Clear custom server URLs
20+
setCustomServerUrls([]);
21+
22+
// Reset to default server URL
23+
const defaultUrls = getDefaultServerUrls();
24+
setServerUrl(defaultUrls[0]);
25+
26+
// Clear from localStorage
27+
removeCurrentServerUrl();
28+
29+
// Refetch after reset
30+
refetch();
31+
};
1432

1533
const renderHeader = () => {
1634
if (!title && !subtitle) {
@@ -47,14 +65,23 @@ export default function PageLayout({ children, maxWidth = "max-w-4xl", title, su
4765
/>
4866
</svg>
4967
</div>
50-
<h3 className="text-lg font-medium text-gray-900 mb-2">Failed to load chains from the Sourcify server</h3>
68+
<h3 className="text-lg font-medium text-gray-900 mb-2">Error</h3>
5169
<p className="text-gray-600 mb-4">{error}</p>
52-
<button
53-
onClick={refetch}
54-
className="bg-cerulean-blue-500 text-white px-4 py-2 rounded-md hover:bg-cerulean-blue-600 focus:outline-none focus:ring-2 focus:ring-cerulean-blue-500 focus:ring-offset-2"
55-
>
56-
Try Again
57-
</button>
70+
<p className="text-gray-600 mb-4">Server URL: {serverUrl}</p>
71+
<div className="flex flex-col space-y-2 items-center">
72+
<button
73+
onClick={refetch}
74+
className="bg-cerulean-blue-500 text-white px-4 py-2 rounded-md hover:bg-cerulean-blue-600 focus:outline-none focus:ring-2 focus:ring-cerulean-blue-500 focus:ring-offset-2"
75+
>
76+
Try Again
77+
</button>
78+
<button
79+
onClick={handleResetServerSettings}
80+
className="text-red-600 px-4 py-2 rounded-md hover:text-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 border border-red-200"
81+
>
82+
Reset Server Settings
83+
</button>
84+
</div>
5885
</div>
5986
);
6087
}

app/components/verification/Settings.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ import { useServerConfig } from "../../contexts/ServerConfigContext";
55
import { getEtherscanApiKey, setEtherscanApiKey, removeEtherscanApiKey } from "../../utils/etherscanStorage";
66

77
export default function Settings() {
8-
const { serverUrl, setServerUrl, getDefaultServerUrls } = useServerConfig();
8+
const { serverUrl, setServerUrl, getDefaultServerUrls, customServerUrls, setCustomServerUrls } = useServerConfig();
99
const [showSettings, setShowSettings] = useState(false);
10-
const [customServerUrls, setCustomServerUrls] = useState<string[]>([]);
1110
const [editingCustomUrl, setEditingCustomUrl] = useState<string | null>(null);
1211
const [newCustomUrl, setNewCustomUrl] = useState("");
1312
const [urlError, setUrlError] = useState<string | null>(null);
@@ -155,6 +154,7 @@ export default function Settings() {
155154
return key.substring(0, 4) + "*".repeat(key.length - 4);
156155
};
157156

157+
158158
return (
159159
<div
160160
className={`bg-gray-50 border border-gray-200 transition-all duration-300 overflow-hidden ${

app/contexts/ServerConfigContext.tsx

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
import { createContext, useContext, useState } from "react";
1+
import { createContext, useContext, useState, useEffect } from "react";
22
import type { ReactNode } from "react";
3+
import { getCurrentServerUrl, setCurrentServerUrl, getCustomServerUrls, setCustomServerUrls } from "../utils/serverStorage";
34

45
interface ServerConfigContextType {
56
serverUrl: string;
67
setServerUrl: (url: string) => void;
78
getDefaultServerUrls: () => string[];
9+
customServerUrls: string[];
10+
setCustomServerUrls: (urls: string[]) => void;
811
}
912

1013
const ServerConfigContext = createContext<ServerConfigContextType | undefined>(undefined);
1114

1215
export function ServerConfigProvider({ children }: { children: ReactNode }) {
13-
const [serverUrl, setServerUrl] = useState(import.meta.env.VITE_SOURCIFY_SERVER_URL || "https://sourcify.dev/server");
14-
1516
const getDefaultServerUrls = () => {
1617
const urls = [import.meta.env.VITE_SOURCIFY_SERVER_URL || "https://sourcify.dev/server"];
1718
if (import.meta.env.DEV) {
@@ -20,8 +21,53 @@ export function ServerConfigProvider({ children }: { children: ReactNode }) {
2021
return urls;
2122
};
2223

24+
// Initialize server URL from localStorage or use default
25+
const [serverUrl, setServerUrlState] = useState(() => {
26+
const storedUrl = getCurrentServerUrl();
27+
return storedUrl || getDefaultServerUrls()[0];
28+
});
29+
30+
// Initialize custom server URLs from localStorage
31+
const [customServerUrls, setCustomServerUrlsState] = useState(() => {
32+
return getCustomServerUrls();
33+
});
34+
35+
// Sync with localStorage when server URL changes
36+
const handleSetServerUrl = (url: string) => {
37+
setCurrentServerUrl(url);
38+
setServerUrlState(url);
39+
};
40+
41+
// Sync with localStorage when custom URLs change
42+
const handleSetCustomServerUrls = (urls: string[]) => {
43+
setCustomServerUrls(urls);
44+
setCustomServerUrlsState(urls);
45+
};
46+
47+
// Listen for storage changes from other tabs/windows
48+
useEffect(() => {
49+
const handleStorageChange = () => {
50+
const storedUrl = getCurrentServerUrl();
51+
const storedCustomUrls = getCustomServerUrls();
52+
53+
if (storedUrl && storedUrl !== serverUrl) {
54+
setServerUrlState(storedUrl);
55+
}
56+
setCustomServerUrlsState(storedCustomUrls);
57+
};
58+
59+
window.addEventListener("serverUrlChanged", handleStorageChange);
60+
return () => window.removeEventListener("serverUrlChanged", handleStorageChange);
61+
}, [serverUrl]);
62+
2363
return (
24-
<ServerConfigContext.Provider value={{ serverUrl, setServerUrl, getDefaultServerUrls }}>
64+
<ServerConfigContext.Provider value={{
65+
serverUrl,
66+
setServerUrl: handleSetServerUrl,
67+
getDefaultServerUrls,
68+
customServerUrls,
69+
setCustomServerUrls: handleSetCustomServerUrls
70+
}}>
2571
{children}
2672
</ServerConfigContext.Provider>
2773
);

app/utils/serverStorage.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { useState, useEffect } from "react";
2+
3+
const CURRENT_SERVER_URL_STORAGE_KEY = "sourcify-current-server-url";
4+
const CUSTOM_SERVER_URLS_STORAGE_KEY = "sourcify-custom-server-urls";
5+
const SERVER_URL_CHANGED_EVENT = "serverUrlChanged";
6+
7+
export const getCurrentServerUrl = (): string | null => {
8+
if (typeof window === "undefined") return null;
9+
return localStorage.getItem(CURRENT_SERVER_URL_STORAGE_KEY);
10+
};
11+
12+
export const setCurrentServerUrl = (url: string): void => {
13+
if (typeof window === "undefined") return;
14+
localStorage.setItem(CURRENT_SERVER_URL_STORAGE_KEY, url);
15+
window.dispatchEvent(new CustomEvent(SERVER_URL_CHANGED_EVENT));
16+
};
17+
18+
export const removeCurrentServerUrl = (): void => {
19+
if (typeof window === "undefined") return;
20+
localStorage.removeItem(CURRENT_SERVER_URL_STORAGE_KEY);
21+
window.dispatchEvent(new CustomEvent(SERVER_URL_CHANGED_EVENT));
22+
};
23+
24+
export const getCustomServerUrls = (): string[] => {
25+
if (typeof window === "undefined") return [];
26+
const stored = localStorage.getItem(CUSTOM_SERVER_URLS_STORAGE_KEY);
27+
return stored ? JSON.parse(stored) : [];
28+
};
29+
30+
export const setCustomServerUrls = (urls: string[]): void => {
31+
if (typeof window === "undefined") return;
32+
localStorage.setItem(CUSTOM_SERVER_URLS_STORAGE_KEY, JSON.stringify(urls));
33+
window.dispatchEvent(new CustomEvent(SERVER_URL_CHANGED_EVENT));
34+
};
35+
36+
export const useServerUrl = () => {
37+
const [currentServerUrl, setCurrentServerUrlState] = useState<string | null>(getCurrentServerUrl());
38+
const [customServerUrls, setCustomServerUrlsState] = useState<string[]>(getCustomServerUrls());
39+
40+
useEffect(() => {
41+
const handleServerUrlChange = () => {
42+
setCurrentServerUrlState(getCurrentServerUrl());
43+
setCustomServerUrlsState(getCustomServerUrls());
44+
};
45+
46+
window.addEventListener(SERVER_URL_CHANGED_EVENT, handleServerUrlChange);
47+
return () => window.removeEventListener(SERVER_URL_CHANGED_EVENT, handleServerUrlChange);
48+
}, []);
49+
50+
return { currentServerUrl, customServerUrls };
51+
};

0 commit comments

Comments
 (0)