diff --git a/PLAN-4.1.2.md b/PLAN-4.1.2.md index dbdea5b..a4e82da 100644 --- a/PLAN-4.1.2.md +++ b/PLAN-4.1.2.md @@ -167,21 +167,24 @@ Polish and UX improvements after the 4.1.1 stability release. ## 4. Forced First-Login Setup -**Status:** Planned +**Status:** Done **Problem:** Users can navigate the app without creating an admin account first. Should force password setup before anything else. **Solution:** Middleware/decorator that redirects to setup page if no users exist. -### Files to Modify -- `frontends/web/app.py` (add before_request check) -- `frontends/web/templates/setup.html` (ensure it blocks other nav) +### Implementation +- Added `@app.before_request` hook that redirects to /setup if no users exist +- Skips redirect for static files and setup-related routes + +### Files Modified +- `frontends/web/app.py` (added require_setup before_request hook) --- ## 5. Dropzone UX Fixes -**Status:** Planned +**Status:** Done **Problem:** Dropzone has some interaction bugs: - Dropzone doesn't clear properly if first QR image fails @@ -189,9 +192,14 @@ Polish and UX improvements after the 4.1.1 stability release. **Solution:** Fix JS event handling and state management -### Files to Modify +### Implementation +- Added click handler on preview images to trigger file input +- Made entire drop zone clickable (not just label) +- QR zone now resets after 2 seconds on error, allowing retry +- Clear file input on QR error so same file can be re-selected + +### Files Modified - `frontends/web/static/js/stegasoo.js` -- `frontends/web/static/css/style.css` (clickable preview) --- @@ -272,6 +280,6 @@ Polish and UX improvements after the 4.1.1 stability release. ## Notes -- Keep 4.1.2 focused - 9 features (2 done) +- Keep 4.1.2 focused - 9 features (4 done) - Don't break DCT compatibility (4.1.1 RS format is stable) - Test on Pi before release diff --git a/frontends/web/app.py b/frontends/web/app.py index af0c88d..6a23c0b 100644 --- a/frontends/web/app.py +++ b/frontends/web/app.py @@ -192,6 +192,24 @@ app.config["HTTPS_ENABLED"] = os.environ.get("STEGASOO_HTTPS_ENABLED", "false"). # Initialize auth module init_auth(app) + +@app.before_request +def require_setup(): + """Force redirect to setup if no users exist (first-run).""" + if not app.config.get("AUTH_ENABLED", True): + return None + + # Skip for static files and setup-related routes + if request.endpoint in ("static", "setup", "setup_recovery", None): + return None + + # If no users exist, redirect to setup + if not user_exists(): + return redirect(url_for("setup")) + + return None + + # Temporary file storage for sharing (file_id -> {data, timestamp, filename}) TEMP_FILES: dict[str, dict] = {} THUMBNAIL_FILES: dict[str, bytes] = {} diff --git a/frontends/web/static/js/stegasoo.js b/frontends/web/static/js/stegasoo.js index 556b69d..7b40f4c 100644 --- a/frontends/web/static/js/stegasoo.js +++ b/frontends/web/static/js/stegasoo.js @@ -99,6 +99,23 @@ const Stegasoo = { } }); } + + // Make preview clickable to replace file + if (preview) { + preview.style.cursor = 'pointer'; + preview.addEventListener('click', (e) => { + e.stopPropagation(); + input.click(); + }); + } + + // Make entire zone clickable (in case label/preview don't cover it) + zone.addEventListener('click', (e) => { + // Only trigger if not clicking directly on the input + if (e.target !== input) { + input.click(); + } + }); }); }, @@ -575,7 +592,7 @@ const Stegasoo = { console.log('QR crop/extract error:', err); container.classList.remove('loading', 'scanning'); container.classList.add('error'); - + // Update loader to show error const loader = container.querySelector('.qr-loader'); if (loader) { @@ -584,6 +601,17 @@ const Stegasoo = { No QR code detected `; } + + // Reset after delay so user can try again + setTimeout(() => { + container.classList.remove('error'); + container.classList.add('d-none'); + label?.classList.remove('d-none'); + // Clear the file input so same file can be re-selected + input.value = ''; + // Remove loader + if (loader) loader.remove(); + }, 2000); }); }); },