From 283a80a30231f3fe695bc6c1e0fcab8563c50077 Mon Sep 17 00:00:00 2001 From: George Wu Date: Fri, 20 Feb 2026 19:02:01 -0800 Subject: [PATCH] =?UTF-8?q?fix:=20display=20unlimited=20quota=20(=E2=88=9E?= =?UTF-8?q?)=20when=20storage=5Fquota=5Fbytes=20=3D=200?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added formatQuotaSize() function to display ∞ for unlimited (0) quota - Added format_quota_size() Rust function matching JavaScript behavior - Updated quota defaulting logic from '||' to '== null' check - This allows 0 (unlimited) to pass through while defaulting to 10 GB only when the value is null/undefined - Call sites now use dedicated formatQuotaSize() or format_quota_size() instead of options parameter for cleaner API --- src/application/dtos/display_helpers.rs | 19 +++++++++++++++++++ static/js/app/main.js | 5 +++-- static/js/app/userMenu.js | 10 +++++----- static/js/core/formatters.js | 7 +++++++ 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/application/dtos/display_helpers.rs b/src/application/dtos/display_helpers.rs index ebc92f41..9062ee5f 100644 --- a/src/application/dtos/display_helpers.rs +++ b/src/application/dtos/display_helpers.rs @@ -367,6 +367,16 @@ pub fn format_file_size(bytes: u64) -> String { format!("{} {}", formatted, SIZES[i]) } +/// Formats a byte count for quota display. When bytes is 0, returns "∞" (unlimited). +/// +/// Matches the JavaScript `formatQuotaSize()` output. +pub fn format_quota_size(bytes: u64) -> String { + if bytes == 0 { + return "∞".to_string(); + } + format_file_size(bytes) +} + #[cfg(test)] mod tests { use super::*; @@ -382,6 +392,15 @@ mod tests { assert_eq!(format_file_size(1_073_741_824), "1 GB"); } + #[test] + fn test_format_quota_size() { + // Unlimited quota (0) should show infinity symbol + assert_eq!(format_quota_size(0), "∞"); + // Non-zero values should format normally + assert_eq!(format_quota_size(500), "500 Bytes"); + assert_eq!(format_quota_size(1_073_741_824), "1 GB"); + } + #[test] fn test_icon_class_for_with_extension_fallback() { // Specific MIME types diff --git a/static/js/app/main.js b/static/js/app/main.js index 789c6171..ae66f0b1 100644 --- a/static/js/app/main.js +++ b/static/js/app/main.js @@ -567,7 +567,8 @@ function updateStorageUsageDisplay(userData) { // Get values from user data if available if (userData) { usedBytes = userData.storage_used_bytes || 0; - quotaBytes = userData.storage_quota_bytes || DEFAULT_QUOTA; + // Use == null to allow 0 (unlimited) to pass through; only default to DEFAULT_QUOTA when null/undefined + quotaBytes = userData.storage_quota_bytes == null ? DEFAULT_QUOTA : userData.storage_quota_bytes; // Calculate percentage (avoid division by zero) if (quotaBytes > 0) { @@ -577,7 +578,7 @@ function updateStorageUsageDisplay(userData) { // Format the numbers for display const usedFormatted = formatFileSize(usedBytes); - const quotaFormatted = formatFileSize(quotaBytes); + const quotaFormatted = formatQuotaSize(quotaBytes); // Update the storage display elements const storageFill = document.querySelector('.storage-fill'); diff --git a/static/js/app/userMenu.js b/static/js/app/userMenu.js index 40279650..aa5ca916 100644 --- a/static/js/app/userMenu.js +++ b/static/js/app/userMenu.js @@ -135,14 +135,14 @@ function updateUserMenuData() { } const usedBytes = userData.storage_used_bytes || 0; - const quotaBytes = userData.storage_quota_bytes || (10 * 1024 * 1024 * 1024); + const quotaBytes = userData.storage_quota_bytes == null ? (10 * 1024 * 1024 * 1024) : userData.storage_quota_bytes; const percentage = quotaBytes > 0 ? Math.min(Math.round((usedBytes / quotaBytes) * 100), 100) : 0; if (storageFill) storageFill.style.width = percentage + '%'; if (storageText) { const used = window.formatFileSize(usedBytes); - const total = window.formatFileSize(quotaBytes); - storageText.textContent = `${percentage}% · ${used} / ${total}`; + const total = window.formatQuotaSize(quotaBytes); + storageText.textContent = `${quotaBytes > 0 ? `${percentage}% · ` : ''}${used} / ${total}`; } } @@ -169,7 +169,7 @@ function showUserProfileModal() { const role = userData.role || 'user'; const initials = username.substring(0, 2).toUpperCase(); const usedBytes = userData.storage_used_bytes || 0; - const quotaBytes = userData.storage_quota_bytes || (10 * 1024 * 1024 * 1024); + const quotaBytes = userData.storage_quota_bytes == null ? (10 * 1024 * 1024 * 1024) : userData.storage_quota_bytes; const percentage = quotaBytes > 0 ? Math.min(Math.round((usedBytes / quotaBytes) * 100), 100) : 0; const barColor = percentage > 90 ? '#ef4444' : percentage > 70 ? '#f59e0b' : '#22c55e'; @@ -200,7 +200,7 @@ function showUserProfileModal() {
-
${percentage}% · ${window.formatFileSize(usedBytes)} / ${quotaBytes > 0 ? window.formatFileSize(quotaBytes) : '∞'}
+
${percentage}% · ${window.formatFileSize(usedBytes)} / ${window.formatQuotaSize(quotaBytes)}
diff --git a/static/js/core/formatters.js b/static/js/core/formatters.js index 6318aaad..ee3d5a2e 100644 --- a/static/js/core/formatters.js +++ b/static/js/core/formatters.js @@ -23,6 +23,12 @@ function formatFileSize(bytes) { return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } +/// Formats a byte count for quota display. When bytes is 0, returns "∞" (unlimited). +function formatQuotaSize(bytes) { + if (bytes === 0) return '∞'; + return formatFileSize(bytes); +} + function formatDateTime(value) { if (!value) return ''; let dateValue; @@ -58,6 +64,7 @@ function isTextViewable(mimeType) { window.escapeHtml = escapeHtml; window.formatFileSize = formatFileSize; +window.formatQuotaSize = formatQuotaSize; window.formatDateTime = formatDateTime; window.formatDateShort = formatDateShort; window.isTextViewable = isTextViewable;