diff --git a/Makefile b/Makefile index 148b103..12ed8be 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ dev: run: @echo "Starting MyWebdav application..." - @echo "Access the application at http://localhost:8000" + @echo "Access the application at http://localhost:9004" $(PYTHON) -m mywebdav.main test: @@ -147,9 +147,9 @@ setup: setup-env install dev init-db @echo "Next steps:" @echo " 1. Update .env with your configuration (especially Stripe keys)" @echo " 2. Run 'make run' or 'make all' to start the application" - @echo " 3. Access the application at http://localhost:8000" + @echo " 3. Access the application at http://localhost:9004" docs: @echo "Generating API documentation..." - @echo "API documentation available at http://localhost:8000/docs when running" - @echo "ReDoc available at http://localhost:8000/redoc when running" + @echo "API documentation available at http://localhost:9004/docs when running" + @echo "ReDoc available at http://localhost:9004/redoc when running" diff --git a/mywebdav/main.py b/mywebdav/main.py index 52fb445..6288d96 100644 --- a/mywebdav/main.py +++ b/mywebdav/main.py @@ -315,7 +315,7 @@ def main(): parser.add_argument( "--host", type=str, default="0.0.0.0", help="Host address to bind to" ) - parser.add_argument("--port", type=int, default=8000, help="Port to listen on") + parser.add_argument("--port", type=int, default=9004, help="Port to listen on") args = parser.parse_args() uvicorn.run(app, host=args.host, port=args.port) diff --git a/static/css/mobile.css b/static/css/mobile.css index 8e40fa1..4d7f8fd 100644 --- a/static/css/mobile.css +++ b/static/css/mobile.css @@ -1,5 +1,4 @@ /* retoor */ -/* Mobile-First Responsive Styles */ :root { --touch-target-min: 44px; @@ -12,7 +11,6 @@ --safe-area-inset-right: env(safe-area-inset-right, 0); } -/* Hamburger Button */ .hamburger-btn { display: none; flex-direction: column; @@ -55,7 +53,6 @@ background-color: rgba(0, 51, 153, 0.1); } -/* Sidebar Overlay */ .sidebar-overlay { display: none; position: fixed; @@ -76,7 +73,6 @@ pointer-events: auto; } -/* Pull to Refresh Indicator */ .pull-to-refresh-indicator { display: flex; align-items: center; @@ -115,10 +111,9 @@ to { transform: rotate(360deg); } } -/* Context Menu */ .context-menu { position: fixed; - min-width: 180px; + min-width: 200px; max-width: 280px; background-color: var(--accent-color); border-radius: 12px; @@ -141,7 +136,7 @@ align-items: center; gap: 12px; width: 100%; - padding: 12px 16px; + padding: 14px 16px; background: none; border: none; font-size: 1rem; @@ -161,8 +156,9 @@ } .context-menu-icon { - width: 20px; + width: 24px; text-align: center; + font-size: 1.1rem; } .context-menu-separator { @@ -171,7 +167,27 @@ margin: 8px 0; } -/* Mobile Base Styles (320px+) */ +.mobile-more-btn { + display: none; + width: 36px; + height: 36px; + min-width: 36px; + background: transparent; + border: none; + border-radius: 50%; + cursor: pointer; + font-size: 1.25rem; + color: var(--text-color-light); + -webkit-tap-highlight-color: transparent; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.mobile-more-btn:active { + background-color: rgba(0, 51, 153, 0.1); +} + @media (max-width: 767px) { .hamburger-btn { display: flex; @@ -222,7 +238,7 @@ .header-right .button { min-width: var(--touch-target-min); min-height: var(--touch-target-min); - padding: 8px 10px; + padding: 8px 12px; font-size: 0.875rem; } @@ -299,46 +315,74 @@ flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; + padding: 12px; + } + + body.logged-in .app-footer { + display: none; } .app-footer { - padding: 12px 16px; - padding-bottom: calc(12px + var(--safe-area-inset-bottom)); + padding: 8px 12px; + padding-bottom: calc(8px + var(--safe-area-inset-bottom)); } .footer-links { flex-wrap: wrap; - gap: 2px 10px; + gap: 2px 8px; justify-content: center; } .footer-links li a { - font-size: 0.65rem; - min-height: 32px; + font-size: 0.6rem; + min-height: 28px; display: flex; align-items: center; - padding: 4px 0; + padding: 2px 0; } .footer-text { - font-size: 0.65rem; - margin-top: 8px; + font-size: 0.6rem; + margin-top: 4px; + } + + .file-list-container { + padding: 0; + background: transparent; } - /* File List Mobile */ .file-list-header { - padding: 12px; - flex-wrap: wrap; - gap: 8px; + padding: 8px 0; + flex-direction: column; + align-items: stretch; + gap: 12px; + border-bottom: none; + margin-bottom: 8px; } - .file-list-title { + .file-list-header .header-left { + flex-direction: row; + justify-content: space-between; + align-items: center; + width: 100%; + } + + .file-list-title, + .file-list-header h2 { font-size: 1.25rem; + margin: 0; + } + + .selection-controls { + flex-shrink: 0; + } + + .selection-controls label { + font-size: 0.75rem; } .file-actions { display: flex; - flex-wrap: wrap; gap: 8px; width: 100%; } @@ -346,93 +390,137 @@ .file-actions .button { min-height: var(--touch-target-min); flex: 1; - min-width: calc(50% - 4px); justify-content: center; + font-size: 0.875rem; + padding: 10px 12px; } .file-grid { - grid-template-columns: 1fr; + display: flex; + flex-direction: column; gap: 1px; background-color: var(--border-color); - padding: 0; + border-radius: 8px; + overflow: hidden; } .file-item { + display: flex; flex-direction: row; align-items: center; - min-height: 56px; - padding: 12px 16px; + min-height: 60px; + padding: 10px 12px; background-color: var(--accent-color); - gap: 12px; + gap: 10px; border-radius: 0; border: none; + cursor: pointer; } - .file-item-checkbox { - width: 24px; - height: 24px; - min-width: 24px; + .file-item:first-child { + border-radius: 8px 8px 0 0; } - .file-item-checkbox::before { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: var(--touch-target-min); - height: var(--touch-target-min); + .file-item:last-child { + border-radius: 0 0 8px 8px; } - .file-item-icon { + .file-item:only-child { + border-radius: 8px; + } + + .file-item .select-item { + position: relative; + top: auto; + left: auto; + width: 20px; + height: 20px; + min-width: 20px; + opacity: 1; + cursor: pointer; + flex-shrink: 0; + } + + .file-icon { width: 40px; height: 40px; + min-width: 40px; + font-size: 2rem; + display: flex; + align-items: center; + justify-content: center; + margin: 0; + flex-shrink: 0; } - .file-item-info { + .file-item-info, + .file-item .file-name { flex: 1; min-width: 0; + overflow: hidden; } - .file-item-name { - font-size: 1rem; + .file-name { + font-size: 0.9rem; + font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + margin: 0; + text-align: left; } - .file-item-meta { + .file-size { font-size: 0.75rem; color: var(--text-color-light); + margin: 0; + flex-shrink: 0; + min-width: 50px; + text-align: right; } - .file-item-actions { + .file-actions-menu { + display: none; + } + + .mobile-more-btn { display: flex; - gap: 4px; } - .file-item-actions .action-btn { - width: var(--touch-target-min); - height: var(--touch-target-min); - min-width: var(--touch-target-min); + .batch-actions { + display: flex; + flex-wrap: wrap; + gap: 6px; + padding: 10px; + background-color: var(--accent-color); + border-radius: 8px; + margin-bottom: 12px; } - /* Breadcrumb Mobile */ - .breadcrumb { - padding: 8px 12px; + .batch-actions .button, + .batch-actions .button-small { + min-height: 36px; + padding: 6px 12px; + font-size: 0.75rem; + flex: 0 0 auto; + } + + .breadcrumb-nav { + padding: 8px 0; overflow-x: auto; -webkit-overflow-scrolling: touch; white-space: nowrap; + margin-bottom: 8px; + font-size: 0.8rem; } .breadcrumb-item { min-height: var(--touch-target-min); display: inline-flex; align-items: center; - padding: 0 8px; + padding: 0 4px; } - /* Overlays & Modals Mobile */ .modal-overlay, .file-upload-overlay, .file-preview-overlay, @@ -452,17 +540,18 @@ margin: 0; } - /* Bottom Sheet Style Modal */ .bottom-sheet { position: fixed; bottom: 0; left: 0; right: 0; - max-height: 90vh; + max-height: 85vh; border-radius: 16px 16px 0 0; padding-bottom: var(--safe-area-inset-bottom); transform: translateY(100%); transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); + background-color: var(--accent-color); + z-index: 1001; } .bottom-sheet.visible { @@ -477,7 +566,52 @@ margin: 8px auto 16px; } - /* Login View Mobile */ + .bottom-sheet-header { + padding: 16px; + border-bottom: 1px solid var(--border-color); + } + + .bottom-sheet-title { + margin: 0; + font-size: 1rem; + font-weight: 600; + } + + .bottom-sheet-content { + padding: 8px 0; + max-height: 60vh; + overflow-y: auto; + } + + .bottom-sheet-item { + display: flex; + align-items: center; + gap: 16px; + padding: 14px 20px; + background: none; + border: none; + width: 100%; + text-align: left; + font-size: 1rem; + color: var(--text-color); + cursor: pointer; + -webkit-tap-highlight-color: transparent; + } + + .bottom-sheet-item:active { + background-color: var(--background-color); + } + + .bottom-sheet-item.destructive { + color: #d32f2f; + } + + .bottom-sheet-icon { + font-size: 1.25rem; + width: 28px; + text-align: center; + } + .login-container { padding: 16px; padding-top: calc(16px + var(--safe-area-inset-top)); @@ -503,21 +637,24 @@ font-size: 1rem; } - /* Photo Gallery Mobile */ .gallery-grid { grid-template-columns: repeat(2, 1fr); gap: 2px; } - .gallery-item { + .gallery-item, + .photo-item { aspect-ratio: 1; } - /* Toast Mobile */ + .photo-item img { + height: 100%; + } + .toast-notification { - left: 16px; - right: 16px; - bottom: calc(16px + var(--safe-area-inset-bottom)); + left: 12px; + right: 12px; + bottom: calc(12px + var(--safe-area-inset-bottom)); max-width: none; transform: translateY(100px); } @@ -526,9 +663,8 @@ transform: translateY(0); } - /* Billing Dashboard Mobile */ .billing-container { - padding: 16px; + padding: 12px; } .billing-cards { @@ -539,32 +675,19 @@ width: 100%; } - .invoice-table-container { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - - .invoice-table { - min-width: 500px; - } - - /* Admin Dashboard Mobile */ - .admin-container { - padding: 16px; - } - + .invoice-table-container, .user-table-container { overflow-x: auto; -webkit-overflow-scrolling: touch; } + .invoice-table, .user-table { - min-width: 600px; + min-width: 500px; } - /* User Settings Mobile */ .settings-container { - padding: 16px; + padding: 12px; } .settings-section .button { @@ -572,7 +695,6 @@ min-height: var(--touch-target-comfortable); } - /* Share Modal Mobile - Bottom Sheet */ .share-modal { top: auto; bottom: 0; @@ -603,7 +725,6 @@ width: 100%; } - /* Cookie Consent Mobile */ .cookie-consent { left: 8px; right: 8px; @@ -620,21 +741,160 @@ width: 100%; min-height: var(--touch-target-min); } + + .upload-modal-content, + .share-modal-content { + max-height: 90vh; + } + + .admin-dashboard-container { + padding: 12px; + } +} + +@media (max-width: 479px) { + .app-title { + font-size: 1rem; + } + + .header-right .button { + padding: 8px 10px; + font-size: 0.8rem; + } + + .file-actions .button { + font-size: 0.8rem; + padding: 8px 10px; + } + + .file-name { + font-size: 0.85rem; + } + + .file-size { + font-size: 0.7rem; + min-width: 40px; + } + + .file-icon { + width: 36px; + height: 36px; + min-width: 36px; + font-size: 1.75rem; + } + + .batch-actions .button { + font-size: 0.7rem; + padding: 5px 8px; + } + + .gallery-grid { + grid-template-columns: repeat(2, 1fr); + gap: 1px; + } + + .breadcrumb-nav { + font-size: 0.75rem; + } + + .footer-links li a { + font-size: 0.55rem; + } + + .footer-text { + font-size: 0.55rem; + } +} + +@media (max-width: 359px) { + .app-header { + padding: 0 4px; + } + + .app-title { + font-size: 0.9rem; + } + + .header-center { + margin: 0 4px; + } + + .search-input { + font-size: 14px; + padding: 6px; + } + + .header-right .button { + padding: 6px 8px; + font-size: 0.75rem; + min-width: 38px; + } + + .app-main { + padding: 8px; + } + + .file-item { + padding: 8px; + gap: 8px; + min-height: 52px; + } + + .file-icon { + width: 32px; + height: 32px; + min-width: 32px; + font-size: 1.5rem; + } + + .file-name { + font-size: 0.8rem; + } + + .file-size { + font-size: 0.65rem; + min-width: 35px; + } + + .mobile-more-btn { + width: 32px; + height: 32px; + min-width: 32px; + } + + .file-actions { + flex-direction: column; + } + + .file-actions .button { + width: 100%; + } + + .batch-actions { + padding: 8px; + } + + .batch-actions .button { + font-size: 0.65rem; + padding: 4px 6px; + min-height: 32px; + } + + .selection-controls label { + font-size: 0.7rem; + } } -/* Mobile Landscape (480px+) */ @media (min-width: 480px) and (max-width: 767px) { .gallery-grid { grid-template-columns: repeat(3, 1fr); } .file-actions .button { - min-width: auto; flex: 0 1 auto; } } -/* Tablet (768px+) */ @media (min-width: 768px) { .hamburger-btn { display: none; @@ -651,7 +911,24 @@ } .file-grid { + display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + background-color: transparent; + gap: calc(var(--spacing-unit) * 2); + } + + .file-item { + flex-direction: column; + border: 1px solid var(--border-color); + border-radius: 8px; + } + + .file-actions-menu { + display: flex; + } + + .mobile-more-btn { + display: none; } .gallery-grid { @@ -659,7 +936,6 @@ } } -/* Desktop (1024px+) */ @media (min-width: 1024px) { .file-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); @@ -670,12 +946,13 @@ } } -/* Touch-specific styles */ @media (hover: none) and (pointer: coarse) { .button:hover, .nav-link:hover, .file-item:hover { background-color: inherit; + box-shadow: none; + border-color: var(--border-color); } .button:active, @@ -692,7 +969,6 @@ } } -/* Dark Mode Mobile Adjustments */ body.dark-mode .hamburger-btn span { background-color: var(--text-color); } @@ -706,12 +982,14 @@ body.dark-mode .button:active { background-color: rgba(255, 255, 255, 0.1); } -body.dark-mode .context-menu { +body.dark-mode .context-menu, +body.dark-mode .bottom-sheet { background-color: #333; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4); } -body.dark-mode .context-menu-item:active { +body.dark-mode .context-menu-item:active, +body.dark-mode .bottom-sheet-item:active { background-color: rgba(255, 255, 255, 0.1); } @@ -720,5 +998,5 @@ body.dark-mode .pull-to-refresh-indicator { } body.dark-mode .file-item { - background-color: var(--background-color); + background-color: var(--accent-color); } diff --git a/static/js/components/file-list.js b/static/js/components/file-list.js index 2454661..ceae325 100644 --- a/static/js/components/file-list.js +++ b/static/js/components/file-list.js @@ -138,7 +138,7 @@ export class FileList extends HTMLElement { renderFolder(folder) { const isSelected = this.selectedFolders.has(folder.id); - const starIcon = folder.is_starred ? '★' : '☆'; // Filled star or empty star + const starIcon = folder.is_starred ? '★' : '☆'; const starAction = folder.is_starred ? 'unstar-folder' : 'star-folder'; return `
@@ -149,6 +149,7 @@ export class FileList extends HTMLElement {
+ `; } @@ -157,7 +158,7 @@ export class FileList extends HTMLElement { const isSelected = this.selectedFiles.has(file.id); const icon = this.getFileIcon(file.mime_type); const size = this.formatFileSize(file.size); - const starIcon = file.is_starred ? '★' : '☆'; // Filled star or empty star + const starIcon = file.is_starred ? '★' : '☆'; const starAction = file.is_starred ? 'unstar-file' : 'star-file'; return ` @@ -173,6 +174,7 @@ export class FileList extends HTMLElement { + `; } @@ -260,6 +262,26 @@ export class FileList extends HTMLElement { return; } + if (target.classList.contains('mobile-more-btn')) { + e.stopPropagation(); + const fileId = target.dataset.fileId; + const folderId = target.dataset.folderId; + const rect = target.getBoundingClientRect(); + + if (fileId) { + const file = this.files.find(f => f.id === parseInt(fileId)); + if (file) { + this.showFileContextMenu(rect.left, rect.bottom, file); + } + } else if (folderId) { + const folder = this.folders.find(f => f.id === parseInt(folderId)); + if (folder) { + this.showFolderContextMenu(rect.left, rect.bottom, folder); + } + } + return; + } + if (target.classList.contains('select-item')) { e.stopPropagation(); return; diff --git a/static/js/components/mywebdav-app.js b/static/js/components/mywebdav-app.js index 8fee5e5..5a061de 100644 --- a/static/js/components/mywebdav-app.js +++ b/static/js/components/mywebdav-app.js @@ -100,6 +100,7 @@ export class MyWebdavApp extends HTMLElement { } showLogin() { + document.body.classList.remove('logged-in'); this.innerHTML = `
@@ -126,6 +127,7 @@ export class MyWebdavApp extends HTMLElement { } render() { + document.body.classList.add('logged-in'); this.innerHTML = `