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

Creat and Fund Account Page #788

Merged
merged 10 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v18
v18
2 changes: 1 addition & 1 deletion src/app/(sidebar)/account/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default function CreateAccount() {
</Button>
</div>
</div>
<ExpandBox isExpanded={Boolean(account.publicKey && secretKey)}>
<ExpandBox isExpanded={Boolean(account.publicKey)}>
<GenerateKeypair
publicKey={account.publicKey}
secretKey={secretKey}
Expand Down
182 changes: 181 additions & 1 deletion src/app/(sidebar)/account/fund/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,185 @@
"use client";

import { useState } from "react";
import Link from "next/link";

import {
Card,
Icon,
IconButton,
Input,
Text,
Button,
} from "@stellar/design-system";

import { shortenStellarAddress } from "@/helpers/shortenStellarAddress";
import { validatePublicKey } from "@/helpers/validatePublicKey";
import { useStore } from "@/store/useStore";

import { AlertBox } from "@/components/AlertBox";

import "../styles.scss";

const callFriendBot = async ({
network,
publicKey,
}: {
network: string;
publicKey: string;
}) => {
const friendbotURL =
network === "futurenet"
? "https://friendbot-futurenet.stellar.org"
: "https://friendbot.stellar.org";
const response = await fetch(friendbotURL + "/?addr=" + publicKey);
jeesunikim marked this conversation as resolved.
Show resolved Hide resolved

return response;
};

export default function FundAccount() {
return <div>Fund Account</div>;
const { account, network } = useStore();

const [isSuccess, setIsSuccess] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState("");

const [generatedPublicKey, setGeneratedPublicKey] = useState("");
const [errorMessage, setErrorMessage] = useState("");
jeesunikim marked this conversation as resolved.
Show resolved Hide resolved

const fundAccount = async () => {
try {
setIsLoading(true);
setError("");

const data = await callFriendBot({
network: network.id,
publicKey: account.publicKey,
});

if (!data.ok) {
const errorBody = await data.json();

throw new Error(errorBody.status || "An error occurred");
}

setIsSuccess(true);
} catch (error: any) {
let errorMessage = "";

if (error.status === 0) {
errorMessage = `Unable to reach Friendbot server at ${network.id}`;
} else {
errorMessage = `Failed to fund ${account.publicKey} on the ${network.id}`;
}
setError(errorMessage);
} finally {
setIsLoading(false);
}
};

return (
<div className="Account">
jeesunikim marked this conversation as resolved.
Show resolved Hide resolved
<Card>
<div className="Account__card">
<div className="CardText">
<Text size="lg" as="h1" weight="medium">
Friendbot: fund a {network.id} network account
</Text>

<Text size="sm" as="p">
The friendbot is a horizon API endpoint that will fund an account
with 10,000 lumens on the {network.id} network.
</Text>
</div>

<Input
id="generate-keypair-publickey"
fieldSize="md"
label="Public Key"
value={generatedPublicKey}
onChange={(e) => {
setGeneratedPublicKey(e.target.value);
jeesunikim marked this conversation as resolved.
Show resolved Hide resolved
}}
placeholder="Ex: GCEXAMPLE5HWNK4AYSTEQ4UWDKHTCKADVS2AHF3UI2ZMO3DPUSM6Q4UG"
rightElement={<Icon.Wallet03 />}
error={errorMessage}
/>

<div className="Account__CTA">
<Button
disabled={!generatedPublicKey}
size="md"
variant={error ? "error" : "secondary"}
isLoading={isLoading}
onClick={() => {
const error = validatePublicKey(generatedPublicKey);
setErrorMessage(error);

if (!error) {
fundAccount();
}
}}
>
Get lumens
</Button>

<Button
disabled={!account.publicKey}
jeesunikim marked this conversation as resolved.
Show resolved Hide resolved
size="md"
variant="tertiary"
onClick={() => setGeneratedPublicKey(account.publicKey)}
>
Fill in with generated key
</Button>
</div>
</div>
</Card>

{isSuccess && (
<AlertBox>
<div className="Account__alertbox">
<div className="Account__alertbox__icon">
<IconButton
altText="Info"
icon={<Icon.InfoCircle />}
variant="success"
/>
</div>
<div className="Account__alertbox__content">
<Text
size="md"
as="span"
weight="medium"
addlClassName="Text--dark"
>
Successfully funded {shortenStellarAddress(account.publicKey)}{" "}
on {network.id}
</Text>
<div className="Account__alertbox__CTA">
<Text size="md" as="span" weight="semi-bold">
Dismiss
</Text>
<Text
size="md"
as="span"
weight="semi-bold"
addlClassName="Text--purple"
>
<Link
href={`https://stellar.expert/explorer/${network.id}/account/${account.publicKey}`}
passHref
legacyBehavior
>
<a target="_blank" rel="noopener noreferrer">
View on stellar.expert
</a>
</Link>
</Text>
</div>
</div>
</div>
</AlertBox>
)}
</div>
);
}
37 changes: 37 additions & 0 deletions src/app/(sidebar)/account/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
align-self: flex-start;
}

.Text--dark {
jeesunikim marked this conversation as resolved.
Show resolved Hide resolved
color: var(--sds-clr-gray-12);
}

.Text--purple {
color: var(--sds-clr-lilac-11);
}

jeesunikim marked this conversation as resolved.
Show resolved Hide resolved
&__CTA {
display: flex;
gap: pxToRem(18px) pxToRem(8px);
Expand All @@ -26,4 +34,33 @@
cursor: pointer;
}
}

&__alertbox {
display: flex;
padding: pxToRem(12px);
gap: pxToRem(12px);

.Text {
margin-bottom: 0;
}

&__content {
display: flex;
flex-direction: column;
gap: pxToRem(12px);
}

&__icon {
padding: pxToRem(9.5px);
background-color: var(--sds-clr-gray-02);
border: 1px solid var(--sds-clr-gray-06);
border-radius: 6px;
align-self: flex-start;
}

&__CTA {
display: flex;
gap: pxToRem(12px);
}
}
jeesunikim marked this conversation as resolved.
Show resolved Hide resolved
}
24 changes: 24 additions & 0 deletions src/components/AlertBox/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { useState } from "react";
jeesunikim marked this conversation as resolved.
Show resolved Hide resolved

import { Card, Icon, IconButton } from "@stellar/design-system";

import "./styles.scss";

export const AlertBox = ({ children }: { children: React.ReactNode }) => {
const [isVisible, setIsVisible] = useState(true);

return (
<div className="AlertBox" data-is-visible={isVisible}>
<Card noPadding>
<div className="AlertBox__close-btn">
<IconButton
altText="Default"
icon={<Icon.X />}
onClick={() => setIsVisible(false)}
/>
</div>
<div className="AlertBox__inset">{children}</div>
</Card>
</div>
);
};
18 changes: 18 additions & 0 deletions src/components/AlertBox/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.AlertBox {
position: relative;
margin: 12px 0;

&[data-is-visible="false"] {
visibility: hidden;
}

&__close-btn {
position: absolute;
right: 12px;
top: 12px;

.IconButton svg {
stroke: var(--sds-clr-gray-09);
}
}
}
60 changes: 21 additions & 39 deletions src/components/FormElements/PubKeyPicker.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
jeesunikim marked this conversation as resolved.
Show resolved Hide resolved
import { StrKey } from "stellar-sdk";
import { Input, InputProps } from "@stellar/design-system";
import { validatePublicKey } from "@/helpers/validatePublicKey";

interface PubKeyPickerProps extends Omit<InputProps, "fieldSize"> {
id: string;
Expand All @@ -27,41 +27,23 @@ export const PubKeyPicker = ({
onChange,
onBlur,
...props
}: PubKeyPickerProps) => {
const validatePublicKey = (issuer: string) => {
if (!issuer) {
return "Asset issuer is required.";
}

if (issuer.startsWith("M")) {
if (!StrKey.isValidMed25519PublicKey(issuer)) {
return "Muxed account address is invalid.";
}
} else if (!StrKey.isValidEd25519PublicKey(issuer)) {
return "Public key is invalid.";
}

return "";
};

return (
<Input
id={id}
fieldSize={fieldSize}
label={label}
labelSuffix={labelSuffix}
placeholder={placeholder}
value={value}
onChange={(e) => {
const error = validatePublicKey(e.target.value);
onChange(e.target.value, error);
}}
onBlur={(e) => {
const error = validatePublicKey(e.target.value);
onBlur(e.target.value, error);
}}
error={error}
{...props}
/>
);
};
}: PubKeyPickerProps) => (
<Input
id={id}
fieldSize={fieldSize}
label={label}
labelSuffix={labelSuffix}
placeholder={placeholder}
value={value}
onChange={(e) => {
const error = validatePublicKey(e.target.value);
onChange(e.target.value, error);
}}
onBlur={(e) => {
const error = validatePublicKey(e.target.value);
onBlur(e.target.value, error);
}}
error={error}
{...props}
/>
);
7 changes: 4 additions & 3 deletions src/components/NetworkSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import React, {
useState,
} from "react";
import { Button, Icon, Input, Notification } from "@stellar/design-system";
import { Networks } from "stellar-sdk";

import { NetworkIndicator } from "@/components/NetworkIndicator";
import { localStorageSavedNetwork } from "@/helpers/localStorageSavedNetwork";
Expand All @@ -20,21 +21,21 @@ const NetworkOptions: Network[] = [
label: "Futurenet",
horizonUrl: "https://horizon-futurenet.stellar.org",
rpcUrl: "https://rpc-futurenet.stellar.org",
passphrase: "Test SDF Future Network ; October 2022",
passphrase: Networks.FUTURENET,
jeesunikim marked this conversation as resolved.
Show resolved Hide resolved
},
{
id: "testnet",
label: "Testnet",
horizonUrl: "https://horizon-testnet.stellar.org",
rpcUrl: "https://soroban-testnet.stellar.org",
passphrase: "Test SDF Network ; September 2015",
passphrase: Networks.TESTNET,
},
{
id: "mainnet",
label: "Mainnet",
horizonUrl: "https://horizon.stellar.org",
rpcUrl: "",
passphrase: "Public Global Stellar Network ; September 2015",
passphrase: Networks.PUBLIC,
},
{
id: "custom",
Expand Down
Loading
Loading