Spaces:
Running
Running
Upload 9 files
Browse files- src/services/classroom.js +24 -24
- src/views/StudentView.js +10 -3
src/services/classroom.js
CHANGED
|
@@ -131,6 +131,7 @@ export async function submitPrompt(userId, roomCode, challengeId, prompt) {
|
|
| 131 |
await updateDoc(docRef, {
|
| 132 |
status: "completed",
|
| 133 |
submission_prompt: text,
|
|
|
|
| 134 |
timestamp: serverTimestamp()
|
| 135 |
});
|
| 136 |
} else {
|
|
@@ -258,13 +259,8 @@ export function subscribeToRoom(roomCode, callback) {
|
|
| 258 |
*/
|
| 259 |
export async function getPeerPrompts(roomCode, challengeId) {
|
| 260 |
const progressRef = collection(db, PROGRESS_COLLECTION);
|
| 261 |
-
// We need nickname too. progress collection only has userId.
|
| 262 |
-
// We might need to fetch user info or store nickname in progress (denormalization).
|
| 263 |
-
// Let's store nickname in progress for easier read? Or fetch users.
|
| 264 |
-
// For now, let's fetch matching progress, then unique userIds, then fetch those users.
|
| 265 |
|
| 266 |
-
//
|
| 267 |
-
// This avoids needing complex Composite Indexes for every room/challenge combination.
|
| 268 |
const q = query(
|
| 269 |
progressRef,
|
| 270 |
where("challengeId", "==", challengeId),
|
|
@@ -278,27 +274,31 @@ export async function getPeerPrompts(roomCode, challengeId) {
|
|
| 278 |
for (const docSnapshot of snapshot.docs) {
|
| 279 |
const data = docSnapshot.data();
|
| 280 |
|
| 281 |
-
//
|
| 282 |
-
//
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
continue;
|
| 287 |
}
|
| 288 |
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
likes: data.likes || 0,
|
| 299 |
-
likedBy: data.likedBy || []
|
| 300 |
-
});
|
| 301 |
-
}
|
| 302 |
}
|
| 303 |
return entries;
|
| 304 |
|
|
|
|
| 131 |
await updateDoc(docRef, {
|
| 132 |
status: "completed",
|
| 133 |
submission_prompt: text,
|
| 134 |
+
roomCode: roomCode, // Ensure roomCode is updated for legacy docs
|
| 135 |
timestamp: serverTimestamp()
|
| 136 |
});
|
| 137 |
} else {
|
|
|
|
| 259 |
*/
|
| 260 |
export async function getPeerPrompts(roomCode, challengeId) {
|
| 261 |
const progressRef = collection(db, PROGRESS_COLLECTION);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
|
| 263 |
+
// Query completions (filtered by challenge, status)
|
|
|
|
| 264 |
const q = query(
|
| 265 |
progressRef,
|
| 266 |
where("challengeId", "==", challengeId),
|
|
|
|
| 274 |
for (const docSnapshot of snapshot.docs) {
|
| 275 |
const data = docSnapshot.data();
|
| 276 |
|
| 277 |
+
// Fetch nickname & user room info
|
| 278 |
+
// We fetch user first to verify room if needed
|
| 279 |
+
const userSnap = await getDoc(doc(db, USERS_COLLECTION, data.userId));
|
| 280 |
+
if (!userSnap.exists()) continue;
|
| 281 |
+
|
| 282 |
+
const userData = userSnap.data();
|
| 283 |
+
|
| 284 |
+
// Room Code Check
|
| 285 |
+
// 1. Check direct reference in progress (Fastest)
|
| 286 |
+
// 2. Fallback to user's current room (Legacy support)
|
| 287 |
+
const targetRoom = data.roomCode || userData.current_room;
|
| 288 |
+
|
| 289 |
+
if (String(targetRoom) !== String(roomCode)) {
|
| 290 |
continue;
|
| 291 |
}
|
| 292 |
|
| 293 |
+
entries.push({
|
| 294 |
+
id: docSnapshot.id,
|
| 295 |
+
userId: data.userId,
|
| 296 |
+
nickname: userData.nickname,
|
| 297 |
+
prompt: data.submission_prompt,
|
| 298 |
+
timestamp: data.timestamp,
|
| 299 |
+
likes: data.likes || 0,
|
| 300 |
+
likedBy: data.likedBy || []
|
| 301 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
}
|
| 303 |
return entries;
|
| 304 |
|
src/views/StudentView.js
CHANGED
|
@@ -57,7 +57,7 @@ function renderTaskCard(c, userProgress) {
|
|
| 57 |
|
| 58 |
${!isStarted ? `
|
| 59 |
<!-- Not Started State -->
|
| 60 |
-
<div class="mt-4">
|
| 61 |
<button onclick="window.startLevel('${c.id}', '${c.link}')"
|
| 62 |
class="w-full sm:w-auto bg-gray-700 hover:bg-cyan-600 hover:text-white text-gray-200 font-bold py-3 px-6 rounded-xl transition-all flex items-center justify-center space-x-2 shadow-lg">
|
| 63 |
<span>🚀 開始任務 (Start Task)</span>
|
|
@@ -668,7 +668,11 @@ window.triggerEvolution = async (currentStage, nextStage, likes, classSize, curr
|
|
| 668 |
// DB Update with Monster ID
|
| 669 |
const userId = localStorage.getItem('vibecoding_user_id');
|
| 670 |
await updateUserMonster(userId, nextStage, nextMonster.id);
|
| 671 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 672 |
}, 1200);
|
| 673 |
}, 1000);
|
| 674 |
}, 300);
|
|
@@ -678,8 +682,11 @@ window.triggerEvolution = async (currentStage, nextStage, likes, classSize, curr
|
|
| 678 |
playFlicker();
|
| 679 |
|
| 680 |
} catch (e) {
|
|
|
|
| 681 |
console.error(e);
|
| 682 |
alert("進化失敗...");
|
| 683 |
-
|
|
|
|
|
|
|
| 684 |
}
|
| 685 |
};
|
|
|
|
| 57 |
|
| 58 |
${!isStarted ? `
|
| 59 |
<!-- Not Started State -->
|
| 60 |
+
<div class="mt-4 flex justify-center">
|
| 61 |
<button onclick="window.startLevel('${c.id}', '${c.link}')"
|
| 62 |
class="w-full sm:w-auto bg-gray-700 hover:bg-cyan-600 hover:text-white text-gray-200 font-bold py-3 px-6 rounded-xl transition-all flex items-center justify-center space-x-2 shadow-lg">
|
| 63 |
<span>🚀 開始任務 (Start Task)</span>
|
|
|
|
| 668 |
// DB Update with Monster ID
|
| 669 |
const userId = localStorage.getItem('vibecoding_user_id');
|
| 670 |
await updateUserMonster(userId, nextStage, nextMonster.id);
|
| 671 |
+
|
| 672 |
+
// Soft refresh to prevent screen flicker
|
| 673 |
+
const app = document.querySelector('#app');
|
| 674 |
+
if (app) app.innerHTML = await renderStudentView();
|
| 675 |
+
// Also need to ensure monster container is updated (handle in renderStudentView)
|
| 676 |
}, 1200);
|
| 677 |
}, 1000);
|
| 678 |
}, 300);
|
|
|
|
| 682 |
playFlicker();
|
| 683 |
|
| 684 |
} catch (e) {
|
| 685 |
+
console.error(e);
|
| 686 |
console.error(e);
|
| 687 |
alert("進化失敗...");
|
| 688 |
+
// Re-render instead of reload to keep state
|
| 689 |
+
const app = document.querySelector('#app');
|
| 690 |
+
if (app) app.innerHTML = await renderStudentView();
|
| 691 |
}
|
| 692 |
};
|