|
| 1 | +'use client'; |
| 2 | + |
| 3 | +import Button from '@/components/common/Button'; |
| 4 | +import ConfirmModal from '@/components/common/ConfirmModal'; |
| 5 | +import { theme } from '@/styles/theme'; |
| 6 | +import React, { useEffect, useState } from 'react'; |
| 7 | +import styled from 'styled-components'; |
| 8 | + |
| 9 | +declare global { |
| 10 | + interface WindowEventMap { |
| 11 | + beforeinstallprompt: BeforeInstallPromptEvent; |
| 12 | + } |
| 13 | +} |
| 14 | + |
| 15 | +interface BeforeInstallPromptEvent extends Event { |
| 16 | + readonly platforms: Array<string>; |
| 17 | + readonly userChoice: Promise<{ |
| 18 | + outcome: 'accepted' | 'dismissed'; |
| 19 | + platform: string; |
| 20 | + }>; |
| 21 | + prompt(): Promise<void>; |
| 22 | +} |
| 23 | + |
| 24 | +const page = () => { |
| 25 | + const [deferredPrompt, setDeferredPrompt] = useState(null); |
| 26 | + const [isIOS, setIsIOS] = useState(false); |
| 27 | + const [isAndroid, setIsAndroid] = useState(false); |
| 28 | + |
| 29 | + const [showModal, setShowModal] = useState(false); |
| 30 | + |
| 31 | + useEffect(() => { |
| 32 | + const userAgent = window.navigator.userAgent.toLowerCase(); |
| 33 | + const isIOSDevice = /iphone|ipad|ipod/.test(userAgent); |
| 34 | + const isAndroidDevice = /android/.test(userAgent); |
| 35 | + |
| 36 | + setIsIOS(isIOSDevice); |
| 37 | + setIsAndroid(isAndroidDevice); |
| 38 | + |
| 39 | + // Android에서만 beforeinstallprompt 이벤트 처리 |
| 40 | + if (isAndroidDevice) { |
| 41 | + const handler = (e: any) => { |
| 42 | + console.log('🔥 beforeinstallprompt 발생'); |
| 43 | + e.preventDefault(); |
| 44 | + setDeferredPrompt(e); |
| 45 | + }; |
| 46 | + |
| 47 | + window.addEventListener('beforeinstallprompt', handler); |
| 48 | + return () => { |
| 49 | + window.removeEventListener('beforeinstallprompt', handler); |
| 50 | + }; |
| 51 | + } |
| 52 | + }, []); |
| 53 | + |
| 54 | + const handleInstallClick = async () => { |
| 55 | + if (isAndroid && deferredPrompt) { |
| 56 | + deferredPrompt.prompt(); |
| 57 | + const { outcome } = await deferredPrompt.userChoice; |
| 58 | + console.log('Android 사용자 선택:', outcome); |
| 59 | + setDeferredPrompt(null); |
| 60 | + setShowModal(false); |
| 61 | + } else if (isIOS) { |
| 62 | + // iOS는 안내만 |
| 63 | + alert( |
| 64 | + 'iPhone 사용자는 Safari에서 공유 버튼을 눌러 [홈 화면에 추가]를 선택해주세요!' |
| 65 | + ); |
| 66 | + setShowModal(false); |
| 67 | + } else { |
| 68 | + alert('설치 기능이 현재 환경에서 지원되지 않습니다.'); |
| 69 | + setShowModal(false); |
| 70 | + } |
| 71 | + }; |
| 72 | + |
| 73 | + const handleShowModal = () => { |
| 74 | + if (isAndroid && deferredPrompt) { |
| 75 | + setShowModal(true); |
| 76 | + } else if (isIOS) { |
| 77 | + setShowModal(true); |
| 78 | + } else { |
| 79 | + alert('브라우저가 아직 설치를 허용하지 않았습니다.'); |
| 80 | + } |
| 81 | + }; |
| 82 | + |
| 83 | + return ( |
| 84 | + <Container> |
| 85 | + <Title>PWA 설치 유도 TEST</Title> |
| 86 | + <Button |
| 87 | + buttonType="primary" |
| 88 | + text="앱 설치하기" |
| 89 | + onClick={handleShowModal} |
| 90 | + /> |
| 91 | + {showModal && ( |
| 92 | + <ConfirmModal |
| 93 | + title="PWA 설치하기" |
| 94 | + sub={ |
| 95 | + isIOS |
| 96 | + ? 'Safari 공유 버튼을 눌러 [홈 화면에 추가]를 선택해주세요.' |
| 97 | + : '홈 화면에 추가하시겠어요?' |
| 98 | + } |
| 99 | + confirmText={isIOS ? '알겠습니다' : '설치하기'} |
| 100 | + cancelText="취소" |
| 101 | + onConfirm={handleInstallClick} |
| 102 | + onCancel={() => setShowModal(false)} |
| 103 | + /> |
| 104 | + )} |
| 105 | + </Container> |
| 106 | + ); |
| 107 | +}; |
| 108 | + |
| 109 | +export default page; |
| 110 | + |
| 111 | +const Container = styled.div` |
| 112 | + display: flex; |
| 113 | + box-sizing: border-box; |
| 114 | + padding: 100px 30px; |
| 115 | + height: 100vh; |
| 116 | + flex-direction: column; |
| 117 | + justify-content: space-between; |
| 118 | + background-image: url('/assets/login/login_bg.png'); |
| 119 | + background-size: cover; |
| 120 | + background-position: center; |
| 121 | + background-repeat: no-repeat; |
| 122 | + -webkit-scrollbar { |
| 123 | + display: none; |
| 124 | + } |
| 125 | +`; |
| 126 | + |
| 127 | +const Title = styled.h1` |
| 128 | + color: ${theme.colors.white}; |
| 129 | + text-align: center; |
| 130 | +`; |
0 commit comments