feat: refactor splash screen component (#5833)
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -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/
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,28 +141,22 @@ 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 {
|
.splash-fade-leave-active {
|
||||||
|
transition: opacity 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.splash-fade-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.app-buttons {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
z-index: 9999;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-logo-wrapper {
|
.app-logo-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
84
apps/app-frontend/src/components/ui/WindowControls.vue
Normal file
84
apps/app-frontend/src/components/ui/WindowControls.vue
Normal 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>
|
||||||
Reference in New Issue
Block a user