From 7f8f634348bf1b91db43ee04723d664d2020b537 Mon Sep 17 00:00:00 2001 From: Xoconoch Date: Tue, 5 Aug 2025 12:12:14 -0600 Subject: [PATCH] Optimized frontend --- app.py | 2 +- spotizerr-ui/package.json | 2 +- spotizerr-ui/src/router.tsx | 20 ++++---- spotizerr-ui/src/routes/config.tsx | 75 +++++++++++++++++++++++------- spotizerr-ui/vite.config.ts | 46 ++++++++++++++++++ 5 files changed, 117 insertions(+), 28 deletions(-) diff --git a/app.py b/app.py index 73f75ea..b044505 100755 --- a/app.py +++ b/app.py @@ -167,7 +167,7 @@ def create_app(): app = FastAPI( title="Spotizerr API", description="Music download service API", - version="1.0.0", + version="3.0.0", lifespan=lifespan, redirect_slashes=True # Enable automatic trailing slash redirects ) diff --git a/spotizerr-ui/package.json b/spotizerr-ui/package.json index 7e56235..2916fb0 100644 --- a/spotizerr-ui/package.json +++ b/spotizerr-ui/package.json @@ -1,7 +1,7 @@ { "name": "spotizerr-ui", "private": true, - "version": "0.0.0", + "version": "3.0.0", "type": "module", "scripts": { "dev": "vite", diff --git a/spotizerr-ui/src/router.tsx b/spotizerr-ui/src/router.tsx index 212971c..c125d57 100644 --- a/spotizerr-ui/src/router.tsx +++ b/spotizerr-ui/src/router.tsx @@ -1,16 +1,18 @@ -import { createRouter, createRootRoute, createRoute } from "@tanstack/react-router"; +import { createRouter, createRootRoute, createRoute, lazyRouteComponent } from "@tanstack/react-router"; import Root from "./routes/root"; -import { Album } from "./routes/album"; -import { Artist } from "./routes/artist"; -import { Track } from "./routes/track"; -import { Home } from "./routes/home"; -import { Config } from "./routes/config"; -import { Playlist } from "./routes/playlist"; -import { History } from "./routes/history"; -import { Watchlist } from "./routes/watchlist"; import apiClient from "./lib/api-client"; import type { SearchResult } from "./types/spotify"; +// Lazy load route components for code splitting +const Album = lazyRouteComponent(() => import("./routes/album").then(m => ({ default: m.Album }))); +const Artist = lazyRouteComponent(() => import("./routes/artist").then(m => ({ default: m.Artist }))); +const Track = lazyRouteComponent(() => import("./routes/track").then(m => ({ default: m.Track }))); +const Home = lazyRouteComponent(() => import("./routes/home").then(m => ({ default: m.Home }))); +const Config = lazyRouteComponent(() => import("./routes/config").then(m => ({ default: m.Config }))); +const Playlist = lazyRouteComponent(() => import("./routes/playlist").then(m => ({ default: m.Playlist }))); +const History = lazyRouteComponent(() => import("./routes/history").then(m => ({ default: m.History }))); +const Watchlist = lazyRouteComponent(() => import("./routes/watchlist").then(m => ({ default: m.Watchlist }))); + const rootRoute = createRootRoute({ component: Root, }); diff --git a/spotizerr-ui/src/routes/config.tsx b/spotizerr-ui/src/routes/config.tsx index 4b148bd..3351ec6 100644 --- a/spotizerr-ui/src/routes/config.tsx +++ b/spotizerr-ui/src/routes/config.tsx @@ -1,17 +1,26 @@ -import { useState, useEffect, useRef } from "react"; +import { useState, useEffect, useRef, Suspense, lazy } from "react"; import { useSearch } from "@tanstack/react-router"; -import { GeneralTab } from "../components/config/GeneralTab"; -import { DownloadsTab } from "../components/config/DownloadsTab"; -import { FormattingTab } from "../components/config/FormattingTab"; -import { AccountsTab } from "../components/config/AccountsTab"; -import { WatchTab } from "../components/config/WatchTab"; -import { ServerTab } from "../components/config/ServerTab"; -import { UserManagementTab } from "../components/config/UserManagementTab"; -import { ProfileTab } from "../components/config/ProfileTab"; import { useSettings } from "../contexts/settings-context"; import { useAuth } from "../contexts/auth-context"; import { LoginScreen } from "../components/auth/LoginScreen"; +// Lazy load config tab components for better code splitting +const GeneralTab = lazy(() => import("../components/config/GeneralTab").then(m => ({ default: m.GeneralTab }))); +const DownloadsTab = lazy(() => import("../components/config/DownloadsTab").then(m => ({ default: m.DownloadsTab }))); +const FormattingTab = lazy(() => import("../components/config/FormattingTab").then(m => ({ default: m.FormattingTab }))); +const AccountsTab = lazy(() => import("../components/config/AccountsTab").then(m => ({ default: m.AccountsTab }))); +const WatchTab = lazy(() => import("../components/config/WatchTab").then(m => ({ default: m.WatchTab }))); +const ServerTab = lazy(() => import("../components/config/ServerTab").then(m => ({ default: m.ServerTab }))); +const UserManagementTab = lazy(() => import("../components/config/UserManagementTab").then(m => ({ default: m.UserManagementTab }))); +const ProfileTab = lazy(() => import("../components/config/ProfileTab").then(m => ({ default: m.ProfileTab }))); + +// Loading component for tab transitions +const TabLoading = () => ( +
+
+
+); + const ConfigComponent = () => { const { tab } = useSearch({ from: "/config" }); const { user, isAuthenticated, authEnabled, isLoading: authLoading } = useAuth(); @@ -102,11 +111,19 @@ const ConfigComponent = () => { const renderTabContent = () => { // User management and profile don't need config data if (activeTab === "user-management") { - return ; + return ( + }> + + + ); } if (activeTab === "profile") { - return ; + return ( + }> + + + ); } if (isLoading) return

Loading configuration...

; @@ -114,17 +131,41 @@ const ConfigComponent = () => { switch (activeTab) { case "general": - return ; + return ( + }> + + + ); case "downloads": - return ; + return ( + }> + + + ); case "formatting": - return ; + return ( + }> + + + ); case "accounts": - return ; + return ( + }> + + + ); case "watch": - return ; + return ( + }> + + + ); case "server": - return ; + return ( + }> + + + ); default: return null; } diff --git a/spotizerr-ui/vite.config.ts b/spotizerr-ui/vite.config.ts index fbe71b0..7db129c 100644 --- a/spotizerr-ui/vite.config.ts +++ b/spotizerr-ui/vite.config.ts @@ -99,6 +99,52 @@ export default defineConfig({ "@": resolve(__dirname, "./src"), }, }, + build: { + chunkSizeWarningLimit: 1000, // Increase warning limit to 1MB + rollupOptions: { + output: { + manualChunks: { + // Core React and routing + 'react-vendor': ['react', 'react-dom'], + 'router-vendor': ['@tanstack/react-router'], + + // Query and state management + 'query-vendor': ['@tanstack/react-query'], + + // UI and icon libraries + 'ui-vendor': ['lucide-react', 'react-icons', 'sonner'], + + // Table components (only used in specific routes) + 'table-vendor': ['@tanstack/react-table'], + + // Form handling + 'form-vendor': ['react-hook-form', 'use-debounce'], + + // HTTP client + 'http-vendor': ['axios'], + + // Config components (heavy route with many tabs) + 'config-components': [ + './src/components/config/GeneralTab', + './src/components/config/DownloadsTab', + './src/components/config/FormattingTab', + './src/components/config/AccountsTab', + './src/components/config/WatchTab', + './src/components/config/ServerTab', + './src/components/config/UserManagementTab', + './src/components/config/ProfileTab' + ], + + // Utilities and helpers + 'utils-vendor': ['uuid'], + }, + // Additional chunk optimization + chunkFileNames: () => { + return `assets/[name]-[hash].js`; + }, + }, + }, + }, server: { host: true, port: 5173,