Skip to content

Commit

Permalink
Fix account radio layout
Browse files Browse the repository at this point in the history
  • Loading branch information
Igor Stadnyk committed Jul 14, 2024
1 parent b7cc6bb commit d82bf9a
Show file tree
Hide file tree
Showing 14 changed files with 631 additions and 90 deletions.
244 changes: 244 additions & 0 deletions frontend/app/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
"use client";

import Image from "next/image";
import { useEffect, useState } from "react";
import { Link } from "@nextui-org/link";
import { Button } from "@nextui-org/button";
import { CircularProgress } from "@nextui-org/progress";
import { useWalletClient } from "wagmi";
import { walletClientToSmartAccountSigner } from "permissionless";

import keyImage from "@/images/key.png";
import ConnectSafeButton from "@/components/ConnectSafeButton";
import useUniversalAccountInfo from "@/hooks/useUniversalAccountInfo";
import { getSafesByOwner } from "@/services/getSafesByOwner";
import AccountRadioGroup from "@/components/AccountRadioGroup";
import { useCreateNewWallet } from "@/hooks/useCreateNewWallet";
import { isWingmanModuleInitialized } from "@/services/installModule";
import { useSafeInfoContextProvider } from "@/context/SafeInfoContextProvider";
import {
prepareSafeAccount,
prepareSmartAccountClient,
} from "@/services/prepareSmartAccountClient";

export default function Home() {
const { connectedTo, address } = useUniversalAccountInfo();

const [safes, setSafes] = useState({
isLoaded: false,
data: [],
});

useEffect(() => {
if (!address) return;

getSafesByOwner(address).then((data) => {
console.log("data", data);
setSafes({
isLoaded: true,
data,
});
});
}, [address]);

const [stage, setStage] = useState(1);

const { data: walletClient } = useWalletClient();

const { setSafeInfo, safeInfo } = useSafeInfoContextProvider();

useEffect(() => {
(async () => {
if (!connectedTo || !walletClient) {
console.log("disconnected or walletClient missing");

return setStage(1);
}

if (!safeInfo.address && safes.isLoaded) {
console.log("safes not loaded or not safe wallet - fetch safes");

return setStage(2);
}

console.log(safeInfo);
const isModuleSupported = await safeInfo.accountClient
.supportsModule({
type: "fallback",
})
.catch((err) => {
console.log(err);

return false;
});

if (!isModuleSupported) {
console.log("Unsupported Safe Wallet");

return setStage(5);
} //dead end

const isWingmanDeployed = await isWingmanModuleInitialized(
safeInfo.accountClient,
);

if (!isWingmanDeployed) {
console.log("compatible wallet, need to install module");

return setStage(3); //but to install wingman only
}

console.log("wingman installed");

return setStage(4);
})();
}, [connectedTo, safes.isLoaded, walletClient, safeInfo]);

async function handleSelectExternalAddress(address) {
console.log("wallet client", walletClient);
const smartAccountSigner =
await walletClientToSmartAccountSigner(walletClient);
const safeSmartAccount = await prepareSafeAccount(
smartAccountSigner,
address,
);
const smartAccountClient =
await prepareSmartAccountClient(safeSmartAccount);

setSafeInfo({
address: address,
accountClient: smartAccountClient,
});
}

return (
<section className="flex flex-nowrap align-middle pt-12 ">
<div className="flex flex-col gap-8 max-w-xl text-start justify-center w-full">
{stage === 1 ? <StageOne /> : null}

{stage === 2 ? (
<StageTwo
createNewSafe={() => setStage(3)}
safes={safes.data}
selectAddress={handleSelectExternalAddress}
/>
) : null}

{stage === 3 ? <StageThree proceed={() => setStage(4)} /> : null}

{stage === 4 ? <StageFour /> : null}

{stage === 5 ? (
<>
Your Safe wallet do not support erc7579, choose different wallet or
connect metamask to create new one
</>
) : null}
</div>
<div className="flex align-middle justify-center w-full">
<Image alt="key image" className="h-96 w-96" src={keyImage} />
</div>
</section>
);
}

function StageOne() {
return (
<>
<h1 className="font-black text-[50px]">
Staying On-Chain in Any Situation
</h1>
<p className="text-lg">
Prepare for the unexpected. Web3 Wingman lets you set automated
transfers from your wallet to a chosen receiver on a specific date.
Whether facing a medical procedure or an adventure, safeguard your
assets and support your loved ones if things don&apos;t go as planned.
</p>

<ConnectSafeButton />
</>
);
}

function StageTwo({ safes, selectAddress, createNewSafe }) {
return (
<>
{safes.length ? (
<>
<h3 className="font-black text-[25px]">Select Safe account to use</h3>
<AccountRadioGroup safes={safes} onChange={selectAddress} />
</>
) : null}

<h3 className="font-black text-[25px]">
{safes.length ? "Or create a new one" : "Create new Safe account"}
</h3>
<Button
className="font-semibold"
color="primary"
size="lg"
onClick={createNewSafe}
>
Create new Safe
</Button>
</>
);
}

function StageThree() {
const { safeInfo, setSafeInfo } = useSafeInfoContextProvider();
const { createNewWallet, status } = useCreateNewWallet(
safeInfo.accountClient,
);

function create() {
(async () => {
createNewWallet().then((newAccountClient) => {
setSafeInfo({
accountClient: newAccountClient,
address: newAccountClient.account.address,
});
});
})();
}

return (
<>
<h1 className="font-black text-[50px]">Creating new wallet</h1>

{!status ? (
<Button
className="font-semibold"
color="primary"
size="lg"
onClick={create}
>
Create new Safe
</Button>
) : null}

{!!status ? <CircularProgress aria-label="Loading..." size="lg" /> : null}

<p className="text-lg">{status}</p>
</>
);
}

function StageFour() {
return (
<>
{/* eslint-disable-next-line react/no-unescaped-entities */}
<h1 className="font-black text-[50px]">You're all set</h1>

<Button
showAnchorIcon
as={Link}
color="primary"
href={"/backup"}
variant="solid"
>
Go to dashboard
</Button>
</>
);
}
10 changes: 7 additions & 3 deletions frontend/app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ export interface ProvidersProps {
themeProps?: ThemeProviderProps;
}

import { SafeInfoContextProvider } from "@/context/SafeInfoContextProvider";

export function Providers({ children, themeProps }: ProvidersProps) {
const router = useRouter();

return (
<Web3ModalProvider>
<NextUIProvider navigate={router.push}>
<NextThemesProvider {...themeProps}>{children}</NextThemesProvider>
</NextUIProvider>
<SafeInfoContextProvider>
<NextUIProvider navigate={router.push}>
<NextThemesProvider {...themeProps}>{children}</NextThemesProvider>
</NextUIProvider>
</SafeInfoContextProvider>
</Web3ModalProvider>
);
}
70 changes: 25 additions & 45 deletions frontend/components/AccountRadioGroup.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,27 @@
import React from "react";
import {RadioGroup, Radio, cn} from "@nextui-org/react";
import React from 'react';
import { Button } from '@nextui-org/react';
export default function AccountRadioGroup({ safes, onChange }) {
return (
<div className="flex flex-col gap-4">
{safes.map((safe) => {
const isCompatible = safe.version === '1.4.1';
const description = !isCompatible ? "Can't be used" : '';

export default function AccountRadioGroup({safes, onChange}) {

console.log('account radio', safes);

return (
<RadioGroup
label="Accounts"
description="Select Safe to use"
onValueChange={onChange}
>
{
safes.map((safe) => {
const isCompatible = safe.version === '1.4.1';
const description = !isCompatible ? 'Can\'t be used' : '';
return (
<CustomRadio value={safe.address} disabled={!isCompatible} description={description}>
{safe.address}
</CustomRadio>
)
})
}
</RadioGroup>
);
return (
<Button
key={safe.address}
className=" py-2 bg-primary-200"
isDisabled={!isCompatible}
size="md w-full"
onClick={onChange}
>
<span className="flex flex-col text-left py-8">
<span>{safe.address}</span>
<span className=" text-xs">{description}</span>
</span>
</Button>
);
})}
</div>
);
}


export const CustomRadio = (props) => {
const {children, ...otherProps} = props;

return (
<Radio
{...otherProps}
classNames={{
base: cn(
"inline-flex m-0 bg-content1 hover:bg-content2 items-center justify-between",
"flex-row-reverse max-w-[300px] cursor-pointer rounded-lg gap-4 p-4 border-2 border-transparent",
"data-[selected=true]:border-primary"
),
}}
>
{children}
</Radio>
);
};
4 changes: 2 additions & 2 deletions frontend/components/ConnectSafeButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ const ConnectSafeButton = () => {
className="font-semibold"
color="primary"
size="lg"
startContent={<Image alt="SAFE logo" src={logoSafe} />}
// startContent={<Image alt="SAFE logo" src={logoSafe} />}
onClick={() => open({ view: 'Connect' })}
>
Connect Safe
Connect wallet
</Button>
</div>
);
Expand Down
3 changes: 3 additions & 0 deletions frontend/components/LoadingState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function LoadingState() {

}
Loading

0 comments on commit d82bf9a

Please sign in to comment.