Skip to content

Commit

Permalink
Create all permits list
Browse files Browse the repository at this point in the history
  • Loading branch information
architect-dev committed Nov 27, 2024
1 parent 74052b8 commit 1295e88
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 7 deletions.
9 changes: 5 additions & 4 deletions packages/nextjs/components/PermitModal/Create/Create.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -131,19 +132,19 @@ export const PermitV2ModalCreate = () => {
<>
{/* Type */}
<div className="flex flex-row items-center justify-start gap-4">
<div className="text-sm font-bold">Type:</div>
<div className="text-sm font-bold">Purpose:</div>
<button
className={`btn btn-sm ${type === PermitV2CreateType.Using ? "btn-primary" : "btn-ghost"}`}
onClick={() => setType(PermitV2CreateType.Using)}
>
For Using
For Using <ArrowDownTrayIcon className="w-4 h-4" />
</button>
/
<button
className={`btn btn-sm ${type === PermitV2CreateType.Sharing ? "btn-primary" : "btn-ghost"}`}
onClick={() => setType(PermitV2CreateType.Sharing)}
>
For Sharing
For Sharing <ArrowUpTrayIcon className="w-4 h-4 rotate-90" />
</button>
</div>

Expand Down
117 changes: 114 additions & 3 deletions packages/nextjs/components/PermitModal/Select/Select.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<tr className={`${className}`}>
<td className="p-2 text-sm">{permit.name != null && permit.name.length > 0 ? permit.name : "Unnamed Permit"}</td>
<td className="p-2 text-sm place-items-center">
{permit.type === "self" && <ArrowDownTrayIcon className="w-4 h-4" />}
{permit.type === "sharing" && <ArrowUpTrayIcon className="w-4 h-4 rotate-90" />}
{permit.type === "recipient" && <ArrowDownTrayIcon className="w-4 h-4 rotate-90" />}
</td>
<td className="p-2 text-sm place-items-center text-center">
{permit.expiration > timestamp && timeUntilExpiration(permit.expiration)}
{permit.expiration <= timestamp && <div className="w-[12px] h-[12px] rounded-full bg-bg-surface-error" />}
</td>
<td className="p-2 text-sm place-items-center">
<div
className={`w-[12px] h-[12px] rounded-full ${satisfies ? "bg-bg-surface-success" : "bg-bg-surface-error"}`}
/>
</td>
<td className="p-2">
<div className="flex flex-row items-center justify-end gap-1">{children}</div>
</td>
</tr>
);
};

const SelectedPermitRow: React.FC<{ permit: PermitV2 }> = ({ permit }) => {
return (
<PermitRow permit={permit} className="bg-base-200">
<button className="btn btn-sm btn-secondary btn-ghost">Open</button>
</PermitRow>
);
};

const SelectPermitRow: React.FC<{ permit: PermitV2 }> = ({ permit }) => {
return (
<PermitRow permit={permit}>
<button className="btn btn-sm btn-secondary btn-ghost">Open</button>
<button className="btn btn-sm btn-primary">Use</button>
</PermitRow>
);
};

export const PermitV2ModalSelect = () => {
// List of permits
Expand All @@ -7,8 +85,41 @@ export const PermitV2ModalSelect = () => {
// Expand opens sub page with action buttons <back, use>

const permits = useFhenixAllPermits();
const activePermitHash = useFhenixActivePermitHash();

return (
<>
<table className="table">
<thead>
<tr>
<th className="p-2">Name</th>
<th className="p-2 text-center">Type</th>
<th className="p-2 text-center">Exp.</th>
<th className="p-2 text-center">Access</th>
<th className="p-2 text-right">Actions</th>
</tr>
</thead>
<tbody>
{/* <div className="flex w-full flex-col items-start justify-start">
<div className="text-sm font-bold">Selected:</div> */}

console.log({ permits });
<div className="text-xs font-bold mt-4">Selected:</div>
{activePermitHash != null && permits[activePermitHash] != null && (
<SelectedPermitRow permit={permits[activePermitHash]} />
)}
{/* </div>
<div className="flex flex-col w-full gap-0.5">
<div className="text-sm font-bold">Available:</div> */}

return <>List of permits</>;
<div className="text-xs font-bold mt-4">Available:</div>
{Object.entries(permits)
.filter(([hash]) => hash !== activePermitHash)
.map(([hash, permit]) => {
return <SelectPermitRow key={hash} permit={permit} />;
})}
{/* </div> */}
</tbody>
</table>
</>
);
};
10 changes: 10 additions & 0 deletions packages/nextjs/permits/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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" });

Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/permits/permitV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
27 changes: 27 additions & 0 deletions packages/nextjs/services/store/permitV2ModalStore.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useCallback, useMemo } from "react";
import { create } from "zustand";
import { PermitV2 } from "~~/permits/permitV2";

export enum PermitV2Tab {
Create = "Create",
Expand Down Expand Up @@ -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]);
};

0 comments on commit 1295e88

Please sign in to comment.