feat: refactor splash screen component (#5833)

This commit is contained in:
Calum H.
2026-04-17 17:34:58 +01:00
committed by GitHub
parent 9483656881
commit 5244060588
4 changed files with 195 additions and 211 deletions

3
.gitignore vendored
View File

@@ -78,3 +78,6 @@ storybook-static
# frontend robots.txt # frontend robots.txt
apps/frontend/src/public/robots.txt apps/frontend/src/public/robots.txt
# Oh My Code
.omc/

View File

@@ -18,13 +18,10 @@ import {
LibraryIcon, LibraryIcon,
LogInIcon, LogInIcon,
LogOutIcon, LogOutIcon,
MaximizeIcon,
MinimizeIcon,
NewspaperIcon, NewspaperIcon,
NotepadTextIcon, NotepadTextIcon,
PlusIcon, PlusIcon,
RefreshCwIcon, RefreshCwIcon,
RestoreIcon,
RightArrowIcon, RightArrowIcon,
ServerStackIcon, ServerStackIcon,
SettingsIcon, SettingsIcon,
@@ -35,7 +32,6 @@ import {
import { import {
Admonition, Admonition,
Avatar, Avatar,
Button,
ButtonStyled, ButtonStyled,
commonMessages, commonMessages,
ContentInstallModal, ContentInstallModal,
@@ -87,6 +83,7 @@ import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
import QuickInstanceSwitcher from '@/components/ui/QuickInstanceSwitcher.vue' import QuickInstanceSwitcher from '@/components/ui/QuickInstanceSwitcher.vue'
import RunningAppBar from '@/components/ui/RunningAppBar.vue' import RunningAppBar from '@/components/ui/RunningAppBar.vue'
import SplashScreen from '@/components/ui/SplashScreen.vue' import SplashScreen from '@/components/ui/SplashScreen.vue'
import WindowControls from '@/components/ui/WindowControls.vue'
import { useCheckDisableMouseover } from '@/composables/macCssFix.js' import { useCheckDisableMouseover } from '@/composables/macCssFix.js'
import { config } from '@/config' import { config } from '@/config'
import { hide_ads_window, init_ads_window, show_ads_window } from '@/helpers/ads.js' import { hide_ads_window, init_ads_window, show_ads_window } from '@/helpers/ads.js'
@@ -999,6 +996,7 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
</script> </script>
<template> <template>
<WindowControls />
<SplashScreen v-if="!stateFailed" ref="splashScreen" data-tauri-drag-region /> <SplashScreen v-if="!stateFailed" ref="splashScreen" data-tauri-drag-region />
<div id="teleports"></div> <div id="teleports"></div>
<div <div
@@ -1196,22 +1194,6 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
<RunningAppBar /> <RunningAppBar />
</Suspense> </Suspense>
</div> </div>
<section v-if="!nativeDecorations" class="window-controls" data-tauri-drag-region-exclude>
<Button class="titlebar-button" icon-only @click="() => getCurrentWindow().minimize()">
<MinimizeIcon />
</Button>
<Button
class="titlebar-button"
icon-only
@click="() => getCurrentWindow().toggleMaximize()"
>
<RestoreIcon v-if="isMaximized" />
<MaximizeIcon v-else />
</Button>
<Button class="titlebar-button close" icon-only @click="handleClose">
<XIcon />
</Button>
</section>
</section> </section>
</div> </div>
</div> </div>
@@ -1297,7 +1279,11 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
loading.startLoading() loading.startLoading()
} }
" "
@resolve="loading.stopLoading()" @resolve="
() => {
loading.stopLoading()
}
"
> >
<component :is="Component"></component> <component :is="Component"></component>
</Suspense> </Suspense>
@@ -1391,72 +1377,6 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.window-controls {
z-index: 20;
display: none;
flex-direction: row;
align-items: center;
.titlebar-button {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all ease-in-out 0.1s;
background-color: transparent;
color: var(--color-base);
height: 100%;
width: 3rem;
position: relative;
box-shadow: none;
&:last-child {
padding-right: 0.75rem;
width: 3.75rem;
}
svg {
width: 1.25rem;
height: 1.25rem;
}
&::before {
content: '';
border-radius: 999999px;
width: 3rem;
height: 3rem;
aspect-ratio: 1 / 1;
margin-block: auto;
position: absolute;
background-color: transparent;
scale: 0.9;
transition: all ease-in-out 0.2s;
z-index: -1;
}
&.close {
&:hover,
&:active {
color: var(--color-accent-contrast);
&::before {
background-color: var(--color-red);
}
}
}
&:hover,
&:active {
color: var(--color-contrast);
&::before {
background-color: var(--color-button-bg);
scale: 1;
}
}
}
}
.app-grid-layout, .app-grid-layout,
.app-contents { .app-contents {
--top-bar-height: 3rem; --top-bar-height: 3rem;
@@ -1690,10 +1610,6 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
height: 2.5rem !important; height: 2.5rem !important;
} }
.window-controls {
display: flex !important;
}
.info-card { .info-card {
right: 22rem; right: 22rem;
} }

View File

@@ -1,26 +1,17 @@
<template> <template>
<div v-if="!hidden" class="splash-screen dark" :class="{ 'fade-out': doneLoading }"> <Transition name="splash-fade" @after-leave="onAfterLeave">
<div v-if="os !== 'MacOS'" class="app-buttons"> <div v-if="!doneLoading" class="splash-screen dark">
<button
class="btn icon-only transparent"
icon-only
@click="() => getCurrentWindow().minimize()"
>
<MinimizeIcon />
</button>
<button class="btn icon-only transparent" @click="() => getCurrentWindow().toggleMaximize()">
<MaximizeIcon />
</button>
<button class="btn icon-only transparent" @click="handleClose">
<XIcon />
</button>
</div>
<div class="app-logo-wrapper" data-tauri-drag-region> <div class="app-logo-wrapper" data-tauri-drag-region>
<svg <svg
class="app-logo" class="app-logo"
viewBox="0 0 1215 175" viewBox="0 0 1215 175"
xml:space="preserve" xml:space="preserve"
style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 2" style="
fill-rule: evenodd;
clip-rule: evenodd;
stroke-linejoin: round;
stroke-miterlimit: 2;
"
color="var(--color-contrast)" color="var(--color-contrast)"
> >
<g transform="matrix(1,0,0,1,-3395.45,-1175)"> <g transform="matrix(1,0,0,1,-3395.45,-1175)">
@@ -83,52 +74,51 @@
<div class="cube-bg"></div> <div class="cube-bg"></div>
<div class="base-bg"></div> <div class="base-bg"></div>
</div> </div>
</Transition>
</template> </template>
<script setup> <script setup>
import { MaximizeIcon, MinimizeIcon, XIcon } from '@modrinth/assets'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
import ProgressBar from '@/components/ui/ProgressBar.vue' import ProgressBar from '@/components/ui/ProgressBar.vue'
import { loading_listener } from '@/helpers/events.js' import { loading_listener } from '@/helpers/events.js'
import { getOS } from '@/helpers/utils.js'
import { useLoading } from '@/store/loading.js' import { useLoading } from '@/store/loading.js'
const doneLoading = ref(false) const doneLoading = ref(false)
const loadingProgress = ref(0) const loadingProgress = ref(0)
const hidden = ref(false)
const message = ref() const message = ref()
// const MIN_DISPLAY_MS = 1000 const MIN_DISPLAY_MS = 500
// const mountedAt = Date.now() const mountedAt = Date.now()
const loading = useLoading() const loading = useLoading()
function onAfterLeave() {
loading.setEnabled(true)
}
watch( watch(
loading, loading,
(newValue) => { (newValue) => {
if (!newValue.barEnabled) { if (newValue.barEnabled) {
return
}
if (loading.loading) { if (loading.loading) {
loadingProgress.value = 0 loadingProgress.value = 0
fakeLoadingIncrease() fakeLoadingIncrease()
} else { return
// const elapsed = Date.now() - mountedAt }
// const delay = Math.max(0, MIN_DISPLAY_MS - elapsed)
// setTimeout(() => { const elapsed = Date.now() - mountedAt
// if (loading.loading) return const delay = Math.max(0, MIN_DISPLAY_MS - elapsed)
loadingProgress.value = 100
doneLoading.value = true
setTimeout(() => { setTimeout(() => {
hidden.value = true if (loading.loading) {
loading.setEnabled(true) return
}, 50)
// }, delay)
}
} }
doneLoading.value = true
}, delay)
}, },
{ immediate: true }, { immediate: true },
) )
@@ -142,9 +132,6 @@ function fakeLoadingIncrease() {
} }
} }
const os = ref('')
getOS().then((x) => (os.value = x))
loading_listener(async (e) => { loading_listener(async (e) => {
if (e.event.type === 'directory_move') { if (e.event.type === 'directory_move') {
loadingProgress.value = 100 * (e.fraction ?? 1) loadingProgress.value = 100 * (e.fraction ?? 1)
@@ -154,27 +141,21 @@ loading_listener(async (e) => {
message.value = 'Checking for updates...' message.value = 'Checking for updates...'
} }
}) })
const handleClose = async () => {
await getCurrentWindow().close()
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.splash-screen { .splash-screen {
transition: opacity 0.25s ease-in-out; position: fixed;
opacity: 1; inset: 0;
z-index: 10000;
&.fade-out {
opacity: 0;
}
} }
.app-buttons { .splash-fade-leave-active {
position: absolute; transition: opacity 0.3s ease-in-out;
right: 0; }
z-index: 9999;
display: flex; .splash-fade-leave-to {
opacity: 0;
} }
.app-logo-wrapper { .app-logo-wrapper {

View File

@@ -0,0 +1,84 @@
<template>
<section
v-if="!nativeDecorations && os !== 'MacOS'"
class="window-controls"
data-tauri-drag-region-exclude
>
<ButtonStyled type="transparent" circular>
<button class="titlebar-button" @click="() => getCurrentWindow().minimize()">
<MinimizeIcon />
</button>
</ButtonStyled>
<ButtonStyled type="transparent" circular>
<button class="titlebar-button" @click="() => getCurrentWindow().toggleMaximize()">
<RestoreIcon v-if="isMaximized" />
<MaximizeIcon v-else />
</button>
</ButtonStyled>
<ButtonStyled type="transparent" circular>
<button class="titlebar-button close" @click="handleClose">
<XIcon />
</button>
</ButtonStyled>
</section>
</template>
<script setup>
import { MaximizeIcon, MinimizeIcon, RestoreIcon, XIcon } from '@modrinth/assets'
import { ButtonStyled } from '@modrinth/ui'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state'
import { onMounted, onUnmounted, ref } from 'vue'
import { getOS } from '@/helpers/utils.js'
const nativeDecorations = ref(true)
const isMaximized = ref(false)
const os = ref('')
onMounted(async () => {
os.value = await getOS()
const settings = await import('@/helpers/settings.ts').then((m) => m.get())
nativeDecorations.value = settings.native_decorations
if (os.value !== 'MacOS') {
await getCurrentWindow().setDecorations(nativeDecorations.value)
}
isMaximized.value = await getCurrentWindow().isMaximized()
const unlisten = await getCurrentWindow().onResized(async () => {
isMaximized.value = await getCurrentWindow().isMaximized()
})
onUnmounted(() => {
unlisten()
})
})
const handleClose = async () => {
await saveWindowState(StateFlags.ALL)
await getCurrentWindow().close()
}
</script>
<style lang="scss" scoped>
.window-controls {
position: fixed;
top: 0;
right: 0;
z-index: 10001;
display: flex;
flex-direction: row;
align-items: center;
height: var(--top-bar-height, 3rem);
padding-right: 0.5rem;
gap: 0.25rem;
.titlebar-button.close:hover {
background-color: var(--color-red);
color: var(--color-accent-contrast);
}
}
</style>