Skip to content

Commit 4e9fde1

Browse files
committed
feat(web): add query provider and upgrade alex sdk package
1 parent f7ccb19 commit 4e9fde1

File tree

13 files changed

+214
-100
lines changed

13 files changed

+214
-100
lines changed

apps/web/app/app.tsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import {
2+
Links,
3+
Meta,
4+
Outlet,
5+
Scripts,
6+
ScrollRestoration,
7+
isRouteErrorResponse,
8+
} from 'react-router';
9+
10+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
11+
import { Flex, styled } from 'leather-styles/jsx';
12+
13+
import type { LeatherProvider } from '@leather.io/rpc';
14+
15+
import type { Route } from '../.react-router/types/app/+types/root';
16+
import { Footer } from './layouts/footer/footer';
17+
import { GlobalLoader } from './layouts/nav/global-loader';
18+
import { Nav } from './layouts/nav/nav';
19+
20+
declare global {
21+
interface Window {
22+
LeatherProvider?: LeatherProvider;
23+
}
24+
}
25+
26+
export function Layout({ children }: { children: React.ReactNode }) {
27+
return (
28+
<html lang="en">
29+
<head>
30+
<meta charSet="utf-8" />
31+
<meta name="viewport" content="width=device-width, initial-scale=1" />
32+
<Meta />
33+
<Links />
34+
</head>
35+
<styled.body>
36+
<GlobalLoader />
37+
<Nav />
38+
<Flex flexDir="column" marginLeft="navbar" minHeight="100vh">
39+
<styled.main flex={1}>{children}</styled.main>
40+
<Footer />
41+
</Flex>
42+
<ScrollRestoration />
43+
<Scripts />
44+
</styled.body>
45+
</html>
46+
);
47+
}
48+
49+
export const queryClient = new QueryClient({
50+
defaultOptions: {
51+
queries: {
52+
gcTime: 1,
53+
},
54+
},
55+
});
56+
57+
export default function App() {
58+
return (
59+
<QueryClientProvider client={queryClient}>
60+
<Outlet />
61+
</QueryClientProvider>
62+
);
63+
}
64+
65+
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
66+
let message = 'Oops!';
67+
let details = 'An unexpected error occurred.';
68+
let stack: string | undefined;
69+
70+
if (isRouteErrorResponse(error)) {
71+
message = error.status === 404 ? '404' : 'Error';
72+
details =
73+
error.status === 404 ? 'The requested page could not be found.' : error.statusText || details;
74+
} else if (import.meta.env.DEV && error && error instanceof Error) {
75+
details = error.message;
76+
stack = error.stack;
77+
}
78+
79+
return (
80+
<main>
81+
<h1>{message}</h1>
82+
<p>{details}</p>
83+
{stack && (
84+
<pre>
85+
<code>{stack}</code>
86+
</pre>
87+
)}
88+
</main>
89+
);
90+
}

apps/web/app/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ import packageJson from '../package.json';
33
export const DEFAULT_DEVNET_SERVER = 'http://localhost:3999';
44

55
export const VERSION = packageJson.version;
6+
7+
export const GITHUB_ORG = 'leather-io';
8+
export const GITHUB_REPO = 'mono';

apps/web/app/constants/environment.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const APP_ENVIRONMENT = 'unknown';
2+
export const BRANCH_NAME = 'dev';
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { useStacksNetwork } from '~/store/stacks-network';
2+
3+
import { type StacksClient, stacksClient } from '@leather.io/query';
4+
5+
export function useStacksClient(): StacksClient {
6+
const { networkPreference } = useStacksNetwork();
7+
return stacksClient(networkPreference.chain.stacks.url);
8+
}

apps/web/app/root.tsx

Lines changed: 9 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
1-
import {
2-
Links,
3-
Meta,
4-
Outlet,
5-
Scripts,
6-
ScrollRestoration,
7-
isRouteErrorResponse,
8-
} from 'react-router';
1+
import { Buffer } from 'safe-buffer';
92

10-
import { Flex, styled } from 'leather-styles/jsx';
11-
12-
import type { LeatherProvider } from '@leather.io/rpc';
133
import leatherUiStyles from '@leather.io/ui/styles?url';
144

155
import type { Route } from './+types/root';
166
import stylesheet from './app.css?url';
17-
import { Footer } from './layouts/footer/footer';
18-
import { GlobalLoader } from './layouts/nav/global-loader';
19-
import { Nav } from './layouts/nav/nav';
7+
8+
/**
9+
* Polyfill global Buffer
10+
* (should be called before another code)
11+
*/
12+
// @ts-expect-error // safe-buffer typings are too old
13+
globalThis.Buffer = Buffer;
2014

2115
export function links() {
2216
return [
@@ -25,62 +19,4 @@ export function links() {
2519
] satisfies Route.LinkDescriptors;
2620
}
2721

28-
declare global {
29-
interface Window {
30-
LeatherProvider?: LeatherProvider;
31-
}
32-
}
33-
34-
export function Layout({ children }: { children: React.ReactNode }) {
35-
return (
36-
<html lang="en">
37-
<head>
38-
<meta charSet="utf-8" />
39-
<meta name="viewport" content="width=device-width, initial-scale=1" />
40-
<Meta />
41-
<Links />
42-
</head>
43-
<styled.body>
44-
<GlobalLoader />
45-
<Nav />
46-
<Flex flexDir="column" marginLeft="navbar" minHeight="100vh">
47-
<styled.main flex={1}>{children}</styled.main>
48-
<Footer />
49-
</Flex>
50-
<ScrollRestoration />
51-
<Scripts />
52-
</styled.body>
53-
</html>
54-
);
55-
}
56-
57-
export default function App() {
58-
return <Outlet />;
59-
}
60-
61-
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
62-
let message = 'Oops!';
63-
let details = 'An unexpected error occurred.';
64-
let stack: string | undefined;
65-
66-
if (isRouteErrorResponse(error)) {
67-
message = error.status === 404 ? '404' : 'Error';
68-
details =
69-
error.status === 404 ? 'The requested page could not be found.' : error.statusText || details;
70-
} else if (import.meta.env.DEV && error && error instanceof Error) {
71-
details = error.message;
72-
stack = error.stack;
73-
}
74-
75-
return (
76-
<main>
77-
<h1>{message}</h1>
78-
<p>{details}</p>
79-
{stack && (
80-
<pre>
81-
<code>{stack}</code>
82-
</pre>
83-
)}
84-
</main>
85-
);
86-
}
22+
export { default, Layout, ErrorBoundary } from './app';

apps/web/app/store/stacks-network.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { StacksNetworkName, networkFrom } from '@stacks/network';
2+
import { useAtom } from 'jotai/index';
3+
import { atomWithStorage } from 'jotai/utils';
4+
import { getNetworkInstance } from '~/features/stacking/utils/utils-preset-pools';
5+
6+
import { defaultNetworksKeyedById } from '@leather.io/models';
7+
8+
export const networkNameAtom = atomWithStorage<StacksNetworkName>('network', 'mainnet');
9+
10+
export function useStacksNetwork() {
11+
const [networkName, setNetworkName] = useAtom(networkNameAtom);
12+
13+
const network = networkFrom(networkName);
14+
15+
const networkInstance = getNetworkInstance(network);
16+
17+
const networkPreference =
18+
defaultNetworksKeyedById[networkName === 'mocknet' ? 'testnet' : networkName];
19+
20+
return {
21+
networkName,
22+
setNetworkName,
23+
network,
24+
networkInstance,
25+
networkPreference,
26+
networkLabel: networkName, // TODO: Use AppContextProvider networks from leather/earn
27+
};
28+
}

apps/web/app/types/network.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ChainId, StacksNetworkName } from '@stacks/network';
2+
3+
export interface Network {
4+
label: string;
5+
url: string;
6+
networkId: ChainId;
7+
mode: StacksNetworkName;
8+
wsUrl?: string;
9+
isCustomNetwork?: boolean;
10+
}
11+
12+
interface WhenStacksNetworkMap<T> {
13+
mainnet: T;
14+
testnet: T;
15+
devnet: T;
16+
mocknet: T;
17+
}
18+
19+
export function whenStacksNetwork(mode: StacksNetworkName) {
20+
return <T>(modeMap: WhenStacksNetworkMap<T>): T => modeMap[mode];
21+
}

apps/web/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"dependencies": {
1616
"@leather.io/models": "workspace:*",
1717
"@leather.io/panda-preset": "workspace:*",
18+
"@leather.io/query": "workspace:*",
1819
"@leather.io/rpc": "workspace:*",
1920
"@leather.io/sdk": "workspace:*",
2021
"@leather.io/ui": "workspace:*",
@@ -24,14 +25,17 @@
2425
"@stacks/network": "7.0.2",
2526
"@stacks/stacking": "7.0.5",
2627
"@stacks/transactions": "7.0.5",
28+
"@tanstack/react-query": "5.59.16",
2729
"@tanstack/react-table": "8.21.2",
2830
"bignumber.js": "9.1.2",
2931
"isbot": "5.1.17",
3032
"jotai": "2.12.2",
3133
"nprogress": "0.2.0",
3234
"react": "19.0.0",
3335
"react-dom": "19.0.0",
36+
"react-hook-form": "7.53.2",
3437
"react-router": "7.4.0",
38+
"safe-buffer": "5.2.1",
3539
"zod": "3.24.1"
3640
},
3741
"devDependencies": {

apps/web/vite.config.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ import tsconfigPaths from 'vite-tsconfig-paths';
99

1010
export default defineConfig(({ isSsrBuild }) => ({
1111
build: {
12-
rollupOptions: isSsrBuild
13-
? {
14-
input: './workers/app.ts',
15-
}
16-
: undefined,
12+
rollupOptions: {
13+
...(isSsrBuild
14+
? {
15+
input: './workers/app.ts',
16+
}
17+
: undefined),
18+
},
1719
},
1820
css: {
1921
postcss: {

apps/web/wrangler.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
workers_dev = true
22
name = "leather-web"
33
compatibility_date = "2024-11-18"
4+
compatibility_flags = [ "nodejs_compat" ]
45
main = "./build/server/index.js"
56
assets = { directory = "./build/client/" }
67
send_metrics = false

0 commit comments

Comments
 (0)