Skip to content

Commit 729fcab

Browse files
authored
Merge pull request #356 from NOOB-3301/streakfeature
I have added streak feature backend
2 parents b672c4f + 08f9f1a commit 729fcab

File tree

5 files changed

+493
-290
lines changed

5 files changed

+493
-290
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Streak } from "../models/streak.model";
2+
3+
const updateStreak = async (userId) => {
4+
try {
5+
// Find the user's streak record
6+
let streak = await Streak.findOne({ user: userId });
7+
8+
if (!streak) {
9+
// If no streak exists, create a new one starting at 1
10+
streak = new Streak({ user: userId, streak: 1, lastUpdate: new Date() });
11+
} else {
12+
const currentDate = new Date();
13+
const lastUpdateDate = new Date(streak.lastUpdate);
14+
15+
// Calculate the difference in days
16+
const timeDifference = currentDate.getTime() - lastUpdateDate.getTime();
17+
const daysDifference = timeDifference / (1000 * 3600 * 24);
18+
19+
if (daysDifference < 1) {
20+
// User already published today, do nothing
21+
console.log(`User ${userId} already published today. Streak unchanged.`);
22+
return;
23+
} else if (daysDifference < 2) {
24+
// If it's the next day, increment streak
25+
streak.streak += 1;
26+
} else {
27+
// If more than one day is missed, reset streak to 1
28+
streak.streak = 1;
29+
}
30+
}
31+
32+
// Update last update time
33+
streak.lastUpdate = new Date();
34+
await streak.save();
35+
36+
console.log(`Streak updated for user ${userId}: ${streak.streak}`);
37+
} catch (error) {
38+
console.error("Error updating streak:", error);
39+
}
40+
};
41+
42+
export { updateStreak };
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Streak } from "../models/streak.model.js";
2+
3+
// Middleware to check user's streak and reset if necessary
4+
const checkStreak = async (req, res, next) => {
5+
try {
6+
const { u_id } = req.body; // Extract user ID from request
7+
8+
if (!u_id) {
9+
return res.status(400).json({ message: "User ID is required" });
10+
}
11+
12+
// Find user's streak record
13+
let streak = await Streak.findOne({ user: u_id });
14+
15+
// If no streak record exists, create one with streak 0
16+
if (!streak) {
17+
streak = new Streak({ user: u_id, streak: 0, lastUpdate: new Date() });
18+
await streak.save();
19+
return next(); // No need to reset if it's the first streak entry
20+
}
21+
22+
// Get the current date and last update date
23+
const currentDate = new Date();
24+
const lastUpdateDate = new Date(streak.lastUpdate);
25+
26+
// Calculate the difference in days between last update and today
27+
const timeDifference = currentDate.getTime() - lastUpdateDate.getTime();
28+
const daysDifference = timeDifference / (1000 * 3600 * 24); // Convert ms to days
29+
30+
if (daysDifference >= 2) {
31+
// If more than 1 day has passed, reset the streak
32+
console.log(`User ${u_id} missed a day. Resetting streak.`);
33+
streak.streak = 0;
34+
}
35+
36+
// Update last checked date
37+
streak.lastUpdate = currentDate;
38+
await streak.save();
39+
40+
next(); // Move to next middleware or route handler
41+
} catch (error) {
42+
console.error("Error checking streak:", error);
43+
res.status(500).json({ message: "Error checking streak", error: error.message });
44+
}
45+
};
46+
47+
export { checkStreak };

back-end/models/streak.model.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import mongoose from 'mongoose';
2+
3+
const streakSchema = new mongoose.Schema({
4+
user: {
5+
type: mongoose.Schema.Types.ObjectId,
6+
ref: 'User',
7+
required: true,
8+
},
9+
streak: {
10+
type: Number,
11+
required: true,
12+
default: 0,
13+
},
14+
lastUpdate: {
15+
type: Date,
16+
required: true,
17+
default: Date.now,
18+
},
19+
});
20+
21+
const Streak = mongoose.model('Streak', streakSchema);
22+
23+
export { Streak };
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { useState } from "react";
2+
import { motion } from "framer-motion";
3+
4+
const PaginatedArticles = ({ articles, articlesPerPage = 3 }) => {
5+
console.log(articles)
6+
const [currentPage, setCurrentPage] = useState(1);
7+
const totalPages = Math.ceil(articles.length / articlesPerPage);
8+
9+
const startIndex = (currentPage - 1) * articlesPerPage;
10+
const displayedArticles = articles.slice(startIndex, startIndex + articlesPerPage);
11+
12+
const handlePageChange = (newPage) => {
13+
if (newPage >= 1 && newPage <= totalPages) {
14+
setCurrentPage(newPage);
15+
}
16+
};
17+
18+
return (
19+
<div className="max-w-5xl mx-auto p-6 bg-white dark:bg-gray-800 rounded-lg shadow-lg">
20+
<motion.div
21+
initial={{ opacity: 0, y: 20 }}
22+
animate={{ opacity: 1, y: 0 }}
23+
transition={{ duration: 0.5 }}
24+
className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6"
25+
>
26+
{displayedArticles.map((article, index) => (
27+
<motion.div
28+
key={article.id || index}
29+
className="bg-gray-100 dark:bg-gray-700 rounded-lg shadow-md overflow-hidden"
30+
whileHover={{ scale: 1.02 }}
31+
whileTap={{ scale: 0.98 }}
32+
>
33+
<img
34+
src={article.thumbnail}
35+
alt={article.title}
36+
className="w-full h-40 object-cover"
37+
/>
38+
<div className="p-4">
39+
<h2 className="text-lg font-bold text-gray-800 dark:text-gray-200">
40+
{article.title}
41+
</h2>
42+
<p className="text-gray-600 dark:text-gray-400 mt-2 text-sm">
43+
{article.summary}
44+
</p>
45+
</div>
46+
</motion.div>
47+
))}
48+
</motion.div>
49+
50+
{/* Pagination Controls */}
51+
<div className="flex justify-center items-center gap-4 mt-6">
52+
<button
53+
className="px-4 py-2 rounded bg-blue-600 text-white hover:bg-blue-700 disabled:bg-gray-400 transition"
54+
onClick={() => handlePageChange(currentPage - 1)}
55+
disabled={currentPage === 1}
56+
>
57+
Previous
58+
</button>
59+
<span className="text-gray-800 dark:text-gray-200">
60+
Page {currentPage} of {totalPages}
61+
</span>
62+
<button
63+
className="px-4 py-2 rounded bg-blue-600 text-white hover:bg-blue-700 disabled:bg-gray-400 transition"
64+
onClick={() => handlePageChange(currentPage + 1)}
65+
disabled={currentPage === totalPages}
66+
>
67+
Next
68+
</button>
69+
</div>
70+
</div>
71+
);
72+
};
73+
74+
export default PaginatedArticles;

0 commit comments

Comments
 (0)