Skip to content
This repository was archived by the owner on Sep 19, 2025. It is now read-only.

Commit 08fd818

Browse files
committed
Added startup loading screen.
- Reader serach enhancements
1 parent 8766f26 commit 08fd818

9 files changed

Lines changed: 425 additions & 265 deletions

File tree

bun.lock

Lines changed: 28 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"@tailwindcss/vite": "^4.1.4",
4242
"class-variance-authority": "^0.7.1",
4343
"clsx": "^2.1.1",
44+
"framer-motion": "^12.9.7",
4445
"i18next": "^24.2.3",
4546
"i18next-http-backend": "^3.0.2",
4647
"input-otp": "^1.4.2",

src/frontend/App.tsx

Lines changed: 98 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
2-
import { useEffect } from 'react';
2+
import { useEffect, useState } from 'react';
33
import Register from './components/pages/Register';
44
import Login from './components/pages/Login';
55
import Tos from './components/pages/Tos';
@@ -11,8 +11,35 @@ import MainLayout from './components/layout/MainLayout';
1111
import { toggleDevTools } from './lib/utils';
1212
import PrivacyPolicy from './components/pages/PrivacyPolicy';
1313
import FileReader from './components/pages/FileReader';
14+
import ApplicationSplash from './components/modals/ApplicationSplash';
15+
import { AnimatePresence } from 'framer-motion';
16+
17+
// Key for session storage to check if app has been loaded before
18+
const APP_LOADED_KEY = 'cloudnotes-app-loaded';
1419

1520
function App() {
21+
const [isLoading, setIsLoading] = useState(true);
22+
23+
// Handle application loading state
24+
useEffect(() => {
25+
// Check if this is the first load of the app in this session
26+
const hasLoadedBefore = sessionStorage.getItem(APP_LOADED_KEY);
27+
28+
if (hasLoadedBefore) {
29+
// If app has been loaded before in this session, skip the loading screen
30+
setIsLoading(false);
31+
} else {
32+
// First load in this session - show loading for 10 seconds
33+
const loadingTimer = setTimeout(() => {
34+
setIsLoading(false);
35+
// Mark app as loaded for this session
36+
sessionStorage.setItem(APP_LOADED_KEY, 'true');
37+
}, 8000);
38+
39+
return () => clearTimeout(loadingTimer);
40+
}
41+
}, []);
42+
1643
// Add keyboard listeners for development tools
1744
useEffect(() => {
1845
const handleKeyDown = (e: KeyboardEvent) => {
@@ -30,58 +57,77 @@ function App() {
3057
}, []);
3158

3259
return (
33-
<Router>
34-
<Routes>
35-
{/* Auth routes without MainLayout */}
36-
<Route path="/register" element={
37-
<MainLayout>
38-
<Register />
39-
</MainLayout>
40-
} />
41-
<Route path="/login" element={
42-
<MainLayout>
43-
<Login />
44-
</MainLayout>
45-
} />
46-
<Route path="/tos" element={
47-
<MainLayout>
48-
<Tos />
49-
</MainLayout>
50-
} />
51-
<Route path="/privacy-policy" element={
52-
<MainLayout>
53-
<PrivacyPolicy />
54-
</MainLayout>
55-
} />
56-
<Route path="/forgot-password" element={
57-
<MainLayout>
58-
<ForgotPassword />
59-
</MainLayout>
60-
} />
61-
<Route path="/verify-otp" element={
62-
<MainLayout>
63-
<VerifyOTP />
64-
</MainLayout>
65-
} />
66-
<Route path="/reset-password" element={
67-
<MainLayout>
68-
<ResetPassword />
69-
</MainLayout>
70-
} />
71-
<Route path="/reset-password-success" element={
72-
<MainLayout>
73-
<ResetPasswordSuccess />
74-
</MainLayout>
75-
} />
76-
77-
{/* FileReader route without MainLayout */}
78-
<Route path="/reader" element={<FileReader />} />
79-
80-
{/* Default route */}
81-
<Route path="*" element={<Navigate to="/register" replace />} />
82-
</Routes>
83-
</Router>
60+
<>
61+
{/* Splash screen with animation */}
62+
<AnimatePresence>
63+
{isLoading && <ApplicationSplash isOpen={true} />}
64+
</AnimatePresence>
65+
66+
{/* Render the app regardless of loading state, but it will be hidden behind the splash screen */}
67+
<div className={isLoading ? 'invisible' : 'visible'}>
68+
<Router>
69+
<Routes>
70+
{/* Auth routes without MainLayout */}
71+
<Route path="/register" element={
72+
<MainLayout>
73+
<Register />
74+
</MainLayout>
75+
} />
76+
<Route path="/login" element={
77+
<MainLayout>
78+
<Login />
79+
</MainLayout>
80+
} />
81+
<Route path="/tos" element={
82+
<MainLayout>
83+
<Tos />
84+
</MainLayout>
85+
} />
86+
<Route path="/privacy-policy" element={
87+
<MainLayout>
88+
<PrivacyPolicy />
89+
</MainLayout>
90+
} />
91+
<Route path="/forgot-password" element={
92+
<MainLayout>
93+
<ForgotPassword />
94+
</MainLayout>
95+
} />
96+
<Route path="/verify-otp" element={
97+
<MainLayout>
98+
<VerifyOTP />
99+
</MainLayout>
100+
} />
101+
<Route path="/reset-password" element={
102+
<MainLayout>
103+
<ResetPassword />
104+
</MainLayout>
105+
} />
106+
<Route path="/reset-password-success" element={
107+
<MainLayout>
108+
<ResetPasswordSuccess />
109+
</MainLayout>
110+
} />
111+
112+
{/* FileReader route without MainLayout */}
113+
<Route path="/reader" element={<FileReader />} />
114+
115+
{/* Default route */}
116+
<Route path="*" element={<Navigate to="/register" replace />} />
117+
</Routes>
118+
</Router>
119+
</div>
120+
</>
84121
);
85122
}
86123

124+
// Function to manually trigger the loading screen (will be exported and used in LeftSidebar)
125+
export function triggerLoadingScreen() {
126+
// Clear the loaded flag so next refresh will show the loading screen
127+
sessionStorage.removeItem(APP_LOADED_KEY);
128+
129+
// Reload the page to show the loading screen
130+
window.location.reload();
131+
}
132+
87133
export default App;

src/frontend/components/layout/LeftSidebar.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {
88
BugIcon,
99
UserIcon,
1010
BellIcon,
11-
FileTextIcon
11+
FileTextIcon,
12+
LoaderIcon
1213
} from 'lucide-react';
1314
import { Button } from "../ui/button";
1415
import { cn } from "@/lib/utils";
@@ -17,16 +18,18 @@ import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
1718
import SettingsModal from '../modals/SettingsModal';
1819
import SessionExpiredModal from '../modals/SessionExpiredModal';
1920
import { toggleDevTools } from '@/lib/utils';
21+
import { triggerLoadingScreen } from '@/App';
2022

2123
interface NavItemProps {
2224
icon: React.ReactNode;
2325
label: string;
2426
to: string;
2527
active?: boolean;
2628
onClick?: () => void;
29+
title?: string;
2730
}
2831

29-
const NavItem: React.FC<NavItemProps> = ({ icon, label, to, active, onClick }) => {
32+
const NavItem: React.FC<NavItemProps> = ({ icon, label, to, active, onClick, title }) => {
3033
const buttonContent = (
3134
<Button
3235
variant="ghost"
@@ -37,6 +40,7 @@ const NavItem: React.FC<NavItemProps> = ({ icon, label, to, active, onClick }) =
3740
"hover:bg-primary/10 rounded-none transition-all duration-200",
3841
active ? "bg-sidebar-accent/70 text-sidebar-foreground font-semibold" : "text-sidebar-foreground"
3942
)}
43+
title={title || label}
4044
>
4145
<div className="mb-1">{icon}</div>
4246
<span className="text-[11px] font-medium select-none">{label}</span>
@@ -50,7 +54,7 @@ const NavItem: React.FC<NavItemProps> = ({ icon, label, to, active, onClick }) =
5054

5155
// Otherwise use AppLink for navigation
5256
return (
53-
<AppLink href={to} className="w-full block hover:no-underline" preventNavigation>
57+
<AppLink href={to} className="w-full block hover:no-underline" preventNavigation title={title}>
5458
{buttonContent}
5559
</AppLink>
5660
);
@@ -78,6 +82,11 @@ const LeftSidebar: React.FC = () => {
7882
busy: 'bg-red-500',
7983
offline: 'bg-gray-400'
8084
};
85+
86+
// Handler for the "Show Loading Screen" button
87+
const handleShowLoadingScreen = () => {
88+
triggerLoadingScreen();
89+
};
8190

8291
return (
8392
<>
@@ -90,6 +99,15 @@ const LeftSidebar: React.FC = () => {
9099
<NavItem icon={<BellIcon size={32} />} label="NOTICE" to="/notifications" />
91100
<NavItem icon={<MoreHorizontalIcon size={32} />} label="MORE" to="/more" />
92101

102+
{/* Loading screen trigger button */}
103+
<NavItem
104+
icon={<LoaderIcon size={32} className="text-blue-500" />}
105+
label="SPLASH"
106+
to="#"
107+
onClick={handleShowLoadingScreen}
108+
title="Show loading splash screen"
109+
/>
110+
93111
{/* Debug tools - only shown in development mode */}
94112
{isDev && (
95113
<NavItem

0 commit comments

Comments
 (0)