Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/1055 - Faucet settings #1056

Merged
merged 4 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions apps/faucet/src/App/App.components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,50 @@ export const ContentContainer = styled.div`
padding: 0 16px;
}
`;

export const InputContainer = styled.div`
margin: 12px 0;
`;

export const ButtonContainer = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 13px 0 0 0;
`;

export const SettingsButtonContainer = styled.div`
display: flex;
align-items: center;
justify-content: flex-end;
width: 100%;
`;

export const SettingsButton = styled.button`
& > svg {
width: 20px;
height: 20px;
color: ${(props) => props.theme.colors.primary.main20};
}
`;

export const SettingsContainer = styled.div`
flex-direction: column;
justify-content: start;
align-items: center;
box-sizing: border-box;
background-color: ${(props) =>
getColor(ComponentColor.BackgroundColor, props.theme)};
border: 1px solid ${(props) => props.theme.colors.primary.main20};
border-radius: ${(props) => props.theme.borderRadius.mainContainer};
transition: background-color 0.3s linear;
`;

export const SettingsFormContainer = styled.form`
flex-direction: column;
justify-content: start;
align-items: center;
padding: 32px 40px;
width: 500px;
`;
138 changes: 80 additions & 58 deletions apps/faucet/src/App/App.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,50 @@
import React, { createContext, useCallback, useEffect, useState } from "react";
import { GoGear } from "react-icons/go";
import { ThemeProvider } from "styled-components";

import { ActionButton, Alert, Heading } from "@namada/components";
import { ActionButton, Alert, Modal } from "@namada/components";
import { Namada } from "@namada/integrations";
import { ColorMode, getTheme } from "@namada/utils";

import {
AppContainer,
BackgroundImage,
Banner,
BannerContents,
BottomSection,
ContentContainer,
FaucetContainer,
GlobalStyles,
InfoContainer,
SettingsButton,
SettingsButtonContainer,
TopSection,
} from "App/App.components";
import { FaucetForm } from "App/Faucet";

import { chains } from "@namada/chains";
import { useUntil } from "@namada/hooks";
import { Account } from "@namada/types";
import { API } from "utils";
import { API, toNam } from "utils";
import dotsBackground from "../../public/bg-dots.svg";
import { CallToActionCard } from "./CallToActionCard";
import { CardsContainer } from "./Card.components";
import { Faq } from "./Faq";
import {
AppBanner,
AppHeader,
CallToActionCard,
CardsContainer,
Faq,
} from "./Common";
import { SettingsForm } from "./SettingsForm";

const DEFAULT_URL = "http://localhost:5000";
const DEFAULT_ENDPOINT = "/api/v1/faucet";
const DEFAULT_FAUCET_LIMIT = "1000";
const DEFAULT_LIMIT = 1_000_000_000;

const {
NAMADA_INTERFACE_FAUCET_API_URL: faucetApiUrl = DEFAULT_URL,
NAMADA_INTERFACE_FAUCET_API_ENDPOINT: faucetApiEndpoint = DEFAULT_ENDPOINT,
NAMADA_INTERFACE_FAUCET_LIMIT: faucetLimit = DEFAULT_FAUCET_LIMIT,
NAMADA_INTERFACE_PROXY: isProxied,
NAMADA_INTERFACE_PROXY_PORT: proxyPort = 9000,
} = process.env;

const apiUrl = isProxied ? `http://localhost:${proxyPort}/proxy` : faucetApiUrl;
const url = `${apiUrl}${faucetApiEndpoint}`;
const api = new API(url);
const limit = parseInt(faucetLimit);
const baseUrl =
isProxied ? `http://localhost:${proxyPort}/proxy` : faucetApiUrl;
const runFullNodeUrl = "https://docs.namada.net/operators/ledger";
const becomeBuilderUrl = "https://docs.namada.net/integrating-with-namada";

Expand All @@ -52,13 +53,18 @@ type Settings = {
tokens?: Record<string, string>;
startsAt: number;
startsAtText?: string;
withdrawLimit: number;
};

type AppContext = Settings & {
limit: number;
url: string;
type AppContext = {
baseUrl: string;
settingsError?: string;
api: API;
isTestnetLive: boolean;
settings: Settings;
setApi: (api: API) => void;
setUrl: (url: string) => void;
setIsModalOpen: (value: boolean) => void;
};

const START_TIME_UTC = 1702918800;
Expand All @@ -74,17 +80,7 @@ const START_TIME_TEXT = new Date(START_TIME_UTC * 1000).toLocaleString(
}
);

const defaults = {
startsAt: START_TIME_UTC,
startsAtText: `${START_TIME_TEXT} UTC`,
};

export const AppContext = createContext<AppContext>({
...defaults,
limit,
url,
api,
});
export const AppContext = createContext<AppContext | null>(null);

enum ExtensionAttachStatus {
PendingDetection,
Expand All @@ -104,8 +100,13 @@ export const App: React.FC = () => {
const [colorMode, _] = useState<ColorMode>(initialColorMode);
const [isTestnetLive, setIsTestnetLive] = useState(true);
const [settings, setSettings] = useState<Settings>({
...defaults,
startsAt: START_TIME_UTC,
startsAtText: `${START_TIME_TEXT} UTC`,
withdrawLimit: toNam(DEFAULT_LIMIT),
});
const [url, setUrl] = useState(localStorage.getItem("baseUrl") || baseUrl);
const [api, setApi] = useState<API>(new API(url));
const [isModalOpen, setIsModalOpen] = useState(false);
const [settingsError, setSettingsError] = useState<string>();
const theme = getTheme(colorMode);

Expand All @@ -124,6 +125,10 @@ export const App: React.FC = () => {
);

useEffect(() => {
// Sync url to localStorage
localStorage.setItem("baseUrl", url);
const api = new API(url);
setApi(api);
const { startsAt } = settings;
const now = new Date();
const nowUTC = Date.UTC(
Expand All @@ -141,26 +146,28 @@ export const App: React.FC = () => {
// Fetch settings from faucet API
(async () => {
try {
const { difficulty, tokens_alias_to_address: tokens } = await api
.settings()
.catch((e) => {
const message = e.errors?.message;
setSettingsError(
`Error requesting settings: ${message?.join(" ")}`
);
throw new Error(e);
});
const {
difficulty,
tokens_alias_to_address: tokens,
withdraw_limit: withdrawLimit = DEFAULT_LIMIT,
} = await api.settings().catch((e) => {
const message = e.errors?.message;
setSettingsError(`Error requesting settings: ${message?.join(" ")}`);
throw new Error(e);
});
// Append difficulty level and tokens to settings
setSettings({
...settings,
difficulty,
tokens,
withdrawLimit: toNam(withdrawLimit),
});
setSettingsError(undefined);
} catch (e) {
setSettingsError(`Failed to load settings! ${e}`);
}
})();
}, []);
}, [url]);

const handleConnectExtensionClick = useCallback(async (): Promise<void> => {
if (integration) {
Expand All @@ -186,43 +193,53 @@ export const App: React.FC = () => {
return (
<AppContext.Provider
value={{
settingsError,
limit,
url,
api,
...settings,
isTestnetLive,
baseUrl: url,
settingsError,
settings,
setApi,
setUrl,
setIsModalOpen,
}}
>
<ThemeProvider theme={theme}>
<GlobalStyles colorMode={colorMode} />
{!isTestnetLive && settings?.startsAtText && (
<Banner>
<BannerContents>
Testnet will go live {settings.startsAtText}! Faucet is disabled
until then.
</BannerContents>
</Banner>
)}
<AppBanner />
<BackgroundImage imageUrl={dotsBackground} />
<AppContainer>
<ContentContainer>
<SettingsButtonContainer>
<SettingsButton
onClick={() => setIsModalOpen(true)}
title="Settings"
>
<GoGear />
</SettingsButton>
</SettingsButtonContainer>

<TopSection>
<Heading className="uppercase text-black text-4xl" level="h1">
Namada Faucet
</Heading>
<AppHeader />
</TopSection>
<FaucetContainer>
{extensionAttachStatus ===
ExtensionAttachStatus.PendingDetection && (
{settingsError && (
<InfoContainer>
<Alert type="info">Detecting extension...</Alert>
<Alert type="error">{settingsError}</Alert>
</InfoContainer>
)}

{extensionAttachStatus ===
ExtensionAttachStatus.PendingDetection && (
<InfoContainer>
<Alert type="info">Detecting extension...</Alert>
</InfoContainer>
)}
{extensionAttachStatus === ExtensionAttachStatus.NotInstalled && (
<InfoContainer>
<Alert type="error">You must download the extension!</Alert>
</InfoContainer>
)}

{isExtensionConnected && (
<FaucetForm
accounts={accounts}
Expand All @@ -239,6 +256,11 @@ export const App: React.FC = () => {
</InfoContainer>
)}
</FaucetContainer>
{isModalOpen && (
<Modal onClose={() => setIsModalOpen(false)}>
<SettingsForm />
</Modal>
)}
<BottomSection>
<CardsContainer>
<CallToActionCard
Expand Down
19 changes: 19 additions & 0 deletions apps/faucet/src/App/Common/AppBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { AppContext } from "App/App";
import React, { useContext } from "react";
import { Banner, BannerContents } from "./Banner.components";

export const AppBanner: React.FC = () => {
const { isTestnetLive, settings } = useContext(AppContext)!;
return (
<>
{!isTestnetLive && settings?.startsAtText && (
<Banner>
<BannerContents>
Testnet will go live {settings.startsAtText}! Faucet is disabled
until then.
</BannerContents>
</Banner>
)}
</>
);
};
7 changes: 7 additions & 0 deletions apps/faucet/src/App/Common/AppHeader.components.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import styled from "styled-components";

export const AppHeaderContainer = styled.div`
display: flex;
align-items: center;
justify-content: center;
`;
13 changes: 13 additions & 0 deletions apps/faucet/src/App/Common/AppHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Heading } from "@namada/components";
import React from "react";
import { AppHeaderContainer } from "./AppHeader.components";

export const AppHeader: React.FC = () => {
return (
<AppHeaderContainer>
<Heading className="uppercase text-black text-4xl" level="h1">
Namada Faucet
</Heading>
</AppHeaderContainer>
);
};
21 changes: 21 additions & 0 deletions apps/faucet/src/App/Common/Banner.components.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import styled from "styled-components";

export const Banner = styled.div`
width: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: ${(props) => props.theme.colors.utility3.highAttention};
color: ${(props) => props.theme.colors.primary.main20};
font-size: 13px;
font-weight: bold;
`;

export const BannerContents = styled.div`
display: flex;
width: 100%;
align-items: center;
max-width: 762px;
padding: 8px 0;
margin: 0 20px;
`;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState } from "react";
import InclineArrowBlack from "../../public/incline-arrow-black.svg";
import InclineArrowYellow from "../../public/incline-arrow-yellow.svg";
import InclineArrowBlack from "../../../public/incline-arrow-black.svg";
import InclineArrowYellow from "../../../public/incline-arrow-yellow.svg";
import {
BottomBorder,
CallToActionContainer,
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState } from "react";
import plusIcon from "../../public/plus-icon.svg";
import plusIcon from "../../../public/plus-icon.svg";
import {
DropDownTitle,
DropDownTitleText,
Expand Down
6 changes: 6 additions & 0 deletions apps/faucet/src/App/Common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from "./AppBanner";
export * from "./AppHeader";
export * from "./CallToActionCard";
export * from "./Card.components";
export * from "./Faq";
export * from "./FaqDropdown";
Loading
Loading