سرویس Pastebin (نسخه آزمایشی)
import "./style.css";
import "./queries.css";
import { useEffect, useState } from "react";
import supabase from "./supabase";
const CATEGORIES = [
{ name: "technology", color: "#3b82f6" },
{ name: "science", color: "#16a34a" },
{ name: "finance", color: "#ef4444" },
{ name: "society", color: "#eab308" },
{ name: "entertainment", color: "#db2777" },
{ name: "health", color: "#14b8a6" },
{ name: "history", color: "#f97316" },
{ name: "news", color: "#8b5cf6" }
];
function App() {
const [showForm, setShowForm] = useState(false);
const [facts, setFacts] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [currentCategory, setCurrentCategory] = useState("all");
useEffect(
function () {
async function getFacts() {
setIsLoading(true);
let query = supabase.from("facts").select("*");
if (currentCategory !== "all")
query = query.eq("category", currentCategory);
const { data: facts, error } = await query
.order("created_at", { ascending: false })
.limit(1000);
if (error) {
alert(`Error while fetching facts from Supabase. ${error.message}`);
} else {
setIsLoading(false);
setFacts(facts);
}
}
getFacts();
},
[currentCategory]
);
return (
<>
<Header showForm={showForm} setShowForm={setShowForm} />
{showForm ? (
<FactForm setFacts={setFacts} setShowForm={setShowForm} />
) : null}
<main className="main">
<CategoryFilter setCurrentCategory={setCurrentCategory} />
{isLoading ? (
<Loader />
) : (
<FactsList
facts={facts}
setFacts={setFacts}
currentCategory={currentCategory}
setCurrentCategory={setCurrentCategory}
/>
)}
</main>
</>
);
}
function Loader() {
return <p className="message">Loading...</p>;
}
function Header({ showForm, setShowForm }) {
const appTitle = "Today I Learned";
return (
<header className="header">
<div className="logo">
<img
src="./logo.png"
height="68"
width="68"
alt="Today I Learned logo"
/>
<h1 className="title">{appTitle}</h1>
</div>
<button
className="btn btn--large"
id="share-a-fact"
onClick={() => setShowForm((show) => !show)}>
{showForm ? "Close" : "Share a fact"}
</button>
</header>
);
}
function isValidUrl(string) {
let url;
try {
url = new URL(string);
} catch (_) {
console.error(
"Invalid source URL. Make sure to add `http://` or `https://` at the beginning"
);
return false;
}
return url.protocol === "http:" || url.protocol === "https:";
}
function FactForm({ setFacts, setShowForm }) {
const [text, setText] = useState("");
const [source, setSource] = useState("http://");
const [category, setCategory] = useState("");
const [isUploading, setIsUploading] = useState(false);
const textLength = text.length;
async function handleSubmit(e) {
e.preventDefault();
if (text && isValidUrl(source) && category && textLength <= 200) {
setIsUploading(true);
const { data: newFact, error } = await supabase
.from("facts")
.insert([{ text, source, category }])
.select();
if (!error) {
setIsUploading(false);
setFacts((facts) => [newFact[0], ...facts]);
setText("");
setSource("");
setCategory("");
setShowForm(false);
} else alert(`Error while submitting fact. ${error.message}`);
} else alert("Invalid inputs. Please check and try again.");
}
return (
<form className="form" onSubmit={handleSubmit}>
<input
type="text"
placeholder="Share a fact with the world..."
value={text}
onChange={(e) => setText(e.target.value)}
size={200}
disabled={isUploading}
/>
<span>{200 - textLength}</span>
<input
type="text"
placeholder="Trustworthy source..."
value={source}
onChange={(e) => setSource(e.target.value)}
disabled={isUploading}
/>
<select
value={category}
disabled={isUploading}
onChange={(e) => setCategory(e.target.value)}>
<option value="">Choose category</option>
{CATEGORIES.map((cat) => (
<option key={cat.name} value={cat.name}>
{cat.name.toUpperCase()}
</option>
))}
</select>
<button className="btn btn--large" disabled={isUploading}>
Post
</button>
</form>
);
}
function CategoryFilter({ setCurrentCategory }) {
return (
<aside>
<ul id="categories-list">
<li className="category">
<button
className="btn btn--all"
onClick={() => setCurrentCategory("all")}>
All
</button>
</li>
{CATEGORIES.map((cat) => (
<li className="category" key={cat.name}>
<button
className="btn btn--category"
style={{ backgroundColor: `${cat.color}` }}
onClick={() => setCurrentCategory(cat.name)}>
{cat.name}
</button>
</li>
))}
</ul>
</aside>
);
}
function FactsList({ facts, setFacts, currentCategory, setCurrentCategory }) {
if (facts.length === 0) {
return (
<p className="message">
No facts for this category, yet. Create a new one now.
</p>
);
}
return (
<section>
<p style={{ marginBottom: "16px" }}>
There are now {facts.length} fact{facts.length === 1 ? "" : "s"} in the
database
{currentCategory !== "all"
? ` in ${currentCategory.toUpperCase()} category.`
: "."}
</p>
<ul className="facts-list">
{facts.map((fact) => (
<Fact
key={fact.id}
fact={fact}
setCurrentCategory={setCurrentCategory}
setFacts={setFacts}
/>
))}
</ul>
</section>
);
}
function Fact({ fact, setCurrentCategory, setFacts }) {
const [isUpdating, setIsUpdating] = useState(false);
async function handleVote(vote) {
if (vote) {
setIsUpdating(true);
const { data: updatedFact, error } = await supabase
.from("facts")
.update({ [vote]: fact[vote] + 1 })
.eq("id", fact.id)
.select();
if (!error) {
setIsUpdating(false);
setFacts((facts) =>
facts.map((f) => (f.id === fact.id ? updatedFact[0] : f))
);
}
}
}
return (
<li className="fact">
<p>
{fact.text}
<a
className="source"
href={fact.source}
target="_blank"
rel="noreferrer">
(Source)
</a>
</p>
<span
className="tag"
style={{
backgroundColor: CATEGORIES.find((cat) => cat.name === fact.category)
.color,
}}
onClick={() => setCurrentCategory(fact.category)}>
{fact.category}
</span>
<div className="vote-buttons">
<button
onClick={() => handleVote("vote_interesting")}
disabled={isUpdating}>
👍 {fact.vote_interesting}
</button>
<button
onClick={() => handleVote("vote_mindBlowing")}
disabled={isUpdating}>
🤯 {fact.vote_mindBlowing}
</button>
<button onClick={() => handleVote("vote_false")} disabled={isUpdating}>
⛔ {fact.vote_false}
</button>
</div>
</li>
);
}
export default App;