Files
web-temp/app/app.vue
2026-01-08 19:02:48 +09:00

182 lines
4.6 KiB
Vue

<script setup lang="ts">
import { useNuxtApp } from 'nuxt/app'
import type {
GameDataFavicon,
GameDataMetaTag,
GameDataValue,
} from '#layers/types/api/gameData'
const nuxtApp = useNuxtApp()
const { locale } = useI18n()
const gameDataStore = useGameDataStore()
const modalStore = useModalStore()
const scrollStore = useScrollStore()
const { setGameData } = gameDataStore
const { gameData } = storeToRefs(gameDataStore)
const { confirm, alert } = modalStore
const { scrollGnbPosition } = storeToRefs(scrollStore)
// 통합 메타데이터 설정
const setupAllMetaData = (data: GameDataValue) => {
const meta = data.meta_tag_json ?? ({} as GameDataMetaTag)
const faviconPath = data.favicon_json ?? ({} as GameDataFavicon)
const theme = data.design_theme === 1 ? 'light' : 'dark'
// 파비콘 링크 생성
const faviconLinks = [
{
rel: 'icon',
type: 'image/x-icon',
href: formatPathHost(faviconPath[0]),
},
{
rel: 'apple-touch-icon',
href: formatPathHost(faviconPath[1]),
},
{
rel: 'icon',
type: 'image/png',
href: formatPathHost(faviconPath[2]),
},
]
// 색상 CSS 변수 생성
const cssColorVariables = Object.entries(data.key_color_json ?? {})
.map(([key, value]) => `--${key}: ${value};`)
.join('\n ')
const commImgVariables =
data.comm_img_json?.groups
?.map(
({ img_name, img_path }) =>
`--${img_name}: url(${formatPathHost(img_path?.comm ?? '')});`
)
.join('\n ') ?? ''
const cssContent = `
:root {
${cssColorVariables}
${commImgVariables}
}
`
useHead({
title: meta?.page_title ?? '',
meta: [
{ name: 'description', content: meta.page_desc },
{ property: 'og:title', content: meta.og_title },
{ property: 'og:description', content: meta.og_desc },
{ property: 'og:image', content: formatPathHost(meta.og_image) },
{ name: 'twitter:title', content: meta.x_title },
{ name: 'twitter:description', content: meta.x_desc },
{ name: 'twitter:image', content: formatPathHost(meta.x_image) },
],
htmlAttrs: {
'data-game': data.game_name || '',
'data-theme': theme,
lang: locale.value ?? data.default_lang_code,
},
link: faviconLinks,
style: [
{
innerHTML: cssContent,
id: 'game-css-variables',
},
],
})
}
if (import.meta.server) {
const gameData = nuxtApp.ssrContext?.event?.context?.gameData
if (gameData) {
setGameData(gameData)
setupAllMetaData(gameData)
}
}
let rafId: number | null = null
let stopWatch: (() => void) | null = null
onMounted(() => {
useEventListener('scroll', scrollStore.updateScrollValue, { passive: true })
stopWatch = watch(
scrollGnbPosition,
newValue => {
if (rafId) {
cancelAnimationFrame(rafId)
}
rafId = requestAnimationFrame(() => {
document.documentElement.style.setProperty(
'--scroll-position',
`${newValue}px`
)
rafId = null
})
},
{ immediate: true }
)
const { gtag, initialize } = useGtag()
initialize(gameData.value?.ga_code)
gtag('event', 'screen_view', {
app_name: 'My App',
screen_name: 'Home',
})
})
onBeforeUnmount(() => {
// watch 정리
if (stopWatch) {
stopWatch()
stopWatch = null
}
// requestAnimationFrame 정리
if (rafId) {
cancelAnimationFrame(rafId)
rafId = null
}
})
</script>
<template>
<h1 class="sr-only">{{ gameData?.game_name }}</h1>
<NuxtPage />
<!-- 공통 모달 컴포넌트 -->
<WidgetsModalClient />
<BlocksModalYouTube />
<BlocksModalContent />
<BlocksModalConfirm
v-model:is-open="confirm.storeIsOpen"
:is-show-dimmed="confirm.storeIsShowDimmed"
:content-text="confirm.storeContentText"
:confirm-button-text="confirm.storeConfirmButtonText"
:cancel-button-text="confirm.storeCancelButtonText"
:is-outside-close="confirm.storeIsOutsideClose"
:modal-name="confirm.storeModalName"
@confirm-button-event="confirm.storeConfirmButtonEvent"
@cancel-button-event="confirm.storeCancelButtonEvent"
/>
<BlocksModalAlert
v-model:is-open="alert.storeIsOpen"
:is-show-dimmed="alert.storeIsShowDimmed"
:content-text="alert.storeContentText"
:confirm-button-text="alert.storeConfirmButtonText"
:is-outside-close="alert.storeIsOutsideClose"
:modal-name="alert.storeModalName"
@confirm-button-event="alert.storeConfirmButtonEvent"
/>
<BlocksModalToast />
<!-- 로딩 컴포넌트 -->
<AtomsLoadingFull />
<AtomsLoadingLocal />
</template>
<style>
@import '#layers/assets/css/app.css';
</style>