From bf7984c42e4edd263452c54fa104bb1c7b34d007 Mon Sep 17 00:00:00 2001 From: Eli Date: Mon, 9 Sep 2024 11:17:10 -0700 Subject: [PATCH] refresh, wallet, mobile --- .gitignore | 2 + .prettierrc | 4 + components/Header.tsx | 33 ++++ components/home/DesktopIconsBasic.tsx | 27 ++- components/home/Layout.tsx | 221 +++++++++++++++------- components/icons/PowerIcon.tsx | 18 ++ components/wallet/WalletSidebar.tsx | 260 ++++++++++++++++++++++++++ pages/api/governance/[...slug].ts | 1 - pages/api/stream/[...slug].ts | 54 ++++++ pages/index.tsx | 2 +- pages/txn.tsx | 60 ++++++ 11 files changed, 603 insertions(+), 79 deletions(-) create mode 100644 .prettierrc create mode 100644 components/Header.tsx create mode 100644 components/icons/PowerIcon.tsx create mode 100644 components/wallet/WalletSidebar.tsx create mode 100644 pages/api/stream/[...slug].ts create mode 100644 pages/txn.tsx diff --git a/.gitignore b/.gitignore index f2ad8de..87b2612 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +.env + # dependencies /node_modules /.pnp diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..222861c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "tabWidth": 2, + "useTabs": false +} diff --git a/components/Header.tsx b/components/Header.tsx new file mode 100644 index 0000000..eefac72 --- /dev/null +++ b/components/Header.tsx @@ -0,0 +1,33 @@ +import Image from "next/image"; +import { useState } from "react"; + +const Header = () => { + const [isConnected, setIsConnected] = useState(false); + + const handleConnect = () => { + // Implement your connection logic here + setIsConnected(!isConnected); + }; + + return ( +
+
+ Logo +
+ +
+ ); +}; + +export default Header; diff --git a/components/home/DesktopIconsBasic.tsx b/components/home/DesktopIconsBasic.tsx index ff6ea5d..886cc85 100644 --- a/components/home/DesktopIconsBasic.tsx +++ b/components/home/DesktopIconsBasic.tsx @@ -5,28 +5,24 @@ import Help from "../apps/help"; import { useAppLauncher } from "../../hooks/useAppLauncher"; export function DesktopIconsBasic() { - return (
- +

Discord

-
-

Files

+

FAQ

-
- */} + {/* @@ -34,14 +30,17 @@ export function DesktopIconsBasic() {

Notion

+ */} + +
+ +

Voting

+
- +
-

Calendar

+

FAQ

diff --git a/components/home/Layout.tsx b/components/home/Layout.tsx index 4878744..ffe9760 100644 --- a/components/home/Layout.tsx +++ b/components/home/Layout.tsx @@ -1,10 +1,10 @@ import Head from "next/head"; -import { ReactNode, useEffect, useRef } from "react"; +import { ReactNode, useEffect, useRef, useState } from "react"; import { dao, snapshotSpace, snapshotUrl, themes } from "../../config"; import { useGetCommands } from "../../hooks/useGetCommands"; import { Command } from "../../types/Command"; import { useConnect, useDisconnect, useEnsName } from "wagmi"; -import { useGetUserProfile } from "../../hooks/users/useGetUserProfile"; +// import { useGetUserProfile } from "../../hooks/users/useGetUserProfile"; import { useUserAddress } from "../../hooks/ethereum/useUserAddress"; import { useSignIn } from "../../hooks/sign-in/useSignIn"; import { useIsNewUser } from "../../hooks/useIsNewUser"; @@ -23,6 +23,16 @@ import { filterActive } from "@/types/Proposal"; import { useAppLauncher } from "@/hooks/useAppLauncher"; import { RootState } from "@/redux/app/store"; import { useSIWE } from "@/hooks/sign-in/useSIWE"; +const WalletSidebar = dynamic(() => import("../wallet/WalletSidebar")); + +import { ethers } from "ethers"; + +const KRAUSE_TOKEN_ADDRESS = "0x9f6f91078a5072a8b54695dafa2374ab3ccd603b"; +const NFT_TICKET_ADDRESS = "0xc4e0f3ec24972c75df7c716922096f4270b7bb4e"; +const KRAUSE_COURT_PIECES_ADDRESS = + "0x591E13ed6C78c0dc715336947db818ddB85a6ffE"; +const SEED_TOKEN_ADDRESS = "0xf76d80200226ac250665139b9e435617e4ba55f9"; +const KRAUSEHOUSE_ETH_ADDRESS = "0xE4762eAcEbDb7585D32079fdcbA5Bb94eb5d76F2"; interface Props { children?: ReactNode; @@ -51,29 +61,130 @@ export default function Layout({ isReconnecting, } = useConnect(); const address = useUserAddress(); - const { signOut, signIn, signedIn } = useSignIn(); - const { signedIn: signedInSIWE } = useSIWE(); + // const { signOut, signIn, signedIn } = useSignIn(); + const { signedIn, signIn, signOut } = useSIWE(); const { disconnect } = useDisconnect(); const { data: ensName } = useEnsName({ address }); const connector = connectors[1]; + const displayName = ensName || address; + // Redux hooks, window management - const dispatch = useAppDispatch(); - const toggleSearch = () => dispatch(toggle({ windowName: "search" })); - const { launchCreateProfile } = useAppLauncher(); - const searchOpen = useAppSelector( - (state: RootState) => state.windows.open.search - ); + // const dispatch = useAppDispatch(); + // const toggleSearch = () => dispatch(toggle({ windowName: "search" })); + // const { launchCreateProfile } = useAppLauncher(); + // const searchOpen = useAppSelector( + // (state: RootState) => state.windows.open.search + // ); // TODO: should load a lot of this in via redux, and then get from app state const commands: Array = useGetCommands(); - const user = useGetUserProfile(); - const newUserFlow = useIsNewUser(); + // const user = useGetUserProfile(); + // const newUserFlow = useIsNewUser(); const path = usePath(); const proposals = useGetProposals(snapshotSpace); const countActive = length(filterActive(proposals)); const date = new Date(); + const [isSidebarOpen, setIsSidebarOpen] = useState(isConnected); + const [krauseBalance, setKrauseBalance] = useState("0"); + const [nftBalance, setNftBalance] = useState("0"); + const [krauseCourtPiecesBalance, setKrauseCourtPiecesBalance] = + useState("0"); + const [treasuryEthBalance, setTreasuryEthBalance] = useState("0"); + const [treasuryKrauseBalance, setTreasuryKrauseBalance] = + useState("0"); + const [treasurySeedBalance, setTreasurySeedBalance] = useState("0"); + const [treasuryNftBalance, setTreasuryNftBalance] = useState("0"); + + useEffect(() => { + if (isConnected) { + setIsSidebarOpen(true); + } + }, [isConnected]); + + useEffect(() => { + const fetchBalances = async () => { + if (address) { + const provider = new ethers.providers.InfuraProvider( + "mainnet", + process.env.NEXT_PUBLIC_INFURA_KEY + ); + + // Fetch ERC20 balance + const erc20Abi = ["function balanceOf(address) view returns (uint256)"]; + const krauseContract = new ethers.Contract( + KRAUSE_TOKEN_ADDRESS, + erc20Abi, + provider + ); + const krauseBalanceWei = await krauseContract.balanceOf(address); + setKrauseBalance( + parseFloat(ethers.utils.formatEther(krauseBalanceWei)).toFixed(2) + ); + + // Fetch NFT balance + const erc721Abi = [ + "function balanceOf(address) view returns (uint256)", + ]; + const nftContract = new ethers.Contract( + NFT_TICKET_ADDRESS, + erc721Abi, + provider + ); + const nftBalanceWei = await nftContract.balanceOf(address); + setNftBalance(nftBalanceWei.toString()); + + const krauseCourtPiecesContract = new ethers.Contract( + KRAUSE_COURT_PIECES_ADDRESS, + erc721Abi, + provider + ); + const krauseCourtPiecesBalanceWei = + await krauseCourtPiecesContract.balanceOf(address); + setKrauseCourtPiecesBalance(krauseCourtPiecesBalanceWei.toString()); + + // Fetch treasury balances + const treasuryEthBalanceWei = await provider.getBalance( + KRAUSEHOUSE_ETH_ADDRESS + ); + setTreasuryEthBalance( + parseFloat(ethers.utils.formatEther(treasuryEthBalanceWei)).toFixed(2) + ); + + const treasuryKrauseBalanceWei = await krauseContract.balanceOf( + KRAUSEHOUSE_ETH_ADDRESS + ); + setTreasuryKrauseBalance( + parseFloat( + ethers.utils.formatEther(treasuryKrauseBalanceWei) + ).toFixed(2) + ); + + const seedContract = new ethers.Contract( + SEED_TOKEN_ADDRESS, + erc20Abi, + provider + ); + const treasurySeedBalanceWei = await seedContract.balanceOf( + KRAUSEHOUSE_ETH_ADDRESS + ); + setTreasurySeedBalance( + parseFloat(ethers.utils.formatEther(treasurySeedBalanceWei)).toFixed( + 2 + ) + ); + + const treasuryNftBalanceWei = await nftContract.balanceOf( + KRAUSEHOUSE_ETH_ADDRESS + ); + setTreasuryNftBalance(treasuryNftBalanceWei.toString()); + } + }; + + fetchBalances(); + }, [address]); + return (
@@ -88,19 +199,19 @@ export default function Layout({ /> -
+
-
+
-
+
  • @@ -110,7 +221,7 @@ export default function Layout({ {path.map(({ pathSlice, route }, i) => (
  • - {pathSlice == "" ? "Desktop" : pathSlice.slice(0, 10)} + {pathSlice == "" ? "Owners' Box" : pathSlice.slice(0, 10)}
  • ))} @@ -122,61 +233,29 @@ export default function Layout({ )}
    - {/* <> - {!signedIn ? ( - !isConnected ? ( - isReconnecting ? ( - - ) : ( - - ) - ) : !signedInSIWE ? ( - ) : ( - ) - ) : !newUserFlow ? ( - ) : ( - )} - */} - + */} {/*
+ {isSidebarOpen && ( + { + setIsSidebarOpen(false); + disconnect(); + }} + address={address} + krauseBalance={krauseBalance} + nftBalance={nftBalance} + krauseCourtPiecesBalance={krauseCourtPiecesBalance} + treasuryEthBalance={treasuryEthBalance} + treasuryKrauseBalance={treasuryKrauseBalance} + treasurySeedBalance={treasurySeedBalance} + treasuryNftBalance={treasuryNftBalance} + /> + )}
); } diff --git a/components/icons/PowerIcon.tsx b/components/icons/PowerIcon.tsx new file mode 100644 index 0000000..0aa469d --- /dev/null +++ b/components/icons/PowerIcon.tsx @@ -0,0 +1,18 @@ +export function PowerIcon({ strokeWidth = 1.5 }: { strokeWidth?: number }) { + return ( + + + + ); +} diff --git a/components/wallet/WalletSidebar.tsx b/components/wallet/WalletSidebar.tsx new file mode 100644 index 0000000..4115dc7 --- /dev/null +++ b/components/wallet/WalletSidebar.tsx @@ -0,0 +1,260 @@ +import { useState } from "react"; +import Image from "next/image"; +import { PowerIcon } from "../icons/PowerIcon"; +import { useSIWE } from "@/hooks/sign-in/useSIWE"; + +interface WalletSidebarProps { + onClose: () => void; + address: string; + krauseBalance: string; + nftBalance: string; + krauseCourtPiecesBalance: string; + treasuryEthBalance: string; + treasuryKrauseBalance: string; + treasurySeedBalance: string; + treasuryNftBalance: string; +} + +type Section = "wallet" | "treasury" | "portfolio" | "links"; + +const WalletSidebar: React.FC = ({ + onClose, + address, + krauseBalance, + nftBalance, + krauseCourtPiecesBalance, + treasuryEthBalance, + treasuryKrauseBalance, + treasurySeedBalance, + treasuryNftBalance, +}) => { + const [activeSection, setActiveSection] = useState
("wallet"); + const [expandedAsset, setExpandedAsset] = useState(null); + + const formatNumber = (num: string) => { + const parts = num.split("."); + parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); + return parts.join("."); + }; + + const assetDescriptions = { + $KRAUSE: + "Krause House's governance token used to vote on proposals. 5 million supply total.", + "Genesis Tickets": "Original NFTs representing membership in Krause House.", + "Krause Court Pieces": + "NFTs representing ownership of virtual court pieces.", + ETH: "Ethereum, the native cryptocurrency of the Ethereum blockchain.", + $SEED: + "Seed Club is a DAO accelerator that Krause House participated in. $SEED is their governance token we received as part of this.", + NFTs: "Non-fungible tokens owned by the Krause House treasury.", + }; + + const renderAssetItem = ( + item: { balance: string; name: string }, + isWallet: boolean = true + ) => ( +
+

{formatNumber(item.balance)}

+

{item.name}

+

{assetDescriptions[item.name]}

+ {isWallet && item.name !== "$KRAUSE" && ( + + )} +
+ ); + + const renderContent = () => { + switch (activeSection) { + case "wallet": + return ( +
+ {[ + { balance: krauseBalance, name: "$KRAUSE" }, + { balance: nftBalance, name: "Genesis Tickets" }, + { + balance: krauseCourtPiecesBalance, + name: "Krause Court Pieces", + }, + ].map((item, index) => renderAssetItem(item))} +
+ ); + case "treasury": + return ( +
+ {[ + { balance: treasuryEthBalance, name: "ETH" }, + { balance: treasuryKrauseBalance, name: "$KRAUSE" }, + { balance: treasurySeedBalance, name: "$SEED" }, + { balance: treasuryNftBalance, name: "NFTs" }, + ].map((item, index) => renderAssetItem(item, false))} +
+ ); + case "portfolio": + return ( +
+
+

Ball Hogs

+

+ Krause House's first portfolio investment. The Ball Hogs are a + part of the Big3, a 3v3 league started by Ice Cube, and coached + by the legend Rick Barry. +

+ +
+ {/* You can add more teams here in the future */} +
+ ); + case "links": + return ( +
+ {[ + { + title: "Governance", + description: "Participate in Krause House decision-making", + url: "https://snapshot.org/#/krausehouse.eth", + }, + { + title: "Discord", + description: "Join our community chat", + url: "https://discord.gg/wAjEq3CM", + }, + { + title: "Twitter", + description: "Follow us on Twitter", + url: "https://twitter.com/KrauseHouseDAO", + }, + { + title: "FAQ", + description: "Learn more about Krause House", + url: "https://docs.krausehouse.club", + }, + { + title: "Krause House Website", + description: "Learn more about Krause House", + url: "https://krausehouse.club", + }, + { + title: "Ball Hogs Website", + description: "Learn more about the Ball Hogs", + url: "https://www.ballhogs.club/", + }, + { + title: "Blog", + description: "Read our latest updates and announcements", + url: "https://krausehouse.mirror.xyz/", + }, + ].map((link, index) => ( + + ))} +
+ ); + } + }; + + return ( +
+
+
+ + Logo + +

{`${address.slice(0, 6)}...${address.slice(-4)}`}

+
+ +
+ +
+ {(["wallet", "treasury", "portfolio", "links"] as Section[]).map( + (section) => ( + + ) + )} +
+ + {renderContent()} +
+ ); +}; + +export default WalletSidebar; diff --git a/pages/api/governance/[...slug].ts b/pages/api/governance/[...slug].ts index ddde9a7..827c920 100644 --- a/pages/api/governance/[...slug].ts +++ b/pages/api/governance/[...slug].ts @@ -3,7 +3,6 @@ import type { NextApiRequest, NextApiResponse } from 'next' type Data = Array -const curr = "https://github.com/Krause-House/org/tree/fce91f81f34f16b8f462f91dad56064e60a56ee5" export default function handler( req: NextApiRequest, res: NextApiResponse diff --git a/pages/api/stream/[...slug].ts b/pages/api/stream/[...slug].ts new file mode 100644 index 0000000..59a3917 --- /dev/null +++ b/pages/api/stream/[...slug].ts @@ -0,0 +1,54 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction +import type { NextApiRequest, NextApiResponse } from 'next' + +type Data = Array + +export default function handler( + req: NextApiRequest, + res: NextApiResponse +) { + res.status(200).json([ + { + "to":"0xC1f3af5DC05b0C51955804b2afc80eF8FeED67b9", + "value":"0", + "data":null, + "contractMethod": { + "inputs": [ + { + "internalType":"address", + "name":"recipient", + "type":"address" + }, + { + "internalType":"uint256", + "name":"deposit","type":"uint256" + }, + { + "internalType":"address", + "name":"tokenAddress","type":"address" + }, + { + "internalType":"uint256", + "name":"startTime", + "type":"uint256" + }, + { + "internalType":"uint256", + "name":"stopTime", + "type":"uint256" + } + ], + "name":"createStream", + "payable":false + }, + "contractInputsValues": { + "recipient":"0x586D2Dc020bb532cE27f896231d8964CeC7A85fB", + "deposit":"100", + "tokenAddress":"0xC1f3af5DC05b0C51955804b2afc80eF8FeED67b9", + "startTime":"1663979627", + "stopTime":"1664066027" + } + } + ]) +} + diff --git a/pages/index.tsx b/pages/index.tsx index cf433dd..eec6f3b 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -24,7 +24,7 @@ export default function Home() { const { quit } = useAppLauncher(); return ( - +
diff --git a/pages/txn.tsx b/pages/txn.tsx new file mode 100644 index 0000000..8a3644c --- /dev/null +++ b/pages/txn.tsx @@ -0,0 +1,60 @@ +import { useAppDispatch, useAppSelector } from "@/redux/app/hooks"; +import AppFrame from "@/components/layout/AppFrame"; +import dynamic from "next/dynamic"; +import Image from "next/image"; +import { Footer } from "../components/home/Footer"; +import { DesktopIcons } from "../components/home/DesktopIcons"; +const Layout = dynamic(() => import("../components/home/Layout")); +import { RootState } from "@/redux/app/store"; +import { useAppLauncher } from "@/hooks/useAppLauncher"; +import { useFormText } from "@/hooks/generic/useFormText"; +import { useState } from "react"; + +export default function TxnBuilder() { + const openApp = useAppSelector( + (state: RootState) => state.windows.primaryApp + ); + const width = useAppSelector( + (state: RootState) => state.windows.primaryAppWidth + ); + const height = useAppSelector( + (state: RootState) => state.windows.primaryAppHeight + ); + const padding = useAppSelector( + (state: RootState) => state.windows.primaryAppPadding + ); + const { quit } = useAppLauncher(); + + const {text, updateText, clear} = useFormText(); + const [res, setRes] = useState(); + + const getTxn = (txnJson: string) => { + return "hi" + } + + return ( + +
+ +
+ +