-
-
Notifications
You must be signed in to change notification settings - Fork 64
Pagination for home page and category page #158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,69 @@ | ||||||
# Import required modules and functions | ||||||
from modules import ( | ||||||
getHomeFeedData, | ||||||
Blueprint, | ||||||
make_response, | ||||||
getSlugFromPostTitle, | ||||||
url_for, | ||||||
getProfilePicture, | ||||||
request, | ||||||
) | ||||||
|
||||||
returnHomeFeedDataBlueprint = Blueprint("returnHomeFeedData", __name__) | ||||||
|
||||||
|
||||||
@returnHomeFeedDataBlueprint.route("/api/v1/homeFeedData") | ||||||
def homeFeedData(): | ||||||
""" | ||||||
API endpoint to fetch home feed data. | ||||||
Accepts query parameters: category, by, sort, limit, offset | ||||||
""" | ||||||
|
||||||
# Get query parameters with default values if not provided | ||||||
category = request.args.get("category", type=str, default="all") | ||||||
by = request.args.get("by", type=str, default="hot") | ||||||
sort = request.args.get("sort", type=str, default="desc") | ||||||
limit = request.args.get("limit", type=int, default="4") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default value for 'limit' is set as a string instead of an integer; change it to default=4 to ensure correct type conversion.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||
offset = request.args.get("offset", type=int, default="0") | ||||||
|
||||||
try: | ||||||
# Fetch raw home feed data based on parameters | ||||||
rawHomeFeedData = getHomeFeedData( | ||||||
category=category, by=by, sort=sort, limit=limit, offset=offset | ||||||
) | ||||||
|
||||||
listOfHomeFeedData = [] | ||||||
|
||||||
# Process each post's raw data | ||||||
for data in rawHomeFeedData: | ||||||
homeFeedObj = {} | ||||||
homeFeedObj["id"] = data[0] # Post ID | ||||||
homeFeedObj["title"] = data[1] # Post Title | ||||||
homeFeedObj["content"] = data[2] # Post Content | ||||||
homeFeedObj["author"] = data[3] # Author Name | ||||||
homeFeedObj["timeStamp"] = data[4] # Timestamp | ||||||
homeFeedObj["category"] = data[5] # Post Category | ||||||
homeFeedObj["urlID"] = data[6] # URL ID | ||||||
|
||||||
# Generate URL for the post's banner image | ||||||
homeFeedObj["bannerImgSrc"] = url_for( | ||||||
"returnPostBanner.returnPostBanner", postID=data[0] | ||||||
) | ||||||
|
||||||
# Get the author's profile picture | ||||||
homeFeedObj["authorProfile"] = getProfilePicture(data[3]) | ||||||
|
||||||
# Generate URL for viewing the full post | ||||||
homeFeedObj["postLink"] = url_for( | ||||||
"post.post", slug=getSlugFromPostTitle(data[1]), urlID=data[6] | ||||||
) | ||||||
|
||||||
# Add the processed post data to the list | ||||||
listOfHomeFeedData.append(homeFeedObj) | ||||||
|
||||||
# Return the list as a JSON payload with status 200 OK | ||||||
return make_response({"payload": listOfHomeFeedData}, 200) | ||||||
|
||||||
except Exception as e: | ||||||
# In case of any error, return a JSON error response with status 500 Internal Server Error | ||||||
return make_response({"error": f"{e}"}, 500) |
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,122 @@ | ||||||||
// Initialize variable for postCardMacro and postContainer | ||||||||
let postCardMacro; | ||||||||
let postContainer; | ||||||||
|
||||||||
// Limit | ||||||||
let limit = 6; | ||||||||
// Offset | ||||||||
let offset = 0; | ||||||||
|
||||||||
// Id of div elements and hidden input values | ||||||||
let homeSpinner = document.getElementById("homeSpinner"); | ||||||||
let loadMoreSpinner = document.getElementById("loadMoreSpinner"); | ||||||||
let currentCategory = document.getElementById("currentCategoryText"); | ||||||||
let loadMoreButtonDiv = document.getElementById("loadMoreButton"); | ||||||||
const sortBy = document.getElementById("currentSortText"); | ||||||||
const orderby = document.getElementById("currentOrderText"); | ||||||||
|
||||||||
// Categories and associated icons | ||||||||
const categoryList = { | ||||||||
'Games': '<a href="/category/Games"><i class="ti ti-device-gamepad text-rose-500 hover:text-rose-600 duration-150"></i></a>', | ||||||||
'History': '<a href="/category/History"><i class="ti ti-books text-sky-500 hover:text-sky-600 duration-150"></i></a>', | ||||||||
'Science': '<a href="/category/Science"><i class="ti ti-square-root-2 text-emerald-500 hover:text-emerald-600 duration-150"></i></a>', | ||||||||
'Code': '<a href="/category/Code"><i class="ti ti-code text-indigo-500 hover:text-indigo-600 duration-150"></i></a>', | ||||||||
'Technology': '<a href="/category/Technology"><i class="ti ti-cpu text-slate-500 hover:text-slate-600 duration-150"></i></a>', | ||||||||
'Education': '<a href="/category/Education"><i class="ti ti-school text-blue-500 hover:text-blue-600 duration-150"></i></a>', | ||||||||
'Sports': '<a href="/category/Sports"><i class="ti ti-shirt-sport text-cyan-500 hover:text-cyan-600 duration-150"></i></a>', | ||||||||
'Foods': '<a href="/category/Foods"><i class="ti ti-meat text-lime-500 hover:text-lime-600 duration-150"></i></a>', | ||||||||
'Health': '<a href="/category/Health"><i class="ti ti-heartbeat text-red-500 hover:text-red-600 duration-150"></i></a>', | ||||||||
'Apps': '<a href="/category/Apps"><i class="ti ti-apps text-pink-500 hover:text-pink-600 duration-150"></i></a>', | ||||||||
'Movies': '<a href="/category/Movies"><i class="ti ti-movie text-teal-500 hover:text-teal-600 duration-150"></i></a>', | ||||||||
'Series': '<a href="/category/Series"><i class="ti ti-player-play text-yellow-500 hover:text-yellow-600 duration-150"></i></a>', | ||||||||
'Travel': '<a href="/category/Travel"><i class="ti ti-plane text-zinc-500 hover:text-zinc-600 duration-150"></i></a>', | ||||||||
'Books': '<a href="/category/Books"><i class="ti ti-book text-violet-500 hover:text-violet-600 duration-150"></i></a>', | ||||||||
'Music': '<a href="/category/Music"><i class="ti ti-music text-orange-500 hover:text-orange-600 duration-150"></i></a>', | ||||||||
'Nature': '<a href="/category/Nature"><i class="ti ti-trees text-emerald-500 hover:text-emerald-600 duration-150"></i></a>', | ||||||||
'Art': '<a href="/category/Art"><i class="ti ti-brush text-amber-500 hover:text-amber-600 duration-150"></i></a>', | ||||||||
'Finance': '<a href="/category/Finance"><i class="ti ti-coin text-green-500 hover:text-green-600 duration-150"></i></a>', | ||||||||
'Business': '<a href="/category/Business"><i class="ti ti-tie text-stone-500 hover:text-stone-600 duration-150"></i></a>', | ||||||||
'Web': '<a href="/category/Web"><i class="ti ti-world text-purple-500 hover:text-purple-600 duration-150"></i></a>', | ||||||||
'Other': '<a href="/category/Other"><i class="ti ti-dots text-gray-500 hover:text-gray-600 duration-150"></i></a>', | ||||||||
'Default': '<a href="/"><i class="ti ti-stack-2 text-neutral-500 hover:text-neutral-600 duration-150"></i></a>' | ||||||||
}; | ||||||||
|
||||||||
// Function to inject html post macro in document body | ||||||||
async function intializePostCard() { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The function name 'intializePostCard' appears to be misspelled; consider renaming it to 'initializePostCard' for clarity.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||
fetch(postCardMacroPath) | ||||||||
.then(res => res.text()) | ||||||||
.then(postCardHtml => { | ||||||||
// Insert the fetched template into a hidden container | ||||||||
const tempContainer = document.createElement('div'); | ||||||||
tempContainer.innerHTML = postCardHtml; | ||||||||
|
||||||||
// Register macro in dome | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo in comment: 'dome' should be corrected to 'DOM'.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||
document.body.appendChild(tempContainer); // or keep it detached | ||||||||
|
||||||||
/// Assign html element's id of content to variable | ||||||||
postCardMacro = document.getElementById("postCardMacro"); | ||||||||
postContainer = document.getElementById("postCardContainer"); | ||||||||
}); | ||||||||
} | ||||||||
// Call initialize post card function to inject post macro | ||||||||
intializePostCard(); | ||||||||
|
||||||||
// Function to fetch homeFeedData from backend | ||||||||
async function fetchHomeFeedData() { | ||||||||
try { | ||||||||
let connection = await fetch(`/api/v1/homeFeedData?category=${currentCategory.value}&by=${sortBy.value}&sort=${orderby.value}&limit=${limit}&offset=${offset}`); | ||||||||
let res = await connection.json(); | ||||||||
|
||||||||
if (connection.ok) { | ||||||||
Comment on lines
+69
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The variable 'posts' is assigned without declaration, which could lead to unintended global scope. Consider declaring it with 'let' or 'const'.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||
let posts = res.payload; | ||||||||
|
||||||||
posts.forEach(post => { | ||||||||
const clone = postCardMacro.content.cloneNode(true); | ||||||||
clone.querySelector(".postTitle").innerText = post.title; | ||||||||
clone.querySelector(".postTitle").href = post.postLink; | ||||||||
clone.querySelector(".postContent").innerHTML = post.content; | ||||||||
clone.querySelector(".postBanner").src = post.bannerImgSrc; | ||||||||
clone.querySelector(".postAuthorPicture").src = post.authorProfile; | ||||||||
clone.querySelector(".postAuthor").innerText = post.author; | ||||||||
clone.querySelector(".postAuthor").href = `/user/${post.author}`; | ||||||||
clone.querySelector(".postCategory").innerHTML = categoryList[post.category] || categoryList["Default"]; | ||||||||
clone.querySelector(".postTimeStamp").innerText = post.timeStamp; | ||||||||
postContainer.appendChild(clone); | ||||||||
}); | ||||||||
|
||||||||
// Increase the offset with the value of limit | ||||||||
offset += limit; | ||||||||
|
||||||||
// Check if posts length is not less than limit | ||||||||
if (posts.length < limit) { | ||||||||
// Hide button | ||||||||
loadMoreButtonDiv.classList.add("hidden"); | ||||||||
} | ||||||||
} else { | ||||||||
// Print error on console | ||||||||
console.error(connection.status); | ||||||||
} | ||||||||
} catch (error) { | ||||||||
// Print error on console | ||||||||
console.error(error); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
async function loadMoreButton() { | ||||||||
// Show spinner | ||||||||
loadMoreSpinner.classList.remove("hidden"); | ||||||||
// Fetch homeFeed | ||||||||
await fetchHomeFeedData(); | ||||||||
// Hide spinner | ||||||||
loadMoreSpinner.classList.add("hidden"); | ||||||||
} | ||||||||
|
||||||||
// Call the function to load data | ||||||||
window.onload = async function () { | ||||||||
// Show spinner | ||||||||
homeSpinner.classList.remove("hidden"); | ||||||||
// Fetch initial homeFeed | ||||||||
await fetchHomeFeedData(); | ||||||||
// Hide spinner | ||||||||
homeSpinner.classList.add("hidden"); | ||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<template id="postCardMacro"> | ||
<!-- Macro to generate a post card with details from the given 'post' and 'authorProfilePicture' --> | ||
<div | ||
class="w-[17rem] group flex flex-col justify-between h-min-max mx-auto rounded-md overflow-hidden shadow-lg hover:scale-105 transition duration-150"> | ||
<!-- Post Image --> | ||
<div class="overflow-hidden"> | ||
<img | ||
class="postBanner h-48 group-hover:scale-110 transition duration-150 object-cover w-max rounded-t-md mx-auto select-none" /> | ||
</div> | ||
|
||
<!-- Post Title --> | ||
<div class="px-2 mt-1"> | ||
<a class="postTitle text-rose-500 text-lg hover:text-rose-500/75 duration-150 break-words font-medium"> </a> | ||
</div> | ||
|
||
<!-- Post Content --> | ||
<div class="postContent text-base px-2 overflow-y-hidden h-[7.5rem]"> | ||
</div> | ||
|
||
<!-- Post Author and Category --> | ||
<div class="flex justify-between text-sm font-medium px-2 select-none"> | ||
<!-- Author Section --> | ||
<div class="my-2 flex items-center w-fit gap-2"> | ||
<img class="postAuthorPicture block w-8 h-8"/> | ||
<a class="postAuthor"></a> | ||
</div> | ||
|
||
<!-- Category Section --> | ||
<div class="flex items-center"> | ||
<div class="postCategory mr-1 text-base"> | ||
|
||
</div> | ||
|
||
<!-- Date --> | ||
<p class="postTimeStamp date"></p> | ||
</div> | ||
</div> | ||
</div> | ||
</template> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment contains a spelling mistake: 'homeFeddData' should be corrected to 'homeFeedData'.
Copilot uses AI. Check for mistakes.