session persistent Items Per Page & dark/light modes
This commit is contained in:
230
auth.js
230
auth.js
@@ -1,11 +1,10 @@
|
||||
// auth.js
|
||||
|
||||
import { sendRequest } from './networkUtils.js';
|
||||
import { toggleVisibility, showToast } from './domUtils.js';
|
||||
// Import loadFileList from fileManager.js to refresh the file list upon login.
|
||||
import { loadFileList } from './fileManager.js';
|
||||
// Import loadFileList and renderFileTable from fileManager.js to refresh the file list upon login.
|
||||
import { loadFileList, renderFileTable, displayFilePreview, initFileActions } from './fileManager.js';
|
||||
import { loadFolderTree } from './folderManager.js';
|
||||
|
||||
export function initAuth() {
|
||||
function initAuth() {
|
||||
// First, check if the user is already authenticated.
|
||||
checkAuthentication();
|
||||
|
||||
@@ -31,99 +30,99 @@ export function initAuth() {
|
||||
})
|
||||
.catch(error => console.error("❌ Error logging in:", error));
|
||||
});
|
||||
|
||||
// Set up the logout button.
|
||||
document.getElementById("logoutBtn").addEventListener("click", function () {
|
||||
fetch("logout.php", {
|
||||
method: "POST",
|
||||
credentials: "include" // Ensure the session cookie is sent.
|
||||
})
|
||||
.then(() => window.location.reload(true))
|
||||
.catch(error => console.error("Logout error:", error));
|
||||
});
|
||||
|
||||
// Set up Add User functionality.
|
||||
document.getElementById("addUserBtn").addEventListener("click", function () {
|
||||
resetUserForm();
|
||||
toggleVisibility("addUserModal", true);
|
||||
});
|
||||
|
||||
document.getElementById("saveUserBtn").addEventListener("click", function () {
|
||||
const newUsername = document.getElementById("newUsername").value.trim();
|
||||
const newPassword = document.getElementById("newPassword").value.trim();
|
||||
const isAdmin = document.getElementById("isAdmin").checked;
|
||||
if (!newUsername || !newPassword) {
|
||||
showToast("Username and password are required!");
|
||||
return;
|
||||
}
|
||||
let url = "addUser.php";
|
||||
if (window.setupMode) {
|
||||
url += "?setup=1";
|
||||
}
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ username: newUsername, password: newPassword, isAdmin })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast("User added successfully!");
|
||||
closeAddUserModal();
|
||||
checkAuthentication();
|
||||
} else {
|
||||
showToast("Error: " + (data.error || "Could not add user"));
|
||||
}
|
||||
})
|
||||
.catch(error => console.error("Error adding user:", error));
|
||||
});
|
||||
|
||||
document.getElementById("cancelUserBtn").addEventListener("click", function () {
|
||||
closeAddUserModal();
|
||||
});
|
||||
|
||||
// Set up Remove User functionality.
|
||||
document.getElementById("removeUserBtn").addEventListener("click", function () {
|
||||
loadUserList();
|
||||
toggleVisibility("removeUserModal", true);
|
||||
});
|
||||
|
||||
document.getElementById("deleteUserBtn").addEventListener("click", function () {
|
||||
const selectElem = document.getElementById("removeUsernameSelect");
|
||||
const usernameToRemove = selectElem.value;
|
||||
if (!usernameToRemove) {
|
||||
showToast("Please select a user to remove.");
|
||||
return;
|
||||
}
|
||||
if (!confirm("Are you sure you want to delete user " + usernameToRemove + "?")) {
|
||||
return;
|
||||
}
|
||||
fetch("removeUser.php", {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ username: usernameToRemove })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast("User removed successfully!");
|
||||
closeRemoveUserModal();
|
||||
loadUserList();
|
||||
} else {
|
||||
showToast("Error: " + (data.error || "Could not remove user"));
|
||||
}
|
||||
})
|
||||
.catch(error => console.error("Error removing user:", error));
|
||||
});
|
||||
|
||||
document.getElementById("cancelRemoveUserBtn").addEventListener("click", function () {
|
||||
closeRemoveUserModal();
|
||||
});
|
||||
}
|
||||
|
||||
// Set up the logout button.
|
||||
document.getElementById("logoutBtn").addEventListener("click", function () {
|
||||
fetch("logout.php", {
|
||||
method: "POST",
|
||||
credentials: "include" // Ensure the session cookie is sent.
|
||||
})
|
||||
.then(() => window.location.reload(true))
|
||||
.catch(error => console.error("Logout error:", error));
|
||||
});
|
||||
|
||||
// Set up Add User functionality.
|
||||
document.getElementById("addUserBtn").addEventListener("click", function () {
|
||||
resetUserForm();
|
||||
toggleVisibility("addUserModal", true);
|
||||
});
|
||||
|
||||
document.getElementById("saveUserBtn").addEventListener("click", function () {
|
||||
const newUsername = document.getElementById("newUsername").value.trim();
|
||||
const newPassword = document.getElementById("newPassword").value.trim();
|
||||
const isAdmin = document.getElementById("isAdmin").checked;
|
||||
if (!newUsername || !newPassword) {
|
||||
showToast("Username and password are required!");
|
||||
return;
|
||||
}
|
||||
let url = "addUser.php";
|
||||
if (window.setupMode) {
|
||||
url += "?setup=1";
|
||||
}
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ username: newUsername, password: newPassword, isAdmin })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast("User added successfully!");
|
||||
closeAddUserModal();
|
||||
checkAuthentication();
|
||||
} else {
|
||||
showToast("Error: " + (data.error || "Could not add user"));
|
||||
}
|
||||
})
|
||||
.catch(error => console.error("Error adding user:", error));
|
||||
});
|
||||
|
||||
document.getElementById("cancelUserBtn").addEventListener("click", function () {
|
||||
closeAddUserModal();
|
||||
});
|
||||
|
||||
// Set up Remove User functionality.
|
||||
document.getElementById("removeUserBtn").addEventListener("click", function () {
|
||||
loadUserList();
|
||||
toggleVisibility("removeUserModal", true);
|
||||
});
|
||||
|
||||
document.getElementById("deleteUserBtn").addEventListener("click", function () {
|
||||
const selectElem = document.getElementById("removeUsernameSelect");
|
||||
const usernameToRemove = selectElem.value;
|
||||
if (!usernameToRemove) {
|
||||
showToast("Please select a user to remove.");
|
||||
return;
|
||||
}
|
||||
if (!confirm("Are you sure you want to delete user " + usernameToRemove + "?")) {
|
||||
return;
|
||||
}
|
||||
fetch("removeUser.php", {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ username: usernameToRemove })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast("User removed successfully!");
|
||||
closeRemoveUserModal();
|
||||
loadUserList();
|
||||
} else {
|
||||
showToast("Error: " + (data.error || "Could not remove user"));
|
||||
}
|
||||
})
|
||||
.catch(error => console.error("Error removing user:", error));
|
||||
});
|
||||
|
||||
document.getElementById("cancelRemoveUserBtn").addEventListener("click", function () {
|
||||
closeRemoveUserModal();
|
||||
});
|
||||
|
||||
export function checkAuthentication() {
|
||||
function checkAuthentication() {
|
||||
// Return the promise from sendRequest
|
||||
return sendRequest("checkAuth.php")
|
||||
.then(data => {
|
||||
@@ -157,6 +156,12 @@ export function checkAuthentication() {
|
||||
if (removeUserBtn) removeUserBtn.style.display = "none";
|
||||
}
|
||||
document.querySelector(".header-buttons").style.visibility = "visible";
|
||||
// Update persistent items-per-page select once main operations are visible.
|
||||
const selectElem = document.querySelector(".form-control.bottom-select");
|
||||
if (selectElem) {
|
||||
const stored = localStorage.getItem("itemsPerPage") || "10";
|
||||
selectElem.value = stored;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
showToast("Please log in to continue.");
|
||||
@@ -175,7 +180,34 @@ export function checkAuthentication() {
|
||||
}
|
||||
window.checkAuthentication = checkAuthentication;
|
||||
|
||||
// Helper functions for auth modals.
|
||||
/* ------------------------------
|
||||
Persistent Items-Per-Page Setting
|
||||
------------------------------ */
|
||||
// When the select value changes, save it to localStorage and refresh the file list.
|
||||
window.changeItemsPerPage = function (value) {
|
||||
console.log("Saving itemsPerPage:", value);
|
||||
localStorage.setItem("itemsPerPage", value);
|
||||
// Refresh the file list automatically.
|
||||
const folder = window.currentFolder || "root";
|
||||
if (typeof renderFileTable === "function") {
|
||||
renderFileTable(folder);
|
||||
}
|
||||
};
|
||||
|
||||
// On DOMContentLoaded, set the select to the persisted value.
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const selectElem = document.querySelector(".form-control.bottom-select");
|
||||
if (selectElem) {
|
||||
const stored = localStorage.getItem("itemsPerPage") || "10";
|
||||
console.log("Loaded itemsPerPage from localStorage:", stored);
|
||||
selectElem.value = stored;
|
||||
}
|
||||
});
|
||||
|
||||
/* ------------------------------
|
||||
Helper functions for modals and user list
|
||||
------------------------------ */
|
||||
|
||||
function resetUserForm() {
|
||||
document.getElementById("newUsername").value = "";
|
||||
document.getElementById("newPassword").value = "";
|
||||
@@ -214,4 +246,6 @@ function loadUserList() {
|
||||
}
|
||||
})
|
||||
.catch(error => console.error("Error loading user list:", error));
|
||||
}
|
||||
}
|
||||
|
||||
export { initAuth, checkAuthentication };
|
||||
@@ -128,16 +128,21 @@ export function renderFileTable(folder) {
|
||||
const folderPath = (folder === "root")
|
||||
? "uploads/"
|
||||
: "uploads/" + folder.split("/").map(encodeURIComponent).join("/") + "/";
|
||||
let searchInputElement = document.getElementById("searchInput");
|
||||
|
||||
// Attempt to get the search input element.
|
||||
const searchInputElement = document.getElementById("searchInput");
|
||||
const searchHadFocus = searchInputElement && (document.activeElement === searchInputElement);
|
||||
let searchTerm = searchInputElement ? searchInputElement.value : "";
|
||||
const searchTerm = searchInputElement ? searchInputElement.value : "";
|
||||
|
||||
const filteredFiles = fileData.filter(file =>
|
||||
file.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
const itemsPerPage = window.itemsPerPage || 10;
|
||||
|
||||
// Read persistent items per page from localStorage, default to 10.
|
||||
const itemsPerPageSetting = parseInt(localStorage.getItem('itemsPerPage') || '10', 10);
|
||||
const currentPage = window.currentPage || 1;
|
||||
const totalFiles = filteredFiles.length;
|
||||
const totalPages = Math.ceil(totalFiles / itemsPerPage);
|
||||
const totalPages = Math.ceil(totalFiles / itemsPerPageSetting);
|
||||
const safeSearchTerm = escapeHTML(searchTerm);
|
||||
|
||||
const topControlsHTML = `
|
||||
@@ -177,8 +182,8 @@ export function renderFileTable(folder) {
|
||||
</thead>
|
||||
`;
|
||||
|
||||
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||
const endIndex = Math.min(startIndex + itemsPerPage, totalFiles);
|
||||
const startIndex = (currentPage - 1) * itemsPerPageSetting;
|
||||
const endIndex = Math.min(startIndex + itemsPerPageSetting, totalFiles);
|
||||
let tableBody = `<tbody>`;
|
||||
|
||||
if (totalFiles > 0) {
|
||||
@@ -190,10 +195,9 @@ export function renderFileTable(folder) {
|
||||
const safeSize = escapeHTML(file.size);
|
||||
const safeUploader = escapeHTML(file.uploader || "Unknown");
|
||||
|
||||
// Check if the file is an image using a regex
|
||||
// Check if file is an image.
|
||||
const isImage = /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(file.name);
|
||||
|
||||
// Build the preview button HTML string using the file's properties directly.
|
||||
const previewButton = isImage
|
||||
? `<button class="btn btn-sm btn-info ml-2" onclick="event.stopPropagation(); previewImage('${folderPath + encodeURIComponent(file.name)}', '${safeFileName}')">
|
||||
<i class="material-icons">image</i>
|
||||
@@ -230,7 +234,9 @@ export function renderFileTable(folder) {
|
||||
<div class="d-flex align-items-center mt-3 bottom-controls">
|
||||
<label class="label-inline mr-2 mb-0">Show</label>
|
||||
<select class="form-control bottom-select" onchange="changeItemsPerPage(this.value)">
|
||||
${[10, 20, 50, 100].map(num => `<option value="${num}" ${num === itemsPerPage ? "selected" : ""}>${num}</option>`).join("")}
|
||||
${[10, 20, 50, 100]
|
||||
.map(num => `<option value="${num}" ${num === itemsPerPageSetting ? "selected" : ""}>${num}</option>`)
|
||||
.join("")}
|
||||
</select>
|
||||
<span class="items-per-page-text ml-2 mb-0">items per page</span>
|
||||
</div>
|
||||
@@ -238,28 +244,28 @@ export function renderFileTable(folder) {
|
||||
|
||||
fileListContainer.innerHTML = topControlsHTML + tableHTML + tableBody + bottomControlsHTML;
|
||||
|
||||
const newSearchInput = document.getElementById("searchInput");
|
||||
if (searchHadFocus && newSearchInput) {
|
||||
newSearchInput.focus();
|
||||
newSearchInput.setSelectionRange(newSearchInput.value.length, newSearchInput.value.length);
|
||||
// Only add event listeners if searchInputElement exists.
|
||||
if (searchInputElement) {
|
||||
searchInputElement.addEventListener("input", function () {
|
||||
window.currentPage = 1;
|
||||
renderFileTable(folder);
|
||||
});
|
||||
}
|
||||
newSearchInput.addEventListener("input", function () {
|
||||
window.currentPage = 1;
|
||||
renderFileTable(folder);
|
||||
});
|
||||
const headerCells = document.querySelectorAll("table.table thead th[data-column]");
|
||||
headerCells.forEach(cell => {
|
||||
|
||||
document.querySelectorAll("table.table thead th[data-column]").forEach(cell => {
|
||||
cell.addEventListener("click", function () {
|
||||
const column = this.getAttribute("data-column");
|
||||
sortFiles(column, folder);
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('#fileList .file-checkbox').forEach(checkbox => {
|
||||
checkbox.addEventListener('change', function (e) {
|
||||
updateRowHighlight(e.target);
|
||||
updateFileActionButtons();
|
||||
});
|
||||
});
|
||||
|
||||
updateFileActionButtons();
|
||||
}
|
||||
|
||||
|
||||
180
index.html
180
index.html
@@ -70,100 +70,98 @@
|
||||
</div>
|
||||
|
||||
<!-- Main Operations: Upload and Folder Management -->
|
||||
<div id="mainOperations" style="display: none;">
|
||||
<div class="row align-items-stretch" id="uploadFolderRow">
|
||||
<!-- Upload Card: 50% width on medium, 58% on large -->
|
||||
<div class="container" style="max-width: 1400px; margin: 0 auto;">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-lg-7 d-flex">
|
||||
<div class="card flex-fill" style="max-width: 900px; width: 100%;">
|
||||
<div class="card-header">Upload Files</div>
|
||||
<!-- Card body is a flex container in column direction -->
|
||||
<div class="card-body d-flex flex-column">
|
||||
<form id="uploadFileForm" method="post" enctype="multipart/form-data" class="d-flex flex-column"
|
||||
style="height: 100%;">
|
||||
<div class="form-group flex-grow-1" style="margin-bottom: 1rem;">
|
||||
<div id="uploadDropArea"
|
||||
style="border:2px dashed #ccc; padding:20px; cursor:pointer; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center;">
|
||||
<span>Drop files here or click 'Choose files'</span>
|
||||
<br />
|
||||
<input type="file" id="file" name="file[]" class="form-control-file" multiple required
|
||||
style="display:none;" />
|
||||
</div>
|
||||
<div id="mainOperations">
|
||||
<div class="container" style="max-width: 1400px; margin: 0 auto;">
|
||||
<div class="row align-items-stretch" id="uploadFolderRow">
|
||||
<!-- Upload Card: 50% width on medium, 58% on large -->
|
||||
<div class="col-md-6 col-lg-7 d-flex">
|
||||
<div id="uploadCard" class="card flex-fill" style="max-width: 900px; width: 100%;">
|
||||
<div class="card-header">Upload Files/Folders</div>
|
||||
<div class="card-body d-flex flex-column">
|
||||
<form id="uploadFileForm" method="post" enctype="multipart/form-data" class="d-flex flex-column"
|
||||
style="height: 100%;">
|
||||
<div class="form-group flex-grow-1" style="margin-bottom: 1rem;">
|
||||
<div id="uploadDropArea"
|
||||
style="border:2px dashed #ccc; padding:20px; cursor:pointer; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center;">
|
||||
<span>Drop files/folders here or click 'Choose files'</span>
|
||||
<br />
|
||||
<input type="file" id="file" name="file[]" class="form-control-file" multiple required
|
||||
webkitdirectory directory mozdirectory style="opacity:0; position:absolute; z-index:-1;" />
|
||||
<button type="button" onclick="document.getElementById('file').click();">Choose Folder</button>
|
||||
</div>
|
||||
<button type="submit" id="uploadBtn" class="btn btn-primary d-block mx-auto">Upload</button>
|
||||
<div id="uploadProgressContainer"></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" id="uploadBtn" class="btn btn-primary d-block mx-auto">Upload</button>
|
||||
<div id="uploadProgressContainer"></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Folder Management Card: 50% width on medium, 42% on large -->
|
||||
<div class="col-md-6 col-lg-5 d-flex">
|
||||
<div class="card flex-fill" style="max-width: 900px; width: 100%;">
|
||||
<div class="card-header">Folder Navigation & Management</div>
|
||||
<div class="card-body custom-folder-card-body">
|
||||
<div class="form-group d-flex align-items-top" style="padding-top:0; margin-bottom:0;">
|
||||
<div id="folderTreeContainer"></div>
|
||||
</div>
|
||||
<!-- Folder actions -->
|
||||
<div class="folder-actions mt-3">
|
||||
<button id="createFolderBtn" class="btn btn-primary">Create Folder</button>
|
||||
<!-- Create Folder Modal -->
|
||||
<div id="createFolderModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Create Folder</h4>
|
||||
<input type="text" id="newFolderName" class="form-control" placeholder="Enter folder name"
|
||||
style="margin-top:10px;" />
|
||||
<div style="margin-top:15px; text-align:right;">
|
||||
<button id="cancelCreateFolder" class="btn btn-secondary">Cancel</button>
|
||||
<button id="submitCreateFolder" class="btn btn-primary">Create</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="renameFolderBtn" class="btn btn-secondary ml-2" title="Rename Folder">
|
||||
<i class="material-icons">edit</i>
|
||||
</button>
|
||||
<!-- Rename Folder Modal -->
|
||||
<div id="renameFolderModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Rename Folder</h4>
|
||||
<input type="text" id="newRenameFolderName" class="form-control"
|
||||
placeholder="Enter new folder name" style="margin-top:10px;" />
|
||||
<div style="margin-top:15px; text-align:right;">
|
||||
<button id="cancelRenameFolder" class="btn btn-secondary">Cancel</button>
|
||||
<button id="submitRenameFolder" class="btn btn-primary">Rename</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="deleteFolderBtn" class="btn btn-danger ml-2" title="Delete Folder">
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
<!-- Delete Folder Modal -->
|
||||
<div id="deleteFolderModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Delete Folder</h4>
|
||||
<p id="deleteFolderMessage">Are you sure you want to delete this folder?</p>
|
||||
<div style="margin-top:15px; text-align:right;">
|
||||
<button id="cancelDeleteFolder" class="btn btn-secondary">Cancel</button>
|
||||
<button id="confirmDeleteFolder" class="btn btn-danger">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Folder Management Card: 50% width on medium, 42% on large -->
|
||||
<div class="col-md-6 col-lg-5 d-flex">
|
||||
<div class="card flex-fill" style="max-width: 900px; width: 100%;">
|
||||
<div class="card-header">Folder Navigation & Management</div>
|
||||
<div class="card-body custom-folder-card-body">
|
||||
<div class="form-group d-flex align-items-top" style="padding-top:0; margin-bottom:0;">
|
||||
<div id="folderTreeContainer"></div>
|
||||
</div>
|
||||
<!-- Folder actions (create, rename, delete) -->
|
||||
<div class="folder-actions mt-3">
|
||||
<button id="createFolderBtn" class="btn btn-primary">Create Folder</button>
|
||||
<!-- Create Folder Modal -->
|
||||
<div id="createFolderModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Create Folder</h4>
|
||||
<input type="text" id="newFolderName" class="form-control" placeholder="Enter folder name"
|
||||
style="margin-top:10px;" />
|
||||
<div style="margin-top:15px; text-align:right;">
|
||||
<button id="cancelCreateFolder" class="btn btn-secondary">Cancel</button>
|
||||
<button id="submitCreateFolder" class="btn btn-primary">Create</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="folderExplanation" class="folder-explanation">
|
||||
<details class="folder-help-details">
|
||||
<summary class="folder-help-summary">
|
||||
<i class="material-icons folder-help-icon">info</i>
|
||||
Folder Navigation & Management Help Info
|
||||
</summary>
|
||||
<ul class="folder-help-list">
|
||||
<li>Click on a folder in the tree to view its files.</li>
|
||||
<li>Use [-] to collapse and [+] to expand folders.</li>
|
||||
<li>Select a folder and click "Create Folder" to add a subfolder.</li>
|
||||
<li>To rename or delete a folder, select it and then click the appropriate button.</li>
|
||||
</ul>
|
||||
</details>
|
||||
<button id="renameFolderBtn" class="btn btn-secondary ml-2" title="Rename Folder">
|
||||
<i class="material-icons">edit</i>
|
||||
</button>
|
||||
<!-- Rename Folder Modal -->
|
||||
<div id="renameFolderModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Rename Folder</h4>
|
||||
<input type="text" id="newRenameFolderName" class="form-control"
|
||||
placeholder="Enter new folder name" style="margin-top:10px;" />
|
||||
<div style="margin-top:15px; text-align:right;">
|
||||
<button id="cancelRenameFolder" class="btn btn-secondary">Cancel</button>
|
||||
<button id="submitRenameFolder" class="btn btn-primary">Rename</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="deleteFolderBtn" class="btn btn-danger ml-2" title="Delete Folder">
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
<!-- Delete Folder Modal -->
|
||||
<div id="deleteFolderModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Delete Folder</h4>
|
||||
<p id="deleteFolderMessage">Are you sure you want to delete this folder?</p>
|
||||
<div style="margin-top:15px; text-align:right;">
|
||||
<button id="cancelDeleteFolder" class="btn btn-secondary">Cancel</button>
|
||||
<button id="confirmDeleteFolder" class="btn btn-danger">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="folderExplanation" class="folder-explanation">
|
||||
<details class="folder-help-details">
|
||||
<summary class="folder-help-summary">
|
||||
<i class="material-icons folder-help-icon">info</i>
|
||||
Folder Navigation & Management Help Info
|
||||
</summary>
|
||||
<ul class="folder-help-list">
|
||||
<li>Click on a folder in the tree to view its files.</li>
|
||||
<li>Use [-] to collapse and [+] to expand folders.</li>
|
||||
<li>Select a folder and click "Create Folder" to add a subfolder.</li>
|
||||
<li>To rename or delete a folder, select it and then click the appropriate button.</li>
|
||||
</ul>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -278,14 +276,6 @@
|
||||
<!-- JavaScript Files -->
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
||||
<script type="module" src="main.js"></script>
|
||||
<script>
|
||||
// Dark mode toggle script
|
||||
const darkModeToggle = document.getElementById('darkModeToggle');
|
||||
darkModeToggle.addEventListener('click', () => {
|
||||
document.body.classList.toggle('dark-mode');
|
||||
darkModeToggle.innerText = document.body.classList.contains('dark-mode') ? 'Light Mode' : 'Dark Mode';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
90
main.js
90
main.js
@@ -1,17 +1,15 @@
|
||||
// main.js
|
||||
|
||||
import { sendRequest } from './networkUtils.js';
|
||||
import {
|
||||
toggleVisibility,
|
||||
toggleAllCheckboxes,
|
||||
import {
|
||||
toggleVisibility,
|
||||
toggleAllCheckboxes,
|
||||
updateFileActionButtons,
|
||||
showToast
|
||||
} from './domUtils.js';
|
||||
import {
|
||||
loadFileList,
|
||||
initFileActions,
|
||||
editFile,
|
||||
saveFile,
|
||||
import {
|
||||
loadFileList,
|
||||
initFileActions,
|
||||
editFile,
|
||||
saveFile,
|
||||
displayFilePreview,
|
||||
renameFile
|
||||
} from './fileManager.js';
|
||||
@@ -27,38 +25,66 @@ window.editFile = editFile;
|
||||
window.saveFile = saveFile;
|
||||
window.renameFile = renameFile;
|
||||
|
||||
|
||||
// Global variable for the current folder.
|
||||
window.currentFolder = "root";
|
||||
|
||||
// DOMContentLoaded initialization.
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// Call initAuth synchronously.
|
||||
initAuth();
|
||||
|
||||
// Check OS theme preference & apply dark mode
|
||||
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
||||
document.body.classList.add("dark-mode"); // Enable dark mode if OS is set to dark
|
||||
}
|
||||
|
||||
// Listen for real-time OS theme changes
|
||||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => {
|
||||
if (event.matches) {
|
||||
document.body.classList.add("dark-mode"); // Enable dark mode
|
||||
} else {
|
||||
document.body.classList.remove("dark-mode"); // Disable dark mode
|
||||
}
|
||||
});
|
||||
|
||||
// ✅ Fix the Button Label on Page Load
|
||||
// --- Dark Mode Persistence ---
|
||||
// Get the dark mode toggle button.
|
||||
const darkModeToggle = document.getElementById("darkModeToggle");
|
||||
if (document.body.classList.contains("dark-mode")) {
|
||||
darkModeToggle.textContent = "Light Mode";
|
||||
// Retrieve stored user preference (if any).
|
||||
const storedDarkMode = localStorage.getItem("darkMode");
|
||||
|
||||
// Apply stored preference; if none, fall back to OS setting.
|
||||
if (storedDarkMode === "true") {
|
||||
document.body.classList.add("dark-mode");
|
||||
} else if (storedDarkMode === "false") {
|
||||
document.body.classList.remove("dark-mode");
|
||||
} else {
|
||||
darkModeToggle.textContent = "Dark Mode";
|
||||
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
||||
document.body.classList.add("dark-mode");
|
||||
} else {
|
||||
document.body.classList.remove("dark-mode");
|
||||
}
|
||||
}
|
||||
|
||||
// Set the initial button label.
|
||||
if (darkModeToggle) {
|
||||
darkModeToggle.textContent = document.body.classList.contains("dark-mode")
|
||||
? "Light Mode"
|
||||
: "Dark Mode";
|
||||
|
||||
// When clicked, toggle dark mode and store preference.
|
||||
darkModeToggle.addEventListener("click", function () {
|
||||
if (document.body.classList.contains("dark-mode")) {
|
||||
document.body.classList.remove("dark-mode");
|
||||
localStorage.setItem("darkMode", "false");
|
||||
darkModeToggle.textContent = "Dark Mode";
|
||||
} else {
|
||||
document.body.classList.add("dark-mode");
|
||||
localStorage.setItem("darkMode", "true");
|
||||
darkModeToggle.textContent = "Light Mode";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Listen for OS theme changes if no user preference is set.
|
||||
if (localStorage.getItem("darkMode") === null && window.matchMedia) {
|
||||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => {
|
||||
if (event.matches) {
|
||||
document.body.classList.add("dark-mode");
|
||||
if (darkModeToggle) darkModeToggle.textContent = "Light Mode";
|
||||
} else {
|
||||
document.body.classList.remove("dark-mode");
|
||||
if (darkModeToggle) darkModeToggle.textContent = "Dark Mode";
|
||||
}
|
||||
});
|
||||
}
|
||||
// --- End Dark Mode Persistence ---
|
||||
|
||||
const message = sessionStorage.getItem("welcomeMessage");
|
||||
if (message) {
|
||||
showToast(message);
|
||||
@@ -76,4 +102,4 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
console.warn("User not authenticated. Data loading deferred.");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
11
styles.css
11
styles.css
@@ -294,13 +294,13 @@ body.dark-mode header {
|
||||
/* folder icon and remove file to be uploaded */
|
||||
.material-icons.folder-icon {
|
||||
color: black;
|
||||
margin-right: 3px;
|
||||
margin-right: 5px;
|
||||
/* adjust the value as needed */
|
||||
}
|
||||
|
||||
body.dark-mode .material-icons.folder-icon {
|
||||
color: white;
|
||||
margin-right: 3px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* Remove button default styling */
|
||||
@@ -1514,6 +1514,13 @@ body.dark-mode .file-icon {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.upload-progress-wrapper {
|
||||
max-height: 300px;
|
||||
/* Adjust the value as needed */
|
||||
overflow-y: auto;
|
||||
/* Optional: Add some padding or border if desired */
|
||||
}
|
||||
|
||||
.upload-progress-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
|
||||
34
upload.js
34
upload.js
@@ -45,6 +45,25 @@ function getFilesFromDataTransferItems(items) {
|
||||
return Promise.all(promises).then(results => results.flat());
|
||||
}
|
||||
|
||||
function adjustFolderHelpExpansion() {
|
||||
const uploadCard = document.getElementById("uploadCard");
|
||||
const folderHelpDetails = document.querySelector(".folder-help-details");
|
||||
if (uploadCard && folderHelpDetails) {
|
||||
if (uploadCard.offsetHeight > 400) {
|
||||
folderHelpDetails.setAttribute("open", "");
|
||||
} else {
|
||||
folderHelpDetails.removeAttribute("open");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function adjustFolderHelpExpansionClosed() {
|
||||
const folderHelpDetails = document.querySelector(".folder-help-details");
|
||||
if (folderHelpDetails) {
|
||||
folderHelpDetails.removeAttribute("open");
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: Update file info container count/preview.
|
||||
function updateFileInfoCount() {
|
||||
const fileInfoContainer = document.getElementById("fileInfoContainer");
|
||||
@@ -226,9 +245,20 @@ function processFiles(filesInput) {
|
||||
list.appendChild(extra);
|
||||
}
|
||||
}
|
||||
progressContainer.appendChild(list);
|
||||
const listWrapper = document.createElement("div");
|
||||
listWrapper.classList.add("upload-progress-wrapper");
|
||||
// Set a maximum height (adjust as needed) and enable vertical scrolling.
|
||||
listWrapper.style.maxHeight = "300px"; // for example, 300px
|
||||
listWrapper.style.overflowY = "auto";
|
||||
listWrapper.appendChild(list);
|
||||
progressContainer.appendChild(listWrapper);
|
||||
}
|
||||
|
||||
// Call once on page load:
|
||||
adjustFolderHelpExpansion();
|
||||
// Also call on window resize:
|
||||
window.addEventListener("resize", adjustFolderHelpExpansion);
|
||||
|
||||
// Store files globally for submission.
|
||||
window.selectedFiles = files;
|
||||
updateFileInfoCount();
|
||||
@@ -367,6 +397,8 @@ function submitFiles(allFiles) {
|
||||
removeBtns.forEach(btn => btn.style.display = "none");
|
||||
progressContainer.innerHTML = "";
|
||||
window.selectedFiles = [];
|
||||
adjustFolderHelpExpansionClosed();
|
||||
window.addEventListener("resize", adjustFolderHelpExpansionClosed);
|
||||
const fileInfoContainer = document.getElementById("fileInfoContainer");
|
||||
if (fileInfoContainer) {
|
||||
fileInfoContainer.innerHTML = `<span id="fileInfoDefault">No files selected</span>`;
|
||||
|
||||
Reference in New Issue
Block a user