Skip to content

Commit 290c822

Browse files
committed
Improved UI
1 parent 09bcc91 commit 290c822

File tree

14 files changed

+1037
-219
lines changed

14 files changed

+1037
-219
lines changed

packages/foundry/contracts/BasedShop.sol

Lines changed: 2 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ contract BasedShop {
3232
uint256 price,
3333
uint256 date
3434
);
35+
3536
event ArticlePriceUpdated(
3637
uint256 indexed articleId, uint256 oldPrice, uint256 newPrice
3738
);
@@ -40,12 +41,7 @@ contract BasedShop {
4041
uint256 indexed articleId, uint256 oldAmount, uint256 newAmount
4142
);
4243
event ArticleDeleted(uint256 indexed articleId, uint256 date);
43-
event ArticleLiked(
44-
uint256 indexed articleID, address indexed user, uint256 date
45-
);
46-
event ArticleUnliked(
47-
uint256 indexed articleID, address indexed user, uint256 date
48-
);
44+
4945
event ArticleCommented(
5046
uint256 indexed articleID,
5147
address indexed user,
@@ -88,11 +84,6 @@ contract BasedShop {
8884
mapping(uint256 => uint256) public articleAmounts;
8985
mapping(uint256 => mapping(address => bool)) public articleBuyers;
9086

91-
// Likes
92-
mapping(uint256 article => uint256 likes) public articleToLikes;
93-
mapping(address user => mapping(uint256 article => bool liked)) public
94-
userToArticleLikes;
95-
9687
// Comments
9788
mapping(uint256 articleId => Comment[]) public articleToComments;
9889
mapping(uint256 articleId => mapping(uint256 commentId => address user))
@@ -209,36 +200,6 @@ contract BasedShop {
209200
emit ArticleDeleted(_articleId, block.timestamp);
210201
}
211202

212-
function likeArticle(
213-
uint256 _articleId
214-
) public {
215-
require(
216-
articleBuyers[_articleId][msg.sender],
217-
"You must buy the article to like it"
218-
);
219-
_requireArticleExists(_articleId);
220-
require(
221-
!userToArticleLikes[msg.sender][_articleId],
222-
"You have already liked this article"
223-
);
224-
userToArticleLikes[msg.sender][_articleId] = true;
225-
articleToLikes[_articleId]++;
226-
emit ArticleLiked(_articleId, msg.sender, block.timestamp);
227-
}
228-
229-
function unlikeArticle(
230-
uint256 _articleId
231-
) public {
232-
_requireArticleExists(_articleId);
233-
require(
234-
userToArticleLikes[msg.sender][_articleId],
235-
"You have not liked this article yet"
236-
);
237-
userToArticleLikes[msg.sender][_articleId] = false;
238-
articleToLikes[_articleId]--;
239-
emit ArticleUnliked(_articleId, msg.sender, block.timestamp);
240-
}
241-
242203
function commentOnArticle(uint256 _articleId, string memory _text) public {
243204
require(
244205
articleBuyers[_articleId][msg.sender],

packages/nextjs/app/create/Create.tsx

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@ import { InputBase } from "~~/components/scaffold-eth";
1212
const Create = ({ onClose }: { onClose: any }) => {
1313
const [name, setName] = useState("");
1414
const [description, setDescription] = useState("");
15-
const [externalUrl, setExternalUrl] = useState("");
1615
const [price, setPrice] = useState("");
1716
const [amount, setAmount] = useState("");
18-
const [urlError, setUrlError] = useState("");
1917
const [yourJSON, setYourJSON] = useState<object>({});
2018
const [uploadedImageIpfsPath, setUploadedImageIpfsPath] = useState(""); // NEW: For image IPFS path
2119

@@ -31,29 +29,15 @@ const Create = ({ onClose }: { onClose: any }) => {
3129
window.location.reload();
3230
};
3331

34-
const validateUrl = (url: string) => {
35-
const pattern = /^(https?:\/\/)/;
36-
return pattern.test(url);
37-
};
38-
39-
const handleUrlChange = (url: string) => {
40-
setExternalUrl(url);
41-
if (!validateUrl(url)) {
42-
setUrlError("URL must start with http:// or https://");
43-
} else {
44-
setUrlError("");
45-
}
46-
};
47-
4832
useEffect(() => {
4933
const generateTokenURIString = () => {
5034
const fullImageUrl = `https://ipfs.io/ipfs/${uploadedImageIpfsPath}`;
51-
const tokenURI = generateTokenURI(name, description, fullImageUrl, externalUrl);
35+
const tokenURI = generateTokenURI(name, description, fullImageUrl);
5236
setYourJSON(JSON.parse(atob(tokenURI.split(",")[1])));
5337
};
5438

5539
generateTokenURIString();
56-
}, [name, externalUrl, description, uploadedImageIpfsPath]);
40+
}, [name, description, uploadedImageIpfsPath]);
5741

5842
return (
5943
<>
@@ -75,8 +59,6 @@ const Create = ({ onClose }: { onClose: any }) => {
7559
<div className="flex flex-col gap-3 text-left flex-shrink-0 w-full">
7660
<InputBase placeholder="Article name" value={name} onChange={setName} />
7761
<TextInput description={description} setDescription={setDescription} />
78-
<InputBase placeholder="URL for your article (https://)" value={externalUrl} onChange={handleUrlChange} />
79-
{externalUrl && urlError && <span className="text-red-500 text-sm">{urlError}</span>}
8062
<div className="flex flex-row gap-3">
8163
<div className="w-1/2">
8264
<InputBase placeholder="Price in ETH" value={price} onChange={setPrice} />

packages/nextjs/app/create/_components/generateTokenURI.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
const generateTokenURI = (name: string, description: string, image: string, externalUrl: string) => {
1+
const generateTokenURI = (name: string, description: string, image: string) => {
22
// Base metadata object
33
const metadata: any = {
44
name,
55
description,
66
image,
7-
external_url: externalUrl,
87
};
98

109
// Convert the metadata object to a JSON string

packages/nextjs/app/explore/Explore.tsx

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ export const Explore = () => {
2323
const [loading, setLoading] = useState(true);
2424
const [loadingMore, setLoadingMore] = useState(false);
2525
const [page, setPage] = useState(0);
26-
const [activeTab, setActiveTab] = useState("All");
27-
28-
const handleTabClick = (tab: any) => {
29-
setActiveTab(tab);
30-
};
3126

3227
const observer = useRef<IntersectionObserver | null>(null);
3328

@@ -122,17 +117,6 @@ export const Explore = () => {
122117

123118
return (
124119
<div className="flex flex-col items-center justify-center">
125-
<div className="tabs-bar ">
126-
<button className={`tab ${activeTab === "All" ? "active" : ""}`} onClick={() => handleTabClick("All")}>
127-
All
128-
</button>
129-
<button
130-
className={`tab text-red-600 ${activeTab === "Following" ? "active" : ""}`}
131-
onClick={() => handleTabClick("Following")}
132-
>
133-
Following
134-
</button>
135-
</div>
136120
<NewsFeed articles={articles} />
137121
<div ref={lastPostElementRef}></div>
138122
{loadingMore && <LoadingBars />}

packages/nextjs/app/profile/[address]/page.tsx

Lines changed: 9 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
"use client";
22

3-
import { useCallback, useEffect, useRef, useState } from "react";
3+
import { useState } from "react";
44
import { usePathname } from "next/navigation";
5-
import { ErrorComponent } from "../../../components/punk-society/ErrorComponent";
6-
import { LoadingBars } from "../../../components/punk-society/LoadingBars";
7-
import { NewsFeed } from "../../../components/punk-society/NewsFeed";
5+
import BookmarkedArticles from "../_components/BookmarkedArticles";
6+
import BoughtArticles from "../_components/BoughtArticles";
7+
import ListedArticles from "../_components/ListedArticles";
88
import ProfileInfo from "../_components/ProfileInfo";
99
import { NextPage } from "next";
10-
import { useScaffoldEventHistory } from "~~/hooks/scaffold-eth";
11-
import { notification } from "~~/utils/scaffold-eth";
12-
import { getMetadataFromIPFS } from "~~/utils/simpleNFT/ipfs-fetch";
1310
import { NFTMetaData } from "~~/utils/simpleNFT/nftsMetadata";
1411

1512
export interface Post extends Partial<NFTMetaData> {
@@ -24,120 +21,20 @@ export interface Post extends Partial<NFTMetaData> {
2421
}
2522

2623
const ProfilePage: NextPage = () => {
27-
const [articles, setArticles] = useState<Post[]>([]);
28-
const [loading, setLoading] = useState(true);
29-
const [loadingMore, setLoadingMore] = useState(true);
30-
const [page, setPage] = useState(1); // Start from page 1 to get the last post first
3124
const [activeTab, setActiveTab] = useState("Listed");
3225

3326
const handleTabClick = (tab: any) => {
3427
setActiveTab(tab);
3528
};
3629

37-
const observer = useRef<IntersectionObserver | null>(null);
38-
3930
const pathname = usePathname();
4031
const address = pathname.split("/").pop();
4132

42-
const {
43-
data: createEvents,
44-
// isLoading: createIsLoadingEvents,
45-
error: createErrorReadingEvents,
46-
} = useScaffoldEventHistory({
47-
contractName: "BasedShop",
48-
eventName: "ArticleCreated",
49-
fromBlock: 0n,
50-
watch: true,
51-
});
52-
53-
const fetchArticles = useCallback(
54-
async (page: number) => {
55-
if (!createEvents) return;
56-
57-
setLoadingMore(true);
58-
try {
59-
// Calculate the start and end indices for the current page
60-
const start = (page - 1) * 8;
61-
const end = page * 8;
62-
const eventsToFetch = createEvents.slice(start, end);
63-
64-
const articlesUpdate: Post[] = [];
65-
66-
for (const event of eventsToFetch) {
67-
try {
68-
const { args } = event;
69-
const user = args?.user;
70-
const tokenURI = args?.tokenURI;
71-
const date = args?.date;
72-
const price = args?.price;
73-
const amount = args?.amount;
74-
75-
if (args?.user !== address) continue;
76-
if (!tokenURI) continue;
77-
78-
const ipfsHash = tokenURI.replace("https://ipfs.io/ipfs/", "");
79-
const nftMetadata: NFTMetaData = await getMetadataFromIPFS(ipfsHash);
80-
81-
// Temporary fix for V1
82-
// Check if the image attribute is valid and does not point to [object Object]
83-
if (nftMetadata.image === "https://ipfs.io/ipfs/[object Object]") {
84-
console.warn(`Skipping post with invalid image URL: ${nftMetadata.image}`);
85-
continue;
86-
}
87-
88-
articlesUpdate.push({
89-
listingId: undefined,
90-
uri: tokenURI,
91-
user: user || "",
92-
date: date?.toString() || "",
93-
price: price?.toString() || "",
94-
amount: amount?.toString() || "",
95-
...nftMetadata,
96-
});
97-
} catch (e) {
98-
notification.error("Error fetching articles");
99-
console.error(e);
100-
}
101-
}
102-
103-
setArticles(prevArticles => [...prevArticles, ...articlesUpdate]);
104-
} catch (error) {
105-
notification.error("Failed to load articles");
106-
} finally {
107-
setLoadingMore(false);
108-
}
109-
},
110-
[createEvents, address],
111-
);
112-
113-
useEffect(() => {
114-
setLoading(true);
115-
fetchArticles(page).finally(() => setLoading(false));
116-
}, [page, fetchArticles]);
117-
118-
const lastPostElementRef = useCallback(
119-
(node: any) => {
120-
if (loadingMore) return;
121-
if (observer.current) observer.current.disconnect();
122-
observer.current = new IntersectionObserver(entries => {
123-
if (entries[0].isIntersecting) {
124-
setPage(prevPage => prevPage + 1);
125-
}
126-
});
127-
if (node) observer.current.observe(node);
128-
},
129-
[loadingMore],
130-
);
131-
13233
// Ensure the address is available before rendering the component
13334
if (!address) {
13435
return <p>Inexistent address, try again...</p>;
13536
}
13637

137-
if (createErrorReadingEvents) {
138-
return <ErrorComponent message={createErrorReadingEvents?.message || "Error loading events"} />;
139-
}
140-
14138
return (
14239
<>
14340
<ProfileInfo address={address} />
@@ -146,16 +43,10 @@ const ProfilePage: NextPage = () => {
14643
<button className={`tab ${activeTab === "Listed" ? "active" : ""}`} onClick={() => handleTabClick("Listed")}>
14744
Listed
14845
</button>
149-
<button
150-
className={`tab text-red-600 ${activeTab === "Bought" ? "active" : ""}`}
151-
onClick={() => handleTabClick("Bought")}
152-
>
46+
<button className={`tab ${activeTab === "Bought" ? "active" : ""}`} onClick={() => handleTabClick("Bought")}>
15347
Bought
15448
</button>
155-
<button
156-
className={`tab text-red-600 ${activeTab === "Saved" ? "active" : ""}`}
157-
onClick={() => handleTabClick("Saved")}
158-
>
49+
<button className={`tab 0 ${activeTab === "Saved" ? "active" : ""}`} onClick={() => handleTabClick("Saved")}>
15950
Saved
16051
</button>
16152
<button
@@ -171,15 +62,9 @@ const ProfilePage: NextPage = () => {
17162
Revenue
17263
</button>
17364
</div>
174-
{loading && page === 1 ? (
175-
<LoadingBars />
176-
) : articles.length === 0 ? (
177-
<p>This user has no articles</p>
178-
) : (
179-
<NewsFeed articles={articles} />
180-
)}
181-
<div ref={lastPostElementRef}></div>
182-
{page !== 1 && loadingMore && <LoadingBars />}
65+
{activeTab === "Listed" && <ListedArticles />}
66+
{activeTab === "Bought" && <BoughtArticles />}
67+
{activeTab === "Saved" && <BookmarkedArticles />}
18368
</div>
18469
</>
18570
);

0 commit comments

Comments
 (0)