Spaces:
Running
Running
Upload 8 files
Browse files- src/services/classroom.js +78 -3
src/services/classroom.js
CHANGED
|
@@ -268,14 +268,16 @@ export async function getPeerPrompts(roomCode, challengeId) {
|
|
| 268 |
}
|
| 269 |
|
| 270 |
// Fetch nickname
|
| 271 |
-
// Optimization: In a real app, store nickname in 'progress' to avoid N+1 queries.
|
| 272 |
-
// For now, fetch user.
|
| 273 |
const userSnap = await getDoc(doc(db, USERS_COLLECTION, data.userId));
|
| 274 |
if (userSnap.exists()) {
|
| 275 |
entries.push({
|
|
|
|
|
|
|
| 276 |
nickname: userSnap.data().nickname,
|
| 277 |
prompt: data.submission_prompt,
|
| 278 |
-
timestamp: data.timestamp
|
|
|
|
|
|
|
| 279 |
});
|
| 280 |
}
|
| 281 |
}
|
|
@@ -349,3 +351,76 @@ export async function updateChallenge(id, data) {
|
|
| 349 |
export async function deleteChallenge(id) {
|
| 350 |
await deleteDoc(doc(db, CHALLENGES_COLLECTION, id));
|
| 351 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 268 |
}
|
| 269 |
|
| 270 |
// Fetch nickname
|
|
|
|
|
|
|
| 271 |
const userSnap = await getDoc(doc(db, USERS_COLLECTION, data.userId));
|
| 272 |
if (userSnap.exists()) {
|
| 273 |
entries.push({
|
| 274 |
+
id: docSnapshot.id,
|
| 275 |
+
userId: data.userId,
|
| 276 |
nickname: userSnap.data().nickname,
|
| 277 |
prompt: data.submission_prompt,
|
| 278 |
+
timestamp: data.timestamp,
|
| 279 |
+
likes: data.likes || 0,
|
| 280 |
+
likedBy: data.likedBy || []
|
| 281 |
});
|
| 282 |
}
|
| 283 |
}
|
|
|
|
| 351 |
export async function deleteChallenge(id) {
|
| 352 |
await deleteDoc(doc(db, CHALLENGES_COLLECTION, id));
|
| 353 |
}
|
| 354 |
+
|
| 355 |
+
// --- Social Features ---
|
| 356 |
+
|
| 357 |
+
const NOTIFICATIONS_COLLECTION = "notifications";
|
| 358 |
+
|
| 359 |
+
/**
|
| 360 |
+
* Toggles like on a progress submission
|
| 361 |
+
*/
|
| 362 |
+
export async function toggleLike(progressId, currentUserId, currentNickname, targetUserId, challengeTitle) {
|
| 363 |
+
const progressRef = doc(db, PROGRESS_COLLECTION, progressId);
|
| 364 |
+
const progressSnap = await getDoc(progressRef);
|
| 365 |
+
|
| 366 |
+
if (!progressSnap.exists()) return;
|
| 367 |
+
|
| 368 |
+
const data = progressSnap.data();
|
| 369 |
+
const likedBy = data.likedBy || [];
|
| 370 |
+
const isLiked = likedBy.includes(currentUserId);
|
| 371 |
+
|
| 372 |
+
if (isLiked) {
|
| 373 |
+
// Unlike
|
| 374 |
+
await updateDoc(progressRef, {
|
| 375 |
+
likes: (data.likes || 1) - 1,
|
| 376 |
+
likedBy: likedBy.filter(id => id !== currentUserId)
|
| 377 |
+
});
|
| 378 |
+
} else {
|
| 379 |
+
// Like
|
| 380 |
+
await updateDoc(progressRef, {
|
| 381 |
+
likes: (data.likes || 0) + 1,
|
| 382 |
+
likedBy: [...likedBy, currentUserId]
|
| 383 |
+
});
|
| 384 |
+
|
| 385 |
+
// Send Notification if not liking self
|
| 386 |
+
if (targetUserId !== currentUserId) {
|
| 387 |
+
await addDoc(collection(db, NOTIFICATIONS_COLLECTION), {
|
| 388 |
+
recipientId: targetUserId,
|
| 389 |
+
senderNickname: currentNickname,
|
| 390 |
+
challengeTitle: challengeTitle,
|
| 391 |
+
type: 'like',
|
| 392 |
+
timestamp: serverTimestamp(),
|
| 393 |
+
read: false
|
| 394 |
+
});
|
| 395 |
+
}
|
| 396 |
+
}
|
| 397 |
+
}
|
| 398 |
+
|
| 399 |
+
/**
|
| 400 |
+
* Listen to notifications for a user
|
| 401 |
+
*/
|
| 402 |
+
export function subscribeToNotifications(userId, callback) {
|
| 403 |
+
const q = query(
|
| 404 |
+
collection(db, NOTIFICATIONS_COLLECTION),
|
| 405 |
+
where("recipientId", "==", userId),
|
| 406 |
+
where("read", "==", false),
|
| 407 |
+
orderBy("timestamp", "desc")
|
| 408 |
+
);
|
| 409 |
+
|
| 410 |
+
return onSnapshot(q, (snapshot) => {
|
| 411 |
+
const notifications = [];
|
| 412 |
+
snapshot.forEach(doc => {
|
| 413 |
+
notifications.push({ id: doc.id, ...doc.data() });
|
| 414 |
+
});
|
| 415 |
+
callback(notifications);
|
| 416 |
+
});
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
/**
|
| 420 |
+
* Mark notification as read
|
| 421 |
+
*/
|
| 422 |
+
export async function markNotificationRead(notificationId) {
|
| 423 |
+
await updateDoc(doc(db, NOTIFICATIONS_COLLECTION, notificationId), {
|
| 424 |
+
read: true
|
| 425 |
+
});
|
| 426 |
+
}
|