Skip to content

Conversation

@adikatre
Copy link
Contributor

  • PR for JPA
  • Allows group members to send chat messages
    • Can upload images alongside chat messages as well
  • multimedia uploads (ex. *.docx or *.pdf)

stored in s3 bucket called pages-groups

  • need to take performance into account for future

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the student group UI to support live group chat plus “Shared Files” uploads/downloads (intended to back onto the new S3-backed endpoints from the linked Spring PR).

Changes:

  • Replaces the static chat modal with a tabbed Chat / Shared Files UI and adds image attachment support.
  • Adds frontend fetch helpers to load/send chat messages and to upload/list/download shared files.
  • Extends the group dashboard to include an “Add File” action and wires it to the new chat/files UI.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.

File Description
_posts/teacher_toolkit/StudentManagement/2025-04-07-Groups.html Adds tabbed chat/files UI, chat polling + send, and shared file upload/list/download behavior.
_includes/group_dashboard.html Adds the same chat/files UI to the dashboard modal and introduces dashboard shared-file upload entrypoint.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1019 to +1022
function startChatPolling() {
if (chatPollTimer) clearInterval(chatPollTimer);
chatPollTimer = setInterval(fetchChatMessages, 4000);
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chat polling fetches and re-renders the full message list every 4 seconds. This can become expensive for both client and server as history grows. Consider incremental fetch (cursor/since), caching/ETags, and appending only new messages instead of clearing and rebuilding #chatMessages each poll.

Copilot uses AI. Check for mistakes.
Comment on lines +603 to 605
<script type="module">
import { javaURI, pythonURI, fetchOptions } from '{{site.baseurl}}/assets/js/api/config.js';
// Calendar state
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

group_dashboard.html was converted to <script type="module">, but the page still uses many inline event handlers (e.g., onclick="previousMonth()", onclick="refreshDashboard()", onchange="handleSharedFileUpload(event)"). In module scripts, top-level functions are not added to window, so these handlers will throw ReferenceError at runtime. Either export/assign the referenced functions onto window (e.g., window.previousMonth = previousMonth, etc.) or remove inline handlers and bind listeners in JS after DOM load.

Copilot uses AI. Check for mistakes.
Comment on lines 1351 to 1387
// Fetch group ID using group name
try {
const searchResponse = await fetch(`/api/group/search?name=${encodeURIComponent(groupName)}`);

if (!searchResponse.ok) {
throw new Error('Group not found');
}

// Handle paste events for images
document.addEventListener('paste', function(event) {
// Only handle paste when chat modal is open
if (document.getElementById('chatModal').classList.contains('hidden')) return;
const groupData = await searchResponse.json();
const groupId = groupData.id;

const items = (event.clipboardData || window.clipboardData).items;
let imageFile = null;
if (!groupId) {
alert('Error: Could not retrieve group ID');
event.target.value = '';
return;
}

for (let i = 0; i < items.length; i++) {
if (items[i].type.startsWith('image/')) {
imageFile = items[i].getAsFile();
break;
// Convert file to base64
const reader = new FileReader();
reader.onload = async (e) => {
const base64Data = e.target.result.split(',')[1];

// Prepare request body
const requestBody = {
filename: file.name,
base64Data: base64Data
};

try {
// Make POST request to upload file
const response = await fetch(`/api/groups/chat/${groupId}/files`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody)
});
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleSharedFileUpload is calling /api/group/search and /api/groups/chat/... as relative URLs and without fetchOptions. Elsewhere in this file, API calls consistently use ${javaURI} and fetchOptions (and /api/groups/search returns an array). As written, this will likely hit the GitHub Pages origin, fail auth, and mis-handle the search response shape. Reuse resolveActiveGroupId(...) + ${javaURI}/api/groups/chat/${id}/files with fetchOptions (and handle the search response as an array) to make dashboard uploads work reliably.

Copilot uses AI. Check for mistakes.

if (msg.image) {
const img = document.createElement('img');
img.src = `data:image/png;base64,${msg.image}`;
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chat message rendering hard-codes data:image/png;base64,... for msg.image. Since image selection allows image/* (and pasted images can be JPEG/GIF), non-PNG images will fail to render. Include the image MIME type in the payload/response (or store a full data URL) and use it when constructing img.src.

Suggested change
img.src = `data:image/png;base64,${msg.image}`;
let imageSrc;
if (typeof msg.image === 'string' && msg.image.startsWith('data:')) {
// msg.image is already a full data URL
imageSrc = msg.image;
} else if (msg.imageMimeType) {
// Use provided MIME type for the base64 image data
imageSrc = `data:${msg.imageMimeType};base64,${msg.image}`;
} else {
// Backward-compatible fallback: assume PNG
imageSrc = `data:image/png;base64,${msg.image}`;
}
img.src = imageSrc;

Copilot uses AI. Check for mistakes.
imageMessageBox.innerHTML = '';
}

document.addEventListener('paste', function(event) {
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The paste handler runs on every paste anywhere on the page and will attach an image even when the chat modal is closed. This can lead to unexpected hidden attachments. Add the same guard used in group_dashboard.html (skip when #chatModal has hidden) or scope the listener to the chat input/modal.

Suggested change
document.addEventListener('paste', function(event) {
document.addEventListener('paste', function(event) {
const chatModal = document.getElementById('chatModal');
if (chatModal && chatModal.hasAttribute('hidden')) {
return;
}

Copilot uses AI. Check for mistakes.

if (msg.image) {
const img = document.createElement('img');
img.src = `data:image/png;base64,${msg.image}`;
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chat message rendering hard-codes data:image/png;base64,... for msg.image. Since the UI accepts image/*, non-PNG images (e.g., JPEG) will not render correctly. Include image MIME type in the payload/response (or store a full data URL) and use it when setting img.src.

Suggested change
img.src = `data:image/png;base64,${msg.image}`;
// If msg.image is already a full data URL (e.g., "data:image/jpeg;base64,..."),
// use it directly. Otherwise, include the MIME type if provided, falling back to PNG.
if (typeof msg.image === 'string' && msg.image.startsWith('data:')) {
img.src = msg.image;
} else {
const mimeType = msg.imageMimeType || 'image/png';
img.src = `data:${mimeType};base64,${msg.image}`;
}

Copilot uses AI. Check for mistakes.
Comment on lines +915 to +918
function startChatPolling() {
if (chatPollTimer) clearInterval(chatPollTimer);
chatPollTimer = setInterval(fetchChatMessages, 4000);
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chat polling fetches and re-renders the full message list every 4 seconds (setInterval(fetchChatMessages, 4000) + renderChatMessages clears and rebuilds DOM). This will not scale with message volume and will generate unnecessary backend load. Consider incremental fetching (e.g., since/cursor), ETags, or server push (SSE/WebSocket), and update DOM append-only instead of full re-render.

Copilot uses AI. Check for mistakes.
Comment on lines +1026 to +1038
try {
const base64Data = await fileToBase64(file);
const payload = { filename: file.name, base64Data };

const res = await fetch(`${javaURI}/api/groups/chat/${activeChatGroupId}/files`, {
...fetchOptions,
method: 'POST',
headers: {
...(fetchOptions.headers || {}),
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shared file uploads are base64-encoded and sent in JSON (fileToBase64 + body: JSON.stringify(payload)). For larger media/docs (PDF/DOCX), this increases payload size ~33%, risks hitting request limits, and can freeze the browser due to memory copies. Prefer direct-to-S3 uploads via pre-signed URLs (multipart where needed) and enforce client-side size limits with a user-visible error.

Copilot uses AI. Check for mistakes.
@jm1021
Copy link
Contributor

jm1021 commented Feb 12, 2026

There are some recommend copilot fixes on this that sound legit

@adikatre adikatre marked this pull request as draft February 12, 2026 17:52
@code259
Copy link
Contributor

code259 commented Feb 12, 2026

Rohan:

  • Make profile changes less invasive, test locally, and checkout PR
  • Test bathroom pass with actual system

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants