Skip to content

Commit

Permalink
Opening a focused permit working. Oauth broke though
Browse files Browse the repository at this point in the history
  • Loading branch information
architect-dev committed Nov 27, 2024
1 parent c4f566d commit 9953b85
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 34 deletions.
4 changes: 2 additions & 2 deletions packages/nextjs/components/PermitModal/Create/Create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
usePermitModalOpen,
PermitV2CreateType,
usePermitCreateOptionsAndActions,
usePermitModalFocusedPermit,
usePermitModalFocusedPermitHash,
} from "~~/services/store/permitV2ModalStore";
import { notification } from "~~/utils/scaffold-eth";
import truncateAddress from "~~/utils/truncate-address";
Expand Down Expand Up @@ -46,7 +46,7 @@ const PermitV2ModalCreateButton: React.FC<{ disabled?: boolean }> = ({ disabled
const createOptions = usePermitCreateOptions();
const { setOpen } = usePermitModalOpen();
const [creating, setCreating] = useState(false);
const { setFocusedPermit } = usePermitModalFocusedPermit();
const { setFocusedPermitHash: setFocusedPermit } = usePermitModalFocusedPermitHash();

let cta = createOptions.type === PermitV2CreateType.Using ? "Create" : "Sign and Open";
if (creating) {
Expand Down
149 changes: 149 additions & 0 deletions packages/nextjs/components/PermitModal/Opened/Opened.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { useAccount } from "@account-kit/react";
import {
ArrowDownTrayIcon,
ArrowUpTrayIcon,
ClipboardDocumentCheckIcon,
ClipboardDocumentListIcon,
} from "@heroicons/react/24/outline";
import React, { useState } from "react";
import { InputBase } from "~~/components/scaffold-eth";
import { useFhenixPermitWithHash } from "~~/permits/hooks";
import { updatePermitName } from "~~/permits/store";
import { usePermitModalFocusedPermitHash, usePermitSatisfiesRequirements } from "~~/services/store/permitV2ModalStore";
import truncateAddress from "~~/utils/truncate-address";

export const PermitV2ModalOpened = () => {
const { address } = useAccount({ type: "LightAccount" });
const { focusedPermitHash } = usePermitModalFocusedPermitHash();
const permit = useFhenixPermitWithHash(focusedPermitHash);
const satisfies = usePermitSatisfiesRequirements(permit);
const [copied, setCopied] = useState(false);

if (permit == null) {
return <div>PERMIT NOT FOUND</div>;
}

const { type, name, recipient, expiration, projects, contracts, issuerSignature, recipientSignature } = permit;

return (
<>
{/* Type */}
<div className="flex flex-row items-center justify-between gap-4">
<div className="text-sm font-bold">Type:</div>
<div className="flex flex-row items-center justify-center gap-2">
{type === "self" && (
<>
<div className="text-sm">Self Usage</div>
<ArrowDownTrayIcon className="w-4 h-4" />
</>
)}
{type === "sharing" && (
<>
<div className="text-sm">To Share</div>
<ArrowUpTrayIcon className="w-4 h-4 rotate-90" />
</>
)}
{type === "recipient" && (
<>
<div className="text-sm">Shared with You</div>
<ArrowDownTrayIcon className="w-4 h-4 rotate-90" />
</>
)}
</div>
</div>

{/* Name */}
<div className="flex flex-row items-center justify-start gap-4">
<div className="text-sm font-bold">Name: (editable)</div>
<InputBase
name="permit-name"
value={name}
placeholder="Unnamed Permit"
onChange={(value: string) => updatePermitName(address, permit.getHash(), value)}
/>
</div>

{/* (Sharing) Recipient */}
{type === "sharing" && (
<div className="flex flex-row items-center justify-start gap-4">
<div className={`text-sm font-bold`}>Recipient:</div>
<div className="text-sm">{truncateAddress(recipient)}</div>
</div>
)}

{/* Expiration */}
<div className="flex flex-row items-center justify-between gap-4">
<div className="text-sm font-bold">Expires in:</div>
<div className="text-sm">TODO</div>
</div>

{/* Access */}
<div className="flex flex-col w-full">
<div className={`text-sm font-bold`}>Access:</div>

{/* Access requirements not satisfied */}
{!satisfies && <span className="italic text-sm text-error"> ! dApp{"'"}s access requirements not met !</span>}

{/* Contracts */}
{contracts.length > 0 && (
<div className="flex flex-row items-center justify-between flex-wrap">
<div className={`text-sm font-bold ml-4 mr-4`}>Contracts:</div>
{contracts.map(contract => (
<div key={contract} className="text-sm px-2 border-primary border-2">
{truncateAddress(contract)}
</div>
))}
</div>
)}

{/* Projects */}
{projects.length > 0 && (
<div className="flex flex-row items-center justify-between">
<div className={`text-sm font-bold ml-4 mr-4`}>Projects:</div>
{projects.map(project => (
<button key={project} className="text-sm px-2 border-primary border-2">
{project}
</button>
))}
</div>
)}
</div>

{/* Issuer Signature */}
<div className="flex flex-row items-center justify-between gap-4">
<div className="text-sm font-bold">{type === "recipient" && "Issuer "}Signature:</div>
<div
className={`w-3 h-3 rounded-full ${
issuerSignature !== "0x" ? "bg-bg-surface-success" : "bg-bg-surface-error"
}`}
/>
</div>

{/* Recipient Signature */}
{type === "recipient" && (
<div className="flex flex-row items-center justify-between gap-4">
<div className="text-sm font-bold">Recipient Signature:</div>
<div
className={`w-3 h-3 rounded-full ${
recipientSignature !== "0x" ? "bg-bg-surface-success" : "bg-bg-surface-error"
}`}
/>
</div>
)}

{/* Create Button */}
<div className="divider -my-1" />
<div className="flex flex-row gap-4">
<button className="btn btn-secondary" onClick={() => setCopied(true)}>
Copy Permit Data{" "}
{copied ? (
<ClipboardDocumentCheckIcon className="w-4 h-4" />
) : (
<ClipboardDocumentListIcon className="w-4 h-4" />
)}
</button>
<button className="btn btn-primary flex-[1]">Use</button>
</div>
</>
);
};
10 changes: 5 additions & 5 deletions packages/nextjs/components/PermitModal/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from "react";
import { useFhenixActivePermitHash, useFhenixAllPermits } from "~~/permits/hooks";
import { PermitV2 } from "~~/permits/permitV2";
import { setActivePermitHash } from "~~/permits/store";
import { usePermitModalFocusedPermit, usePermitSatisfiesRequirements } from "~~/services/store/permitV2ModalStore";
import { usePermitModalFocusedPermitHash, usePermitSatisfiesRequirements } from "~~/services/store/permitV2ModalStore";
import truncateAddress from "~~/utils/truncate-address";

const timeUntilExpiration = (ts: number): string => {
Expand Down Expand Up @@ -69,24 +69,24 @@ const PermitRow: React.FC<{ permit: PermitV2; children?: React.ReactNode; classN
};

const SelectedPermitRow: React.FC<{ permit: PermitV2 }> = ({ permit }) => {
const { setFocusedPermit } = usePermitModalFocusedPermit();
const { setFocusedPermitHash } = usePermitModalFocusedPermitHash();

return (
<PermitRow permit={permit} className="bg-base-200">
<button className="btn btn-sm btn-secondary btn-ghost" onClick={() => setFocusedPermit(permit.getHash())}>
<button className="btn btn-sm btn-secondary btn-ghost" onClick={() => setFocusedPermitHash(permit.getHash())}>
Open
</button>
</PermitRow>
);
};

const SelectPermitRow: React.FC<{ permit: PermitV2 }> = ({ permit }) => {
const { setFocusedPermit } = usePermitModalFocusedPermit();
const { setFocusedPermitHash } = usePermitModalFocusedPermitHash();
const { address } = useAccount({ type: "LightAccount" });

return (
<PermitRow permit={permit}>
<button className="btn btn-sm btn-secondary btn-ghost" onClick={() => setFocusedPermit(permit.getHash())}>
<button className="btn btn-sm btn-secondary btn-ghost" onClick={() => setFocusedPermitHash(permit.getHash())}>
Open
</button>
<button
Expand Down
3 changes: 3 additions & 0 deletions packages/nextjs/components/PermitModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { PermitV2ModalTabs } from "./Tabs";
import { PermitV2TabInstructions } from "./TabInstructions";
import { PermitV2ModalCreate } from "./Create/Create";
import { PermitV2ModalSelect } from "./Select/Select";
import { PermitV2ModalOpened } from "./Opened/Opened";

const PermitV2ModalImport = () => {
return <div>PERMIT V2 MODAL IMPORT CONTENT</div>;
Expand All @@ -20,6 +21,8 @@ const PermitV2Content = () => {
return <PermitV2ModalImport />;
case PermitV2Tab.Select:
return <PermitV2ModalSelect />;
case PermitV2Tab.Opened:
return <PermitV2ModalOpened />;
}
};

Expand Down
40 changes: 21 additions & 19 deletions packages/nextjs/components/scaffold-eth/Input/AddressInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,27 +96,29 @@ export const AddressInput = ({
disabled={isEnsAddressLoading || isEnsNameLoading || disabled}
reFocus={reFocus}
prefix={
ensName && useENS ? (
<div className="flex bg-base-300 rounded-l-full items-center">
{isEnsAvatarLoading && <div className="skeleton bg-base-200 w-[35px] h-[35px] rounded-sm shrink-0"></div>}
{ensAvatar ? (
<span className="w-[35px]">
{
// eslint-disable-next-line
<img className="w-full rounded-sm" src={ensAvatar} alt={`${ensAddress} avatar`} />
}
</span>
) : null}
<span className="text-accent px-2">{enteredEnsName ?? ensName}</span>
</div>
) : (
(isEnsNameLoading || isEnsAddressLoading) && (
<div className="flex bg-base-300 rounded-l-full items-center gap-2 pr-2">
<div className="skeleton bg-base-200 w-[35px] h-[35px] rounded-sm shrink-0"></div>
<div className="skeleton bg-base-200 h-3 w-20"></div>
useENS ? (
ensName ? (
<div className="flex bg-base-300 rounded-l-full items-center">
{isEnsAvatarLoading && <div className="skeleton bg-base-200 w-[35px] h-[35px] rounded-sm shrink-0"></div>}
{ensAvatar ? (
<span className="w-[35px]">
{
// eslint-disable-next-line
<img className="w-full rounded-sm" src={ensAvatar} alt={`${ensAddress} avatar`} />
}
</span>
) : null}
<span className="text-accent px-2">{enteredEnsName ?? ensName}</span>
</div>
) : (
(isEnsNameLoading || isEnsAddressLoading) && (
<div className="flex bg-base-300 rounded-l-full items-center gap-2 pr-2">
<div className="skeleton bg-base-200 w-[35px] h-[35px] rounded-sm shrink-0"></div>
<div className="skeleton bg-base-200 h-3 w-20"></div>
</div>
)
)
)
) : undefined
}
suffix={
// Don't want to use nextJS Image here (and adding remote patterns for the URL)
Expand Down
14 changes: 14 additions & 0 deletions packages/nextjs/permits/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ export const useFhenixPermit = () => {
});
};

export const useFhenixPermitWithHash = (hash: string | undefined) => {
const { address } = useAccount({ type: "LightAccount" });

return useStore(permitsStore, state => {
if (address == null) return undefined;
if (hash == null) return undefined;

const permit = state.permits[address]?.[hash];
if (permit == null) return undefined;

return PermitV2.deserialize(permit);
});
};

export const useFhenixActivePermitHash = () => {
const { address } = useAccount({ type: "LightAccount" });

Expand Down
4 changes: 4 additions & 0 deletions packages/nextjs/permits/permitV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ export class PermitV2 implements PermitV2Interface {
return permit;
}

updateName = (name: string) => {
this.name = name;
};

/**
* Creates a `PermitV2` from a serialized permit, hydrating methods and classes
* NOTE: Does not return a stringified permit
Expand Down
11 changes: 11 additions & 0 deletions packages/nextjs/permits/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ export const setPermit = (account: string, permitV2: PermitV2) => {
);
};

export const updatePermitName = (account: string | undefined, hash: string | undefined, name: string) => {
permitsStore.setState(
produce<PermitsStore>(state => {
if (account == null || hash == null || state.permits[account]?.[hash] == null) return;
const permit = PermitV2.deserialize(state.permits[account][hash]);
permit.updateName(name);
state.permits[account][hash] = permit.serialize();
}),
);
};

export const removePermit = (account: string, hash: string) => {
permitsStore.setState(
produce<PermitsStore>(state => {
Expand Down
18 changes: 10 additions & 8 deletions packages/nextjs/services/store/permitV2ModalStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type PermitV2AccessRequirementsParams =
type PermitModalState = {
open: boolean;
tab: PermitV2Tab;
focusedPermit: string | undefined;
focusedPermitHash: string | undefined;
createOptions: PermitV2CreateOptions;
// ----
accessRequirements: PermitV2AccessRequirements;
Expand All @@ -60,7 +60,7 @@ const initialCreateOptions: PermitV2CreateOptions = {
export const usePermitModalStore = create<PermitModalState>(() => ({
open: false,
tab: PermitV2Tab.Create,
focusedPermit: undefined,
focusedPermitHash: undefined,
createOptions: initialCreateOptions,
// ----
accessRequirements: {
Expand Down Expand Up @@ -107,13 +107,13 @@ export const usePermitModalTab = () => {
return { tab, setTab };
};

export const usePermitModalFocusedPermit = () => {
const focusedPermit = usePermitModalStore(state => state.focusedPermit);
const setFocusedPermit = useCallback((focusedPermit: string) => {
usePermitModalStore.setState({ focusedPermit, tab: PermitV2Tab.Opened });
export const usePermitModalFocusedPermitHash = () => {
const focusedPermitHash = usePermitModalStore(state => state.focusedPermitHash);
const setFocusedPermitHash = useCallback((focusedPermitHash: string) => {
usePermitModalStore.setState({ focusedPermitHash, tab: PermitV2Tab.Opened });
}, []);

return { focusedPermit, setFocusedPermit };
return { focusedPermitHash, setFocusedPermitHash };
};

export const usePermitCreateOptions = () => usePermitModalStore(state => state.createOptions);
Expand Down Expand Up @@ -214,9 +214,11 @@ export const usePermitCreateOptionsAndActions = () => {
};
};

export const usePermitSatisfiesRequirements = (permit: PermitV2) => {
export const usePermitSatisfiesRequirements = (permit: PermitV2 | undefined) => {
const accessRequirements = usePermitModalStore(state => state.accessRequirements);
return useMemo(() => {
if (permit == null) return false;

// Set to true if requirements includes some contracts
let contractsSatisfied = accessRequirements.contracts.length > 0;
for (const contract of accessRequirements.contracts) {
Expand Down

0 comments on commit 9953b85

Please sign in to comment.