Skip to content

Commit

Permalink
stylizing
Browse files Browse the repository at this point in the history
  • Loading branch information
diyahir committed Feb 17, 2024
1 parent a542946 commit 9c502ff
Show file tree
Hide file tree
Showing 15 changed files with 232 additions and 94 deletions.
7 changes: 7 additions & 0 deletions packages/nextjs/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ export const metadata: Metadata = {
const ScaffoldEthApp = ({ children }: { children: React.ReactNode }) => {
return (
<html>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Inter:wght@100&display=swap"
rel="stylesheet"
/>

<body>
<ScaffoldEthAppWithProviders>{children}</ScaffoldEthAppWithProviders>
</body>
Expand Down
16 changes: 7 additions & 9 deletions packages/nextjs/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,17 @@ const Home: NextPage = () => {
const { isOpen, onClose, onOpen } = useDisclosure();

return (
<Container>
<Container alignContent={"center"} h="95%">
<Card>
<CardHeader>
<Heading mt="10%" textAlign={"center"} fontSize={"x-large"}>
<CardHeader bg="brand.bg">
<Heading fontFamily={"IBM Plex Mono"} mt="10%" textAlign={"center"} fontSize={"x-large"}>
{" "}
<span className="font-bold">
{balance ? `${(balance * 100_000_000).toLocaleString()}` : "Loading Balance..."}
</span>{" "}
sats
<span>{balance ? `${(balance * 100_000_000).toLocaleString()}` : "Loading Balance..."}</span> sats
</Heading>
</CardHeader>

<CardBody>
<Heading fontWeight={"md"} textAlign={"center"} size={"md"}>
<CardBody bg="brand.bg">
<Heading fontFamily={"IBM Plex Mono"} textAlign={"center"} size={"md"}>
History
</Heading>
<Table size={"sm"}>
Expand Down Expand Up @@ -71,6 +68,7 @@ const Home: NextPage = () => {
</CardBody>

<CardFooter
bg="brand.bg"
justify="space-between"
flexWrap="wrap"
sx={{
Expand Down
5 changes: 4 additions & 1 deletion packages/nextjs/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { usePathname } from "next/navigation";
import { Bars3Icon } from "@heroicons/react/24/outline";
import { FaucetButton, RainbowKitCustomConnectButton } from "~~/components/scaffold-eth";
import { useOutsideClick } from "~~/hooks/scaffold-eth";
import { useWebSocket } from "~~/hooks/useWebSocket";

type HeaderMenuLink = {
label: string;
Expand Down Expand Up @@ -62,7 +63,7 @@ export const Header = () => {
burgerMenuRef,
useCallback(() => setIsDrawerOpen(false), []),
);

const { isOpen: isSocketOpen } = useWebSocket("ws://localhost:3003");
return (
<div className="sticky lg:static top-0 navbar bg-base-100 min-h-0 flex-shrink-0 justify-between z-20 px-0 sm:px-2">
<div className="navbar-start w-auto lg:w-1/2">
Expand Down Expand Up @@ -102,6 +103,8 @@ export const Header = () => {
</ul>
</div>
<div className="navbar-end flex-grow mr-4">
{/* a div that shows if the client is connected to the server */}
<div className={`${isSocketOpen ? "bg-success" : "bg-error"} rounded-full w-2 h-2 self-center`}></div>
<RainbowKitCustomConnectButton />
<FaucetButton />
</div>
Expand Down
85 changes: 70 additions & 15 deletions packages/nextjs/components/PaymentInvoice.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
import React from "react";
import { CopyIcon } from "@chakra-ui/icons";
import { Button, ButtonGroup, Flex, Icon, Table, Tbody, Td, Tr } from "@chakra-ui/react";
import {
Box,
Button,
ButtonGroup,
Flex,
Icon,
Step,
StepDescription,
StepIcon,
StepIndicator,
StepNumber,
StepSeparator,
StepStatus,
StepTitle,
Stepper,
Table,
Tbody,
Td,
Tr,
} from "@chakra-ui/react";
import { LnPaymentInvoice } from "~~/types/utils";

/**
Expand All @@ -11,29 +30,47 @@ type PaymentInvoiceProps = {
contractId: string | null;
submitPayment: () => void;
cancelPayment: () => void;
step: number;
};

export const PaymentInvoice = ({ invoice, contractId, submitPayment, cancelPayment }: PaymentInvoiceProps) => {
export const steps = [
{ title: "Verify Invoice", description: "Verify the invoice is correct" },
{ title: "Pay deposit", description: "On-chain invoice locked in smart contract" },
{
title: "On-chain invoice sent to provider",
description: "The invoice id is sent and verified by the lightning provider",
},
{ title: "Paid", description: "The lightning provider pays lightning invoice. The reciever must be online." },
];

export const PaymentInvoice = ({ invoice, contractId, submitPayment, cancelPayment, step }: PaymentInvoiceProps) => {
const expiryDate = new Date(invoice.timeExpireDate * 1000);

return (
<Flex h="100%" flexDir={"column"} justifyContent={"center"} alignContent={"center"}>
<Table>
<Flex h="100%" flexDir={"column"} justifyContent={"space-evenly"} alignContent={"space-evenly"}>
<Table size={"xs"}>
<Tbody>
<Tr>
<Td>Expiry Time</Td>
<Td textAlign={"end"}>{expiryDate.toLocaleTimeString()}</Td>
<Td border="transparent">Expiry Time</Td>
<Td border="transparent" textAlign={"end"}>
{expiryDate.toLocaleTimeString()}
</Td>
</Tr>
<Tr>
<Td>Amount</Td>
<Td textAlign={"end"}>{invoice.satoshis} sats</Td>
<Td border="transparent">Amount</Td>
<Td border="transparent" textAlign={"end"}>
{invoice.satoshis} sats
</Td>
</Tr>
<Tr>
<Td>USD</Td>
<Td textAlign={"end"}>${invoice.satoshis}</Td>
<Td border="transparent">USD</Td>
<Td border="transparent" textAlign={"end"}>
${invoice.satoshis}
</Td>
</Tr>
<Tr>
<Td>Contract Id</Td>
<Td textAlign={"end"}>
<Td border="transparent">Contract Id</Td>
<Td border="transparent" textAlign={"end"}>
{contractId ? contractId.substring(0, 10) + "... " : "Pending"}
{contractId && (
<Button
Expand All @@ -50,11 +87,29 @@ export const PaymentInvoice = ({ invoice, contractId, submitPayment, cancelPayme
</Tr>
</Tbody>
</Table>
<ButtonGroup my="20%" colorScheme="red" width={"100%"}>
<Button width={"100%"} onClick={() => cancelPayment()}>

<Stepper index={step} orientation="vertical" height="" gap="0">
{steps.map((step, index) => (
<Step key={index}>
<StepIndicator>
<StepStatus complete={<StepIcon />} incomplete={<StepNumber />} active={<StepNumber />} />
</StepIndicator>

<Box>
<StepTitle>{step.title}</StepTitle>
<StepDescription>{step.description}</StepDescription>
</Box>

<StepSeparator />
</Step>
))}
</Stepper>

<ButtonGroup colorScheme="red" width={"100%"} isDisabled={step !== 1}>
<Button width={"100%"} onClick={() => cancelPayment()} isLoading={step == 2 || step == 3}>
Cancel
</Button>
<Button colorScheme="green" width={"100%"} onClick={() => submitPayment()}>
<Button colorScheme="green" width={"100%"} onClick={() => submitPayment()} isLoading={step == 2 || step == 3}>
Pay
</Button>
</ButtonGroup>
Expand Down
3 changes: 2 additions & 1 deletion packages/nextjs/components/ScaffoldEthAppWithProviders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Footer } from "~~/components/Footer";
import { Header } from "~~/components/Header";
import { BlockieAvatar } from "~~/components/scaffold-eth";
import { ProgressBar } from "~~/components/scaffold-eth/ProgressBar";
import theme from "~~/components/theme";
import { useNativeCurrencyPrice } from "~~/hooks/scaffold-eth";
import { useDarkMode } from "~~/hooks/scaffold-eth/useDarkMode";
import { useGlobalState } from "~~/services/store/store";
Expand Down Expand Up @@ -41,7 +42,7 @@ export const ScaffoldEthAppWithProviders = ({ children }: { children: React.Reac
const { isDarkMode } = useDarkMode();

return (
<ChakraProvider>
<ChakraProvider theme={theme} cssVarsRoot="body">
<WagmiConfig config={wagmiConfig}>
<ProgressBar />
<RainbowKitProvider
Expand Down
72 changes: 53 additions & 19 deletions packages/nextjs/components/SendModalPopup.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useState } from "react";
import { useEffect, useRef, useState } from "react";
import {
Input,
InputGroup,
Expand All @@ -12,12 +12,14 @@ import {
ModalHeader,
ModalOverlay,
VStack,
useSteps,
} from "@chakra-ui/react";
import { QrScanner } from "@yudiel/react-qr-scanner";
import { PaymentRequestObject, decode } from "bolt11";
import { useWalletClient } from "wagmi";
import { PaymentInvoice } from "~~/components/PaymentInvoice";
import { PaymentInvoice, steps } from "~~/components/PaymentInvoice";
import { useScaffoldContract, useScaffoldEventSubscriber } from "~~/hooks/scaffold-eth";
import { useWebSocket } from "~~/hooks/useWebSocket";
import { LnPaymentInvoice } from "~~/types/utils";

type SendModalProps = {
Expand All @@ -26,24 +28,49 @@ type SendModalProps = {
};
function SendModal({ isOpen, onClose }: SendModalProps) {
const [invoice, setInvoice] = useState<string>("");
const [lnInvoice, setLnInvoice] = useState<LnPaymentInvoice | null>(null);
const lnInvoiceRef = useRef<LnPaymentInvoice | null>(null);
const [contractId, setContractId] = useState<string | null>(null);
const [txHash, setTxHash] = useState<string | null>(null);
const { sendMessage, data } = useWebSocket("ws://localhost:3003");

const { data: walletClient } = useWalletClient();
function cleanAndClose() {
lnInvoiceRef.current = null;
setInvoice("");
setContractId(null);
setTxHash(null);
setActiveStep(1);
onClose();
}

useEffect(() => {
if (data?.status === "success") {
setActiveStep(4);
console.log("data", data);
}
}, [data]);

const { data: walletClient } = useWalletClient();
const { data: yourContract } = useScaffoldContract({
contractName: "HashedTimelock",
walletClient,
});

const { activeStep, setActiveStep } = useSteps({
index: 1,
count: steps.length,
});

useScaffoldEventSubscriber({
contractName: "HashedTimelock",
eventName: "LogHTLCNew",
listener: event => {
const tmpContractId = event[0].args.contractId;
if (event[0].transactionHash === txHash) return;
if (!tmpContractId) return;
if (lnInvoiceRef.current?.lnInvoice === undefined) return;
setContractId(tmpContractId ? tmpContractId.toString() : null);
sendMessage({ contractId: tmpContractId, lnInvoice: lnInvoiceRef.current?.lnInvoice });
setActiveStep(3);
},
});

Expand All @@ -66,16 +93,21 @@ function SendModal({ isOpen, onClose }: SendModalProps) {

function submitPayment() {
if (!yourContract) return;
if (!lnInvoice) return;
if (!lnInvoiceRef.current) return;
yourContract.write
.newContract(
["0xf89335a26933d8Dd6193fD91cAB4e1466e5198Bf", lnInvoice.paymentHash, BigInt(lnInvoice.timeExpireDate)],
[
"0xf89335a26933d8Dd6193fD91cAB4e1466e5198Bf",
lnInvoiceRef.current.paymentHash,
BigInt(lnInvoiceRef.current.timeExpireDate),
],
{
value: BigInt(lnInvoice.satoshis),
value: BigInt(lnInvoiceRef.current.satoshis),
},
)
.then(tx => {
console.log("txHash", tx);
setActiveStep(2);
setTxHash(tx);
})
.catch(e => {
Expand All @@ -87,35 +119,36 @@ function SendModal({ isOpen, onClose }: SendModalProps) {
try {
setInvoice(invoice);
const tempdecoded = decode(invoice);
console.log(tempdecoded);
const paymentHash = getPaymentHash(tempdecoded);

if (!tempdecoded.satoshis) return;
if (!paymentHash) return;
if (!tempdecoded.timeExpireDate) return;

setLnInvoice({
console;

lnInvoiceRef.current = {
satoshis: tempdecoded.satoshis,
timeExpireDate: tempdecoded.timeExpireDate,
paymentHash,
lnInvoice: invoice,
});
};
} catch (e) {
console.error(e);
}
}

return (
<>
<Modal isOpen={isOpen} onClose={onClose}>
<Modal isOpen={isOpen} onClose={cleanAndClose}>
<ModalOverlay />
<ModalContent h={"100%"} m="0">
<ModalHeader textAlign={"center"}>{lnInvoice == null ? "Scan QR Code" : "Review"}</ModalHeader>
<ModalContent bg="brand.bg" h={"100%"} m="0">
<ModalHeader textAlign={"center"}>{lnInvoiceRef.current == null ? "Scan QR Code" : "Review"}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<ModalBody h="100%" alignContent={"space-between"}>
{/* Wallet Section */}
{!lnInvoice && (
<VStack>
{!lnInvoiceRef.current && (
<VStack h="100%" alignContent={"space-between"} gap="20">
<QrScanner
scanDelay={1}
onError={handleError}
Expand Down Expand Up @@ -143,13 +176,14 @@ function SendModal({ isOpen, onClose }: SendModalProps) {
</VStack>
)}

{lnInvoice && (
{lnInvoiceRef.current && (
<PaymentInvoice
invoice={lnInvoice}
invoice={lnInvoiceRef.current}
submitPayment={submitPayment}
contractId={contractId}
step={activeStep}
cancelPayment={() => {
setLnInvoice(null);
lnInvoiceRef.current = null;
setInvoice("");
setContractId(null);
}}
Expand Down
16 changes: 16 additions & 0 deletions packages/nextjs/components/theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { extendTheme } from "@chakra-ui/react";

// 2. Add your color mode config
const theme = extendTheme({
colors: {
brand: {
bg: "#1f1f1f",
// ...
900: "#1a202c",
},
},
});

// 3. extend the theme

export default theme;
Loading

0 comments on commit 9c502ff

Please sign in to comment.