feat: smoother loading when checking auth status

This commit is contained in:
ae 2025-04-19 22:58:12 +03:00
parent cae360fc0e
commit 7a0c0a9007
Signed by: ae
GPG Key ID: 995EFD5C1B532B3E
6 changed files with 161 additions and 196 deletions

View File

@ -501,14 +501,48 @@
} }
/* Loading spinner */ /* Loading spinner */
.loading-container {
@apply pointer-events-none flex h-screen w-full items-center justify-center;
}
.spinner {
@apply relative h-10 w-10;
}
.spinner-dot { .spinner-dot {
@apply bg-[var(--light-accent)]; @apply absolute top-[37.5%] inline-block h-1/4 w-1/4 rounded-full bg-[var(--light-accent)];
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
} }
.dark .spinner-dot { .dark .spinner-dot {
@apply bg-[var(--dark-accent)]; @apply bg-[var(--dark-accent)];
} }
.spinner .bounce1 {
@apply left-0;
animation-delay: -0.32s;
}
.spinner .bounce2 {
@apply left-[37.5%];
animation-delay: -0.16s;
}
.spinner .bounce3 {
@apply left-3/4;
}
@keyframes sk-bouncedelay {
0%,
80%,
100% {
transform: scale(0);
}
40% {
transform: scale(1);
}
}
/* Main layout */ /* Main layout */
.main-layout-container { .main-layout-container {
@apply flex h-screen bg-[var(--light-background)]; @apply flex h-screen bg-[var(--light-background)];

View File

@ -5,6 +5,15 @@
<link rel="icon" href="%sveltekit.assets%/favicon.png" /> <link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head% %sveltekit.head%
<script>
const darkMode = localStorage.getItem("darkMode") === "true" ||
(localStorage.getItem("darkMode") === null &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
if (darkMode) {
document.documentElement.classList.add("dark")
}
</script>
</head> </head>
<body data-sveltekit-preload-data="hover"> <body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div> <div style="display: contents">%sveltekit.body%</div>

View File

@ -1,51 +1,7 @@
<script> <div class="loading-container">
export let size = "40px" <div class="spinner">
// export let useDarkAccent = false
</script>
<div class="spinner" style="width: {size}; height: {size};">
<div class="bounce1 spinner-dot"></div> <div class="bounce1 spinner-dot"></div>
<div class="bounce2 spinner-dot"></div> <div class="bounce2 spinner-dot"></div>
<div class="bounce3 spinner-dot"></div> <div class="bounce3 spinner-dot"></div>
</div>
</div> </div>
<style>
.spinner {
position: relative;
}
.spinner > div {
width: 25%;
height: 25%;
border-radius: 100%;
display: inline-block;
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
position: absolute;
top: 37.5%;
}
.spinner .bounce1 {
left: 0;
animation-delay: -0.32s;
}
.spinner .bounce2 {
left: 37.5%;
animation-delay: -0.16s;
}
.spinner .bounce3 {
left: 75%;
}
@keyframes sk-bouncedelay {
0%,
80%,
100% {
transform: scale(0);
}
40% {
transform: scale(1);
}
}
</style>

View File

@ -17,18 +17,14 @@
import ToggleSidebar from "$lib/icons/ToggleSidebar.svelte" import ToggleSidebar from "$lib/icons/ToggleSidebar.svelte"
import VersionArrow from "$lib/icons/VersionArrow.svelte" import VersionArrow from "$lib/icons/VersionArrow.svelte"
// Props
export let isLoading = false
// State // State
let isComponentReady = false
let sidebarOpen = window.innerWidth > 768 let sidebarOpen = window.innerWidth > 768
let showSettings = false let showSettings = false
let showVersionsDropdown = false let showVersionsDropdown = false
let isEditing = false let isEditing = false
onMount(async (): Promise<any> => { onMount(async (): Promise<any> => {
isLoading = true
try { try {
// The following fetch attempts to refresh any expired tokens automatically // The following fetch attempts to refresh any expired tokens automatically
await apiClient.getCurrentUser() await apiClient.getCurrentUser()
@ -44,8 +40,6 @@
} catch (error) { } catch (error) {
console.error(`error during auth: ${error}`) console.error(`error during auth: ${error}`)
goto("/login") goto("/login")
} finally {
isLoading = false
} }
// Default to sidebar closed on mobile // Default to sidebar closed on mobile
@ -60,6 +54,8 @@
// Keep listening to browser's resize events // Keep listening to browser's resize events
window.addEventListener("resize", handleResize) window.addEventListener("resize", handleResize)
isComponentReady = true
return () => { return () => {
window.removeEventListener("resize", handleResize) window.removeEventListener("resize", handleResize)
} }
@ -173,7 +169,8 @@
} }
</script> </script>
<div class="main-layout-container"> {#if isComponentReady}
<div class="main-layout-container">
<!-- Error notification --> <!-- Error notification -->
{#if $cError} {#if $cError}
<div class="main-error-popup"> <div class="main-error-popup">
@ -249,8 +246,8 @@
<span class="versions-dropdown-item-text" <span class="versions-dropdown-item-text"
>{version.createdAt.toLocaleString()}</span >{version.createdAt.toLocaleString()}</span
> >
{#if version.isActive}<span class="versions-dropdown-item-text ml-2 opacity-70" {#if version.isActive}<span
>(active)</span class="versions-dropdown-item-text ml-2 opacity-70">(active)</span
>{/if} >{/if}
</button> </button>
{/each} {/each}
@ -282,4 +279,5 @@
{#if showSettings} {#if showSettings}
<SettingsModal onClose={toggleSettings} /> <SettingsModal onClose={toggleSettings} />
{/if} {/if}
</div> </div>
{/if}

View File

@ -10,25 +10,6 @@
}) })
</script> </script>
<svelte:head>
<script lang="ts">
if (document) {
let darkMode = false
const savedTheme = localStorage.getItem("darkMode")
if (savedTheme !== null) {
darkMode = savedTheme === "true"
} else {
// Fallback to system preference
darkMode = window.matchMedia("(prefers-color-scheme: dark)").matches
localStorage.setItem("darkMode", darkMode ? "true" : "false")
}
document.documentElement.classList.toggle("dark", darkMode)
}
</script>
</svelte:head>
<button <button
on:click={() => { on:click={() => {
themeState.isDarkMode = !themeState.isDarkMode themeState.isDarkMode = !themeState.isDarkMode

View File

@ -1,34 +1,21 @@
<script> <script>
import NoteView from "$lib/components/NoteView.svelte"
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte" import LoadingSpinner from "$lib/components/LoadingSpinner.svelte"
import NoteView from "$lib/components/NoteView.svelte"
import { onMount } from "svelte" import { onMount } from "svelte"
import { fade } from "svelte/transition"
let isLoading = true let isLoading = true
onMount(() => { onMount(() => {
// Set a small timeout to ensure the loading state shows // `NoteView` requires a small timeout as it utilizes `window` during init, additionally
// -> Prevents component flickering during auth checks // especially in prod. we need to give some time for the auth check request
setTimeout(() => { setTimeout(() => {
isLoading = false isLoading = false
}, 500) }, 1000)
}) })
</script> </script>
{#if isLoading} {#if isLoading}
<div class="loading-container" transition:fade={{ duration: 200 }}>
<LoadingSpinner /> <LoadingSpinner />
</div>
{:else} {:else}
<NoteView /> <NoteView />
{/if} {/if}
<style>
.loading-container {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100%;
}
</style>