From 8d936fc4ac47462cd3f06cd574e42f4cceecb48b Mon Sep 17 00:00:00 2001 From: Spencer Schoeneman <78219375+Spencer-Sch@users.noreply.github.com> Date: Tue, 5 Dec 2023 14:42:02 -0600 Subject: [PATCH] Register & Login (#9) * comment out console.logs * comment out failed attempt at making a custom hook * remove dispatch to set provider in auth slice - edit router.push * edit to use isConnected prop * edit to use isConnected from redux state and pass to checkUserAuth * add use of isConnected from redux state, clean up code * move WatchPathname inside of store provider * integrate web3auth, edit inputs, integrate redux state * edit register page/form, integrate web3auth, edit inputs --- .../nextjs/auth/checkUserAuthentication.ts | 10 +- packages/nextjs/auth/web3auth.ts | 79 ++++++++++ .../dash-wind/containers/Header.tsx | 5 +- .../dash-wind/features/charts/index.tsx | 2 +- .../dashboard/components/DashboardTopBar.tsx | 2 +- .../dash-wind/features/user/Login.tsx | 73 ++++----- .../dash-wind/features/user/Register.tsx | 146 +++++++++++------- .../web-3-crew/register-page/DeployForm.tsx | 72 +++++++++ .../register-page/EmployeeRegistered.tsx | 50 ++++++ .../web-3-crew/register-page/RegisterForm.tsx | 63 ++++++++ .../components/web-3-crew/watchPathname.tsx | 8 +- packages/nextjs/pages/_app.tsx | 3 +- packages/nextjs/pages/index.tsx | 14 +- 13 files changed, 400 insertions(+), 127 deletions(-) create mode 100644 packages/nextjs/components/web-3-crew/register-page/DeployForm.tsx create mode 100644 packages/nextjs/components/web-3-crew/register-page/EmployeeRegistered.tsx create mode 100644 packages/nextjs/components/web-3-crew/register-page/RegisterForm.tsx diff --git a/packages/nextjs/auth/checkUserAuthentication.ts b/packages/nextjs/auth/checkUserAuthentication.ts index 2a5e6ad..7c5cd79 100644 --- a/packages/nextjs/auth/checkUserAuthentication.ts +++ b/packages/nextjs/auth/checkUserAuthentication.ts @@ -1,7 +1,8 @@ -import { web3auth } from "~~/auth/web3auth"; +// import { web3auth } from "~~/auth/web3auth"; -export default function checkUserAuthentication(path: string) { - console.log("from checkUserAuthentication - Path = ", path); +export default function checkUserAuthentication(path: string, isConnected: boolean) { + // export default function checkUserAuthentication(path: string) { + // console.log("from checkUserAuthentication - Path = ", path); const protectedRoutes = [ "/dapp/dashboard", @@ -16,7 +17,8 @@ export default function checkUserAuthentication(path: string) { "/dapp/integration", ]; - if (!web3auth.connected && protectedRoutes.includes(path)) { + // if (!web3auth.connected && protectedRoutes.includes(path)) { + if (!isConnected && protectedRoutes.includes(path)) { return false; } return true; diff --git a/packages/nextjs/auth/web3auth.ts b/packages/nextjs/auth/web3auth.ts index 858de5a..f6da7db 100644 --- a/packages/nextjs/auth/web3auth.ts +++ b/packages/nextjs/auth/web3auth.ts @@ -1,5 +1,11 @@ +// import { useEffect, useState } from "react"; import { Web3Auth } from "@web3auth/modal"; +// import { createWalletClient, custom } from "viem"; +// import { polygonMumbai } from "viem/chains"; +// import { AuthProvider, setAuthProvider, setIsConnected } from "~~/auth/authSlice"; +// import { MyState, useMyDispatch, useMySelector } from "~~/components/dash-wind/app/store"; + export const web3auth = new Web3Auth({ clientId: "BM0SLNkhMCfIygw0Xi79dG6qbWGMN0o0mEeDjRT0dxlP3BEok9pnu5aqxCNfj2TZ9XT7sQaXm0ltuWbCQ1tsRNI", // Get your Client ID from the Web3Auth Dashboard web3AuthNetwork: "sapphire_devnet", // Web3Auth Network @@ -21,3 +27,76 @@ export async function web3AuthInit() { console.error(error); } } + +// Failed attempt at making a custom hook +// Saving for future reference / another try +// export function useWeb3auth() { +// const [provider, setProvider] = useState(null); +// const [loggedIn, setLoggedIn] = useState(false); + +// // useEffect(() => { +// // console.log(`web3auth useEffect - provider: ${!!provider} | loggedIn: ${isConnected}`); +// // }, [provider, isConnected]); +// // useEffect(() => { +// // console.log(`web3auth useEffect - provider: ${!!provider} | loggedIn: ${loggedIn}`); +// // }, [provider, loggedIn]); + +// // async function web3AuthInit() { +// // console.log("web3Auth Init..."); +// // try { +// // await web3auth.initModal(); +// // console.log("web3AuthProvider - from init: ", web3auth.provider); +// // setProvider(web3auth.provider); + +// // if (web3auth.connected) { +// // console.log("web3Auth Init connected?: ", web3auth.connected); +// // dispatch(setIsConnected({ isConnected: true })); +// // // setLoggedIn(true); +// // } +// // } catch (error) { +// // console.error(error); +// // } +// // } + +// async function loginW3A() { +// try { +// const web3authProvider = await web3auth.connect(); +// // dispatch(setAuthProvider({ provider: web3authProvider })); +// console.log("web3AuthProvider - from loginW3A: ", web3authProvider); +// setProvider(web3authProvider); +// if (web3auth.connected) { +// dispatch(setIsConnected({ isConnected: true })); +// console.log("web3Auth - loggedIn: ", loggedIn); +// setLoggedIn(true); +// } +// } catch (error) { +// console.error(error); +// } +// } + +// async function logoutW3A() { +// await web3auth.logout(); +// setProvider(null); +// setLoggedIn(false); +// console.log("logged out"); +// } + +// async function getAccountsW3A() { +// if (!provider) { +// // uiConsole("provider not initialized yet"); +// console.log("from getAccountsW3A: provider not defined"); +// return; +// } +// const client = createWalletClient({ +// chain: polygonMumbai, +// transport: custom(provider), +// }); + +// // Get user's Ethereum public address +// const [address] = await client.getAddresses(); +// // toast message? +// return address; +// } + +// return { provider, loggedIn, web3AuthInit, loginW3A, logoutW3A, getAccountsW3A }; +// } diff --git a/packages/nextjs/components/dash-wind/containers/Header.tsx b/packages/nextjs/components/dash-wind/containers/Header.tsx index c6825dc..d80d64f 100644 --- a/packages/nextjs/components/dash-wind/containers/Header.tsx +++ b/packages/nextjs/components/dash-wind/containers/Header.tsx @@ -9,7 +9,7 @@ import Bars3Icon from "@heroicons/react/24/outline/Bars3Icon"; import BellIcon from "@heroicons/react/24/outline/BellIcon"; import MoonIcon from "@heroicons/react/24/outline/MoonIcon"; import SunIcon from "@heroicons/react/24/outline/SunIcon"; -import { setAuthProvider, setIsConnected } from "~~/auth/authSlice"; +import { setIsConnected } from "~~/auth/authSlice"; import { web3auth } from "~~/auth/web3auth"; // import UserIcon from "@heroicons/react/24/outline/UserIcon"; import { MyState, useMyDispatch, useMySelector } from "~~/components/dash-wind/app/store"; @@ -42,9 +42,8 @@ function Header() { async function logoutUser() { await web3auth.logout(); - dispatch(setAuthProvider({ provider: null })); dispatch(setIsConnected({ isConnected: false })); - router.push("/"); + router.push("/login"); } return ( diff --git a/packages/nextjs/components/dash-wind/features/charts/index.tsx b/packages/nextjs/components/dash-wind/features/charts/index.tsx index 2a1ce5b..3a1db6d 100644 --- a/packages/nextjs/components/dash-wind/features/charts/index.tsx +++ b/packages/nextjs/components/dash-wind/features/charts/index.tsx @@ -17,7 +17,7 @@ function Charts() { // eslint-disable-next-line @typescript-eslint/no-unused-vars const handleDatePickerValueChange = (newValue: DateValueType, e?: HTMLInputElement | null | undefined) => { if (newValue !== null) { - console.log("newValue:", newValue); + // console.log("newValue:", newValue); setDateValue(newValue); } }; diff --git a/packages/nextjs/components/dash-wind/features/dashboard/components/DashboardTopBar.tsx b/packages/nextjs/components/dash-wind/features/dashboard/components/DashboardTopBar.tsx index 62e86de..b1ae716 100644 --- a/packages/nextjs/components/dash-wind/features/dashboard/components/DashboardTopBar.tsx +++ b/packages/nextjs/components/dash-wind/features/dashboard/components/DashboardTopBar.tsx @@ -30,7 +30,7 @@ function DashboardTopBar({ updateDashboardPeriod }: props) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const handleDatePickerValueChange = (newValue: DateValueType, e?: HTMLInputElement | null | undefined) => { if (newValue !== null) { - console.log("newValue:", newValue); + // console.log("newValue:", newValue); setDateValue(newValue); updateDashboardPeriod(newValue); } diff --git a/packages/nextjs/components/dash-wind/features/user/Login.tsx b/packages/nextjs/components/dash-wind/features/user/Login.tsx index 422073c..74f3d13 100644 --- a/packages/nextjs/components/dash-wind/features/user/Login.tsx +++ b/packages/nextjs/components/dash-wind/features/user/Login.tsx @@ -1,58 +1,50 @@ -import { useEffect, useState } from "react"; +import { useState } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; import InputText from "../../components/Input/InputText"; import ErrorText from "../../components/Typography/ErrorText"; import { UpdateFormValues } from "../../types/FormTypes"; import LandingIntro from "./LandingIntro"; -import { setAuthProvider, setIsConnected } from "~~/auth/authSlice"; +import { setIsConnected } from "~~/auth/authSlice"; import { web3auth } from "~~/auth/web3auth"; -import { useMyDispatch } from "~~/components/dash-wind/app/store"; +import { MyState, useMyDispatch, useMySelector } from "~~/components/dash-wind/app/store"; function Login() { const INITIAL_LOGIN_OBJ = { - password: "", + contractAddress: "", emailId: "", }; - const [loading, setLoading] = useState(false); + // const [loading, setLoading] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const [loginObj, setLoginObj] = useState(INITIAL_LOGIN_OBJ); + const { isConnected } = useMySelector((state: MyState) => state.auth); const router = useRouter(); const dispatch = useMyDispatch(); // Web3Auth - useEffect(() => { - const init = async () => { - try { - const web3authProvider = await web3auth.connect(); - dispatch(setAuthProvider({ provider: web3authProvider })); - if (web3auth.connected) { - dispatch(setIsConnected({ isConnected: true })); - } + async function login() { + if (isConnected) { + router.push("/dapp/dashboard"); + } + try { + await web3auth.connect(); + if (web3auth.connected) { + dispatch(setIsConnected({ isConnected: true })); router.push("/dapp/dashboard"); - } catch (error) { - console.error(error); } - }; - init(); - }); + } catch (error) { + console.error(error); + } + } const submitForm = (e: React.FormEvent) => { e.preventDefault(); setErrorMessage(""); - if (loginObj.emailId.trim() === "") return setErrorMessage("Email Id is required! (use any value)"); - if (loginObj.password.trim() === "") return setErrorMessage("Password is required! (use any value)"); - else { - setLoading(true); - // Call API to check user credentials and save token in localstorage - localStorage.setItem("token", "DumyTokenHere"); - setLoading(false); - router.push("/dapp/welcome"); - // window.location.href = "/dapp/welcome"; - } + if (loginObj.emailId.trim() === "") return setErrorMessage("Email is required!"); + if (loginObj.contractAddress.trim() === "") return setErrorMessage("Contract Address is required!"); }; const updateFormValue = ({ updateType, value }: UpdateFormValues) => { @@ -72,34 +64,25 @@ function Login() {
submitForm(e)}>
-
- - - Forgot Password? - - -
- {errorMessage} - diff --git a/packages/nextjs/components/dash-wind/features/user/Register.tsx b/packages/nextjs/components/dash-wind/features/user/Register.tsx index 7496f5b..cc297b2 100644 --- a/packages/nextjs/components/dash-wind/features/user/Register.tsx +++ b/packages/nextjs/components/dash-wind/features/user/Register.tsx @@ -1,34 +1,92 @@ import { useState } from "react"; -import Link from "next/link"; -import InputText from "../../components/Input/InputText"; -import ErrorText from "../../components/Typography/ErrorText"; import { UpdateFormValues } from "../../types/FormTypes"; import LandingIntro from "./LandingIntro"; +import { Address, createWalletClient, custom } from "viem"; +import { polygonMumbai } from "viem/chains"; +import { setIsConnected } from "~~/auth/authSlice"; +import { web3auth } from "~~/auth/web3auth"; +import { useMyDispatch } from "~~/components/dash-wind/app/store"; +import DeployForm from "~~/components/web-3-crew/register-page/DeployForm"; +import EmployeeRegistered from "~~/components/web-3-crew/register-page/EmployeeRegistered"; +import RegisterForm from "~~/components/web-3-crew/register-page/RegisterForm"; + +type RegisterState = "init" | "company-deploy" | "employee-complete" | "loading"; function Register() { const INITIAL_REGISTER_OBJ = { - name: "", - password: "", emailId: "", }; - const [loading, setLoading] = useState(false); + // const [loading, setLoading] = useState(false); const [errorMessage, setErrorMessage] = useState(""); + const [registerState, setRegisterState] = useState("init"); const [registerObj, setRegisterObj] = useState(INITIAL_REGISTER_OBJ); + const [walletAddress, setWalletAddress] = useState
(null); + + const dispatch = useMyDispatch(); + + async function login() { + try { + await web3auth.connect(); + if (web3auth.connected) { + dispatch(setIsConnected({ isConnected: true })); + } + } catch (error) { + console.error(error); + } + } + + async function getAccounts() { + if (!web3auth.provider) { + console.log("from Register - getAccounts: provider not defined"); + return; + } + const client = createWalletClient({ + chain: polygonMumbai, + transport: custom(web3auth.provider), + }); + + // Get user's public address + const [address] = await client.getAddresses(); + return address; + } - const submitForm = (e: React.FormEvent) => { + const registerCompany = async (e: React.MouseEvent) => { e.preventDefault(); setErrorMessage(""); - if (registerObj.name.trim() === "") return setErrorMessage("Name is required! (use any value)"); - if (registerObj.emailId.trim() === "") return setErrorMessage("Email Id is required! (use any value)"); - if (registerObj.password.trim() === "") return setErrorMessage("Password is required! (use any value)"); + if (registerObj.emailId.trim() === "") return setErrorMessage("Email is required!"); else { - setLoading(true); - // Call API to check user credentials and save token in localstorage - localStorage.setItem("token", "DumyTokenHere"); - setLoading(false); - window.location.href = "/dapp/welcome"; + setRegisterState("loading"); + console.log("logging in company..."); + await login(); + // Account Abstraction goes here...? + console.log("getting account address..."); + const address = await getAccounts(); + if (address) { + // Prompt user to fund wallet? + setWalletAddress(address); + // Prompt use to deploy contract using wallet address + setRegisterState("company-deploy"); + } + } + }; + + const registerEmployee = async (e: React.MouseEvent) => { + e.preventDefault(); + setErrorMessage(""); + + if (registerObj.emailId.trim() === "") return setErrorMessage("Email is required!"); + else { + setRegisterState("loading"); + console.log("logging in employee..."); + await login(); + // Account Abstraction goes here...? + const address = await getAccounts(); + if (address) { + setWalletAddress(address); + setRegisterState("employee-complete"); + } } }; @@ -44,51 +102,19 @@ function Register() {
-
-

Register

- submitForm(e)}> -
- - - - - -
- - {errorMessage} - - -
- Already have an account?{" "} - - - Login - - -
- -
+ {registerState === "loading" &&
Loading...
} + {registerState === "init" && ( + + )} + {registerState === "company-deploy" && } + {registerState === "employee-complete" && } diff --git a/packages/nextjs/components/web-3-crew/register-page/DeployForm.tsx b/packages/nextjs/components/web-3-crew/register-page/DeployForm.tsx new file mode 100644 index 0000000..677742e --- /dev/null +++ b/packages/nextjs/components/web-3-crew/register-page/DeployForm.tsx @@ -0,0 +1,72 @@ +import React from "react"; +import Link from "next/link"; +import { Abi, Address, parseEther } from "viem"; +import { useContractWrite, usePrepareContractWrite } from "wagmi"; +import ErrorText from "~~/components/dash-wind/components/Typography/ErrorText"; +import { Address as AddressDisplay } from "~~/components/scaffold-eth/Address"; + +interface props { + ownerAddress: Address | null; +} + +const payrollFactoryAddress = ""; +const payrollFactoryABI: Abi = []; + +export default function DeployForm({ ownerAddress }: props) { + const { config } = usePrepareContractWrite({ + address: payrollFactoryAddress, + abi: payrollFactoryABI, + functionName: "deployPayrollAndTokenTransferor", + value: parseEther("1", "wei"), + onSuccess(data) { + console.log("contract deployed! Data: ", data); + }, + onError(error) { + console.error("contract deploy error!", error); + }, + }); + + const { data, isLoading, isSuccess, write } = useContractWrite(config); + + return ( +
+

Deploy Payroll Contract

+
+ {ownerAddress ? ( + <> +
+

Account Owner Address:

+ +
+ {/* + + ) : ( + Error: Owner Address Not Found + )} + + {data && ( +
+

Payroll Contract Address:

+ {/*

{data}

*/} + {/* */} +
+ )} + +
+ + + Go Home + + +
+
+
+ ); +} diff --git a/packages/nextjs/components/web-3-crew/register-page/EmployeeRegistered.tsx b/packages/nextjs/components/web-3-crew/register-page/EmployeeRegistered.tsx new file mode 100644 index 0000000..8e3e618 --- /dev/null +++ b/packages/nextjs/components/web-3-crew/register-page/EmployeeRegistered.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import Link from "next/link"; +import { Address } from "viem"; +import ErrorText from "~~/components/dash-wind/components/Typography/ErrorText"; +import { Address as AddressDisplay } from "~~/components/scaffold-eth/Address"; + +interface props { + employeeAddress: Address | null; +} + +export default function EmployeeRegistered({ employeeAddress }: props) { + return ( +
+ {employeeAddress ? ( + <> +

Registration Complete!

+
+
+

Your New Address:

+ +
+

+ Give this address to your company administrator +

+ +
+ + + Go Home + + +
+
+ + ) : ( + <> +

Registration Error!

+ Error: Employee Address Not Found +
+ + + Go Home + + +
+ + )} +
+ ); +} diff --git a/packages/nextjs/components/web-3-crew/register-page/RegisterForm.tsx b/packages/nextjs/components/web-3-crew/register-page/RegisterForm.tsx new file mode 100644 index 0000000..c305a18 --- /dev/null +++ b/packages/nextjs/components/web-3-crew/register-page/RegisterForm.tsx @@ -0,0 +1,63 @@ +import React from "react"; +import Link from "next/link"; +import InputText from "~~/components/dash-wind/components/Input/InputText"; +import ErrorText from "~~/components/dash-wind/components/Typography/ErrorText"; +import { UpdateFormValues } from "~~/components/dash-wind/types/FormTypes"; + +interface props { + updateFormValue: ({ updateType, value }: UpdateFormValues) => void; + registerObj: { emailId: string }; + loading?: boolean; + errorMessage: string; + registerCompany: (e: React.MouseEvent) => void; + registerEmployee: (e: React.MouseEvent) => void; +} + +export default function registerForm({ + updateFormValue, + registerObj, + loading, + errorMessage, + registerCompany, + registerEmployee, +}: props) { + return ( +
+

Register

+
+
+ +
+ + {errorMessage} + + + +
+ Already have an account?{" "} + + + Login + + +
+
+
+ ); +} diff --git a/packages/nextjs/components/web-3-crew/watchPathname.tsx b/packages/nextjs/components/web-3-crew/watchPathname.tsx index 6b03fb7..c60263f 100644 --- a/packages/nextjs/components/web-3-crew/watchPathname.tsx +++ b/packages/nextjs/components/web-3-crew/watchPathname.tsx @@ -4,20 +4,22 @@ import React, { useEffect } from "react"; import { usePathname } from "next/navigation"; import { useRouter } from "next/router"; import checkUserAuthentication from "~~/auth/checkUserAuthentication"; +import { MyState, useMySelector } from "~~/components/dash-wind/app/store"; export default function WatchPathname() { const pathname = usePathname(); const router = useRouter(); + const { isConnected } = useMySelector((state: MyState) => state.auth); useEffect(() => { - console.log(`Route changed to: ${pathname}`); + // console.log(`Route changed to: ${pathname}`); if (pathname !== null) { - const userIsAuthenticated = checkUserAuthentication(pathname); + const userIsAuthenticated = checkUserAuthentication(pathname, isConnected); if (!userIsAuthenticated) { router.replace("/login"); } } - }, [pathname, router]); + }, [pathname, router, isConnected]); return <>; } diff --git a/packages/nextjs/pages/_app.tsx b/packages/nextjs/pages/_app.tsx index 59df41e..2218962 100644 --- a/packages/nextjs/pages/_app.tsx +++ b/packages/nextjs/pages/_app.tsx @@ -61,7 +61,7 @@ const ScaffoldEthApp = ({ Component, pageProps }: AppPropsWithLayout) => { return ( - + {/* */} { theme={isDarkTheme ? darkTheme() : lightTheme()} > +
{getLayout()}
{/*
{getLayout()}
*/}
diff --git a/packages/nextjs/pages/index.tsx b/packages/nextjs/pages/index.tsx index 8a51d66..a91e4ec 100644 --- a/packages/nextjs/pages/index.tsx +++ b/packages/nextjs/pages/index.tsx @@ -2,31 +2,30 @@ import { ReactElement, useEffect } from "react"; // import Link from "next/link"; import { useRouter } from "next/router"; import type { NextPageWithLayout } from "./_app"; -import { setAuthProvider, setIsConnected } from "~~/auth/authSlice"; +import { setIsConnected } from "~~/auth/authSlice"; import { web3auth } from "~~/auth/web3auth"; import { MetaHeader } from "~~/components/MetaHeader"; -import { useMyDispatch } from "~~/components/dash-wind/app/store"; +import { MyState, useMyDispatch, useMySelector } from "~~/components/dash-wind/app/store"; import CleanLayout from "~~/components/layouts/CleanLayout"; const LandingPage: NextPageWithLayout = () => { const router = useRouter(); const dispatch = useMyDispatch(); + const { isConnected } = useMySelector((state: MyState) => state.auth); useEffect(() => { - dispatch(setAuthProvider({ provider: web3auth.provider })); - if (web3auth.connected) { dispatch(setIsConnected({ isConnected: true })); } }, [dispatch]); function launchDapp() { - if (web3auth.connected) { + if (isConnected) { // redirect to dashboard if logged in router.push("/dapp/dashboard"); return; } - // redirect to login if not connected + // redirect to login if not logged in router.push("/login"); } return ( @@ -47,9 +46,6 @@ const LandingPage: NextPageWithLayout = () => { - {/* - Launch Dapp - */} );