feat: smoother loading when checking auth status
This commit is contained in:
parent
cae360fc0e
commit
7a0c0a9007
@ -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)];
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
|
||||||
|
@ -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}
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user