Skip to content

Commit 583ce29

Browse files
psellechadoh
andauthored
feat!: Friendbot integration (#25)
* Start fund button * Add a notification provider system * Add fade out animations * Funding takes a little to create the account, add a starter notification * Disable the button once clicked * Use Stellar button * Use local faucet instead of remote web url * Add helper text * Update vite config to be network aware * Layout and typo fix * Leverage useTransition and be aware of account funding status * Integrate tooltip * Show and hide tooltip on hover * Modify CSS for transitions, and for tooltip to stay same size regardless of inner content * Update Context.Provider references to simply Context according to https://react.dev/blog/2024/12/05/react-19\#context-as-a-provider * add local env var to build environment * Incorporate zod for types * Fix complaint about break statement * Some refactors of the provider and moving hook to its own file * Update vite.config.ts Co-authored-by: Chad Ostrowski <221614+chadoh@users.noreply.github.com> * Change notification hook filename * Add comment on Stellar networks * Validate body without zod * Fix linting error * Rename function to make clearer --------- Co-authored-by: Chad Ostrowski <221614+chadoh@users.noreply.github.com>
1 parent 06b3705 commit 583ce29

File tree

12 files changed

+237
-9
lines changed

12 files changed

+237
-9
lines changed

.env.example

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1-
# Prefix with "PUBLIC_" to make available in Astro frontend files
1+
# Prefix with "PUBLIC_" to make available in frontend files
2+
# Which Stellar network to use in the frontend: local, testnet, futurenet, or mainnet
3+
# More on Stellar networks: https://developers.stellar.org/docs/networks
4+
PUBLIC_STELLAR_NETWORK="local"
5+
# The Stellar network passphrase, this is local
26
PUBLIC_STELLAR_NETWORK_PASSPHRASE="Standalone Network ; February 2017"
3-
PUBLIC_STELLAR_RPC_URL="http://localhost:8000/rpc"
7+
# The Stellar network RPC URL. this is local
8+
PUBLIC_STELLAR_RPC_URL="http://localhost:8000/rpc"

.github/workflows/node.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ on:
88
env:
99
CARGO_TERM_COLOR: always
1010
PKG_CONFIG_PATH: /usr/lib/pkgconfig
11+
PUBLIC_STELLAR_NETWORK: local
1112

1213
jobs:
1314
build:

src/App.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { Layout } from "@stellar/design-system";
22
import "./App.module.css"
3+
import ConnectAccount from "./components/ConnectAccount.tsx";
34

45
function App() {
56
return (
67
<main>
78
<Layout.Header
89
projectId="My App"
910
projectTitle="My App"
10-
contentRight={<>Connect wallet component</>}
11+
contentRight={<ConnectAccount />}
1112
/>
1213
<Layout.Content>
1314
<Layout.Inset>

src/components/.gitkeep

Whitespace-only changes.

src/components/ConnectAccount.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
import { stellarNetwork } from '../contracts/util';
3+
import FundAccountButton from './FundAccountButton';
4+
5+
const ConnectAccount: React.FC = () => {
6+
return (
7+
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: '10px', verticalAlign: 'middle' }}>
8+
<div>Wallet Button here</div>
9+
{stellarNetwork !== "mainnet" && <FundAccountButton />}
10+
</div>
11+
);
12+
};
13+
14+
export default ConnectAccount;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import React, { useState, useTransition } from 'react';
2+
import { useNotification } from '../hooks/useNotification.ts';
3+
import { Button, Tooltip } from '@stellar/design-system';
4+
5+
6+
const FundAccountButton: React.FC = () => {
7+
const { addNotification } = useNotification();
8+
const [isPending, startTransition] = useTransition();
9+
const [isFunded, setIsFunded] = useState(false);
10+
const [isTooltipVisible, setIsTooltipVisible] = useState(false);
11+
// TODO: replace with account from wallet
12+
const account = "GDVWY6R4RP37DQPAWBNQKQIZAGDVHUAYMYXKUDSY2O7PJWZNSIZIJNHQ";
13+
14+
const handleFundAccount = (account: string) => {
15+
startTransition(async () => {
16+
try {
17+
const response = await fetch(`/friendbot?addr=${account}`, {
18+
method: 'GET',
19+
});
20+
21+
if (response.ok) {
22+
addNotification('Account funded successfully!', 'success');
23+
setIsFunded(true);
24+
} else {
25+
const body: unknown = await response.json();
26+
if (
27+
body !== null &&
28+
typeof body === 'object' &&
29+
'detail' in body &&
30+
typeof body.detail === 'string'
31+
) {
32+
if (body.detail === "account already funded to starting balance") {
33+
setIsFunded(true);
34+
}
35+
addNotification(`Error funding account: ${body.detail}`, 'error');
36+
} else {
37+
addNotification('Error funding account: Unknown error', 'error');
38+
}
39+
}
40+
} catch {
41+
addNotification('Error funding account. Please try again.', 'error');
42+
}
43+
});
44+
};
45+
46+
return (
47+
<div
48+
onMouseEnter={() => setIsTooltipVisible(true)}
49+
onMouseLeave={() => setIsTooltipVisible(false)}
50+
>
51+
<Tooltip
52+
isVisible={isTooltipVisible}
53+
isContrast
54+
title="Fund Account"
55+
placement="bottom"
56+
triggerEl={
57+
<Button
58+
disabled={isPending || isFunded}
59+
onClick={handleFundAccount.bind(this, account)}
60+
variant="primary"
61+
size="md"
62+
>
63+
Fund Account
64+
</Button>
65+
}
66+
>
67+
<div style={{ width: '13em' }} >
68+
{isFunded ? "Account is already funded" : "Fund your account using the Stellar Friendbot"}
69+
</div>
70+
</Tooltip>
71+
</div>
72+
);
73+
};
74+
75+
export default FundAccountButton;

src/contracts/util.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
const envRPC = import.meta.env.PUBLIC_STELLAR_RPC_URL;
1+
const envStellarNetwork = import.meta.env.PUBLIC_STELLAR_NETWORK;
2+
export const stellarNetwork =
3+
envStellarNetwork && typeof envStellarNetwork === "string"
4+
? envStellarNetwork : "local";
5+
6+
const envRPC = import.meta.env.PUBLIC_STELLAR_RPC_URL;
27
export const rpcUrl =
38
envRPC && typeof envRPC === "string"
49
? envRPC : "http://localhost:8000/rpc";

src/hooks/useNotification.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { use } from 'react';
2+
import { NotificationContext, NotificationContextType } from '../providers/NotificationProvider';
3+
4+
export const useNotification = (): NotificationContextType => {
5+
const context = use(NotificationContext);
6+
if (!context) {
7+
throw new Error('useNotification must be used within a NotificationProvider');
8+
}
9+
return context;
10+
};
11+

src/main.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ import { createRoot } from "react-dom/client";
33
import "./index.css";
44
import App from "./App.tsx";
55
import "@stellar/design-system/build/styles.min.css";
6+
import { NotificationProvider } from "./providers/NotificationProvider.tsx"
67

7-
createRoot(document.getElementById("root")!).render(
8+
createRoot(document.getElementById('root') as HTMLElement).render(
89
<StrictMode>
9-
<App />
10+
<NotificationProvider>
11+
<App />
12+
</NotificationProvider>
1013
</StrictMode>
1114
);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
.notification-container {
2+
position: fixed;
3+
top: 10px;
4+
left: 50%;
5+
transform: translateX(-50%);
6+
display: flex;
7+
flex-direction: column;
8+
gap: 10px;
9+
z-index: 1000;
10+
}
11+
12+
.notification {
13+
transition: transform 0.3s ease, opacity 0.3s ease;
14+
opacity: 1;
15+
}
16+
17+
.notification.slide-in {
18+
transform: translateY(0);
19+
}
20+
21+
.notification.slide-out {
22+
transform: translateY(-20px);
23+
opacity: 0;
24+
}

0 commit comments

Comments
 (0)