Skip to content

Commit ff4dac6

Browse files
Merge pull request #1 from websurferdoteth/feat/add-edit-listing
Small Points from Ori
2 parents 6bda845 + c376785 commit ff4dac6

File tree

6 files changed

+316
-170
lines changed

6 files changed

+316
-170
lines changed

packages/nextjs/app/listing/[id]/page.tsx

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import { Suspense, useCallback, useEffect, useMemo, useState } from "react";
44
import Image from "next/image";
55
import { useRouter, useSearchParams } from "next/navigation";
66
import { useParams } from "next/navigation";
7-
import { Hex, decodeAbiParameters, formatUnits } from "viem";
7+
import { Hex, decodeAbiParameters, formatUnits, keccak256, stringToHex } from "viem";
88
import { useAccount } from "wagmi";
99
import { useMiniapp } from "~~/components/MiniappProvider";
1010
import FcAddressRating from "~~/components/marketplace/FcAddressRating";
1111
import { PayButton } from "~~/components/marketplace/PayButton";
1212
import { Address } from "~~/components/scaffold-eth/Address/Address";
1313
import { useScaffoldReadContract } from "~~/hooks/scaffold-eth/useScaffoldReadContract";
14+
import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth/useScaffoldWriteContract";
1415
import { resolveIpfsUrl } from "~~/services/ipfs/fetch";
1516

1617
const ListingDetailsPageInner = () => {
@@ -20,9 +21,12 @@ const ListingDetailsPageInner = () => {
2021
const [data, setData] = useState<any | null>(null);
2122
const [indexed, setIndexed] = useState<any | null>(null);
2223
const [lightboxOpen, setLightboxOpen] = useState<boolean>(false);
24+
const [deleting, setDeleting] = useState(false);
25+
const [showDeleteModal, setShowDeleteModal] = useState(false);
2326
const { composeCast, isMiniApp } = useMiniapp();
2427
const idNum = useMemo(() => (params?.id ? BigInt(params.id) : undefined), [params?.id]);
25-
useAccount();
28+
const { address: connectedAddress } = useAccount();
29+
const { writeContractAsync: writeMarketplace } = useScaffoldWriteContract({ contractName: "Marketplace" });
2630

2731
const { data: ptr } = useScaffoldReadContract({
2832
contractName: "Marketplace",
@@ -131,6 +135,48 @@ const ListingDetailsPageInner = () => {
131135
const active = data?.decoded?.active ?? indexed?.active ?? true;
132136
const seller = data?.pointer?.creator || indexed?.creator || undefined;
133137

138+
const isCreator = useMemo(() => {
139+
if (!connectedAddress || !seller) return false;
140+
return connectedAddress.toLowerCase() === seller.toLowerCase();
141+
}, [connectedAddress, seller]);
142+
143+
const handleDelete = useCallback(() => {
144+
setShowDeleteModal(true);
145+
}, []);
146+
147+
const confirmDelete = useCallback(async () => {
148+
if (idNum === undefined) {
149+
return;
150+
}
151+
152+
setShowDeleteModal(false);
153+
setDeleting(true);
154+
try {
155+
// Call the close action to deactivate the listing instead of deleting it
156+
const sigHash = keccak256(stringToHex("close(uint256,address,bool,address,bytes)"));
157+
const selector = `0x${sigHash.slice(2, 10)}` as `0x${string}`;
158+
const action = (selector + "0".repeat(64 - 8)) as `0x${string}`;
159+
160+
await writeMarketplace({
161+
functionName: "callAction",
162+
args: [idNum, action, "0x"],
163+
});
164+
router.push("/");
165+
} catch (error) {
166+
console.error("Error closing listing:", error);
167+
alert("Failed to close listing. Please try again.");
168+
setDeleting(false);
169+
}
170+
}, [idNum, writeMarketplace, router]);
171+
172+
const handleOpenEdit = useCallback(() => {
173+
const idStr = params?.id;
174+
if (!idStr) return;
175+
const base = `/listing/new?edit=${encodeURIComponent(idStr)}`;
176+
const from = indexed?.locationId;
177+
router.push(from ? `${base}&loc=${encodeURIComponent(from)}` : base);
178+
}, [params?.id, indexed?.locationId, router]);
179+
134180
const tags = useMemo(() => {
135181
const raw = (data?.tags ?? (indexed as any)?.tags) as unknown;
136182
if (!raw) return [] as string[];
@@ -251,7 +297,28 @@ const ListingDetailsPageInner = () => {
251297
Share
252298
</button>
253299
) : null}
254-
<div className={`badge ${active ? "badge-success" : ""} ml-auto`}>{active ? "Active" : "Sold"}</div>
300+
<div className="flex items-center gap-2 ml-auto">
301+
<div className={`badge ${active ? "badge-success" : ""}`}>{active ? "Active" : "Sold"}</div>
302+
{isCreator && (
303+
<div className="dropdown dropdown-end">
304+
<div tabIndex={0} role="button" className="btn btn-ghost btn-sm text-lg">
305+
306+
</div>
307+
<ul tabIndex={0} className="dropdown-content menu bg-base-100 rounded-box z-[1] w-32 p-2 shadow">
308+
<li>
309+
<button onClick={handleOpenEdit} className="w-full text-left">
310+
Edit
311+
</button>
312+
</li>
313+
<li>
314+
<button onClick={handleDelete} disabled={deleting} className="w-full text-left text-error">
315+
{deleting ? "Deleting..." : "Delete"}
316+
</button>
317+
</li>
318+
</ul>
319+
</div>
320+
)}
321+
</div>
255322
</div>
256323

257324
<div className="flex items-center mb-0">
@@ -372,6 +439,23 @@ const ListingDetailsPageInner = () => {
372439
</div>
373440
</div>
374441
) : null}
442+
443+
{showDeleteModal && (
444+
<div className="modal modal-open" onClick={() => setShowDeleteModal(false)}>
445+
<div className="modal-box" onClick={e => e.stopPropagation()}>
446+
<h3 className="font-bold text-lg">Delete Listing</h3>
447+
<p className="py-4">Are you sure you want to delete this listing? This action cannot be undone.</p>
448+
<div className="modal-action">
449+
<button className="btn btn-secondary" onClick={() => setShowDeleteModal(false)} disabled={deleting}>
450+
Cancel
451+
</button>
452+
<button className="btn btn-error" onClick={confirmDelete} disabled={deleting}>
453+
{deleting ? "Deleting..." : "Delete"}
454+
</button>
455+
</div>
456+
</div>
457+
</div>
458+
)}
375459
</div>
376460
);
377461
};

0 commit comments

Comments
 (0)