diff --git a/packages/nextjs/components/PermitModal/Create/Create.tsx b/packages/nextjs/components/PermitModal/Create/Create.tsx
index a587552..9586de2 100644
--- a/packages/nextjs/components/PermitModal/Create/Create.tsx
+++ b/packages/nextjs/components/PermitModal/Create/Create.tsx
@@ -1,5 +1,5 @@
import { useChain, useAccount } from "@account-kit/react";
-import { PlusIcon, XMarkIcon } from "@heroicons/react/24/outline";
+import { ArrowDownTrayIcon, ArrowUpTrayIcon, PlusIcon, XMarkIcon } from "@heroicons/react/24/outline";
import React from "react";
import { useState } from "react";
import { zeroAddress, isAddress, getAddress } from "viem";
@@ -60,6 +60,7 @@ const PermitV2ModalCreateButton: React.FC<{ disabled?: boolean }> = ({ disabled
const permit = await PermitV2.createAndSign(
{
+ name: createOptions.name.length > 0 ? createOptions.name : "Unnamed Permit",
type: createOptions.type === PermitV2CreateType.Using ? "self" : "sharing",
issuer: account.address,
recipient: createOptions.recipient.length > 0 ? createOptions.recipient : zeroAddress,
@@ -131,19 +132,19 @@ export const PermitV2ModalCreate = () => {
<>
{/* Type */}
-
Type:
+
Purpose:
/
diff --git a/packages/nextjs/components/PermitModal/Select/Select.tsx b/packages/nextjs/components/PermitModal/Select/Select.tsx
index 7f5030d..f24925c 100644
--- a/packages/nextjs/components/PermitModal/Select/Select.tsx
+++ b/packages/nextjs/components/PermitModal/Select/Select.tsx
@@ -1,4 +1,82 @@
-import { useFhenixAllPermits } from "~~/permits/hooks";
+import { ArrowDownTrayIcon, ArrowUpTrayIcon } from "@heroicons/react/24/outline";
+import React from "react";
+import { useFhenixActivePermitHash, useFhenixAllPermits } from "~~/permits/hooks";
+import { PermitV2 } from "~~/permits/permitV2";
+import { usePermitSatisfiesRequirements } from "~~/services/store/permitV2ModalStore";
+
+const timeUntilExpiration = (ts: number): string => {
+ const now = Math.floor(Date.now() / 1000);
+ let diff = Math.max(0, ts - now);
+
+ const units = [
+ { label: "y", value: 365 * 24 * 60 * 60 }, // Years
+ { label: "m", value: 30 * 24 * 60 * 60 }, // Months (approx)
+ { label: "w", value: 7 * 24 * 60 * 60 }, // Weeks
+ { label: "d", value: 24 * 60 * 60 }, // Days
+ { label: "h", value: 60 * 60 }, // Hours
+ { label: "m", value: 60 }, // Minutes
+ ];
+
+ if (diff > units[0].value) return ">1y";
+
+ for (const unit of units) {
+ const unitCount = Math.floor(diff / unit.value);
+ if (unitCount > 0) {
+ return `${unitCount}${unit.label}`;
+ }
+ }
+
+ return "0m";
+};
+
+const PermitRow: React.FC<{ permit: PermitV2; children?: React.ReactNode; className?: string }> = ({
+ permit,
+ children,
+ className,
+}) => {
+ const timestamp = Math.floor(Date.now() / 1000);
+ const satisfies = usePermitSatisfiesRequirements(permit);
+
+ return (
+
+ {permit.name != null && permit.name.length > 0 ? permit.name : "Unnamed Permit"} |
+
+ {permit.type === "self" && }
+ {permit.type === "sharing" && }
+ {permit.type === "recipient" && }
+ |
+
+ {permit.expiration > timestamp && timeUntilExpiration(permit.expiration)}
+ {permit.expiration <= timestamp && }
+ |
+
+
+ |
+
+ {children}
+ |
+
+ );
+};
+
+const SelectedPermitRow: React.FC<{ permit: PermitV2 }> = ({ permit }) => {
+ return (
+
+
+
+ );
+};
+
+const SelectPermitRow: React.FC<{ permit: PermitV2 }> = ({ permit }) => {
+ return (
+
+
+
+
+ );
+};
export const PermitV2ModalSelect = () => {
// List of permits
@@ -7,8 +85,41 @@ export const PermitV2ModalSelect = () => {
// Expand opens sub page with action buttons
const permits = useFhenixAllPermits();
+ const activePermitHash = useFhenixActivePermitHash();
+
+ return (
+ <>
+
+
+
+ Name |
+ Type |
+ Exp. |
+ Access |
+ Actions |
+
+
+
+ {/*
+
Selected:
*/}
- console.log({ permits });
+
Selected:
+ {activePermitHash != null && permits[activePermitHash] != null && (
+
+ )}
+ {/*
+
+
Available:
*/}
- return <>List of permits>;
+
Available:
+ {Object.entries(permits)
+ .filter(([hash]) => hash !== activePermitHash)
+ .map(([hash, permit]) => {
+ return
;
+ })}
+ {/*
*/}
+
+
+ >
+ );
};
diff --git a/packages/nextjs/permits/hooks.ts b/packages/nextjs/permits/hooks.ts
index c4684b2..808a137 100644
--- a/packages/nextjs/permits/hooks.ts
+++ b/packages/nextjs/permits/hooks.ts
@@ -19,6 +19,16 @@ export const useFhenixPermit = () => {
});
};
+export const useFhenixActivePermitHash = () => {
+ const { address } = useAccount({ type: "LightAccount" });
+
+ return useStore(permitsStore, state => {
+ if (address == null) return undefined;
+
+ return state.activePermitHash[address];
+ });
+};
+
export const useFhenixAllPermits = () => {
const { address } = useAccount({ type: "LightAccount" });
diff --git a/packages/nextjs/permits/permitV2.ts b/packages/nextjs/permits/permitV2.ts
index 5efe48a..6c2520a 100644
--- a/packages/nextjs/permits/permitV2.ts
+++ b/packages/nextjs/permits/permitV2.ts
@@ -219,6 +219,7 @@ export class PermitV2 implements PermitV2Interface {
JSON.stringify({
type: this.type,
issuer: this.issuer,
+ expiration: this.expiration,
contracts: this.contracts,
projects: this.projects,
recipient: this.recipient,
diff --git a/packages/nextjs/services/store/permitV2ModalStore.ts b/packages/nextjs/services/store/permitV2ModalStore.ts
index ea6a634..5b7bcbd 100644
--- a/packages/nextjs/services/store/permitV2ModalStore.ts
+++ b/packages/nextjs/services/store/permitV2ModalStore.ts
@@ -1,5 +1,6 @@
import { useCallback, useMemo } from "react";
import { create } from "zustand";
+import { PermitV2 } from "~~/permits/permitV2";
export enum PermitV2Tab {
Create = "Create",
@@ -199,3 +200,29 @@ export const usePermitCreateOptionsAndActions = () => {
reset,
};
};
+
+export const usePermitSatisfiesRequirements = (permit: PermitV2) => {
+ const accessRequirements = usePermitModalStore(state => state.accessRequirements);
+ return useMemo(() => {
+ // Set to true if requirements includes some contracts
+ let contractsSatisfied = accessRequirements.contracts.length > 0;
+ for (const contract of accessRequirements.contracts) {
+ if (!permit.contracts.includes(contract)) {
+ contractsSatisfied = false;
+ }
+ }
+
+ // Set to true if requirements includes some projects
+ let projectsSatisfied = accessRequirements.projects.length > 0;
+ for (const project of accessRequirements.projects) {
+ if (!permit.projects.includes(project)) {
+ projectsSatisfied = false;
+ }
+ }
+
+ // Only need to satisfy one of the options to satisfy the requirements
+ if (contractsSatisfied || projectsSatisfied) return true;
+
+ return false;
+ }, [permit, accessRequirements]);
+};