diff --git a/layers/components/widgets/FixMainTitle.vue b/layers/components/widgets/FixMainTitle.vue new file mode 100644 index 0000000..7e0df58 --- /dev/null +++ b/layers/components/widgets/FixMainTitle.vue @@ -0,0 +1,32 @@ + + + diff --git a/layers/components/widgets/FixSubTitle.vue b/layers/components/widgets/FixSubTitle.vue new file mode 100644 index 0000000..ea8d8d1 --- /dev/null +++ b/layers/components/widgets/FixSubTitle.vue @@ -0,0 +1,72 @@ + + + diff --git a/layers/composables/useCheckPCSpec.ts b/layers/composables/useCheckPCSpec.ts new file mode 100644 index 0000000..132872e --- /dev/null +++ b/layers/composables/useCheckPCSpec.ts @@ -0,0 +1,189 @@ +import { useTokenValidation } from '#layers/composables/useTokenValidation' +import { csrGoStoveLogin } from '#layers/utils/stoveUtil' + +interface ReqCheckSpec { + schemeFormat: string + setupUrl: string + gameNo: string + locale: string +} + +export const useCheckPCSpec = (tm: Function) => { + const { isTokenValid, validateToken } = useTokenValidation() + + // Store + const modalStore = useModalStore() + const { handleOpenConfirm } = modalStore + + const isButtonDisabled = ref(false) // 버튼 비활성화 여부 + + /** + * STOVE 미로그인 상태일 경우 로그인 모달을 표시합니다. + * 케이스에 따라 모달 내용을 변경할 수 있습니다. + * @param content 모달 내용(로그인 안내 메시지) + */ + const showLoginModal = (content: string) => { + handleOpenConfirm({ + contentText: content, + confirmButtonText: tm('Download_Text_StoveLogin'), + modalName: 'modal-login', + confirmButtonEvent: () => { + csrGoStoveLogin() + }, + }) + } + + /** + * PC사양 프로그램이 미설치일 경우 설치 안내 모달을 표시합니다. + * @param {string} setupUrl 설치 프로그램 URL + */ + const showInstallGuideModal = (setupUrl: string) => { + isButtonDisabled.value = false + + handleOpenConfirm({ + contentText: tm('Download_Alert_InstallGuide'), + confirmButtonText: tm('Download_Text_Download'), + modalName: 'modal-download', + isShowDimmed: true, + confirmButtonEvent: () => { + location.href = String(setupUrl) + }, + }) + } + + /** + * iframe을 생성합니다. + * @returns {iframe} 생성된 iframe + */ + const createHiddenIframe = () => { + const iframe = document.createElement('iframe') + iframe.src = 'about:blank' + iframe.id = 'hiddenIframe' + iframe.style.display = 'none' + document.body.appendChild(iframe) + return iframe + } + + /** + * Firefox 브라우저에서 URI를 열기 위한 함수입니다. + * @param {string} uri URI + * @param {Function} successCb 성공 콜백 + * @param {Function} failCb 실패 콜백 + */ + const openUriUsingFirefox = ( + uri: string, + successCb: Function, + failCb: Function + ) => { + let iframe = document.querySelector( + '#hiddenIframe' + ) as HTMLIFrameElement | null + if (!iframe) { + iframe = createHiddenIframe() as HTMLIFrameElement + } + try { + iframe.contentWindow!.location.href = uri + successCb() + } catch (e) { + if ((e as any).name === 'NS_ERROR_UNKNOWN_PROTOCOL') { + failCb() + } + } + } + + /** + * Chrome 브라우저에서 URI를 열기 위한 함수입니다. + * @param {string} uri 사양 프로그램 URI + * @param {Function} successCb 성공 콜백 + * @param {Function} failCb 실패 콜백 + */ + const openUriWithTimeoutHack = ( + uri: string, + successCb: Function, + failCb: Function + ) => { + let called = false + const timeout = setTimeout(() => { + if (!called) { + called = true + window.removeEventListener('blur', onBlur) + failCb() + } + }, 1000) + + const onBlur = () => { + if (!called) { + called = true + clearTimeout(timeout) + window.removeEventListener('blur', onBlur) + successCb() + } + } + + window.addEventListener('blur', onBlur) + window.location.href = uri + } + + /** + * PC사양 프로그램이 설치되어 있는지 여부를 확인합니다. + * @param {string }uri 사양 프로그램 URI + * @param {string} setupUrl 설치 프로그램 URL + */ + const checkPCSpecProgramInstalled = (uri: string, setupUrl: string) => { + const device = useDevice() + + const successCallback = () => { + isButtonDisabled.value = false + } + + if (typeof (navigator as any).msLaunchUri === 'function') { + ;(navigator as any).msLaunchUri( + uri, + successCallback, + showInstallGuideModal(setupUrl) + ) + } else if (device.isFirefox) { + // 파이어폭스 브라우저일 경우 + openUriUsingFirefox(uri, successCallback, () => + showInstallGuideModal(setupUrl) + ) + } else if (device.isChrome || device.isEdge) { + // 크롬 / 엣지 브라우저일 경우 + openUriWithTimeoutHack(uri, successCallback, () => + showInstallGuideModal(setupUrl) + ) + } else { + // 기타 미지원 브라우저일 경우 + showInstallGuideModal(setupUrl) + } + } + + /** + * 시스템 사양 체크를 시작합니다. + * 최종 호출 함수입니다. + * @param {ReqCheckSpec} req {schemeFormat:스킴 포맷, setupUrl:셋업 링크, gameNo:게임넘버, locale:언어} + */ + const checkPCSpec = async (req: ReqCheckSpec) => { + const accessToken = useCookie('SUAT') + await validateToken(accessToken.value || '') + + // 로그인 상태 아닐 경우 + if (!isTokenValid.value) { + showLoginModal(tm('Download_Alert_StoveLogin')) + return + } + + const localeValue: string = req.locale === 'ja' ? 'jp' : req.locale + const uri = `${req.schemeFormat}${accessToken.value}/${req.gameNo}/${localeValue}` + + checkPCSpecProgramInstalled(uri, req.setupUrl) + + isButtonDisabled.value = true + } + + return { + isButtonDisabled, + + checkPCSpec, + } +} diff --git a/layers/composables/useTemplateRegistry.ts b/layers/composables/useTemplateRegistry.ts index 8f97ba3..61e93bd 100644 --- a/layers/composables/useTemplateRegistry.ts +++ b/layers/composables/useTemplateRegistry.ts @@ -9,6 +9,7 @@ import GrDetail02 from '#layers/templates/GrDetail02/index.vue' import GrDetail03 from '#layers/templates/GrDetail03/index.vue' import GrBoard01 from '#layers/templates/GrBoard01/index.vue' import GrContents01 from '#layers/templates/GrContents01/index.vue' +import FxDownload01 from '#layers/templates/FxDownload01/index.vue' const templateRegistry = { GR_VISUAL_01: { component: GrVisual01 }, @@ -22,6 +23,7 @@ const templateRegistry = { GR_DETAIL_02: { component: GrDetail02 }, GR_DETAIL_03: { component: GrDetail03 }, GR_CONTENTS_01: { component: GrContents01 }, + FX_DOWNLOAD_01: { component: FxDownload01 }, } as const type TemplateKey = keyof typeof templateRegistry diff --git a/layers/public/images/common/grades_driver/Type-AMD.png b/layers/public/images/common/grades_driver/Type-AMD.png new file mode 100644 index 0000000..460ef7e Binary files /dev/null and b/layers/public/images/common/grades_driver/Type-AMD.png differ diff --git a/layers/public/images/common/grades_driver/Type-DirectX.png b/layers/public/images/common/grades_driver/Type-DirectX.png new file mode 100644 index 0000000..f1e1bbb Binary files /dev/null and b/layers/public/images/common/grades_driver/Type-DirectX.png differ diff --git a/layers/public/images/common/grades_driver/Type-NVIDIA.png b/layers/public/images/common/grades_driver/Type-NVIDIA.png new file mode 100644 index 0000000..e19d3a6 Binary files /dev/null and b/layers/public/images/common/grades_driver/Type-NVIDIA.png differ diff --git a/layers/public/images/common/grades_driver/Type-Visual.png b/layers/public/images/common/grades_driver/Type-Visual.png new file mode 100644 index 0000000..42c099b Binary files /dev/null and b/layers/public/images/common/grades_driver/Type-Visual.png differ diff --git a/layers/public/images/common/grades_driver/Type-intel.png b/layers/public/images/common/grades_driver/Type-intel.png new file mode 100644 index 0000000..5b700a1 Binary files /dev/null and b/layers/public/images/common/grades_driver/Type-intel.png differ diff --git a/layers/templates/FxDownload01/index.vue b/layers/templates/FxDownload01/index.vue new file mode 100644 index 0000000..9df185c --- /dev/null +++ b/layers/templates/FxDownload01/index.vue @@ -0,0 +1,468 @@ + + + + + diff --git a/public/images/common/grades_driver/Type-AMD.svg b/public/images/common/grades_driver/Type-AMD.svg new file mode 100644 index 0000000..901d425 --- /dev/null +++ b/public/images/common/grades_driver/Type-AMD.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/common/grades_driver/Type-DirectX.svg b/public/images/common/grades_driver/Type-DirectX.svg new file mode 100644 index 0000000..01c97f5 --- /dev/null +++ b/public/images/common/grades_driver/Type-DirectX.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/common/grades_driver/Type-NVIDIA.svg b/public/images/common/grades_driver/Type-NVIDIA.svg new file mode 100644 index 0000000..783753b --- /dev/null +++ b/public/images/common/grades_driver/Type-NVIDIA.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/common/grades_driver/Type-Visual.svg b/public/images/common/grades_driver/Type-Visual.svg new file mode 100644 index 0000000..45602f6 --- /dev/null +++ b/public/images/common/grades_driver/Type-Visual.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/common/grades_driver/Type-intel.svg b/public/images/common/grades_driver/Type-intel.svg new file mode 100644 index 0000000..24e8c1f --- /dev/null +++ b/public/images/common/grades_driver/Type-intel.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/common/ic-v2-control-long-arrow-down-line.svg b/public/images/common/ic-v2-control-long-arrow-down-line.svg new file mode 100644 index 0000000..7bb25fe --- /dev/null +++ b/public/images/common/ic-v2-control-long-arrow-down-line.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/common/ic-v2-hardware-desktop-line.svg b/public/images/common/ic-v2-hardware-desktop-line.svg new file mode 100644 index 0000000..4e07997 --- /dev/null +++ b/public/images/common/ic-v2-hardware-desktop-line.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/common/ic-v2-logo-apple-color.svg b/public/images/common/ic-v2-logo-apple-color.svg new file mode 100644 index 0000000..760d2eb --- /dev/null +++ b/public/images/common/ic-v2-logo-apple-color.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/common/ic-v2-object-download-line.svg b/public/images/common/ic-v2-object-download-line.svg new file mode 100644 index 0000000..ea22f93 --- /dev/null +++ b/public/images/common/ic-v2-object-download-line.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/common/ic-v2-stove-symbol-fill.svg b/public/images/common/ic-v2-stove-symbol-fill.svg new file mode 100644 index 0000000..a0300b6 --- /dev/null +++ b/public/images/common/ic-v2-stove-symbol-fill.svg @@ -0,0 +1,3 @@ + + +