import React, { useCallback, useEffect, useMemo, useRef, useState, } from 'react'; import classNames from 'classnames'; import { AsYouTypeFormatter, PhoneNumberFormat, PhoneNumberUtil, } from 'google-libphonenumber'; import { useDispatch, useSelector } from 'react-redux'; import { off, on } from '@enact/core/dispatcher'; import spotlight, { Spotlight } from '@enact/spotlight'; import { SpotlightContainerDecorator } from '@enact/spotlight/SpotlightContainerDecorator'; import { Spottable } from '@enact/spotlight/Spottable'; import defaultImage from '../../../assets/images/img-thumb-empty-144@3x.png'; import { types } from '../../actions/actionTypes'; import { clearSMS, sendSms } from '../../actions/appDataActions'; import { changeLocalSettings, setHidePopup, setShowPopup, } from '../../actions/commonActions'; import { clearRegisterDeviceInfo, getDeviceAdditionInfo, registerDeviceInfo, } from '../../actions/deviceActions'; import { clearCurationCoupon, setEventIssueReq, } from '../../actions/eventActions'; import { sendLogShopByMobile, sendLogTotalRecommend, } from '../../actions/logActions'; import { ACTIVE_POPUP, LOG_CONTEXT_NAME, LOG_MESSAGE_ID, LOG_TP_NO, } from '../../utils/Config'; import { $L, decryptPhoneNumber, encryptPhoneNumber, formatLocalDateTime, } from '../../utils/helperMethods'; import CustomImage from '../CustomImage/CustomImage'; import TButton from '../TButton/TButton'; import TPopUp from '../TPopUp/TPopUp'; import HistoryPhoneNumber from './HistoryPhoneNumber/HistoryPhoneNumber'; import css from './MobileSendPopUp.module.less'; import PhoneInputSection from './PhoneInputSection'; import SMSNumKeyPad from './SMSNumKeyPad'; const SECRET_KEY = 'fy7BTKuM9eeTQqEC9sF3Iw5qG43Aaip'; const Container = SpotlightContainerDecorator( { enterTo: 'last-focused' }, 'div' ); const InputContainer = SpotlightContainerDecorator( { enterTo: 'last-focused' }, 'div' ); const SpottableComponent = Spottable('div'); export default function MobileSendPopUp({ open, onClose, title, subTitle, productImg, productPrice, brandLogo, productId, smsTpCd, smsText, prdtId, curationId, patnrId, evntId, evntTpCd, isCurationEvnt, hotelId, hotelNm, hotelDtlUrl, shopByMobileLogRef, spotlightId, patncNm, }) { const dispatch = useDispatch(); const deviceInfo = useSelector((state) => state.device.deviceInfo); const { httpHeader } = useSelector((state) => state.common); const regDeviceInfoRetCode = useSelector( (state) => state.device?.regDeviceInfoData?.retCode ); const smsRetCodeResData = useSelector( (state) => state.appData.sendSms?.retCode ); const { phoneNumberList } = useSelector( (state) => state.localSettings.phoneNumbers ); const curationCouponSuccess = useSelector( (state) => state.event.curationCouponSuccess ); const popupVisible = useSelector((state) => state.common.popup.popupVisible); const nowMenu = useSelector((state) => state.common.menu.nowMenu); const entryMenu = useSelector((state) => state.common.menu.entryMenu); const [inputDisabled, setInputDisabled] = useState(true); const [mobileNumber, setMobileNumber] = useState(''); const [recentSentNumber, setRecentSentNumber] = useState([]); const [keyPadOff, setKeyPadOff] = useState(false); const [smsRetCode, setSmsRetCode] = useState(undefined); const agreeBtnClickedRef = useRef(false); const deviceCountryCode = httpHeader['X-Device-Country']; const mobileSendPopUpSpotlightId = useMemo(() => { return !keyPadOff && recentSentNumber.length <= 0 ? 'keypad-number-1' : 'agreeAndSend'; }, [keyPadOff, recentSentNumber]); const getMaxNum = useCallback((_deviceCountryCode) => { if (_deviceCountryCode === 'DE' || _deviceCountryCode === 'GB') { return 11; } else if (_deviceCountryCode === 'KR') { return 12; } else return 10; }, []); const MSG_SUCCESS_SENT = $L('Text Send to') + ' ' + mobileNumber; const MSG_SEND_LINK = $L('Send a purchase link for this item via SMS'); const handleClickSelect = (_phoneNumber) => { setKeyPadOff((state) => !state); setMobileNumber(_phoneNumber); setTimeout(() => { Spotlight.focus('keypad-number-1'); }, 0); }; const handleInputClick = () => { setKeyPadOff(false); if (recentSentNumber && recentSentNumber.length > 0 && keyPadOff) { setMobileNumber(recentSentNumber[0]); } }; const getRawPhoneNumber = useCallback( (key) => { let rawPhoneNumber = `${mobileNumber}${key}`.replace(/\D/g, ''); if (rawPhoneNumber.length === getMaxNum(deviceCountryCode)) { Spotlight.focus('agreeAndSend'); } // 테스트용: 12자리까지 허용 if (rawPhoneNumber.length > 12) { return; } const phoneUtil = PhoneNumberUtil.getInstance(); const asYouTypeFormatter = new AsYouTypeFormatter(deviceCountryCode); try { let numberProto = phoneUtil.parse(rawPhoneNumber, deviceCountryCode); if ( phoneUtil.isValidNumber(numberProto) && phoneUtil.isValidNumberForRegion(numberProto, deviceCountryCode) ) { rawPhoneNumber = phoneUtil.format( numberProto, PhoneNumberFormat.NATIONAL ); if (deviceCountryCode === 'RU' && rawPhoneNumber.startsWith('8')) { rawPhoneNumber = rawPhoneNumber.substring(1); } } else { let formattedNumber = ''; for (let i = 0; i < rawPhoneNumber.length; i++) { formattedNumber = asYouTypeFormatter.inputDigit( Number(rawPhoneNumber[i]) ); } rawPhoneNumber = formattedNumber; } } catch (e) { rawPhoneNumber = `${mobileNumber}${key}`; } finally { setMobileNumber(rawPhoneNumber); } }, [mobileNumber, deviceCountryCode] ); const getBackspaceRawNumber = useCallback(() => { let rawPhoneNumber = mobileNumber.replace(/\D/g, '').slice(0, -1); const phoneUtil = PhoneNumberUtil.getInstance(); const asYouTypeFormatter = new AsYouTypeFormatter(deviceCountryCode); try { let numberProto = phoneUtil.parse(rawPhoneNumber, deviceCountryCode); if ( phoneUtil.isValidNumber(numberProto) && phoneUtil.isValidNumberForRegion(numberProto, deviceCountryCode) ) { rawPhoneNumber = phoneUtil.format( numberProto, PhoneNumberFormat.NATIONAL ); } else { let formattedNumber = ''; for (let i = 0; i < rawPhoneNumber.length; i++) { formattedNumber = asYouTypeFormatter.inputDigit( Number(rawPhoneNumber[i]) ); } rawPhoneNumber = formattedNumber; } } catch (e) { rawPhoneNumber = mobileNumber.slice(0, -1); } finally { setMobileNumber(rawPhoneNumber); } }, [mobileNumber, deviceCountryCode]); const handleKeydown = useCallback( (ev) => { if (ev && ev.key >= 0 && ev.key <= 9) { getRawPhoneNumber(ev.key); } else if (ev.key === 'Backspace') { getBackspaceRawNumber(); } }, [mobileNumber] ); useEffect(() => { on('keydown', handleKeydown); return () => { off('keydown', handleKeydown); }; }, [handleKeydown]); useEffect(() => { if (!deviceInfo) { dispatch(getDeviceAdditionInfo()); } }, [deviceInfo, dispatch]); useEffect(() => { const timer = setTimeout(() => setInputDisabled(mobileSendPopUpSpotlightId === 'keypad-number-1') ); return () => clearTimeout(timer); }, [mobileSendPopUpSpotlightId]); useEffect(() => { if (!agreeBtnClickedRef.current && recentSentNumber) { if (recentSentNumber.length > 0) { setKeyPadOff(true); setMobileNumber(recentSentNumber[0]); } else { setKeyPadOff(false); setMobileNumber(''); } } }, [recentSentNumber]); const numKeypadClicked = useCallback( (key) => { if (key === 'clear') { setMobileNumber(''); } else if (key == 'backspace') { getBackspaceRawNumber(); } else { getRawPhoneNumber(key); } }, [deviceCountryCode, mobileNumber] ); const handleDelete = (selectedIndex) => { if (recentSentNumber) { const updateItems = recentSentNumber.filter( (_, index) => index !== selectedIndex ); setRecentSentNumber(updateItems); const encryptedNumbers = updateItems.map(encryptPhoneNumber); dispatch( changeLocalSettings({ phoneNumbers: { phoneNumberList: encryptedNumbers, }, }) ); } }; const handleAgreeSendClick = useCallback(() => { let naturalNumber = mobileNumber.replace(/\D/g, ''); // 테스트용: 길이 체크를 더 유연하게 (10자리 또는 11자리 허용) if ( !mobileNumber || naturalNumber.length < 10 || naturalNumber.length > 12 ) { setSmsRetCode(907); return; } if (deviceCountryCode === 'KR') { naturalNumber = '82' + naturalNumber; } if (recentSentNumber && recentSentNumber.length > 0) { const updatedNumbers = [...recentSentNumber]; const existingNumberIndex = updatedNumbers.findIndex( (existingNum) => existingNum === mobileNumber ); if (existingNumberIndex !== -1) { updatedNumbers.splice(existingNumberIndex, 1); } if (updatedNumbers.length >= 5) { updatedNumbers.pop(); } updatedNumbers.unshift(mobileNumber); agreeBtnClickedRef.current = true; setRecentSentNumber(updatedNumbers); const encryptedNumbers = updatedNumbers.map(encryptPhoneNumber); dispatch( changeLocalSettings({ phoneNumbers: { phoneNumberList: encryptedNumbers, }, }) ); } else { setRecentSentNumber([mobileNumber]); dispatch( changeLocalSettings({ phoneNumbers: { phoneNumberList: [encryptPhoneNumber(mobileNumber)], }, }) ); } if (isCurationEvnt) { dispatch( setEventIssueReq({ evntTpCd, evntId, mbphNo: naturalNumber, cntryCd: deviceCountryCode, }) ); return; } if (deviceInfo && smsTpCd) { let params = { dvcIndex: deviceInfo.dvcIndex, mbphNo: naturalNumber, smsTpCd, patnrId, evntId, curationId, smsText, prdtId, entryMenu: nowMenu, }; // 호텔일 경우 날려야 하는 경우 if (smsTpCd === 'APP00205') { params = { ...params, hotelId, hotelNm, hotelDtlUrl, curationId }; } if (smsTpCd === 'APP00204') { params = { ...params, curationId }; } dispatch(sendSms(params)); } // EVT00101 & APP00207(welcome) EVT00103 & APP00209 (welcome+Prizes) : smsTpCd 값을 받지 않음 if (evntTpCd === 'EVT00101' || evntTpCd === 'EVT00103') { dispatch( registerDeviceInfo({ evntTpCd, evntId, evntApplcnFlag: 'Y', entryMenu: 'TermsPop', mbphNo: naturalNumber, }) ); } }, [ dispatch, mobileNumber, smsTpCd, evntTpCd, deviceInfo, deviceCountryCode, nowMenu, ]); const _onClose = useCallback( (e) => { if (e.target) { dispatch(clearSMS()); dispatch(clearCurationCoupon()); setSmsRetCode(undefined); } onClose(); dispatch(setShowPopup(ACTIVE_POPUP.smsPopup)); setTimeout(() => Spotlight.focus('agreeAndSend')); }, [dispatch, smsRetCode] ); useEffect(() => { setSmsRetCode(smsRetCodeResData); let timer; if ( smsRetCodeResData === 0 || regDeviceInfoRetCode === 0 || curationCouponSuccess === 0 ) { const logParams = { status: 'send', nowMenu: nowMenu, partner: patncNm ?? shopByMobileLogRef?.current?.patncNm, productId: prdtId ?? shopByMobileLogRef?.current?.prdtId, productTitle: title ?? shopByMobileLogRef?.current?.prdtNm, brand: shopByMobileLogRef?.current?.brndNm, contextName: LOG_CONTEXT_NAME.SHOPBYMOBILE, messageId: LOG_MESSAGE_ID.SMB, }; dispatch(sendLogTotalRecommend(logParams)); if (shopByMobileLogRef) { let params = { ...shopByMobileLogRef.current, locDt: formatLocalDateTime(new Date()), logTpNo: LOG_TP_NO.SHOP_BY_MOBILE.AGREE_AND_SEND, mbphNoFlag: 'Y', trmsAgrFlag: 'Y', }; dispatch(sendLogShopByMobile(params)); shopByMobileLogRef.current = null; } if (spotlightId) Spotlight.focus(spotlightId); else Spotlight.focus(); return () => clearTimeout(timer); } }, [ shopByMobileLogRef, smsRetCodeResData, regDeviceInfoRetCode, curationCouponSuccess, spotlightId, ]); useEffect(() => { // retCode initialize return () => { dispatch(clearSMS()); dispatch(clearRegisterDeviceInfo()); dispatch(clearCurationCoupon()); dispatch(setHidePopup()); }; }, [dispatch]); const getSmsErrorMsg = useMemo(() => { const SMS_ERROR_502 = $L('The event information has not been registered'); const SMS_ERROR_903 = $L('You have exceeded the daily text limit.'); const SMS_ERROR_904 = $L( 'You have exceeded the text limit for this product.' ); const SMS_ERROR_905 = $L( 'This number is currently blocked. To receive a message, please send UNSTOP to the number below. 07860 064195' ); const SMS_ERROR_906 = $L('Sorry. This item is sold out.'); const SMS_ERROR_600 = $L('This device had received first time coupon.'); const SMS_ERROR_601 = $L('There is no coupon.'); const SMS_ERROR_900 = $L('Failed to send text to {mobileNumber}').replace( '{mobileNumber}', mobileNumber ); const SMS_ERROR_907 = $L( 'Only {length} digits is permitted. Please check again' ).replace('{length}', getMaxNum(deviceCountryCode)); switch (smsRetCode) { case 502: return SMS_ERROR_502; case 600: return SMS_ERROR_600; case 601: return SMS_ERROR_601; case 900: return SMS_ERROR_900; case 903: return SMS_ERROR_903; case 904: return SMS_ERROR_904; case 905: return SMS_ERROR_905; case 906: return SMS_ERROR_906; case 907: return smsRetCodeResData === 907 ? SMS_ERROR_900 : SMS_ERROR_907; default: return SMS_ERROR_900; } }, [smsRetCode, mobileNumber, smsRetCodeResData]); const getEvntErrorMsg = useMemo(() => { if (curationCouponSuccess === 600) { return $L('This device had received first time coupon.'); } else if (curationCouponSuccess === 601) { return $L('There is no coupon.'); } else { return $L('Failed to sent text to {mobileNumber}').replace( '{mobileNumber}', mobileNumber ); } }, [curationCouponSuccess, mobileNumber]); const retCodeError = (smsRetCode !== undefined && smsRetCode !== 0) || (curationCouponSuccess !== undefined && curationCouponSuccess !== 0); useEffect(() => { if (phoneNumberList) { const decryptedNumbers = phoneNumberList.map(decryptPhoneNumber); setRecentSentNumber(decryptedNumbers); } }, [phoneNumberList]); return ( <> {smsRetCode === undefined && open && regDeviceInfoRetCode === undefined && curationCouponSuccess === undefined && (
{title || MSG_SEND_LINK}
{productImg && (

)}
{brandLogo && ( Brand )} {productId && patnrId !== '21' && (
ID: {productId}
)} {patnrId === '21' && (
{patncNm}
)}
{subTitle && (
{subTitle}
)} {productPrice && (
{productPrice}
)}
{/* 🔄 [BACKUP] 기존 전화번호 입력 영역 - 원상복구용 */}
{deviceCountryCode && deviceCountryCode === 'RU' && ( {'+7 '} )} {mobileNumber} {keyPadOff && recentSentNumber.length > 0 ? ( ) : ( )}
{deviceCountryCode && ( )}
{/* 🆕 [NEW] PhoneInputSection + instruction 좌우 배치 */} {/*
{deviceCountryCode && (
{$L("By clicking Agree and Send button, I agree that LGE may collect and store my cell phone number to send text messages as I requested, for data analysis and for feature-enhancement purposes.")}
{$L("By entering my cell phone number, I agree to receive messages from LGE with information on how to purchase the product I selected. Message and data rates may apply.")}
)}
*/}
{$L('Agree and Send')} {$L('Cancel')}
)} {retCodeError && ( )} {(smsRetCode === 0 || regDeviceInfoRetCode === 0 || regDeviceInfoRetCode === 0) && ( )} ); }