Spaces:
Running
Running
Upload 8 files
Browse files- src/views/StudentView.js +47 -8
src/views/StudentView.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import { submitPrompt, getChallenges, startChallenge, getUserProgress, getPeerPrompts, resetProgress } from "../services/classroom.js";
|
| 2 |
|
| 3 |
// Cache challenges locally
|
| 4 |
let cachedChallenges = [];
|
|
@@ -357,16 +357,55 @@ window.loadPeerPrompts = async (challengeId) => {
|
|
| 357 |
return;
|
| 358 |
}
|
| 359 |
|
| 360 |
-
container.innerHTML = prompts.map(p =>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 361 |
<div class="bg-gray-700/30 p-4 rounded-xl border border-gray-600">
|
| 362 |
-
<div class="flex items-center
|
| 363 |
-
<div class="
|
| 364 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
</div>
|
| 366 |
-
|
| 367 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 368 |
</div>
|
| 369 |
<p class="text-gray-300 font-mono text-sm bg-black/20 p-3 rounded-lg border border-gray-700/50 whitespace-pre-wrap">${p.prompt}</p>
|
| 370 |
</div>
|
| 371 |
-
`).join('');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 372 |
};
|
|
|
|
| 1 |
+
import { submitPrompt, getChallenges, startChallenge, getUserProgress, getPeerPrompts, resetProgress, toggleLike, subscribeToNotifications, markNotificationRead } from "../services/classroom.js";
|
| 2 |
|
| 3 |
// Cache challenges locally
|
| 4 |
let cachedChallenges = [];
|
|
|
|
| 357 |
return;
|
| 358 |
}
|
| 359 |
|
| 360 |
+
container.innerHTML = prompts.map(p => {
|
| 361 |
+
const currentUserId = localStorage.getItem('vibecoding_user_id');
|
| 362 |
+
const isLiked = p.likedBy && p.likedBy.includes(currentUserId);
|
| 363 |
+
|
| 364 |
+
return `
|
| 365 |
<div class="bg-gray-700/30 p-4 rounded-xl border border-gray-600">
|
| 366 |
+
<div class="flex items-center justify-between mb-2">
|
| 367 |
+
<div class="flex items-center space-x-2">
|
| 368 |
+
<div class="w-6 h-6 rounded-full bg-cyan-600 flex items-center justify-center text-xs font-bold text-white">
|
| 369 |
+
${p.nickname[0]}
|
| 370 |
+
</div>
|
| 371 |
+
<span class="font-bold text-cyan-300 text-sm">${p.nickname}</span>
|
| 372 |
+
<span class="text-gray-500 text-xs">${new Date(p.timestamp.seconds * 1000).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</span>
|
| 373 |
</div>
|
| 374 |
+
|
| 375 |
+
<button onclick="handleLike('${p.id}', '${p.userId}')"
|
| 376 |
+
class="flex items-center space-x-1 px-2 py-1 rounded-full transition-colors ${isLiked ? 'bg-pink-900/50 text-pink-400' : 'bg-gray-800 text-gray-400 hover:bg-gray-700'}">
|
| 377 |
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ${isLiked ? 'fill-current' : 'none'}" viewBox="0 0 24 24" stroke="currentColor" fill="${isLiked ? 'currentColor' : 'none'}">
|
| 378 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
|
| 379 |
+
</svg>
|
| 380 |
+
<span class="text-xs font-bold">${p.likes}</span>
|
| 381 |
+
</button>
|
| 382 |
</div>
|
| 383 |
<p class="text-gray-300 font-mono text-sm bg-black/20 p-3 rounded-lg border border-gray-700/50 whitespace-pre-wrap">${p.prompt}</p>
|
| 384 |
</div>
|
| 385 |
+
`}).join('');
|
| 386 |
+
|
| 387 |
+
// Attach challenge title for notification context
|
| 388 |
+
window.currentPeerChallengeTitle = document.querySelector(`#peer-challenge-select option[value="${challengeId}"]`).text;
|
| 389 |
+
};
|
| 390 |
+
|
| 391 |
+
// Like Handler
|
| 392 |
+
window.handleLike = async (progressId, targetUserId) => {
|
| 393 |
+
const userId = localStorage.getItem('vibecoding_user_id');
|
| 394 |
+
const nickname = localStorage.getItem('vibecoding_nickname');
|
| 395 |
+
const challengeTitle = window.currentPeerChallengeTitle || '挑戰';
|
| 396 |
+
|
| 397 |
+
// Optimistic UI update could go here, but for simplicity let's re-load or just fire and forget (the view won't update until reload currently)
|
| 398 |
+
// To make it responsive, we should probably manually toggle the class on the button immediately.
|
| 399 |
+
// For now, let's just call service and reload the list to see updated count.
|
| 400 |
+
|
| 401 |
+
// Better UX: Find button and toggle 'processing' state?
|
| 402 |
+
// Let's just reload the list for data consistency.
|
| 403 |
+
const { toggleLike } = await import("../services/classroom.js");
|
| 404 |
+
await toggleLike(progressId, userId, nickname, targetUserId, challengeTitle);
|
| 405 |
+
|
| 406 |
+
// Reload to refresh count
|
| 407 |
+
const select = document.getElementById('peer-challenge-select');
|
| 408 |
+
if (select && select.value) {
|
| 409 |
+
loadPeerPrompts(select.value);
|
| 410 |
+
}
|
| 411 |
};
|