From 326ed035d891d0b9988586cca9360486c8dc649e Mon Sep 17 00:00:00 2001 From: Xoconoch Date: Fri, 8 Aug 2025 11:57:02 -0600 Subject: [PATCH] Fix config fallback checking logic and PWA scrolling --- routes/system/config.py | 20 ++++++++++++----- spotizerr-ui/src/index.css | 12 +++++----- spotizerr-ui/src/lib/api-client.ts | 4 ++-- spotizerr-ui/src/routes/home.tsx | 35 +----------------------------- spotizerr-ui/src/routes/root.tsx | 14 ++++++------ 5 files changed, 30 insertions(+), 55 deletions(-) diff --git a/routes/system/config.py b/routes/system/config.py index 5dc88ce..a5b9493 100644 --- a/routes/system/config.py +++ b/routes/system/config.py @@ -23,6 +23,9 @@ from routes.utils.watch.manager import ( from routes.auth.middleware import require_admin_from_state, User from routes.auth import AUTH_ENABLED, DISABLE_REGISTRATION +# Import credential utilities (DB-backed) +from routes.utils.credentials import list_credentials, _get_global_spotify_api_creds + logger = logging.getLogger(__name__) router = APIRouter() @@ -45,13 +48,18 @@ NOTIFY_PARAMETERS = [ def has_credentials(service: str) -> bool: """Check if credentials exist for the specified service (spotify or deezer).""" try: - credentials_path = Path(f"./data/credentials/{service}") - if not credentials_path.exists(): + if service not in ("spotify", "deezer"): return False - - # Check if there are any credential files in the directory - credential_files = list(credentials_path.glob("*.json")) - return len(credential_files) > 0 + + account_names = list_credentials(service) + has_any_accounts = bool(account_names) + + if service == "spotify": + client_id, client_secret = _get_global_spotify_api_creds() + has_global_api_creds = bool(client_id) and bool(client_secret) + return has_any_accounts and has_global_api_creds + + return has_any_accounts except Exception as e: logger.warning(f"Error checking credentials for {service}: {e}") return False diff --git a/spotizerr-ui/src/index.css b/spotizerr-ui/src/index.css index 7551a94..31436db 100644 --- a/spotizerr-ui/src/index.css +++ b/spotizerr-ui/src/index.css @@ -185,23 +185,21 @@ /* PWA specific body styling */ body { background: var(--color-surface); - overscroll-behavior: none; } .dark body { background: var(--color-surface-dark); } /* PWA viewport fixes */ - html { - height: 100vh; - height: 100dvh; /* Dynamic viewport height for mobile */ + html, body { + height: 100%; + min-height: 100%; } body { - min-height: 100vh; - min-height: 100dvh; margin: 0; padding: 0; + overflow-y: auto; } a { @@ -232,6 +230,8 @@ .pwa-main { padding-left: var(--safe-area-inset-left); padding-right: var(--safe-area-inset-right); + overscroll-behavior-y: contain; + -webkit-overflow-scrolling: touch; } } diff --git a/spotizerr-ui/src/lib/api-client.ts b/spotizerr-ui/src/lib/api-client.ts index 11bf3b1..8a2cf02 100644 --- a/spotizerr-ui/src/lib/api-client.ts +++ b/spotizerr-ui/src/lib/api-client.ts @@ -32,8 +32,8 @@ class AuthApiClient { // Request interceptor to add auth token this.apiClient.interceptors.request.use( (config) => { - // Only add auth header if auth is enabled and we have a token - if (this.authEnabled && this.token) { + // Add auth header whenever we have a token so startup validation works before authEnabled is known + if (this.token) { config.headers.Authorization = `Bearer ${this.token}`; } return config; diff --git a/spotizerr-ui/src/routes/home.tsx b/spotizerr-ui/src/routes/home.tsx index 9f59630..f969a5e 100644 --- a/spotizerr-ui/src/routes/home.tsx +++ b/spotizerr-ui/src/routes/home.tsx @@ -40,40 +40,7 @@ export const Home = () => { const context = useContext(QueueContext); const loaderRef = useRef(null); - // Prevent scrolling on mobile only when there are no results (empty state) - useEffect(() => { - const isMobile = window.innerWidth < 768; // md breakpoint - if (!isMobile) return; - - // Only prevent scrolling when there are no results to show - const shouldPreventScroll = !isLoading && displayedResults.length === 0 && !query.trim(); - - if (!shouldPreventScroll) return; - - // Store original styles - const originalOverflow = document.body.style.overflow; - const originalHeight = document.body.style.height; - - // Find the mobile main content container - const mobileMain = document.querySelector('.pwa-main') as HTMLElement; - const originalMainOverflow = mobileMain?.style.overflow; - - // Prevent body and main container scrolling on mobile when empty - document.body.style.overflow = 'hidden'; - document.body.style.height = '100vh'; - if (mobileMain) { - mobileMain.style.overflow = 'hidden'; - } - - // Cleanup function - return () => { - document.body.style.overflow = originalOverflow; - document.body.style.height = originalHeight; - if (mobileMain) { - mobileMain.style.overflow = originalMainOverflow; - } - }; - }, [isLoading, displayedResults.length, query]); + // Removed scroll locking on mobile empty state to avoid blocking scroll globally useEffect(() => { navigate({ search: (prev) => ({ ...prev, q: debouncedQuery, type: searchType }) }); diff --git a/spotizerr-ui/src/routes/root.tsx b/spotizerr-ui/src/routes/root.tsx index 9ba8f8e..f39cc2b 100644 --- a/spotizerr-ui/src/routes/root.tsx +++ b/spotizerr-ui/src/routes/root.tsx @@ -82,9 +82,9 @@ function AppLayout() { const { toggleVisibility, totalTasks } = useContext(QueueContext) || {}; return ( -
+
{/* Desktop Header */} -
+
Spotizerr @@ -119,9 +119,9 @@ function AppLayout() { {/* Mobile Layout Container */} -
+
{/* Mobile Header - Fixed */} -
+
Spotizerr @@ -131,12 +131,12 @@ function AppLayout() {
- {/* Mobile Main Content - Scrollable container */} -
+ {/* Mobile Main Content - Constrained scroll area between headers */} +
-
+
{/* Mobile Bottom Navigation - Fixed */}