11import React , { createContext , useCallback , useEffect , useState } from "react" ;
2+ import { GoGear } from "react-icons/go" ;
23import { ThemeProvider } from "styled-components" ;
34
4- import { ActionButton , Alert , Heading } from "@namada/components" ;
5+ import { ActionButton , Alert , Modal } from "@namada/components" ;
56import { Namada } from "@namada/integrations" ;
67import { ColorMode , getTheme } from "@namada/utils" ;
78
89import {
910 AppContainer ,
1011 BackgroundImage ,
11- Banner ,
12- BannerContents ,
1312 BottomSection ,
1413 ContentContainer ,
1514 FaucetContainer ,
1615 GlobalStyles ,
1716 InfoContainer ,
17+ SettingsButton ,
18+ SettingsButtonContainer ,
1819 TopSection ,
1920} from "App/App.components" ;
2021import { FaucetForm } from "App/Faucet" ;
2122
2223import { chains } from "@namada/chains" ;
2324import { useUntil } from "@namada/hooks" ;
2425import { Account } from "@namada/types" ;
25- import { API } from "utils" ;
26+ import { API , toNam } from "utils" ;
2627import dotsBackground from "../../public/bg-dots.svg" ;
27- import { CallToActionCard } from "./CallToActionCard" ;
28- import { CardsContainer } from "./Card.components" ;
29- import { Faq } from "./Faq" ;
28+ import {
29+ AppBanner ,
30+ AppHeader ,
31+ CallToActionCard ,
32+ CardsContainer ,
33+ Faq ,
34+ } from "./Common" ;
35+ import { SettingsForm } from "./SettingsForm" ;
3036
3137const DEFAULT_URL = "http://localhost:5000" ;
32- const DEFAULT_ENDPOINT = "/api/v1/faucet" ;
33- const DEFAULT_FAUCET_LIMIT = "1000" ;
38+ const DEFAULT_LIMIT = 1_000_000_000 ;
3439
3540const {
3641 NAMADA_INTERFACE_FAUCET_API_URL : faucetApiUrl = DEFAULT_URL ,
37- NAMADA_INTERFACE_FAUCET_API_ENDPOINT : faucetApiEndpoint = DEFAULT_ENDPOINT ,
38- NAMADA_INTERFACE_FAUCET_LIMIT : faucetLimit = DEFAULT_FAUCET_LIMIT ,
3942 NAMADA_INTERFACE_PROXY : isProxied ,
4043 NAMADA_INTERFACE_PROXY_PORT : proxyPort = 9000 ,
4144} = process . env ;
4245
43- const apiUrl = isProxied ? `http://localhost:${ proxyPort } /proxy` : faucetApiUrl ;
44- const url = `${ apiUrl } ${ faucetApiEndpoint } ` ;
45- const api = new API ( url ) ;
46- const limit = parseInt ( faucetLimit ) ;
46+ const baseUrl =
47+ isProxied ? `http://localhost:${ proxyPort } /proxy` : faucetApiUrl ;
4748const runFullNodeUrl = "https://docs.namada.net/operators/ledger" ;
4849const becomeBuilderUrl = "https://docs.namada.net/integrating-with-namada" ;
4950
@@ -52,13 +53,18 @@ type Settings = {
5253 tokens ?: Record < string , string > ;
5354 startsAt : number ;
5455 startsAtText ?: string ;
56+ withdrawLimit : number ;
5557} ;
5658
57- type AppContext = Settings & {
58- limit : number ;
59- url : string ;
59+ type AppContext = {
60+ baseUrl : string ;
6061 settingsError ?: string ;
6162 api : API ;
63+ isTestnetLive : boolean ;
64+ settings : Settings ;
65+ setApi : ( api : API ) => void ;
66+ setUrl : ( url : string ) => void ;
67+ setIsModalOpen : ( value : boolean ) => void ;
6268} ;
6369
6470const START_TIME_UTC = 1702918800 ;
@@ -74,17 +80,7 @@ const START_TIME_TEXT = new Date(START_TIME_UTC * 1000).toLocaleString(
7480 }
7581) ;
7682
77- const defaults = {
78- startsAt : START_TIME_UTC ,
79- startsAtText : `${ START_TIME_TEXT } UTC` ,
80- } ;
81-
82- export const AppContext = createContext < AppContext > ( {
83- ...defaults ,
84- limit,
85- url,
86- api,
87- } ) ;
83+ export const AppContext = createContext < AppContext | null > ( null ) ;
8884
8985enum ExtensionAttachStatus {
9086 PendingDetection ,
@@ -104,8 +100,13 @@ export const App: React.FC = () => {
104100 const [ colorMode , _ ] = useState < ColorMode > ( initialColorMode ) ;
105101 const [ isTestnetLive , setIsTestnetLive ] = useState ( true ) ;
106102 const [ settings , setSettings ] = useState < Settings > ( {
107- ...defaults ,
103+ startsAt : START_TIME_UTC ,
104+ startsAtText : `${ START_TIME_TEXT } UTC` ,
105+ withdrawLimit : toNam ( DEFAULT_LIMIT ) ,
108106 } ) ;
107+ const [ url , setUrl ] = useState ( localStorage . getItem ( "baseUrl" ) || baseUrl ) ;
108+ const [ api , setApi ] = useState < API > ( new API ( url ) ) ;
109+ const [ isModalOpen , setIsModalOpen ] = useState ( false ) ;
109110 const [ settingsError , setSettingsError ] = useState < string > ( ) ;
110111 const theme = getTheme ( colorMode ) ;
111112
@@ -124,6 +125,10 @@ export const App: React.FC = () => {
124125 ) ;
125126
126127 useEffect ( ( ) => {
128+ // Sync url to localStorage
129+ localStorage . setItem ( "baseUrl" , url ) ;
130+ const api = new API ( url ) ;
131+ setApi ( api ) ;
127132 const { startsAt } = settings ;
128133 const now = new Date ( ) ;
129134 const nowUTC = Date . UTC (
@@ -141,26 +146,28 @@ export const App: React.FC = () => {
141146 // Fetch settings from faucet API
142147 ( async ( ) => {
143148 try {
144- const { difficulty , tokens_alias_to_address : tokens } = await api
145- . settings ( )
146- . catch ( ( e ) => {
147- const message = e . errors ?. message ;
148- setSettingsError (
149- `Error requesting settings: ${ message ?. join ( " " ) } `
150- ) ;
151- throw new Error ( e ) ;
152- } ) ;
149+ const {
150+ difficulty ,
151+ tokens_alias_to_address : tokens ,
152+ withdraw_limit : withdrawLimit = DEFAULT_LIMIT ,
153+ } = await api . settings ( ) . catch ( ( e ) => {
154+ const message = e . errors ?. message ;
155+ setSettingsError ( `Error requesting settings: ${ message ?. join ( " " ) } ` ) ;
156+ throw new Error ( e ) ;
157+ } ) ;
153158 // Append difficulty level and tokens to settings
154159 setSettings ( {
155160 ...settings ,
156161 difficulty,
157162 tokens,
163+ withdrawLimit : toNam ( withdrawLimit ) ,
158164 } ) ;
165+ setSettingsError ( undefined ) ;
159166 } catch ( e ) {
160167 setSettingsError ( `Failed to load settings! ${ e } ` ) ;
161168 }
162169 } ) ( ) ;
163- } , [ ] ) ;
170+ } , [ url ] ) ;
164171
165172 const handleConnectExtensionClick = useCallback ( async ( ) : Promise < void > => {
166173 if ( integration ) {
@@ -186,43 +193,53 @@ export const App: React.FC = () => {
186193 return (
187194 < AppContext . Provider
188195 value = { {
189- settingsError,
190- limit,
191- url,
192196 api,
193- ...settings ,
197+ isTestnetLive,
198+ baseUrl : url ,
199+ settingsError,
200+ settings,
201+ setApi,
202+ setUrl,
203+ setIsModalOpen,
194204 } }
195205 >
196206 < ThemeProvider theme = { theme } >
197207 < GlobalStyles colorMode = { colorMode } />
198- { ! isTestnetLive && settings ?. startsAtText && (
199- < Banner >
200- < BannerContents >
201- Testnet will go live { settings . startsAtText } ! Faucet is disabled
202- until then.
203- </ BannerContents >
204- </ Banner >
205- ) }
208+ < AppBanner />
206209 < BackgroundImage imageUrl = { dotsBackground } />
207210 < AppContainer >
208211 < ContentContainer >
212+ < SettingsButtonContainer >
213+ < SettingsButton
214+ onClick = { ( ) => setIsModalOpen ( true ) }
215+ title = "Settings"
216+ >
217+ < GoGear />
218+ </ SettingsButton >
219+ </ SettingsButtonContainer >
220+
209221 < TopSection >
210- < Heading className = "uppercase text-black text-4xl" level = "h1" >
211- Namada Faucet
212- </ Heading >
222+ < AppHeader />
213223 </ TopSection >
214224 < FaucetContainer >
215- { extensionAttachStatus ===
216- ExtensionAttachStatus . PendingDetection && (
225+ { settingsError && (
217226 < InfoContainer >
218- < Alert type = "info" > Detecting extension... </ Alert >
227+ < Alert type = "error" > { settingsError } </ Alert >
219228 </ InfoContainer >
220229 ) }
230+
231+ { extensionAttachStatus ===
232+ ExtensionAttachStatus . PendingDetection && (
233+ < InfoContainer >
234+ < Alert type = "info" > Detecting extension...</ Alert >
235+ </ InfoContainer >
236+ ) }
221237 { extensionAttachStatus === ExtensionAttachStatus . NotInstalled && (
222238 < InfoContainer >
223239 < Alert type = "error" > You must download the extension!</ Alert >
224240 </ InfoContainer >
225241 ) }
242+
226243 { isExtensionConnected && (
227244 < FaucetForm
228245 accounts = { accounts }
@@ -239,6 +256,11 @@ export const App: React.FC = () => {
239256 </ InfoContainer >
240257 ) }
241258 </ FaucetContainer >
259+ { isModalOpen && (
260+ < Modal onClose = { ( ) => setIsModalOpen ( false ) } >
261+ < SettingsForm />
262+ </ Modal >
263+ ) }
242264 < BottomSection >
243265 < CardsContainer >
244266 < CallToActionCard
0 commit comments