Skip to content

Commit

Permalink
Gear Matic
Browse files Browse the repository at this point in the history
  • Loading branch information
MFathinHalim committed Aug 2, 2023
1 parent 972d3b9 commit 6162def
Show file tree
Hide file tree
Showing 6 changed files with 732 additions and 81 deletions.
2 changes: 1 addition & 1 deletion client/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Menfess</title>
</head>
<body class="bg-dark text-white">
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
54 changes: 20 additions & 34 deletions client/src/Router.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,47 +10,33 @@ import ErrorBoundary from "./ErrorBoundary"
import Post from "./pages/Post"
import Posts from "./pages/Posts"

const createPostsLoader = type => () => axios.get(`${process.env.REACT_APP_API_BASE_URL}/${type}/posts?from=0&to=20`)

const createSearchLoader = type => ({ request }) => {
const url = new URL(request.url)
const query = url.searchParams.get("q")
return axios.get(`${process.env.REACT_APP_API_BASE_URL}/${type}/posts?search=${query}`)
}

const createPostLoader = type => ({ params: { id } }) => axios.get(`${process.env.REACT_APP_API_BASE_URL}/${type}/post/${id}`)

const shouldRevalidate = ({ currentParams: { id: currentId = 1 }, nextParams: { id: nextId = 1 } }) => currentId !== nextId

const router = createBrowserRouter(
createRoutesFromElements(
<Route element={<App />} errorElement={<ErrorBoundary />}>
<Route path="/" shouldRevalidate={shouldRevalidate} loader={({ params: { id = 1 } }) => axios.get(`${process.env.REACT_APP_API_BASE_URL}/main/posts?from=${(id - 1) * 10}&to=${id * 10}`)} element={<Posts type="main" />}>
<Route path="page">
<Route path=":id" />
</Route>
</Route>
<Route path="search" loader={({ request }) => {
const url = new URL(request.url)
const query = url.searchParams.get("q")
return axios.get(`${process.env.REACT_APP_API_BASE_URL}/main/posts?search=${query}`)
}} element={<Posts type="main" />} />
<Route path="post/:id" shouldRevalidate={shouldRevalidate} loader={({ params: { id } }) => axios.get(`${process.env.REACT_APP_API_BASE_URL}/main/post/${id}`)} element={<Post type="main" />}/>
<Route index shouldRevalidate={shouldRevalidate} loader={createPostsLoader("main")} element={<Posts type="main" />} />
<Route path="search" loader={createSearchLoader("main")} element={<Posts type="main" />} />
<Route path="post/:id" shouldRevalidate={shouldRevalidate} loader={createPostLoader("main")} element={<Post type="main" />}/>
<Route path="memes">
<Route path="" shouldRevalidate={shouldRevalidate} loader={({ params: { id = 1 } }) => axios.get(`${process.env.REACT_APP_API_BASE_URL}/memes/posts?from=${(id - 1) * 10}&to=${id * 10}`)} element={<Posts type="memes" />}>
<Route path="page">
<Route path=":id" />
</Route>
</Route>
<Route path="search" loader={({ request }) => {
const url = new URL(request.url)
const query = url.searchParams.get("q")
return axios.get(`${process.env.REACT_APP_API_BASE_URL}/memes/posts?search=${query}`)
}} element={<Posts type="memes" />} />
<Route path="post/:id" shouldRevalidate={shouldRevalidate} loader={({ params: { id } }) => axios.get(`${process.env.REACT_APP_API_BASE_URL}/memes/post/${id}`)} element={<Post type="memes" />}/>
<Route index shouldRevalidate={shouldRevalidate} loader={createPostsLoader("memes")} element={<Posts type="memes" />} />
<Route path="search" loader={createSearchLoader("memes")} element={<Posts type="memes" />} />
<Route path="post/:id" shouldRevalidate={shouldRevalidate} loader={createPostLoader("memes")} element={<Post type="memes" />}/>
</Route>
<Route path="anime">
<Route path="" shouldRevalidate={shouldRevalidate} loader={({ params: { id = 1 } }) => axios.get(`${process.env.REACT_APP_API_BASE_URL}/anime/posts?from=${(id - 1) * 10}&to=${id * 10}`)} element={<Posts type="anime" />}>
<Route path="page">
<Route path=":id" />
</Route>
</Route>
<Route path="search" loader={({ request }) => {
const url = new URL(request.url)
const query = url.searchParams.get("q")
return axios.get(`${process.env.REACT_APP_API_BASE_URL}/anime/posts?search=${query}`)
}} element={<Posts type="anime" />} />
<Route path="post/:id" shouldRevalidate={shouldRevalidate} loader={({ params: { id } }) => axios.get(`${process.env.REACT_APP_API_BASE_URL}/anime/post/${id}`)} element={<Post type="anime" />}/>
<Route index shouldRevalidate={shouldRevalidate} loader={createPostsLoader("anime")} element={<Posts type="anime" />} />
<Route path="search" loader={createSearchLoader("anime")} element={<Posts type="anime" />} />
<Route path="post/:id" shouldRevalidate={shouldRevalidate} loader={createPostLoader("anime")} element={<Post type="anime" />}/>
</Route>
</Route>
)
Expand All @@ -60,4 +46,4 @@ function Router() {
return <RouterProvider router={router} />
}

export default Router
export default Router
83 changes: 39 additions & 44 deletions client/src/pages/Posts.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,43 @@ import React, { useEffect, useState } from "react";
import { Button } from "react-bootstrap";
import { useLoaderData, useParams, Link } from "react-router-dom";
import { Helmet } from "react-helmet";
import axios from "axios";
import Post from "../components/Post.js";
import { socket } from "../socket.js";

function Pagination({ id, type, postTotal }) {
if (!postTotal) return;
return (
<div className="m-1">
{id > 1 ? (
<Button
className="m-1"
as={Link}
to={`${type != "main" ? `/${type}` : ""}/page/${id - 1}`}>
{"<"}
</Button>
) : (
""
)}
<Button as={Link} disabled className="m-1">
{id}
</Button>
{id < Math.ceil(postTotal / 10) ? (
<Button
className="m-1"
as={Link}
to={`${type != "main" ? `/${type}` : ""}/page/${id + 1}`}>
{">"}
</Button>
) : (
""
)}
</div>
);
}

function Posts({ type }) {
const id = parseInt(useParams().id) || 1;
const { data } = useLoaderData();
const [posts, setPosts] = useState(data.posts);
const [loading, setLoading] = useState(false);
const [tamat, setTamat] = useState(false)

const handleMentok = async () => {
if (loading || tamat) return;
setLoading(true);
try {
const response = await axios.get(`${process.env.REACT_APP_API_BASE_URL}/${type}/posts?from=${posts.length}&to=${posts.length + 10}`);
console.log(response); // Check the API response
if (response.data.posts.length === 0) setTamat(true)
setPosts((prevPosts) => [...prevPosts, ...response.data.posts]);
setLoading(false);
} catch (e) {
console.error(e);
setLoading(false);
}
};

document.title = `Menfess | ${[
type.split("")[0].toUpperCase(),
...type.split("").splice(1),
].join("")} | ${data.postTotal ? `Page ${id}` : "Search"}`;
const handleScroll = () => {
const sentinel = document.querySelector("#sentinel");
if (sentinel && !loading) {
const distanceFromBottom = sentinel.getBoundingClientRect().bottom - window.innerHeight;
if (distanceFromBottom < 1) {
handleMentok();
}
}
};

useEffect(() => {
const handleNewPost = (typeParam, post) => {
if (id != 1 || !data.postTotal) return;
if (type != typeParam) return;
setPosts([post, ...posts.slice(0, 9)]);
};
Expand All @@ -66,33 +56,38 @@ function Posts({ type }) {

socket.on("newPost", handleNewPost);
socket.on("addLike", handleAddLike);
// Add event listener for scrolling
document.addEventListener("scroll", handleScroll);

return () => {
socket.off("newPost", handleNewPost);
socket.off("addLike", handleAddLike);
};
});
document.removeEventListener("scroll", handleScroll);
}
}, [posts, loading, type]);

useEffect(() => {
setPosts(data.posts);
window.scrollTo({ top: 0 });
}, [data]);


return (
<>
<Helmet>
<title>{`Menfess | ${[type.split("")[0].toUpperCase(), ...type.split("").splice(1)].join("")} | ${data.postTotal ? `Page ${id}` : "Search"}`}</title>
<title>{`Menfess | ${[type.charAt(0).toUpperCase(), ...type.slice(1)].join("")}`}</title>
</Helmet>

<div id="posts">
{posts.map((post) => (
<Post post={post} type={type} key={post.noteId} />
))}
</div>
<div className="d-flex justify-content-center align-items-center ">
<Pagination id={id} type={type} postTotal={data.postTotal} />
{/* Add the sentinel element here */}
<div id="sentinel" style={{ height: "1px" }} />
<p>{loading ? "Loading..." : tamat ? "Dah mentok bang" : ""}</p>
</div>
</>
);
)
}

export default Posts;
2 changes: 1 addition & 1 deletion controllers/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class PostController {
} else if (from && to) {
const posts = this.#data.slice(from, to)
if(!posts || posts === []) return res.status(404).json({ msg: "postingan tidak ditemukan" })
res.json({ posts, postTotal: this.#data.length })
res.json({ posts })

// Kalo ga ya return aja semua
} else {
Expand Down
Loading

0 comments on commit 6162def

Please sign in to comment.