documentation for all JS files

This commit is contained in:
2025-05-28 16:01:18 +02:00
parent 1134f5b099
commit 5c5829c487
22 changed files with 984 additions and 26 deletions

View File

@@ -1,3 +1,18 @@
/**
* @fileoverview Provides avatar image preview functionality.
* This file handles:
* - File selection event handling
* - Image preview generation
* - Real-time avatar preview updates
*/
/**
* Generates a preview of the selected avatar image.
* Reads the selected file and updates the preview image element.
* @function
* @param {Event} event - The file input change event
* @param {FileList} event.target.files - The list of selected files
*/
function previewAvatar(event) { function previewAvatar(event) {
const [file] = event.target.files; const [file] = event.target.files;
if (file) { if (file) {

View File

@@ -1,9 +1,33 @@
/**
* @fileoverview Manages global chat state and polling functionality.
* This file implements a singleton ChatManager that handles:
* - Message polling and state management
* - New message processing and event triggering
* - Connection state tracking
* - Resource cleanup
*/
// Global state and polling management // Global state and polling management
if (typeof window.ChatManager === 'undefined') { if (typeof window.ChatManager === 'undefined') {
window.ChatManager = (function() { window.ChatManager = (function() {
let instance = null; let instance = null;
let pollInterval = null; let pollInterval = null;
/**
* @typedef {Object} ConnectionState
* @property {boolean} hasJoined - Whether the user has joined the conversation
* @property {boolean} isConnected - Current connection status
* @property {number|null} lastMessageId - ID of the last received message
* @property {number} pollAttempts - Number of failed polling attempts
*/
/**
* @typedef {Object} ChatState
* @property {Set<number>} addedMessageIds - Set of message IDs that have been processed
* @property {Set<Object>} messageQueue - Queue of messages waiting to be processed
* @property {ConnectionState} connectionState - Current connection state
*/
const state = { const state = {
addedMessageIds: new Set(), addedMessageIds: new Set(),
messageQueue: new Set(), messageQueue: new Set(),
@@ -15,6 +39,13 @@ if (typeof window.ChatManager === 'undefined') {
} }
}; };
/**
* Initializes a new ChatManager instance for a conversation.
* Sets up message tracking and starts polling for new messages.
* @function
* @param {string} conversationId - The ID of the conversation to manage
* @returns {Object} The ChatManager instance
*/
function init(conversationId) { function init(conversationId) {
if (instance) { if (instance) {
console.log('[ChatManager] Instance already exists, returning existing instance'); console.log('[ChatManager] Instance already exists, returning existing instance');
@@ -47,6 +78,12 @@ if (typeof window.ChatManager === 'undefined') {
return instance; return instance;
} }
/**
* Starts polling for new messages in the conversation.
* Polls every 3 seconds and performs an initial fetch.
* @function
* @param {string} conversationId - The ID of the conversation to poll
*/
function startPolling(conversationId) { function startPolling(conversationId) {
console.log('[ChatManager] Starting polling for conversation:', conversationId); console.log('[ChatManager] Starting polling for conversation:', conversationId);
@@ -67,6 +104,12 @@ if (typeof window.ChatManager === 'undefined') {
fetchNewMessages(conversationId); fetchNewMessages(conversationId);
} }
/**
* Fetches new messages from the server.
* Uses the last message ID to only fetch new messages.
* @function
* @param {string} conversationId - The ID of the conversation to fetch messages from
*/
function fetchNewMessages(conversationId) { function fetchNewMessages(conversationId) {
const url = `/conversations/${conversationId}/messages`; const url = `/conversations/${conversationId}/messages`;
const params = new URLSearchParams(); const params = new URLSearchParams();
@@ -107,6 +150,12 @@ if (typeof window.ChatManager === 'undefined') {
}); });
} }
/**
* Processes new messages and triggers events for each new message.
* Updates the last message ID and tracks processed messages.
* @function
* @param {Array<Object>} messages - Array of new message objects to process
*/
function processNewMessages(messages) { function processNewMessages(messages) {
console.log('[ChatManager] Processing new messages:', { console.log('[ChatManager] Processing new messages:', {
messageCount: messages.length, messageCount: messages.length,
@@ -133,6 +182,11 @@ if (typeof window.ChatManager === 'undefined') {
}); });
} }
/**
* Cleans up polling resources and resets the instance.
* Should be called when the chat is no longer needed.
* @function
*/
function cleanup() { function cleanup() {
console.log('[ChatManager] Cleaning up polling'); console.log('[ChatManager] Cleaning up polling');
if (pollInterval) { if (pollInterval) {
@@ -143,6 +197,12 @@ if (typeof window.ChatManager === 'undefined') {
} }
return { return {
/**
* Gets or creates a ChatManager instance for a conversation.
* @function
* @param {string} conversationId - The ID of the conversation to manage
* @returns {Object} The ChatManager instance
*/
getInstance: function(conversationId) { getInstance: function(conversationId) {
if (!instance) { if (!instance) {
instance = init(conversationId); instance = init(conversationId);

View File

@@ -1,4 +1,22 @@
/**
* @fileoverview Provides logging functionality for CSS color variables.
* This file handles:
* - Logging of base color loading status
* - Logging of CSS file loading status
* - Logging of primary and secondary color values
*/
// Log initial base colors loading
console.log('[CSS] Base colors loaded'); console.log('[CSS] Base colors loaded');
/**
* Logs CSS color information when the DOM is fully loaded.
* Reports:
* - CSS files loading status
* - Primary color value from CSS variables
* - Secondary color value from CSS variables
* @function
*/
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
console.log('[CSS] All CSS files loaded'); console.log('[CSS] All CSS files loaded');
console.log('[CSS] Primary color:', getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim()); console.log('[CSS] Primary color:', getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim());

View File

@@ -1,4 +1,21 @@
// Debounce function /**
* @fileoverview Manages the contacts filtering functionality.
* This file handles:
* - Contact search with debounced input
* - Filter form submission
* - Search state persistence
* - Filter clearing functionality
*/
/**
* Creates a debounced version of a function that delays its execution
* until after a specified wait time has elapsed since the last time it was invoked.
* This helps prevent excessive form submissions during rapid user input.
* @function
* @param {Function} func - The function to debounce
* @param {number} wait - The number of milliseconds to delay (300ms default for search)
* @returns {Function} A debounced version of the provided function
*/
function debounce(func, wait) { function debounce(func, wait) {
let timeout; let timeout;
return function(...args) { return function(...args) {
@@ -7,7 +24,14 @@ function debounce(func, wait) {
}; };
} }
// Initialize filter functionality /**
* Initializes the contacts filter functionality when the DOM is loaded.
* Sets up:
* - Auto-submit on select changes
* - Debounced search input with cursor position persistence
* - Filter clearing functionality
* @function
*/
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Auto-submit the form on select change // Auto-submit the form on select change
document.querySelectorAll('#filterForm select').forEach(function(el) { document.querySelectorAll('#filterForm select').forEach(function(el) {
@@ -41,7 +65,11 @@ document.addEventListener('DOMContentLoaded', function() {
} }
} }
// Clear button resets all filters and submits the form /**
* Handles the clear filters button click.
* Resets all filter inputs and submits the form.
* @event
*/
document.getElementById('clearFilters').addEventListener('click', function() { document.getElementById('clearFilters').addEventListener('click', function() {
document.querySelector('#filterForm input[name="search"]').value = ''; document.querySelector('#filterForm input[name="search"]').value = '';
document.querySelector('#filterForm select[name="role"]').selectedIndex = 0; document.querySelector('#filterForm select[name="role"]').selectedIndex = 0;

View File

@@ -1,4 +1,23 @@
// Initialize chat when document is ready /**
* @fileoverview Manages the real-time conversation functionality.
* This file handles:
* - Chat message display and management
* - Message submission with file attachments
* - Real-time message updates
* - Chat state management
* - UI interactions and animations
*/
/**
* Initializes the chat functionality when the document is ready.
* Sets up:
* - Chat state and message tracking
* - Message display and submission
* - File attachment handling
* - Real-time message updates
* - Cleanup on page unload
* @function
*/
$(document).ready(function() { $(document).ready(function() {
const conversationId = window.conversationId; // Set this in the template const conversationId = window.conversationId; // Set this in the template
const currentUserId = window.currentUserId; // Set this in the template const currentUserId = window.currentUserId; // Set this in the template
@@ -10,7 +29,19 @@ $(document).ready(function() {
// Keep track of messages we've already displayed // Keep track of messages we've already displayed
const displayedMessageIds = new Set(); const displayedMessageIds = new Set();
// Function to append a new message to the chat /**
* Appends a new message to the chat interface.
* Handles message formatting, attachments, and UI updates.
* @function
* @param {Object} message - The message object to append
* @param {string} message.id - The unique message ID
* @param {string} message.content - The message content
* @param {string} message.sender_id - The ID of the message sender
* @param {string} message.sender_name - The name of the message sender
* @param {string} message.sender_avatar - The avatar URL of the sender
* @param {string} message.created_at - The message creation timestamp
* @param {Array} [message.attachments] - Array of file attachments
*/
function appendMessage(message) { function appendMessage(message) {
console.log('[Conversation] Attempting to append message:', { console.log('[Conversation] Attempting to append message:', {
messageId: message.id, messageId: message.id,
@@ -79,14 +110,23 @@ $(document).ready(function() {
} }
}); });
// Scroll to bottom of chat messages /**
* Scrolls the chat window to the bottom.
* @function
*/
function scrollToBottom() { function scrollToBottom() {
const chatMessages = document.getElementById('chatMessages'); const chatMessages = document.getElementById('chatMessages');
chatMessages.scrollTop = chatMessages.scrollHeight; chatMessages.scrollTop = chatMessages.scrollHeight;
} }
scrollToBottom(); scrollToBottom();
// Listen for new messages /**
* Event handler for new messages.
* Appends new messages to the chat when received.
* @event
* @param {Event} event - The event object
* @param {Object} message - The new message object
*/
$(document).on('new_message', function(event, message) { $(document).on('new_message', function(event, message) {
console.log('[Conversation] Received new_message event:', { console.log('[Conversation] Received new_message event:', {
messageId: message.id, messageId: message.id,
@@ -96,7 +136,11 @@ $(document).ready(function() {
appendMessage(message); appendMessage(message);
}); });
// Handle file selection /**
* Handles file selection for message attachments.
* Updates the UI to show selected files.
* @event
*/
$('#fileInput').on('change', function() { $('#fileInput').on('change', function() {
const files = Array.from(this.files); const files = Array.from(this.files);
if (files.length > 0) { if (files.length > 0) {
@@ -107,7 +151,14 @@ $(document).ready(function() {
} }
}); });
// Handle message form submission /**
* Handles message form submission.
* Processes text messages and file attachments.
* Manages submission state and UI feedback.
* @event
* @param {Event} e - The form submission event
* @returns {boolean} false to prevent default form submission
*/
let isSubmitting = false; let isSubmitting = false;
$('#messageForm').off('submit').on('submit', function(e) { $('#messageForm').off('submit').on('submit', function(e) {
e.preventDefault(); e.preventDefault();
@@ -202,7 +253,10 @@ $(document).ready(function() {
return false; return false;
}); });
// Clean up on page unload /**
* Cleans up chat resources when the page is unloaded.
* @event
*/
$(window).on('beforeunload', function() { $(window).on('beforeunload', function() {
console.log('[Conversation] Cleaning up on page unload'); console.log('[Conversation] Cleaning up on page unload');
chat.cleanup(); chat.cleanup();

View File

@@ -1,4 +1,20 @@
// Debounce function /**
* @fileoverview Manages the conversations list functionality.
* This file handles:
* - Conversation search with debounced input
* - Search form submission
* - Clear filter functionality
*/
/**
* Creates a debounced version of a function that delays its execution
* until after a specified wait time has elapsed since the last time it was invoked.
* This helps prevent excessive form submissions during rapid user input.
* @function
* @param {Function} func - The function to debounce
* @param {number} wait - The number of milliseconds to delay (300ms default for search)
* @returns {Function} A debounced version of the provided function
*/
function debounce(func, wait) { function debounce(func, wait) {
let timeout; let timeout;
return function(...args) { return function(...args) {
@@ -7,6 +23,13 @@ function debounce(func, wait) {
}; };
} }
/**
* Initializes the conversations list functionality when the DOM is loaded.
* Sets up:
* - Search input with debounced form submission (300ms delay)
* - Clear filter button to reset search and refresh results
* @function
*/
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const searchInput = document.getElementById('conversationSearchInput'); const searchInput = document.getElementById('conversationSearchInput');
const form = document.getElementById('conversationFilterForm'); const form = document.getElementById('conversationFilterForm');

View File

@@ -1,3 +1,20 @@
/**
* @fileoverview Manages the conversation creation functionality.
* This file handles:
* - User selection interface using Select2
* - Member management for new conversations
* - Form submission with member data
* - UI updates and validation
*/
/**
* Initializes conversation creation functionality when the document is ready.
* Sets up:
* - Select2 dropdown for user selection
* - Member tracking and management
* - Form handling
* @function
*/
$(document).ready(function() { $(document).ready(function() {
$('.select2').select2({ $('.select2').select2({
theme: 'bootstrap-5', theme: 'bootstrap-5',
@@ -11,14 +28,22 @@ $(document).ready(function() {
var creatorId = document.querySelector('.member-row').dataset.userId; var creatorId = document.querySelector('.member-row').dataset.userId;
addedMembers.add(creatorId); addedMembers.add(creatorId);
// Function to show alert modal /**
* Shows an alert modal with the specified message.
* @function
* @param {string} message - The message to display in the alert modal
*/
function showAlert(message) { function showAlert(message) {
$('#alertModalMessage').text(message); $('#alertModalMessage').text(message);
var alertModal = new bootstrap.Modal(document.getElementById('alertModal')); var alertModal = new bootstrap.Modal(document.getElementById('alertModal'));
alertModal.show(); alertModal.show();
} }
// Handle Add Member button click /**
* Handles adding a new member to the conversation.
* Validates the selection and updates the UI accordingly.
* @event
*/
$('#addMemberBtn').click(function() { $('#addMemberBtn').click(function() {
var selectedUserId = $('#user_id').val(); var selectedUserId = $('#user_id').val();
var selectedUserName = $('#user_id option:selected').text(); var selectedUserName = $('#user_id option:selected').text();
@@ -69,6 +94,11 @@ $(document).ready(function() {
updateHiddenInputs(); updateHiddenInputs();
}); });
/**
* Updates the hidden form inputs with the current member list.
* Removes existing member inputs and adds new ones for each current member.
* @function
*/
function updateHiddenInputs() { function updateHiddenInputs() {
// Remove any existing members inputs // Remove any existing members inputs
$('#conversationForm input[name="members"]').remove(); $('#conversationForm input[name="members"]').remove();

View File

@@ -1,3 +1,12 @@
/**
* @fileoverview Provides debugging and maintenance functionality for the application.
* This file handles:
* - File system synchronization
* - Database state verification
* - Orphaned record cleanup
* - Mismatch detection and reporting
*/
// File system sync functionality // File system sync functionality
document.getElementById('syncFilesBtn').addEventListener('click', async function() { document.getElementById('syncFilesBtn').addEventListener('click', async function() {
const btn = this; const btn = this;
@@ -162,7 +171,13 @@ document.getElementById('cleanupOrphanedBtn').addEventListener('click', async fu
} }
}); });
// Helper function to update mismatch sections /**
* Updates a mismatch section in the UI with verification results.
* Displays counts and detailed information about mismatches.
* @function
* @param {string} sectionId - The ID of the section to update
* @param {Array} items - Array of mismatch items to display
*/
function updateMismatchSection(sectionId, items) { function updateMismatchSection(sectionId, items) {
const count = document.getElementById(`${sectionId}Count`); const count = document.getElementById(`${sectionId}Count`);
const list = document.getElementById(`${sectionId}List`); const list = document.getElementById(`${sectionId}List`);
@@ -211,7 +226,13 @@ function updateMismatchSection(sectionId, items) {
`).join(''); `).join('');
} }
// Helper function to format file sizes /**
* Formats a file size in bytes to a human-readable string.
* Converts to appropriate unit (B, KB, MB, GB, TB).
* @function
* @param {number} bytes - The size in bytes
* @returns {string} The formatted size string (e.g., "1.5 MB")
*/
function formatSize(bytes) { function formatSize(bytes) {
if (bytes === 0) return '0 B'; if (bytes === 0) return '0 B';
const k = 1024; const k = 1024;

View File

@@ -1,3 +1,14 @@
/**
* @fileoverview Manages the file grid view functionality.
* This file handles:
* - File grid and list view rendering
* - File sorting and filtering
* - File operations (star, restore, delete)
* - View preferences
* - Search functionality
* - File details display
*/
let currentView = 'grid'; let currentView = 'grid';
let lastSelectedIndex = -1; let lastSelectedIndex = -1;
let sortColumn = 'name'; // Set default sort column to name let sortColumn = 'name'; // Set default sort column to name
@@ -10,7 +21,12 @@ window.isAdmin = document.body.dataset.isAdmin === 'true';
// Check if we're on the trash page // Check if we're on the trash page
const isTrashPage = window.location.pathname.includes('/trash'); const isTrashPage = window.location.pathname.includes('/trash');
// Initialize the view and fetch files /**
* Initializes the file view and fetches files.
* Sets up the preferred view and initial file sorting.
* @async
* @function
*/
async function initializeView() { async function initializeView() {
try { try {
const response = await fetch('/api/user/preferred_view'); const response = await fetch('/api/user/preferred_view');
@@ -31,6 +47,12 @@ async function initializeView() {
sortFiles('name'); sortFiles('name');
} }
/**
* Toggles between grid and list views.
* Updates the UI and saves the user's view preference.
* @function
* @param {string} view - The view to switch to ('grid' or 'list')
*/
function toggleView(view) { function toggleView(view) {
currentView = view; currentView = view;
const grid = document.getElementById('fileGrid'); const grid = document.getElementById('fileGrid');
@@ -77,6 +99,12 @@ function toggleView(view) {
}); });
} }
/**
* Sorts files by the specified column.
* Handles different data types (string, number, date) appropriately.
* @function
* @param {string} column - The column to sort by ('name', 'modified', 'type', 'size', 'auto_delete')
*/
function sortFiles(column) { function sortFiles(column) {
if (sortColumn === column) { if (sortColumn === column) {
sortDirection *= -1; // Toggle direction sortDirection *= -1; // Toggle direction
@@ -102,6 +130,12 @@ function sortFiles(column) {
renderFiles(currentFiles); renderFiles(currentFiles);
} }
/**
* Gets the appropriate icon class for a file based on its extension.
* @function
* @param {string} filename - The name of the file
* @returns {string} The Font Awesome icon class for the file type
*/
function getFileIcon(filename) { function getFileIcon(filename) {
const extension = filename.split('.').pop().toLowerCase(); const extension = filename.split('.').pop().toLowerCase();
@@ -127,6 +161,12 @@ function getFileIcon(filename) {
return iconMap[extension] || 'fa-file'; return iconMap[extension] || 'fa-file';
} }
/**
* Renders the files in either grid or list view.
* Handles both trash and normal file views with appropriate actions.
* @function
* @param {Array} files - Array of file objects to render
*/
function renderFiles(files) { function renderFiles(files) {
if (!files) return; if (!files) return;
currentFiles = files; currentFiles = files;
@@ -223,6 +263,12 @@ function renderFiles(files) {
} }
} }
/**
* Fetches files from the server.
* Handles both trash and starred file endpoints.
* @async
* @function
*/
async function fetchFiles() { async function fetchFiles() {
try { try {
const endpoint = isTrashPage ? '/api/rooms/trash' : '/api/rooms/starred'; const endpoint = isTrashPage ? '/api/rooms/trash' : '/api/rooms/starred';
@@ -244,6 +290,11 @@ async function fetchFiles() {
} }
} }
/**
* Gets the CSRF token from various possible locations in the DOM.
* @function
* @returns {string} The CSRF token or empty string if not found
*/
function getCsrfToken() { function getCsrfToken() {
// First try to get it from the meta tag // First try to get it from the meta tag
const metaTag = document.querySelector('meta[name="csrf-token"]'); const metaTag = document.querySelector('meta[name="csrf-token"]');
@@ -275,6 +326,13 @@ function getCsrfToken() {
return ''; return '';
} }
/**
* Toggles the star status of a file.
* @function
* @param {string} filename - The name of the file
* @param {string} path - The path of the file
* @param {number} roomId - The ID of the room containing the file
*/
function toggleStar(filename, path = '', roomId) { function toggleStar(filename, path = '', roomId) {
const csrfToken = getCsrfToken(); const csrfToken = getCsrfToken();
if (!csrfToken) { if (!csrfToken) {
@@ -308,6 +366,13 @@ function toggleStar(filename, path = '', roomId) {
}); });
} }
/**
* Restores a file from the trash.
* @function
* @param {string} filename - The name of the file
* @param {string} path - The path of the file
* @param {number} roomId - The ID of the room containing the file
*/
function restoreFile(filename, path = '', roomId) { function restoreFile(filename, path = '', roomId) {
const csrfToken = getCsrfToken(); const csrfToken = getCsrfToken();
if (!csrfToken) { if (!csrfToken) {
@@ -341,6 +406,13 @@ function restoreFile(filename, path = '', roomId) {
}); });
} }
/**
* Shows the permanent delete confirmation modal.
* @function
* @param {string} filename - The name of the file
* @param {string} path - The path of the file
* @param {number} roomId - The ID of the room containing the file
*/
function showPermanentDeleteModal(filename, path = '', roomId) { function showPermanentDeleteModal(filename, path = '', roomId) {
fileToDelete = { filename, path, roomId }; fileToDelete = { filename, path, roomId };
document.getElementById('permanentDeleteItemName').textContent = filename; document.getElementById('permanentDeleteItemName').textContent = filename;
@@ -348,6 +420,10 @@ function showPermanentDeleteModal(filename, path = '', roomId) {
modal.show(); modal.show();
} }
/**
* Permanently deletes a file after confirmation.
* @function
*/
function permanentDeleteFile() { function permanentDeleteFile() {
if (!fileToDelete) return; if (!fileToDelete) return;
@@ -405,6 +481,14 @@ function permanentDeleteFile() {
}); });
} }
/**
* Navigates to a file or folder.
* @function
* @param {number} roomId - The ID of the room
* @param {string} filename - The name of the file/folder
* @param {string} path - The path of the file/folder
* @param {string} type - The type of item ('file' or 'folder')
*/
function navigateToFile(roomId, filename, path, type) { function navigateToFile(roomId, filename, path, type) {
if (type === 'folder') { if (type === 'folder') {
window.location.href = `/room/${roomId}?path=${encodeURIComponent(path ? path + '/' + filename : filename)}`; window.location.href = `/room/${roomId}?path=${encodeURIComponent(path ? path + '/' + filename : filename)}`;
@@ -413,11 +497,19 @@ function navigateToFile(roomId, filename, path, type) {
} }
} }
/**
* Shows the empty trash confirmation modal.
* @function
*/
function showEmptyTrashModal() { function showEmptyTrashModal() {
const modal = new bootstrap.Modal(document.getElementById('emptyTrashModal')); const modal = new bootstrap.Modal(document.getElementById('emptyTrashModal'));
modal.show(); modal.show();
} }
/**
* Empties the trash by permanently deleting all trashed files.
* @function
*/
function emptyTrash() { function emptyTrash() {
const csrfToken = getCsrfToken(); const csrfToken = getCsrfToken();
if (!csrfToken) { if (!csrfToken) {
@@ -465,6 +557,11 @@ function emptyTrash() {
}); });
} }
/**
* Shows the file details modal.
* @function
* @param {number} idx - The index of the file in the currentFiles array
*/
function showDetailsModal(idx) { function showDetailsModal(idx) {
const item = currentFiles[idx]; const item = currentFiles[idx];
const icon = item.type === 'folder' const icon = item.type === 'folder'
@@ -498,6 +595,12 @@ function showDetailsModal(idx) {
modal.show(); modal.show();
} }
/**
* Formats a date string to a localized format.
* @function
* @param {string} dateString - The date string to format
* @returns {string} The formatted date string
*/
function formatDate(dateString) { function formatDate(dateString) {
const date = new Date(dateString); const date = new Date(dateString);
return date.toLocaleString(); return date.toLocaleString();

View File

@@ -1,3 +1,21 @@
/**
* @fileoverview Manages room member management functionality.
* This file handles:
* - User selection interface using Select2
* - Adding new members to a room
* - Removing existing members
* - Form submission with member data
*/
/**
* Initializes member management functionality when the document is ready.
* Sets up:
* - Select2 dropdown for user selection
* - Member removal functionality
* - Member addition functionality
* - Form submission handling
* @function
*/
$(document).ready(function() { $(document).ready(function() {
// Initialize Select2 for user selection // Initialize Select2 for user selection
$('.select2').select2({ $('.select2').select2({
@@ -5,13 +23,22 @@ $(document).ready(function() {
width: '100%' width: '100%'
}); });
// Handle member removal /**
* Handles member removal from the list.
* Removes the member row from the UI when the remove button is clicked.
* @event
*/
$(document).on('click', '.btn-remove-member', function() { $(document).on('click', '.btn-remove-member', function() {
const memberRow = $(this).closest('.member-row'); const memberRow = $(this).closest('.member-row');
memberRow.remove(); memberRow.remove();
}); });
// Handle adding new member /**
* Handles adding a new member to the list.
* Validates the selection and adds the member if not already present.
* Creates a new member row with user details and adds it to the list.
* @event
*/
$('#addMemberBtn').on('click', function() { $('#addMemberBtn').on('click', function() {
const select = $('#user_id'); const select = $('#user_id');
const selectedOption = select.find('option:selected'); const selectedOption = select.find('option:selected');
@@ -52,7 +79,12 @@ $(document).ready(function() {
select.val(null).trigger('change'); select.val(null).trigger('change');
}); });
// Handle form submission /**
* Handles form submission.
* Collects all member IDs and adds them as hidden inputs to the form.
* @event
* @param {Event} e - The form submission event
*/
$('#membersForm').on('submit', function(e) { $('#membersForm').on('submit', function(e) {
e.preventDefault(); e.preventDefault();

View File

@@ -1,3 +1,18 @@
/**
* @fileoverview Manages room member functionality and permissions.
* This file handles:
* - User selection interface using Select2
* - Room member permissions management
* - Auto-saving permission changes
*/
/**
* Initializes room member functionality when the document is ready.
* Sets up:
* - Select2 dropdown for user selection with Bootstrap 5 theme
* - Auto-save functionality for permission changes
* @function
*/
$(document).ready(function() { $(document).ready(function() {
// Initialize Select2 for user selection // Initialize Select2 for user selection
$('.select2').select2({ $('.select2').select2({

View File

@@ -1,4 +1,20 @@
// Debounce function /**
* @fileoverview Manages the rooms list view functionality.
* This file handles:
* - Room list search with debounced input
* - Room filter form submission
* - Clear filter functionality for the rooms list
*/
/**
* Creates a debounced version of a function that delays its execution
* until after a specified wait time has elapsed since the last time it was invoked.
* This helps prevent excessive form submissions during rapid user input.
* @function
* @param {Function} func - The function to debounce
* @param {number} wait - The number of milliseconds to delay (300ms default for search)
* @returns {Function} A debounced version of the provided function
*/
function debounce(func, wait) { function debounce(func, wait) {
let timeout; let timeout;
return function(...args) { return function(...args) {
@@ -7,6 +23,13 @@ function debounce(func, wait) {
}; };
} }
/**
* Initializes the rooms list functionality when the DOM is loaded.
* Sets up event listeners for:
* - Search input with debounced form submission (300ms delay)
* - Clear filter button to reset search and refresh results
* @function
*/
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const searchInput = document.getElementById('roomSearchInput'); const searchInput = document.getElementById('roomSearchInput');
const form = document.getElementById('roomFilterForm'); const form = document.getElementById('roomFilterForm');

View File

@@ -1,4 +1,19 @@
// Debounce function /**
* @fileoverview Manages room search and filtering functionality.
* This file handles:
* - Room search input with debounced submission
* - Clear filter functionality
* - Form submission for room filtering
*/
/**
* Creates a debounced version of a function that delays its execution
* until after a specified wait time has elapsed since the last time it was invoked.
* @function
* @param {Function} func - The function to debounce
* @param {number} wait - The number of milliseconds to delay
* @returns {Function} A debounced version of the provided function
*/
function debounce(func, wait) { function debounce(func, wait) {
let timeout; let timeout;
return function(...args) { return function(...args) {
@@ -7,6 +22,13 @@ function debounce(func, wait) {
}; };
} }
/**
* Initializes room search and filtering functionality when the DOM is loaded.
* Sets up event listeners for:
* - Search input with debounced form submission
* - Clear filter button
* @function
*/
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const searchInput = document.getElementById('roomSearchInput'); const searchInput = document.getElementById('roomSearchInput');
const form = document.getElementById('roomFilterForm'); const form = document.getElementById('roomFilterForm');

View File

@@ -1,4 +1,23 @@
/**
* @fileoverview Manages file operations and state for the room interface.
* This file handles:
* - File fetching and state management
* - File operations (delete, rename, move, download)
* - File selection and batch operations
* - Star/unstar functionality
* - Navigation and path management
*/
/**
* @class FileManager
* @classdesc Manages all file-related operations and state in the room interface.
* Handles file operations, selection, and navigation.
*/
export class FileManager { export class FileManager {
/**
* Creates a new FileManager instance.
* @param {RoomManager} roomManager - The parent RoomManager instance
*/
constructor(roomManager) { constructor(roomManager) {
console.log('[FileManager] Initializing...'); console.log('[FileManager] Initializing...');
this.roomManager = roomManager; this.roomManager = roomManager;
@@ -13,6 +32,12 @@ export class FileManager {
console.log('[FileManager] Initialized with roomManager:', roomManager); console.log('[FileManager] Initialized with roomManager:', roomManager);
} }
/**
* Fetches files from the server for the current path.
* @async
* @returns {Promise<Array>} A promise that resolves with the array of files
* @throws {Error} If the fetch operation fails
*/
async fetchFiles() { async fetchFiles() {
console.log('[FileManager] Fetching files...'); console.log('[FileManager] Fetching files...');
try { try {
@@ -44,6 +69,13 @@ export class FileManager {
} }
} }
/**
* Deletes a file from the server.
* @async
* @param {string} filename - The name of the file to delete
* @param {string} [path=''] - The path of the file to delete
* @throws {Error} If the delete operation fails
*/
async deleteFile(filename, path = '') { async deleteFile(filename, path = '') {
console.log('[FileManager] Deleting file:', { filename, path }); console.log('[FileManager] Deleting file:', { filename, path });
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
@@ -90,6 +122,14 @@ export class FileManager {
} }
} }
/**
* Renames a file on the server.
* @async
* @param {string} fileId - The ID of the file to rename
* @param {string} newName - The new name for the file
* @returns {Promise<Object>} A promise that resolves with the result of the rename operation
* @throws {Error} If the rename operation fails
*/
async renameFile(fileId, newName) { async renameFile(fileId, newName) {
console.log('[FileManager] Renaming file:', { fileId, newName }); console.log('[FileManager] Renaming file:', { fileId, newName });
try { try {
@@ -127,6 +167,14 @@ export class FileManager {
} }
} }
/**
* Moves a file to a new location.
* @async
* @param {string} fileId - The ID of the file to move
* @param {string} targetPath - The target path to move the file to
* @returns {Promise<Object>} A promise that resolves with the result of the move operation
* @throws {Error} If the move operation fails
*/
async moveFile(fileId, targetPath) { async moveFile(fileId, targetPath) {
console.log('[FileManager] Starting moveFile...'); console.log('[FileManager] Starting moveFile...');
console.log('[FileManager] Parameters:', { fileId, targetPath }); console.log('[FileManager] Parameters:', { fileId, targetPath });
@@ -178,6 +226,11 @@ export class FileManager {
} }
} }
/**
* Handles the confirmed move operation after user confirmation.
* @async
* @throws {Error} If the move operation fails
*/
async moveFileConfirmed() { async moveFileConfirmed() {
console.log('[FileManager] Starting moveFileConfirmed...'); console.log('[FileManager] Starting moveFileConfirmed...');
console.log('[FileManager] Current state:', { console.log('[FileManager] Current state:', {
@@ -258,6 +311,13 @@ export class FileManager {
} }
} }
/**
* Toggles the star status of a file.
* @async
* @param {string} filename - The name of the file to toggle star for
* @param {string} path - The path of the file
* @throws {Error} If the star toggle operation fails
*/
async toggleStar(filename, path) { async toggleStar(filename, path) {
console.log('[FileManager] Toggling star for:', filename, 'path:', path); console.log('[FileManager] Toggling star for:', filename, 'path:', path);
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
@@ -323,6 +383,13 @@ export class FileManager {
} }
} }
/**
* Downloads a single file.
* @async
* @param {string} filename - The name of the file to download
* @param {string} [path=''] - The path of the file
* @throws {Error} If the download operation fails
*/
async downloadFile(filename, path = '') { async downloadFile(filename, path = '') {
console.log('[FileManager] Downloading file:', { filename, path }); console.log('[FileManager] Downloading file:', { filename, path });
const url = `/api/rooms/${this.roomManager.roomId}/files/${encodeURIComponent(filename)}`; const url = `/api/rooms/${this.roomManager.roomId}/files/${encodeURIComponent(filename)}`;
@@ -360,6 +427,11 @@ export class FileManager {
} }
} }
/**
* Downloads multiple selected files as a zip archive.
* @async
* @throws {Error} If the download operation fails
*/
async downloadSelected() { async downloadSelected() {
console.log('[FileManager] Downloading selected files...'); console.log('[FileManager] Downloading selected files...');
const selectedItems = this.getSelectedItems(); const selectedItems = this.getSelectedItems();
@@ -414,6 +486,12 @@ export class FileManager {
} }
} }
/**
* Handles the confirmed delete operation after user confirmation.
* Supports both single file and batch deletion.
* @async
* @throws {Error} If the delete operation fails
*/
async deleteFileConfirmed() { async deleteFileConfirmed() {
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
if (this.batchDeleteItems && this.batchDeleteItems.length) { if (this.batchDeleteItems && this.batchDeleteItems.length) {
@@ -476,6 +554,12 @@ export class FileManager {
} }
} }
/**
* Updates the file selection based on user interaction.
* Supports single selection, CTRL+click for multiple selection, and SHIFT+click for range selection.
* @param {number} index - The index of the file being selected
* @param {Event} event - The click event that triggered the selection
*/
updateSelection(index, event) { updateSelection(index, event) {
console.log('[FileManager] Updating selection:', { index, event }); console.log('[FileManager] Updating selection:', { index, event });
@@ -521,11 +605,18 @@ export class FileManager {
this.roomManager.viewManager.updateMultiSelectUI(); this.roomManager.viewManager.updateMultiSelectUI();
} }
/**
* Gets an array of currently selected file objects.
* @returns {Array<Object>} Array of selected file objects
*/
getSelectedItems() { getSelectedItems() {
console.log('[FileManager] Getting selected items'); console.log('[FileManager] Getting selected items');
return Array.from(this.selectedItems).map(index => this.currentFiles[index]); return Array.from(this.selectedItems).map(index => this.currentFiles[index]);
} }
/**
* Clears all file selections and updates the UI.
*/
clearSelection() { clearSelection() {
console.log('[FileManager] Clearing selection'); console.log('[FileManager] Clearing selection');
this.selectedItems.clear(); this.selectedItems.clear();
@@ -535,6 +626,9 @@ export class FileManager {
this.roomManager.viewManager.updateMultiSelectUI(); this.roomManager.viewManager.updateMultiSelectUI();
} }
/**
* Navigates to the parent folder of the current path.
*/
navigateToParent() { navigateToParent() {
if (!this.roomManager.currentPath) return; if (!this.roomManager.currentPath) return;
const parts = this.roomManager.currentPath.split('/'); const parts = this.roomManager.currentPath.split('/');

View File

@@ -1,4 +1,24 @@
/**
* @fileoverview Manages modal dialogs for the room interface.
* This file handles:
* - Modal initialization and configuration
* - File operations (delete, rename, move)
* - Folder creation
* - File details display
* - Overwrite confirmation
* - Batch operations
*/
/**
* @class ModalManager
* @classdesc Manages all modal dialogs and their interactions in the room interface.
* Handles file operations, folder creation, and various confirmation dialogs.
*/
export class ModalManager { export class ModalManager {
/**
* Creates a new ModalManager instance.
* @param {RoomManager} roomManager - The parent RoomManager instance
*/
constructor(roomManager) { constructor(roomManager) {
this.roomManager = roomManager; this.roomManager = roomManager;
@@ -16,6 +36,10 @@ export class ModalManager {
this.initializeModals(); this.initializeModals();
} }
/**
* Initializes event listeners for all modals.
* Sets up handlers for delete, new folder, rename, and move operations.
*/
initializeModals() { initializeModals() {
// Initialize delete modal // Initialize delete modal
if (this.roomManager.canDelete) { if (this.roomManager.canDelete) {
@@ -55,6 +79,11 @@ export class ModalManager {
} }
} }
/**
* Shows the delete confirmation modal for a single file.
* @param {string} filename - The name of the file to delete
* @param {string} [path=''] - The path of the file to delete
*/
showDeleteModal(filename, path = '') { showDeleteModal(filename, path = '') {
console.log('[ModalManager] Showing delete modal for:', { filename, path }); console.log('[ModalManager] Showing delete modal for:', { filename, path });
const fileNameEl = document.getElementById('deleteFileName'); const fileNameEl = document.getElementById('deleteFileName');
@@ -82,6 +111,10 @@ export class ModalManager {
this.deleteModal.show(); this.deleteModal.show();
} }
/**
* Shows the delete confirmation modal for multiple selected files.
* Processes all selected checkboxes and prepares for batch deletion.
*/
showBatchDeleteModal() { showBatchDeleteModal() {
const selectedCheckboxes = document.querySelectorAll('.select-item-checkbox:checked'); const selectedCheckboxes = document.querySelectorAll('.select-item-checkbox:checked');
if (selectedCheckboxes.length === 0) return; if (selectedCheckboxes.length === 0) return;
@@ -130,6 +163,10 @@ export class ModalManager {
this.deleteModal.show(); this.deleteModal.show();
} }
/**
* Shows the rename modal for a file or folder.
* @param {string} filename - The current name of the file/folder
*/
showRenameModal(filename) { showRenameModal(filename) {
document.getElementById('renameError').textContent = ''; document.getElementById('renameError').textContent = '';
const ext = filename.includes('.') ? filename.substring(filename.lastIndexOf('.')) : ''; const ext = filename.includes('.') ? filename.substring(filename.lastIndexOf('.')) : '';
@@ -149,6 +186,10 @@ export class ModalManager {
}, 100); }, 100);
} }
/**
* Shows the file details modal.
* @param {Object} file - The file object containing details to display
*/
showDetailsModal(file) { showDetailsModal(file) {
const icon = file.type === 'folder' const icon = file.type === 'folder'
? `<i class='fas fa-folder' style='font-size:2.2rem;color:var(--primary-color);'></i>` ? `<i class='fas fa-folder' style='font-size:2.2rem;color:var(--primary-color);'></i>`
@@ -182,6 +223,11 @@ export class ModalManager {
this.detailsModal.show(); this.detailsModal.show();
} }
/**
* Shows the overwrite confirmation modal.
* @param {string} filename - The name of the file that would be overwritten
* @returns {Promise<string>} A promise that resolves with the user's choice ('skip', 'skip_all', 'overwrite', or 'overwrite_all')
*/
showOverwriteModal(filename) { showOverwriteModal(filename) {
return new Promise((resolve) => { return new Promise((resolve) => {
const fileNameEl = document.getElementById('overwriteFileName'); const fileNameEl = document.getElementById('overwriteFileName');
@@ -206,6 +252,11 @@ export class ModalManager {
}); });
} }
/**
* Shows the move file modal.
* @param {string} fileId - The ID of the file to move
* @param {string} path - The current path of the file
*/
showMoveModal(fileId, path) { showMoveModal(fileId, path) {
console.log('[ModalManager] Showing move modal for file:', { fileId, path }); console.log('[ModalManager] Showing move modal for file:', { fileId, path });
document.getElementById('moveError').textContent = ''; document.getElementById('moveError').textContent = '';
@@ -251,6 +302,10 @@ export class ModalManager {
}); });
} }
/**
* Creates a new folder in the current path.
* @async
*/
async createFolder() { async createFolder() {
const folderName = document.getElementById('folderNameInput').value.trim(); const folderName = document.getElementById('folderNameInput').value.trim();
if (!folderName) { if (!folderName) {
@@ -290,6 +345,10 @@ export class ModalManager {
} }
} }
/**
* Renames a file or folder.
* @async
*/
async renameFile() { async renameFile() {
const newName = document.getElementById('renameInput').value.trim(); const newName = document.getElementById('renameInput').value.trim();
if (!newName) { if (!newName) {

View File

@@ -1,3 +1,13 @@
/**
* @fileoverview Main room management module that coordinates all room functionality.
* This file handles:
* - Room initialization and configuration
* - Manager coordination (File, View, Upload, Search, Modal)
* - Navigation and path management
* - Event handling and user interactions
* - Permission management
*/
console.log('[RoomManager] Script loaded'); console.log('[RoomManager] Script loaded');
// Main room.js file - Coordinates all room functionality // Main room.js file - Coordinates all room functionality
@@ -9,7 +19,23 @@ import { ModalManager } from './modalManager.js';
console.log('[RoomManager] All modules imported successfully'); console.log('[RoomManager] All modules imported successfully');
/**
* @class RoomManager
* @classdesc Main class that coordinates all room functionality and manages the various
* sub-managers (File, View, Upload, Search, Modal).
*/
class RoomManager { class RoomManager {
/**
* Creates a new RoomManager instance.
* @param {Object} config - Configuration object for the room
* @param {string} config.roomId - The ID of the room
* @param {boolean} config.canDelete - Whether the user can delete files
* @param {boolean} config.canShare - Whether the user can share files
* @param {boolean} config.canUpload - Whether the user can upload files
* @param {boolean} config.canDownload - Whether the user can download files
* @param {boolean} config.canRename - Whether the user can rename files
* @param {boolean} config.canMove - Whether the user can move files
*/
constructor(config) { constructor(config) {
console.log('[RoomManager] Initializing with config:', config); console.log('[RoomManager] Initializing with config:', config);
this.roomId = config.roomId; this.roomId = config.roomId;
@@ -33,6 +59,11 @@ class RoomManager {
this.initialize(); this.initialize();
} }
/**
* Initializes the room functionality.
* Sets up the view, fetches files, initializes search, and sets up event listeners.
* @async
*/
async initialize() { async initialize() {
console.log('[RoomManager] Starting initialization...'); console.log('[RoomManager] Starting initialization...');
// Get current path from URL // Get current path from URL
@@ -68,12 +99,19 @@ class RoomManager {
} }
} }
/**
* Navigates to a subfolder within the current path.
* @param {string} folderName - The name of the folder to navigate to
*/
navigateToFolder(folderName) { navigateToFolder(folderName) {
console.log('[RoomManager] Navigating to folder:', folderName); console.log('[RoomManager] Navigating to folder:', folderName);
const newPath = this.currentPath ? `${this.currentPath}/${folderName}` : folderName; const newPath = this.currentPath ? `${this.currentPath}/${folderName}` : folderName;
this.navigateTo(newPath); this.navigateTo(newPath);
} }
/**
* Navigates to the parent folder of the current path.
*/
navigateToParent() { navigateToParent() {
console.log('[RoomManager] Navigating to parent folder'); console.log('[RoomManager] Navigating to parent folder');
if (!this.currentPath) return; if (!this.currentPath) return;
@@ -84,6 +122,10 @@ class RoomManager {
this.navigateTo(newPath); this.navigateTo(newPath);
} }
/**
* Navigates to a specific path and updates the URL.
* @param {string} path - The path to navigate to
*/
navigateTo(path) { navigateTo(path) {
console.log('[RoomManager] Navigating to path:', path); console.log('[RoomManager] Navigating to path:', path);
this.currentPath = path; this.currentPath = path;
@@ -101,11 +143,20 @@ class RoomManager {
}); });
} }
/**
* Gets the current path from the URL parameters.
* @returns {string} The current path from the URL
*/
getPathFromUrl() { getPathFromUrl() {
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('path') || ''; return urlParams.get('path') || '';
} }
/**
* Initializes event listeners for user interactions.
* Sets up handlers for select all, download selected, delete selected,
* and click outside to clear selection.
*/
initializeEventListeners() { initializeEventListeners() {
console.log('[RoomManager] Setting up event listeners'); console.log('[RoomManager] Setting up event listeners');
@@ -151,7 +202,10 @@ class RoomManager {
} }
} }
// Initialize the room manager when the DOM is loaded /**
* Initializes the room manager when the DOM is loaded.
* Creates a new RoomManager instance with configuration from meta tags.
*/
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const config = { const config = {
roomId: document.querySelector('meta[name="room-id"]').getAttribute('content'), roomId: document.querySelector('meta[name="room-id"]').getAttribute('content'),

View File

@@ -1,10 +1,34 @@
/**
* @fileoverview Manages search functionality for the room interface.
* This file handles:
* - Quick search input processing
* - Debounced search execution
* - File filtering based on search terms
* - Search state management
* - Clear search functionality
*/
/**
* @class SearchManager
* @classdesc Manages search operations including input handling, debounced search execution,
* and file filtering based on search criteria.
*/
export class SearchManager { export class SearchManager {
/**
* Creates a new SearchManager instance.
* @param {RoomManager} roomManager - The parent RoomManager instance
*/
constructor(roomManager) { constructor(roomManager) {
this.roomManager = roomManager; this.roomManager = roomManager;
this.searchInput = document.getElementById('quickSearchInput'); this.searchInput = document.getElementById('quickSearchInput');
this.clearSearchBtn = document.getElementById('clearSearchBtn'); this.clearSearchBtn = document.getElementById('clearSearchBtn');
} }
/**
* Initializes the search functionality.
* Sets up event listeners for search input and clear button.
* Creates a debounced search function to prevent excessive searches.
*/
initialize() { initialize() {
if (!this.searchInput) return; if (!this.searchInput) return;
@@ -35,6 +59,11 @@ export class SearchManager {
} }
} }
/**
* Performs the search operation on the current file list.
* Filters files based on name and type matching the search term.
* @param {string} searchTerm - The term to search for
*/
performSearch(searchTerm) { performSearch(searchTerm) {
if (!this.roomManager.fileManager.currentFiles) return; if (!this.roomManager.fileManager.currentFiles) return;
@@ -47,6 +76,14 @@ export class SearchManager {
this.roomManager.viewManager.renderFiles(filteredFiles); this.roomManager.viewManager.renderFiles(filteredFiles);
} }
/**
* Creates a debounced version of a function.
* Delays the execution of the function until after a specified wait time
* has elapsed since the last time it was invoked.
* @param {Function} func - The function to debounce
* @param {number} wait - The number of milliseconds to delay
* @returns {Function} The debounced function
*/
debounce(func, wait) { debounce(func, wait) {
let timeout; let timeout;
return function executedFunction(...args) { return function executedFunction(...args) {

View File

@@ -1,4 +1,24 @@
/**
* @fileoverview Manages file upload functionality for the room interface.
* This file handles:
* - File upload initialization and configuration
* - Drag and drop file handling
* - Upload progress tracking
* - File type validation
* - Overwrite handling
* - Batch upload management
*/
/**
* @class UploadManager
* @classdesc Manages file upload operations including drag-and-drop, progress tracking,
* and handling of file conflicts and validations.
*/
export class UploadManager { export class UploadManager {
/**
* Creates a new UploadManager instance.
* @param {RoomManager} roomManager - The parent RoomManager instance
*/
constructor(roomManager) { constructor(roomManager) {
this.roomManager = roomManager; this.roomManager = roomManager;
this.pendingUploads = []; this.pendingUploads = [];
@@ -19,6 +39,10 @@ export class UploadManager {
this.initializeUploadHandlers(); this.initializeUploadHandlers();
} }
/**
* Initializes event handlers for file upload functionality.
* Sets up drag and drop handlers and file input change handlers.
*/
initializeUploadHandlers() { initializeUploadHandlers() {
if (!this.roomManager.canUpload) return; if (!this.roomManager.canUpload) return;
@@ -44,19 +68,33 @@ export class UploadManager {
this.fileInput.addEventListener('change', this.handleFileSelect.bind(this)); this.fileInput.addEventListener('change', this.handleFileSelect.bind(this));
} }
/**
* Prevents default browser behavior for drag and drop events.
* @param {Event} e - The event object
*/
preventDefaults(e) { preventDefaults(e) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
} }
/**
* Highlights the drop zone when files are dragged over it.
*/
highlight() { highlight() {
this.dropZoneOverlay.style.display = 'block'; this.dropZoneOverlay.style.display = 'block';
} }
/**
* Removes highlight from the drop zone.
*/
unhighlight() { unhighlight() {
this.dropZoneOverlay.style.display = 'none'; this.dropZoneOverlay.style.display = 'none';
} }
/**
* Handles files dropped onto the drop zone.
* @param {DragEvent} e - The drop event object
*/
async handleDrop(e) { async handleDrop(e) {
const dt = e.dataTransfer; const dt = e.dataTransfer;
const files = dt.files; const files = dt.files;
@@ -66,11 +104,18 @@ export class UploadManager {
} }
} }
/**
* Handles files selected through the file input.
*/
async handleFileSelect() { async handleFileSelect() {
if (!this.fileInput.files.length) return; if (!this.fileInput.files.length) return;
await this.startUpload(Array.from(this.fileInput.files)); await this.startUpload(Array.from(this.fileInput.files));
} }
/**
* Initiates the upload process for a set of files.
* @param {Array<File>} files - Array of files to upload
*/
async startUpload(files) { async startUpload(files) {
this.uploadProgressContainer.style.display = 'block'; this.uploadProgressContainer.style.display = 'block';
this.uploadProgressBar.style.width = '0%'; this.uploadProgressBar.style.width = '0%';
@@ -85,6 +130,10 @@ export class UploadManager {
await this.uploadFilesSequentially(); await this.uploadFilesSequentially();
} }
/**
* Uploads files one at a time, handling progress and errors.
* @async
*/
async uploadFilesSequentially() { async uploadFilesSequentially() {
let completedFiles = 0; let completedFiles = 0;
let currentFileIndex = 0; let currentFileIndex = 0;
@@ -182,6 +231,11 @@ export class UploadManager {
await processNextFile(); await processNextFile();
} }
/**
* Uploads a single file to the server.
* @param {FormData} formData - Form data containing the file and upload parameters
* @returns {Promise<Object>} Response object containing success status and error message if any
*/
async uploadFile(formData) { async uploadFile(formData) {
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
const response = await fetch(`/api/rooms/${this.roomManager.roomId}/files/upload`, { const response = await fetch(`/api/rooms/${this.roomManager.roomId}/files/upload`, {
@@ -197,6 +251,11 @@ export class UploadManager {
}; };
} }
/**
* Handles file type validation errors.
* Displays error message with allowed file types.
* @param {File} file - The file that failed validation
*/
handleFileTypeError(file) { handleFileTypeError(file) {
const allowedTypes = [ const allowedTypes = [
'Documents: PDF, DOCX, DOC, TXT, RTF, ODT, MD, CSV', 'Documents: PDF, DOCX, DOC, TXT, RTF, ODT, MD, CSV',
@@ -225,6 +284,12 @@ export class UploadManager {
this.uploadProgressBar.className = 'progress-bar bg-danger-opacity-15 text-danger'; this.uploadProgressBar.className = 'progress-bar bg-danger-opacity-15 text-danger';
} }
/**
* Handles file existence conflicts during upload.
* @param {File} file - The file being uploaded
* @param {FormData} formData - Form data for the upload
* @returns {Promise<Object>} Object containing continue status
*/
async handleFileExists(file, formData) { async handleFileExists(file, formData) {
if (this.overwriteAll) { if (this.overwriteAll) {
formData.append('overwrite', 'true'); formData.append('overwrite', 'true');

View File

@@ -1,4 +1,23 @@
/**
* @fileoverview Manages the file view functionality for the room interface.
* This file handles:
* - Grid and list view rendering
* - File sorting and organization
* - Breadcrumb navigation
* - File action buttons
* - Multi-select functionality
*/
/**
* @class ViewManager
* @classdesc Manages the visual representation and interaction of files in the room interface.
* Handles view switching, file rendering, sorting, and UI updates.
*/
export class ViewManager { export class ViewManager {
/**
* Creates a new ViewManager instance.
* @param {RoomManager} roomManager - The parent RoomManager instance
*/
constructor(roomManager) { constructor(roomManager) {
console.log('[ViewManager] Initializing...'); console.log('[ViewManager] Initializing...');
this.roomManager = roomManager; this.roomManager = roomManager;
@@ -8,6 +27,11 @@ export class ViewManager {
console.log('[ViewManager] Initialized with roomManager:', roomManager); console.log('[ViewManager] Initialized with roomManager:', roomManager);
} }
/**
* Initializes the view with user preferences.
* Fetches and applies the user's preferred view type.
* @async
*/
async initializeView() { async initializeView() {
console.log('[ViewManager] Initializing view...'); console.log('[ViewManager] Initializing view...');
try { try {
@@ -29,6 +53,12 @@ export class ViewManager {
} }
} }
/**
* Toggles between grid and list views.
* Updates UI and saves user preference.
* @async
* @param {string} view - The view type to switch to ('grid' or 'list')
*/
async toggleView(view) { async toggleView(view) {
console.log('[ViewManager] Toggling view to:', view); console.log('[ViewManager] Toggling view to:', view);
this.currentView = view; this.currentView = view;
@@ -60,6 +90,11 @@ export class ViewManager {
} }
} }
/**
* Renders the file list in the current view mode.
* @async
* @param {Array<Object>} files - Array of file objects to render
*/
async renderFiles(files) { async renderFiles(files) {
console.log('[ViewManager] Rendering files:', files); console.log('[ViewManager] Rendering files:', files);
const fileGrid = document.getElementById('fileGrid'); const fileGrid = document.getElementById('fileGrid');
@@ -83,6 +118,10 @@ export class ViewManager {
} }
} }
/**
* Renders the breadcrumb navigation.
* Shows the current path and provides navigation controls.
*/
renderBreadcrumb() { renderBreadcrumb() {
console.log('[ViewManager] Rendering breadcrumb'); console.log('[ViewManager] Rendering breadcrumb');
const breadcrumb = document.getElementById('breadcrumb'); const breadcrumb = document.getElementById('breadcrumb');
@@ -140,6 +179,11 @@ export class ViewManager {
console.log('[ViewManager] Breadcrumb rendered'); console.log('[ViewManager] Breadcrumb rendered');
} }
/**
* Renders the file list in list view mode.
* @async
* @param {Array<Object>} files - Array of file objects to render
*/
async renderListView(files) { async renderListView(files) {
console.log('[ViewManager] Rendering list view'); console.log('[ViewManager] Rendering list view');
const fileGrid = document.getElementById('fileGrid'); const fileGrid = document.getElementById('fileGrid');
@@ -172,6 +216,11 @@ export class ViewManager {
console.log('[ViewManager] List view rendered'); console.log('[ViewManager] List view rendered');
} }
/**
* Renders the file list in grid view mode.
* @async
* @param {Array<Object>} files - Array of file objects to render
*/
async renderGridView(files) { async renderGridView(files) {
console.log('[ViewManager] Rendering grid view'); console.log('[ViewManager] Rendering grid view');
const fileGrid = document.getElementById('fileGrid'); const fileGrid = document.getElementById('fileGrid');
@@ -193,6 +242,12 @@ export class ViewManager {
console.log('[ViewManager] Grid view rendered'); console.log('[ViewManager] Grid view rendered');
} }
/**
* Renders a single file row for list view.
* @param {Object} file - The file object to render
* @param {number} index - The index of the file in the list
* @returns {string} HTML string for the file row
*/
renderFileRow(file, index) { renderFileRow(file, index) {
console.log('[ViewManager] Rendering file row:', { file, index }); console.log('[ViewManager] Rendering file row:', { file, index });
const isFolder = file.type === 'folder'; const isFolder = file.type === 'folder';
@@ -226,6 +281,12 @@ export class ViewManager {
`; `;
} }
/**
* Renders a single file card for grid view.
* @param {Object} file - The file object to render
* @param {number} index - The index of the file in the list
* @returns {string} HTML string for the file card
*/
renderFileCard(file, index) { renderFileCard(file, index) {
console.log('[ViewManager] Rendering file card:', { file, index }); console.log('[ViewManager] Rendering file card:', { file, index });
const isFolder = file.type === 'folder'; const isFolder = file.type === 'folder';
@@ -260,6 +321,12 @@ export class ViewManager {
`; `;
} }
/**
* Renders the action buttons for a file.
* @param {Object} file - The file object
* @param {number} index - The index of the file in the list
* @returns {string} HTML string for the action buttons
*/
renderFileActions(file, index) { renderFileActions(file, index) {
console.log('[ViewManager] Rendering file actions:', { file, index }); console.log('[ViewManager] Rendering file actions:', { file, index });
const actions = []; const actions = [];
@@ -321,6 +388,11 @@ export class ViewManager {
return actions.join(''); return actions.join('');
} }
/**
* Sorts the file list based on current sort settings.
* @param {Array<Object>} files - Array of file objects to sort
* @returns {Array<Object>} Sorted array of file objects
*/
sortFiles(files) { sortFiles(files) {
console.log('[ViewManager] Sorting files:', { console.log('[ViewManager] Sorting files:', {
column: this.sortColumn, column: this.sortColumn,
@@ -342,6 +414,11 @@ export class ViewManager {
}); });
} }
/**
* Gets the appropriate icon class for a file based on its extension.
* @param {string} filename - The name of the file
* @returns {string} Font Awesome icon class name
*/
getFileIcon(filename) { getFileIcon(filename) {
const extension = filename.split('.').pop().toLowerCase(); const extension = filename.split('.').pop().toLowerCase();
console.log('[ViewManager] Getting icon for file:', { filename, extension }); console.log('[ViewManager] Getting icon for file:', { filename, extension });
@@ -368,6 +445,11 @@ export class ViewManager {
return iconMap[extension] || 'fa-file'; return iconMap[extension] || 'fa-file';
} }
/**
* Formats a file size in bytes to a human-readable string.
* @param {number} bytes - The file size in bytes
* @returns {string} Formatted file size string
*/
formatFileSize(bytes) { formatFileSize(bytes) {
if (!bytes) return '0 B'; if (!bytes) return '0 B';
@@ -383,6 +465,9 @@ export class ViewManager {
return `${size.toFixed(1)} ${units[unitIndex]}`; return `${size.toFixed(1)} ${units[unitIndex]}`;
} }
/**
* Updates the multi-select UI based on current selection state.
*/
updateMultiSelectUI() { updateMultiSelectUI() {
console.log('[ViewManager] Updating multi-select UI'); console.log('[ViewManager] Updating multi-select UI');
const selectedItems = this.roomManager.fileManager.getSelectedItems(); const selectedItems = this.roomManager.fileManager.getSelectedItems();

View File

@@ -1,3 +1,13 @@
/**
* @fileoverview Manages the application settings functionality.
* This file handles color settings, tab persistence, and UI customization.
* It provides functionality to:
* - Manage primary and secondary color schemes
* - Handle settings tab navigation and persistence
* - Convert and manipulate colors (hex, RGB)
* - Update UI elements with new color schemes
*/
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const primaryColorInput = document.getElementById('primaryColor'); const primaryColorInput = document.getElementById('primaryColor');
const secondaryColorInput = document.getElementById('secondaryColor'); const secondaryColorInput = document.getElementById('secondaryColor');
@@ -7,7 +17,12 @@ document.addEventListener('DOMContentLoaded', function() {
const settingsTabs = document.querySelectorAll('#settingsTabs button[data-bs-toggle="tab"]'); const settingsTabs = document.querySelectorAll('#settingsTabs button[data-bs-toggle="tab"]');
const tabContent = document.querySelectorAll('.tab-pane'); const tabContent = document.querySelectorAll('.tab-pane');
// Function to activate a specific tab /**
* Activates a specific settings tab and updates the UI accordingly.
* Also persists the selected tab in localStorage.
* @function
* @param {string} tabId - The ID of the tab to activate
*/
function activateTab(tabId) { function activateTab(tabId) {
// Remove active class from all tabs and content // Remove active class from all tabs and content
settingsTabs.forEach(tab => { settingsTabs.forEach(tab => {
@@ -42,7 +57,12 @@ document.addEventListener('DOMContentLoaded', function() {
const savedTab = localStorage.getItem('settingsActiveTab') || window.location.hash.substring(1) || 'colors'; const savedTab = localStorage.getItem('settingsActiveTab') || window.location.hash.substring(1) || 'colors';
activateTab(savedTab); activateTab(savedTab);
// Color manipulation functions /**
* Converts a hexadecimal color code to RGB values.
* @function
* @param {string} hex - The hexadecimal color code (e.g., '#FF0000')
* @returns {Object|null} Object containing r, g, b values or null if invalid hex
*/
function hexToRgb(hex) { function hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? { return result ? {
@@ -52,6 +72,14 @@ document.addEventListener('DOMContentLoaded', function() {
} : null; } : null;
} }
/**
* Converts RGB values to a hexadecimal color code.
* @function
* @param {number} r - Red value (0-255)
* @param {number} g - Green value (0-255)
* @param {number} b - Blue value (0-255)
* @returns {string} Hexadecimal color code
*/
function rgbToHex(r, g, b) { function rgbToHex(r, g, b) {
return '#' + [r, g, b].map(x => { return '#' + [r, g, b].map(x => {
const hex = x.toString(16); const hex = x.toString(16);
@@ -59,6 +87,13 @@ document.addEventListener('DOMContentLoaded', function() {
}).join(''); }).join('');
} }
/**
* Lightens a color by a specified amount.
* @function
* @param {string} color - The hexadecimal color to lighten
* @param {number} amount - The amount to lighten (0-1)
* @returns {string} The lightened hexadecimal color
*/
function lightenColor(color, amount) { function lightenColor(color, amount) {
const rgb = hexToRgb(color); const rgb = hexToRgb(color);
if (!rgb) return color; if (!rgb) return color;
@@ -70,6 +105,13 @@ document.addEventListener('DOMContentLoaded', function() {
); );
} }
/**
* Darkens a color by a specified amount.
* @function
* @param {string} color - The hexadecimal color to darken
* @param {number} amount - The amount to darken (0-1)
* @returns {string} The darkened hexadecimal color
*/
function darkenColor(color, amount) { function darkenColor(color, amount) {
const rgb = hexToRgb(color); const rgb = hexToRgb(color);
if (!rgb) return color; if (!rgb) return color;
@@ -81,6 +123,13 @@ document.addEventListener('DOMContentLoaded', function() {
); );
} }
/**
* Updates all color-related UI elements based on a new color value.
* Updates CSS variables, chart colors, and color previews.
* @function
* @param {string} color - The new color value in hexadecimal
* @param {boolean} isPrimary - Whether this is the primary color (true) or secondary color (false)
*/
function updateAllColors(color, isPrimary) { function updateAllColors(color, isPrimary) {
const prefix = isPrimary ? 'primary' : 'secondary'; const prefix = isPrimary ? 'primary' : 'secondary';
@@ -162,7 +211,11 @@ document.addEventListener('DOMContentLoaded', function() {
updateAllColors(secondaryColorInput.value, false); updateAllColors(secondaryColorInput.value, false);
}); });
// Initialize colors from database values /**
* Initializes the color settings from the current CSS variables.
* Updates input values and color previews.
* @function
*/
function initializeColors() { function initializeColors() {
// Get the current computed CSS variable values // Get the current computed CSS variable values
const computedPrimaryColor = getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim(); const computedPrimaryColor = getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim();

View File

@@ -1,10 +1,29 @@
/**
* @fileoverview Manages the starred files functionality for the document management system.
* This file handles the starred files view and user view preferences.
* It provides functionality to view starred items in either grid or list view
* and manages user view preferences for the starred items section.
*/
let currentView = 'grid'; let currentView = 'grid';
/**
* Initializes the starred files view when the DOM content is loaded.
* Sets up the initial view based on user preferences.
* @function
*/
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Initialize view // Initialize view
initializeView(); initializeView();
}); });
/**
* Initializes the view based on user preferences.
* Fetches the user's preferred view (grid or list) and applies it.
* Falls back to grid view if there's an error.
* @async
* @function
*/
async function initializeView() { async function initializeView() {
try { try {
const response = await fetch('/api/user/preferred_view'); const response = await fetch('/api/user/preferred_view');
@@ -18,6 +37,13 @@ async function initializeView() {
} }
} }
/**
* Toggles between grid and list views for starred files.
* Updates the UI and saves the user's view preference.
* @function
* @param {string} view - The view to switch to ('grid' or 'list')
* @throws {Error} If the view preference cannot be saved
*/
function toggleView(view) { function toggleView(view) {
currentView = view; currentView = view;
const grid = document.getElementById('fileGrid'); const grid = document.getElementById('fileGrid');

View File

@@ -1,6 +1,18 @@
/**
* @fileoverview Manages the trash functionality for the document management system.
* This file handles the trash view, empty trash operations, and view preferences.
* It provides functionality to view trashed items in grid or list view,
* empty the trash, and manage user view preferences.
*/
let currentView = 'grid'; let currentView = 'grid';
// Make functions globally available /**
* Shows the empty trash confirmation modal.
* @function
* @global
* @throws {Error} If the modal element is not found or if there's an error showing the modal
*/
window.showEmptyTrashModal = function() { window.showEmptyTrashModal = function() {
console.log('Showing Empty Trash Modal'); console.log('Showing Empty Trash Modal');
const modalEl = document.getElementById('emptyTrashModal'); const modalEl = document.getElementById('emptyTrashModal');
@@ -20,6 +32,16 @@ window.showEmptyTrashModal = function() {
} }
}; };
/**
* Empties the trash by permanently deleting all trashed files.
* This function:
* 1. Fetches all trashed files to get their room IDs
* 2. Makes API calls to empty trash in each room
* 3. Updates the UI to reflect the changes
* @function
* @global
* @throws {Error} If CSRF token is not available or if API calls fail
*/
window.emptyTrash = function() { window.emptyTrash = function() {
console.log('Emptying Trash'); console.log('Emptying Trash');
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
@@ -85,6 +107,11 @@ window.emptyTrash = function() {
}); });
}; };
/**
* Initializes the trash view when the DOM content is loaded.
* Sets up event listeners for empty trash functionality and initializes the view.
* @function
*/
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
console.log('DOM Content Loaded'); console.log('DOM Content Loaded');
@@ -112,6 +139,13 @@ document.addEventListener('DOMContentLoaded', function() {
initializeView(); initializeView();
}); });
/**
* Initializes the view based on user preferences.
* Fetches the user's preferred view (grid or list) and applies it.
* Falls back to grid view if there's an error.
* @async
* @function
*/
async function initializeView() { async function initializeView() {
try { try {
const response = await fetch('/api/user/preferred_view'); const response = await fetch('/api/user/preferred_view');
@@ -125,6 +159,13 @@ async function initializeView() {
} }
} }
/**
* Toggles between grid and list views.
* Updates the UI and saves the user's view preference.
* @function
* @param {string} view - The view to switch to ('grid' or 'list')
* @throws {Error} If the view preference cannot be saved
*/
function toggleView(view) { function toggleView(view) {
currentView = view; currentView = view;
const grid = document.getElementById('fileGrid'); const grid = document.getElementById('fileGrid');