300 lines
15 KiB
TypeScript
300 lines
15 KiB
TypeScript
import React from "react";
|
|
import { Toaster } from "@/components/ui/toaster";
|
|
import { Toaster as Sonner } from "@/components/ui/sonner";
|
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
import { QueryClientProvider } from "@tanstack/react-query";
|
|
import { queryClient } from "@/lib/queryClient";
|
|
import { BrowserRouter, Routes, Route, useLocation } from "react-router-dom";
|
|
import { AuthProvider, useAuth } from "@/hooks/useAuth";
|
|
|
|
import { OrganizationProvider } from "@/contexts/OrganizationContext";
|
|
import { LogProvider } from "@/contexts/LogContext";
|
|
|
|
import { MediaRefreshProvider } from "@/contexts/MediaRefreshContext";
|
|
import { ProfilesProvider } from "@/contexts/ProfilesContext";
|
|
import { WebSocketProvider } from "@/contexts/WS_Socket";
|
|
import { registerAllWidgets } from "@/lib/registerWidgets";
|
|
import TopNavigation from "@/components/TopNavigation";
|
|
import Footer from "@/components/Footer";
|
|
import { DragDropProvider } from "@/contexts/DragDropContext";
|
|
import { useAppStore } from "@/store/appStore";
|
|
|
|
const GlobalDragDrop = React.lazy(() => import("@/components/GlobalDragDrop"));
|
|
// Register all widgets on app boot
|
|
registerAllWidgets();
|
|
|
|
|
|
import Index from "./pages/Index";
|
|
import Auth from "./pages/Auth";
|
|
|
|
const UpdatePassword = React.lazy(() => import("./pages/UpdatePassword"));
|
|
|
|
import Profile from "./pages/Profile";
|
|
const Post = React.lazy(() => import("./modules/posts/PostPage"));
|
|
|
|
import UserProfile from "./pages/UserProfile";
|
|
import TagPage from "./pages/TagPage";
|
|
import SearchResults from "./pages/SearchResults";
|
|
|
|
const LogsPage = React.lazy(() => import("./components/logging/LogsPage"));
|
|
const Wizard = React.lazy(() => import("./pages/Wizard"));
|
|
const ProviderSettings = React.lazy(() => import("./pages/ProviderSettings"));
|
|
const NotFound = React.lazy(() => import("./pages/NotFound"));
|
|
const AdminPage = React.lazy(() => import("./pages/AdminPage"));
|
|
|
|
const enablePlaygrounds = import.meta.env.VITE_ENABLE_PLAYGROUNDS === 'true';
|
|
|
|
let PlaygroundEditor: any;
|
|
let PlaygroundEditorLLM: any;
|
|
let VideoPlayerPlayground: any;
|
|
let VideoFeedPlayground: any;
|
|
let VideoPlayerPlaygroundIntern: any;
|
|
let PlaygroundImages: any;
|
|
let PlaygroundImageEditor: any;
|
|
let VideoGenPlayground: any;
|
|
let PlaygroundCanvas: any;
|
|
let TypesPlayground: any;
|
|
let VariablePlayground: any;
|
|
let I18nPlayground: any;
|
|
let PlaygroundChat: any;
|
|
let GridSearch: any;
|
|
let PlacesModule: any;
|
|
let LocationDetail: any;
|
|
let Tetris: any;
|
|
|
|
let FileBrowser: any;
|
|
let SupportChat: any;
|
|
|
|
GridSearch = React.lazy(() => import("./modules/places/gridsearch/GridSearch"));
|
|
LocationDetail = React.lazy(() => import("./modules/places/LocationDetail"));
|
|
TypesPlayground = React.lazy(() => import("@/modules/types/TypesPlayground"));
|
|
|
|
if (enablePlaygrounds) {
|
|
PlaygroundEditor = React.lazy(() => import("./pages/PlaygroundEditor"));
|
|
PlaygroundEditorLLM = React.lazy(() => import("./pages/PlaygroundEditorLLM"));
|
|
VideoPlayerPlayground = React.lazy(() => import("./pages/VideoPlayerPlayground"));
|
|
VideoFeedPlayground = React.lazy(() => import("./pages/VideoFeedPlayground"));
|
|
VideoPlayerPlaygroundIntern = React.lazy(() => import("./pages/VideoPlayerPlaygroundIntern"));
|
|
PlaygroundImages = React.lazy(() => import("./pages/PlaygroundImages"));
|
|
PlaygroundImageEditor = React.lazy(() => import("./pages/PlaygroundImageEditor"));
|
|
VideoGenPlayground = React.lazy(() => import("./pages/VideoGenPlayground"));
|
|
PlaygroundCanvas = React.lazy(() => import("./modules/layout/PlaygroundCanvas"));
|
|
VariablePlayground = React.lazy(() => import("./components/variables/VariablesEditor").then(module => ({ default: module.VariablesEditor })));
|
|
I18nPlayground = React.lazy(() => import("./components/playground/I18nPlayground"));
|
|
PlaygroundChat = React.lazy(() => import("./pages/PlaygroundChat"));
|
|
|
|
SupportChat = React.lazy(() => import("./pages/SupportChat"));
|
|
}
|
|
|
|
|
|
Tetris = React.lazy(() => import("./apps/tetris/Tetris"));
|
|
FileBrowser = React.lazy(() => import("./apps/filebrowser/FileBrowser"));
|
|
|
|
|
|
const VersionMap = React.lazy(() => import("./pages/VersionMap"));
|
|
const UserCollections = React.lazy(() => import("./pages/UserCollections"));
|
|
const Collections = React.lazy(() => import("./pages/Collections"));
|
|
const NewCollection = React.lazy(() => import("./pages/NewCollection"));
|
|
const UserPage = React.lazy(() => import("./modules/pages/UserPage"));
|
|
const NewPage = React.lazy(() => import("./modules/pages/NewPage"));
|
|
const NewPost = React.lazy(() => import("./modules/posts/NewPost"));
|
|
const EditPost = React.lazy(() => import("./modules/posts/EditPost"));
|
|
|
|
// <GlobalDebug />
|
|
import { EcommerceBundleWrapper } from "./bundles/ecommerce";
|
|
|
|
// Create a single tracker instance outside the component to avoid re-creation on re-renders
|
|
/*
|
|
const tracker = new Tracker({
|
|
projectKey: import.meta.env.VITE_OPENREPLAY_KEY || 'wiN7QPfX48YLyozADQmW',
|
|
respectDoNotTrack: false,
|
|
__DISABLE_SECURE_MODE: true,
|
|
});
|
|
tracker.use(trackerAssist());
|
|
*/
|
|
|
|
const AppWrapper = () => {
|
|
const location = useLocation();
|
|
const searchParams = new URLSearchParams(location.search);
|
|
const isPageEditor = location.pathname.includes('/pages/') && searchParams.get('edit') === 'true';
|
|
const isFullScreenPage = location.pathname.startsWith('/video-feed') || isPageEditor;
|
|
|
|
const containerClassName = isFullScreenPage
|
|
? "flex flex-col min-h-svh transition-colors duration-200 h-full"
|
|
: "mx-auto max-w-[1400px] flex flex-col min-h-svh transition-colors duration-200 h-full";
|
|
|
|
const { showGlobalFooter } = useAppStore();
|
|
|
|
const ecommerce = import.meta.env.VITE_ENABLE_ECOMMERCE === 'true';
|
|
return (
|
|
<div className={containerClassName}>
|
|
{!isFullScreenPage && <TopNavigation />}
|
|
<React.Suspense fallback={null}><GlobalDragDrop /></React.Suspense>
|
|
<main className="flex-1">
|
|
<Routes>
|
|
{/* Top-level routes (no organization context) */}
|
|
<Route path="/" element={<Index />} />
|
|
<Route path="/auth" element={<Auth />} />
|
|
<Route path="/auth/update-password" element={<React.Suspense fallback={<div>Loading...</div>}><UpdatePassword /></React.Suspense>} />
|
|
<Route path="/profile/*" element={<Profile />} />
|
|
<Route path="/post/new" element={<React.Suspense fallback={<div>Loading...</div>}><EditPost /></React.Suspense>} />
|
|
<Route path="/post/:id" element={<React.Suspense fallback={<div>Loading...</div>}><Post /></React.Suspense>} />
|
|
<Route path="/post/:id/edit" element={<React.Suspense fallback={<div>Loading...</div>}><EditPost /></React.Suspense>} />
|
|
<Route path="/video/:id" element={<React.Suspense fallback={<div>Loading...</div>}><Post /></React.Suspense>} />
|
|
<Route path="/user/:userId" element={<UserProfile />} />
|
|
<Route path="/user/:userId/posts" element={<UserProfile />} />
|
|
<Route path="/user/:userId/pages" element={<UserProfile />} />
|
|
<Route path="/user/:userId/pictures" element={<UserProfile />} />
|
|
<Route path="/user/:userId/collections" element={<React.Suspense fallback={<div>Loading...</div>}><UserCollections /></React.Suspense>} />
|
|
<Route path="/user/:userId/pages/new" element={<React.Suspense fallback={<div>Loading...</div>}><NewPage /></React.Suspense>} />
|
|
<Route path="/user/:username/pages/:slug" element={<React.Suspense fallback={<div>Loading...</div>}><UserPage /></React.Suspense>} />
|
|
<Route path="/collections/new" element={<React.Suspense fallback={<div>Loading...</div>}><NewCollection /></React.Suspense>} />
|
|
<Route path="/collections/:userId/:slug" element={<React.Suspense fallback={<div>Loading...</div>}><Collections /></React.Suspense>} />
|
|
<Route path="/tags/:tag" element={<TagPage />} />
|
|
<Route path="/latest" element={<Index />} />
|
|
<Route path="/top" element={<Index />} />
|
|
<Route path="/posts" element={<Index />} />
|
|
<Route path="/pages" element={<Index />} />
|
|
<Route path="/categories" element={<Index />} />
|
|
<Route path="/categories/*" element={<Index />} />
|
|
<Route path="/search" element={<SearchResults />} />
|
|
<Route path="/wizard" element={<React.Suspense fallback={<div>Loading...</div>}><Wizard /></React.Suspense>} />
|
|
<Route path="/new" element={<React.Suspense fallback={<div>Loading...</div>}><NewPost /></React.Suspense>} />
|
|
<Route path="/version-map/:id" element={
|
|
<React.Suspense fallback={<div className="flex items-center justify-center h-screen">Loading map...</div>}>
|
|
<VersionMap />
|
|
</React.Suspense>
|
|
} />
|
|
<Route path="/settings/providers" element={<React.Suspense fallback={<div>Loading...</div>}><ProviderSettings /></React.Suspense>} />
|
|
|
|
{enablePlaygrounds && (
|
|
<>
|
|
<Route path="/playground/editor" element={<React.Suspense fallback={<div>Loading...</div>}><PlaygroundEditor /></React.Suspense>} />
|
|
<Route path="/playground/editor-llm" element={<React.Suspense fallback={<div>Loading...</div>}><PlaygroundEditorLLM /></React.Suspense>} />
|
|
<Route path="/playground/video-player" element={<React.Suspense fallback={<div>Loading...</div>}><VideoPlayerPlayground /></React.Suspense>} />
|
|
<Route path="/playground-video-player-intern" element={<React.Suspense fallback={<div>Loading...</div>}><VideoPlayerPlaygroundIntern /></React.Suspense>} />
|
|
<Route path="/video-feed" element={<React.Suspense fallback={<div>Loading...</div>}><VideoFeedPlayground /></React.Suspense>} />
|
|
<Route path="/video-feed/:id" element={<React.Suspense fallback={<div>Loading...</div>}><VideoFeedPlayground /></React.Suspense>} />
|
|
</>
|
|
)}
|
|
|
|
{/* Admin Routes */}
|
|
<Route path="/admin/*" element={<React.Suspense fallback={<div>Loading...</div>}><AdminPage /></React.Suspense>} />
|
|
|
|
<Route path="/products/gridsearch" element={<React.Suspense fallback={<div>Loading...</div>}><GridSearch /></React.Suspense>} />
|
|
<Route path="/products/places/detail/:place_id" element={<React.Suspense fallback={<div>Loading...</div>}><LocationDetail /></React.Suspense>} />
|
|
{enablePlaygrounds && <Route path="/products/places/*" element={<React.Suspense fallback={<div>Loading...</div>}><PlacesModule /></React.Suspense>} />}
|
|
|
|
<Route path="/types-editor" element={<React.Suspense fallback={<div>Loading...</div>}><TypesPlayground /></React.Suspense>} />
|
|
|
|
{/* Playground Routes */}
|
|
{enablePlaygrounds && (
|
|
<>
|
|
<Route path="/playground/images" element={<React.Suspense fallback={<div>Loading...</div>}><PlaygroundImages /></React.Suspense>} />
|
|
<Route path="/playground/image-editor" element={<React.Suspense fallback={<div>Loading...</div>}><PlaygroundImageEditor /></React.Suspense>} />
|
|
<Route path="/playground/video-generator" element={<React.Suspense fallback={<div>Loading...</div>}><VideoGenPlayground /></React.Suspense>} />
|
|
<Route path="/playground/canvas" element={<React.Suspense fallback={<div>Loading...</div>}><PlaygroundCanvas /></React.Suspense>} />
|
|
<Route path="/variables-editor" element={<React.Suspense fallback={<div>Loading...</div>}><VariablePlayground /></React.Suspense>} />
|
|
<Route path="/playground/i18n" element={<React.Suspense fallback={<div>Loading...</div>}><I18nPlayground /></React.Suspense>} />
|
|
<Route path="/playground/chat" element={<React.Suspense fallback={<div>Loading...</div>}><PlaygroundChat /></React.Suspense>} />
|
|
</>
|
|
)}
|
|
|
|
{enablePlaygrounds && <Route path="/support-chat" element={<React.Suspense fallback={<div>Loading...</div>}><SupportChat /></React.Suspense>} />}
|
|
|
|
{/* Logs */}
|
|
<Route path="/logs" element={<React.Suspense fallback={<div>Loading...</div>}><LogsPage /></React.Suspense>} />
|
|
|
|
{/* Apps */}
|
|
<Route path="/app/tetris" element={<React.Suspense fallback={<div>Loading...</div>}><Tetris /></React.Suspense>} />
|
|
<Route path="/app/filebrowser/*" element={<React.Suspense fallback={<div>Loading...</div>}><FileBrowser /></React.Suspense>} />
|
|
{enablePlaygrounds && <Route path="/support-chat" element={<React.Suspense fallback={<div>Loading...</div>}><SupportChat /></React.Suspense>} />}
|
|
|
|
{/* Ecommerce Routes */}
|
|
{(ecommerce) && (
|
|
<>
|
|
<Route path="/cart/*" element={<EcommerceBundleWrapper />} />
|
|
<Route path="/checkout/*" element={<EcommerceBundleWrapper />} />
|
|
<Route path="/shipping/*" element={<EcommerceBundleWrapper />} />
|
|
<Route path="/returns/*" element={<EcommerceBundleWrapper />} />
|
|
<Route path="/privacy/*" element={<EcommerceBundleWrapper />} />
|
|
<Route path="/terms/*" element={<EcommerceBundleWrapper />} />
|
|
<Route path="/impressum/*" element={<EcommerceBundleWrapper />} />
|
|
<Route path="/purchases/*" element={<EcommerceBundleWrapper />} />
|
|
</>
|
|
)}
|
|
|
|
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
|
|
<Route path="*" element={<React.Suspense fallback={<div>Loading...</div>}><NotFound /></React.Suspense>} />
|
|
</Routes >
|
|
</main>
|
|
{!isFullScreenPage && showGlobalFooter && <Footer />}
|
|
</div >
|
|
);
|
|
};
|
|
|
|
import { initFormatDetection } from '@/utils/formatDetection';
|
|
|
|
import { SWRConfig } from 'swr';
|
|
|
|
|
|
// ... (imports)
|
|
|
|
import { FeedCacheProvider } from "@/contexts/FeedCacheContext";
|
|
import { StreamProvider } from "@/contexts/StreamContext";
|
|
import { StreamInvalidator } from "@/components/StreamInvalidator";
|
|
|
|
// ... (imports)
|
|
|
|
import { ActionProvider } from "@/actions/ActionProvider";
|
|
import { HelmetProvider } from 'react-helmet-async';
|
|
|
|
// ... previous imports ...
|
|
|
|
const App = () => {
|
|
React.useEffect(() => {
|
|
initFormatDetection();
|
|
}, []);
|
|
|
|
return (
|
|
<HelmetProvider>
|
|
<SWRConfig value={{ provider: () => new Map() }}>
|
|
<QueryClientProvider client={queryClient}>
|
|
<AuthProvider>
|
|
<LogProvider>
|
|
<MediaRefreshProvider>
|
|
<TooltipProvider>
|
|
<Toaster />
|
|
<Sonner />
|
|
<ActionProvider>
|
|
<BrowserRouter>
|
|
<DragDropProvider>
|
|
<OrganizationProvider>
|
|
<ProfilesProvider>
|
|
<WebSocketProvider url={import.meta.env.VITE_SERVER_IMAGE_API_URL}>
|
|
<StreamProvider url={import.meta.env.VITE_SERVER_IMAGE_API_URL}>
|
|
<StreamInvalidator />
|
|
<FeedCacheProvider>
|
|
<AppWrapper />
|
|
</FeedCacheProvider>
|
|
</StreamProvider>
|
|
</WebSocketProvider>
|
|
</ProfilesProvider>
|
|
</OrganizationProvider>
|
|
</DragDropProvider>
|
|
</BrowserRouter>
|
|
</ActionProvider>
|
|
|
|
</TooltipProvider>
|
|
|
|
</MediaRefreshProvider>
|
|
</LogProvider>
|
|
</AuthProvider>
|
|
</QueryClientProvider>
|
|
</SWRConfig>
|
|
</HelmetProvider >
|
|
);
|
|
};
|
|
|
|
export default App;
|