Files
shoptime/com.twin.app.shoptime/src/App/App.js
optrader 276ee65979 [251119] feat: FloatingGradientLayer..Experimental..2
🕐 커밋 시간: 2025. 11. 19. 19:24:28

📊 변경 통계:
  • 총 파일: 10개
  • 추가: +95줄
  • 삭제: -181줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/App/App.js
  ~ com.twin.app.shoptime/src/actions/panelActions.js
  ~ com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx
  ~ com.twin.app.shoptime/src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.jsx
  ~ com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx
  ~ com.twin.app.shoptime/src/views/HomePanel/HomePanel.module.less
  ~ com.twin.app.shoptime/src/views/MainView/MainView.jsx
  ~ com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.v3.jsx

🗑️ 삭제된 파일:
  - com.twin.app.shoptime/src/components/FloatingGradientBackground/FloatingGradientBackground.jsx
  - com.twin.app.shoptime/src/components/FloatingGradientBackground/FloatingGradientBackground.module.less

🔧 주요 변경 내용:
  • 핵심 비즈니스 로직 개선
  • UI 컴포넌트 아키텍처 개선
  • 소규모 기능 개선
  • 코드 정리 및 최적화
  • 모듈 구조 개선

Performance: 코드 최적화로 성능 개선 기대
2025-11-19 19:24:29 +09:00

917 lines
28 KiB
JavaScript

import React, {
// useMemo,
useCallback,
useEffect,
useRef,
// useState,
} from 'react';
import { useSelector, useDispatch } from 'react-redux';
// import { I18nContext } from "@enact/i18n";
// import classNames from "classnames";
// import PropTypes from "prop-types";
import Spotlight from '@enact/spotlight';
import { Job } from '@enact/core/util';
import platform from '@enact/core/platform';
import { ThemeDecorator } from '@enact/sandstone/ThemeDecorator';
// import "../../../assets/fontello/css/fontello.css";
import {
changeAppStatus,
changeLocalSettings,
// cancelFocusElement,
// focusElement,
// setExitApp,
// setPreventMouse,
// setShowPopup,
// setTermsAgreeYn,
getTermsAgreeYn,
deleteOldDb8Datas,
sendBroadCast,
getConnectionStatus,
getConnectionInfo,
getDeviceId,
getHttpHeaderForServiceRequest,
getSystemSettings,
checkFirstLaunch,
setDeepLink,
setGNBMenu,
setSecondLayerInfo,
} from '../actions/commonActions';
import { getShoptimeTerms } from '../actions/empActions';
import { getHomeMenu, getHomeTerms } from '../actions/homeActions';
import { getMyRecommandedKeyword, getMyUpcomingAlertShow } from '../actions/myPageActions';
import { pushPanel } from '../actions/panelActions';
import { enqueuePanelHistory } from '../actions/panelHistoryActions';
import NotSupportedVersion from '../components/NotSupportedVersion/NotSupportedVersion';
import ToastContainer from '../components/TToast/ToastContainer';
import GlobalPopup from '../components/GlobalPopup/GlobalPopup';
import usePrevious from '../hooks/usePrevious';
import { lunaTest } from '../lunaSend/lunaTest';
import { store } from '../store/store';
import * as Config from '../utils/Config';
import {
// $L,
clearLaunchParams,
// getCountry,
getLaunchParams,
// getUUID,
// resizeTo,
} from '../utils/helperMethods';
import { SpotlightIds } from '../utils/SpotlightIds';
import ErrorBoundary from '../views/ErrorBoundary';
import MainView from '../views/MainView/MainView';
import css from './App.module.less';
import { handleBypassLink } from './bypassLinkHandler';
import { handleDeepLink } from './deepLinkHandler';
import { sendLogTotalRecommend } from '../actions/logActions';
import { types } from '../actions/actionTypes';
// import {
// startFocusMonitoring,
// stopFocusMonitoring,
// } from "../utils/focus-monitor";
// import { PanelHoc } from "../components/TPanel/TPanel";
let foreGroundChangeTimer = null;
// 기존 콘솔 메서드를 백업
const originalConsole = {
log: console.log,
error: console.error,
warn: console.warn,
info: console.info,
};
const enableConsole = () => {
console.log = originalConsole.log;
console.error = originalConsole.error;
console.warn = originalConsole.warn;
console.info = originalConsole.info;
};
const disableConsole = () => {
console.log = function () {};
// console.error = function() {};
console.warn = function () {};
console.info = function () {};
};
// console.log 자동 Object 직렬화 오버라이드
const originalConsoleLog = console.log;
const originalConsoleError = console.error;
const originalConsoleWarn = console.warn;
const processArgs = (args) => {
return args.map((arg) => {
if (typeof arg === 'object' && arg !== null && !Array.isArray(arg)) {
try {
return JSON.stringify(arg, null, 2);
} catch (e) {
return `[Object: ${e.message}]`;
}
}
return arg;
});
};
// Voice 관련 로그를 VoicePanel로 전송하는 헬퍼 함수
const sendVoiceLogToPanel = (args) => {
try {
const firstArg = args[0];
// [Voice] 또는 [VoiceConductor] 태그 확인
if (
typeof firstArg === 'string' &&
(firstArg.includes('[Voice]') || firstArg.includes('[VoiceConductor]'))
) {
// 로그 타입 결정
let logType = 'INFO';
let title = firstArg;
if (
firstArg.includes('ERROR') ||
firstArg.includes('Error') ||
firstArg.includes('Failed') ||
firstArg.includes('failed')
) {
logType = 'ERROR';
} else if (
firstArg.includes('Response') ||
firstArg.includes('response') ||
firstArg.includes('success')
) {
logType = 'RESPONSE';
} else if (
firstArg.includes('Registering') ||
firstArg.includes('Sending') ||
firstArg.includes('request')
) {
logType = 'REQUEST';
} else if (
firstArg.includes('received') ||
firstArg.includes('Handling') ||
firstArg.includes('⭐')
) {
logType = 'ACTION';
} else if (firstArg.includes('command')) {
logType = 'COMMAND';
}
// 데이터 수집
const logData = {};
if (args.length > 1) {
args.slice(1).forEach((arg, index) => {
if (typeof arg === 'object') {
Object.assign(logData, arg);
} else {
logData[`arg${index + 1}`] = arg;
}
});
}
// Redux로 dispatch
store.dispatch({
type: types.VOICE_ADD_LOG,
payload: {
timestamp: new Date().toISOString(),
type: logType,
title: title.replace(/^\[Voice\]\s*/, '').replace(/^\[VoiceConductor\]\s*/, ''),
data: Object.keys(logData).length > 0 ? logData : { message: firstArg },
success: logType !== 'ERROR',
},
});
}
} catch (error) {
// 로깅 중 에러가 발생해도 원래 로그는 출력되어야 함
originalConsoleLog.call(console, '[VoiceLog] Error sending to panel:', error);
}
};
console.log = function (...args) {
// Voice 로그를 VoicePanel로 전송
sendVoiceLogToPanel(args);
// 원래 console.log 실행
originalConsoleLog.apply(console, processArgs(args));
};
console.error = function (...args) {
// Voice 로그를 VoicePanel로 전송 (에러는 강제로 ERROR 타입)
try {
const firstArg = args[0];
if (
typeof firstArg === 'string' &&
(firstArg.includes('[Voice]') || firstArg.includes('[VoiceConductor]'))
) {
const logData = {};
if (args.length > 1) {
args.slice(1).forEach((arg, index) => {
if (typeof arg === 'object') {
Object.assign(logData, arg);
} else {
logData[`arg${index + 1}`] = arg;
}
});
}
store.dispatch({
type: types.VOICE_ADD_LOG,
payload: {
timestamp: new Date().toISOString(),
type: 'ERROR',
title: firstArg.replace(/^\[Voice\]\s*/, '').replace(/^\[VoiceConductor\]\s*/, ''),
data: Object.keys(logData).length > 0 ? logData : { message: firstArg },
success: false,
},
});
}
} catch (error) {
originalConsoleError.call(console, '[VoiceLog] Error sending error to panel:', error);
}
originalConsoleError.apply(console, processArgs(args));
};
console.warn = function (...args) {
// Voice 로그를 VoicePanel로 전송 (경고는 ERROR 타입으로)
try {
const firstArg = args[0];
if (
typeof firstArg === 'string' &&
(firstArg.includes('[Voice]') || firstArg.includes('[VoiceConductor]'))
) {
const logData = {};
if (args.length > 1) {
args.slice(1).forEach((arg, index) => {
if (typeof arg === 'object') {
Object.assign(logData, arg);
} else {
logData[`arg${index + 1}`] = arg;
}
});
}
store.dispatch({
type: types.VOICE_ADD_LOG,
payload: {
timestamp: new Date().toISOString(),
type: 'ERROR',
title:
'WARNING: ' +
firstArg.replace(/^\[Voice\]\s*/, '').replace(/^\[VoiceConductor\]\s*/, ''),
data: Object.keys(logData).length > 0 ? logData : { message: firstArg },
success: false,
},
});
}
} catch (error) {
originalConsoleWarn.call(console, '[VoiceLog] Error sending warning to panel:', error);
}
originalConsoleWarn.apply(console, processArgs(args));
};
const originFocus = Spotlight.focus;
const originMove = Spotlight.move;
const originSilentlyFocus = Spotlight.silentlyFocus;
let lastLoggedSpotlightId = null;
let lastLoggedBlurSpotlightId = null;
let focusLoggingSuppressed = 0;
const resolveSpotlightIdFromNode = (node) => {
if (!node) return undefined;
if (node.dataset && node.dataset.spotlightId) {
return node.dataset.spotlightId;
}
if (typeof node.getAttribute === 'function') {
const idFromAttr = node.getAttribute('data-spotlight-id');
if (idFromAttr) {
return idFromAttr;
}
}
if (node.id) {
return node.id;
}
return undefined;
};
const logFocusTransition = (previousNode, currentNode) => {
if (!Config.FOCUS_DEBUG) {
return;
}
const previousId = resolveSpotlightIdFromNode(previousNode);
const currentId = resolveSpotlightIdFromNode(currentNode);
if (previousId && previousId !== currentId) {
console.log(`[SpotlightFocus] blur - ${previousId}`);
lastLoggedBlurSpotlightId = previousId;
}
if (currentId && currentId !== lastLoggedSpotlightId) {
console.log(`[SpotlightFocus] focus - ${currentId}`);
lastLoggedSpotlightId = currentId;
}
};
Spotlight.focus = function (elem, containerOption) {
const previousNode = Spotlight.getCurrent();
const ret = originFocus.apply(this, [elem, containerOption]); // this 바인딩을 유지하여 originFocus 호출
const current = Spotlight.getCurrent();
if ((ret === true || current !== previousNode) && focusLoggingSuppressed === 0) {
logFocusTransition(previousNode, current);
}
if (ret === true) {
const floatLayerNode = document.getElementById('floatLayer');
const tabLayoutNode = document.getElementById(SpotlightIds.TAB_LAYOUT);
//팝업이 존재할 경우
if (floatLayerNode && floatLayerNode.childElementCount > 0) {
if (!floatLayerNode.contains(current)) {
if (floatLayerNode.lastElementChild) {
const spottableNode = floatLayerNode.lastElementChild.querySelector(
'[data-spotlight-container="true"]'
);
if (spottableNode) {
originFocus.apply(this, [spottableNode]); // this 바인딩을 유지하여 originFocus 호출
}
}
}
} else {
//GNB가 열린 상태에서 Panel에 포커스 가는 경우
if (current && store.getState().common.isGnbOpened && !tabLayoutNode.contains(current)) {
store.dispatch(
sendBroadCast({
type: 'deActivateTab',
moreInfo: { reason: 'focus' },
})
);
}
}
}
return ret;
};
Spotlight.move = function (...args) {
if (!originMove) {
return false;
}
const previousNode = Spotlight.getCurrent();
focusLoggingSuppressed += 1;
let ret;
try {
ret = originMove.apply(this, args);
} finally {
focusLoggingSuppressed = Math.max(0, focusLoggingSuppressed - 1);
}
const current = Spotlight.getCurrent();
if (current !== previousNode) {
logFocusTransition(previousNode, current);
}
return ret;
};
Spotlight.silentlyFocus = function (...args) {
if (!originSilentlyFocus) {
return false;
}
const previousNode = Spotlight.getCurrent();
focusLoggingSuppressed += 1;
let ret;
try {
ret = originSilentlyFocus.apply(this, args);
} finally {
focusLoggingSuppressed = Math.max(0, focusLoggingSuppressed - 1);
}
const current = Spotlight.getCurrent();
if (current !== previousNode) {
logFocusTransition(previousNode, current);
}
return ret;
};
const resolveSpotlightIdFromEvent = (event) => {
if (!event) return undefined;
const { detail, target } = event;
if (detail) {
if (detail.spotlightId) {
return detail.spotlightId;
}
if (detail.id) {
return detail.id;
}
if (detail.target && detail.target.dataset && detail.target.dataset.spotlightId) {
return detail.target.dataset.spotlightId;
}
}
if (target && target.dataset && target.dataset.spotlightId) {
return target.dataset.spotlightId;
}
return undefined;
};
// Spotlight Focus 추적 로그 [251115]
// DOM 이벤트 리스너로 대체
document.addEventListener('focusin', (ev) => {
console.log('[SPOTLIGHT FOCUS-IN]', ev.target);
});
document.addEventListener('focusout', (ev) => {
console.log('[SPOTLIGHT FOCUS-OUT]', ev.target);
});
// Spotlight 커스텀 이벤트가 있다면 추가
if (typeof Spotlight !== 'undefined' && Spotlight.addEventListener) {
Spotlight.addEventListener('focus', (ev) => {
console.log('[SPOTLIGHT: focus]', ev.target);
});
}
function AppBase(props) {
const dispatch = useDispatch();
const httpHeader = useSelector((state) => state.common.httpHeader);
const httpHeaderRef = useRef(httpHeader);
const webOSVersion = useSelector((state) => state.common.appStatus.webOSVersion);
const deviceId = useSelector((state) => state.common.appStatus.deviceId);
const loginUserData = useSelector((state) => state.common.appStatus.loginUserData);
const loginUserDataRef = useRef(loginUserData);
const cursorVisible = useSelector((state) => state.common.appStatus.cursorVisible);
const introTermsAgree = useSelector((state) => state.common.introTermsAgree);
const deviceRegistered = useSelector((state) => state.common.deviceRegistered);
// const optionalTermsAgree = useSelector((state) => state.common.optionalTermsAgree);
const termsLoading = useSelector((state) => state.common.termsLoading);
// termsFlag 전체 상태 확인
// const termsFlag = useSelector((state) => state.common.termsFlag);
const termsData = useSelector((state) => state.home.termsData);
// 🔽 Spotlight focus/blur 로그 (옵션)
useEffect(() => {
if (!Config.FOCUS_DEBUG) {
return undefined;
}
const handleFocusLog = (event) => {
const spotlightId = resolveSpotlightIdFromEvent(event);
if (!spotlightId || spotlightId === lastLoggedSpotlightId) {
return;
}
console.log(`[SpotlightFocus] focus - ${spotlightId}`);
lastLoggedSpotlightId = spotlightId;
};
const handleBlurLog = (event) => {
const spotlightId = resolveSpotlightIdFromEvent(event);
if (!spotlightId || spotlightId === lastLoggedBlurSpotlightId) {
return;
}
console.log(`[SpotlightFocus] blur - ${spotlightId}`);
lastLoggedBlurSpotlightId = spotlightId;
};
const hasSpotlightListener = typeof Spotlight.addEventListener === 'function';
if (hasSpotlightListener) {
Spotlight.addEventListener('focus', handleFocusLog);
Spotlight.addEventListener('blur', handleBlurLog);
return () => {
Spotlight.removeEventListener('focus', handleFocusLog);
Spotlight.removeEventListener('blur', handleBlurLog);
};
}
if (typeof document !== 'undefined') {
document.addEventListener('spotlightfocus', handleFocusLog);
document.addEventListener('spotlightblur', handleBlurLog);
return () => {
document.removeEventListener('spotlightfocus', handleFocusLog);
document.removeEventListener('spotlightblur', handleBlurLog);
};
}
return undefined;
}, [Config.FOCUS_DEBUG]);
useEffect(() => {
// Chromium68 호환성을 위해 Optional Chaining 제거
if (termsData && termsData.data && termsData.data.terms) {
dispatch(getTermsAgreeYn());
}
}, [termsData, dispatch]);
const introTermsAgreeRef = usePrevious(introTermsAgree);
const logEnable = useSelector((state) => state.localSettings.logEnable);
const oldDb8Deleted = useSelector((state) => state.localSettings.oldDb8Deleted);
// const macAddress = useSelector((state) => state.common.macAddress);
// Chromium68 호환성을 위해 Optional Chaining 제거
const deviceCountryCode = (httpHeader && httpHeader['X-Device-Country']) || '';
useEffect(() => {
if (!cursorVisible && !Spotlight.getCurrent()) {
Spotlight.focus();
}
}, [cursorVisible]);
useEffect(() => {
if (logEnable) {
enableConsole();
} else {
disableConsole();
}
}, [logEnable]);
useEffect(() => {
if (!oldDb8Deleted) {
dispatch(deleteOldDb8Datas());
}
}, [oldDb8Deleted, dispatch]);
const hideCursor = useRef(
new Job((func) => {
func();
console.log('hide cursor');
}, 5000)
);
// 컴포넌트에서 모니터링 시작 - 한시적 모니터링
// useEffect(() => {
// startFocusMonitoring();
// return () => stopFocusMonitoring();
// }, []);
// 임시 작업용 코드 -> 인트로 팝업 표시
// useEffect(() => {
// const timer = setTimeout(() => {
// dispatch(
// pushPanel({ name: Config.panel_names.INTRO_PANEL, panelInfo: {} })
// );
// }, 1000);
// return () => clearTimeout(timer);
// }, [dispatch]);
// called by [receive httpHeader, launch, relaunch]
const initService = useCallback(
(haveyInit = true) => {
// console.log(
// "<<<<<<<<<<<<< appinfo >>>>>>>>>>>>{heavyInit, appinfo} ",
// haveyInit,
// appinfo
// );
console.log('[App.js] initService,httpHeaderRef.current', httpHeaderRef.current);
console.log('[App.js] haveyInit', haveyInit);
// 앱 초기화 시 HomePanel 자동 기록
console.log('[App.js] Recording initial HomePanel on app start');
dispatch(
enqueuePanelHistory(
'homepanel',
{},
'APP_INITIALIZE',
new Date().toISOString(),
false, // fromGNB: false (앱 초기화)
false // fromResetPanel: false (앱 초기화)
)
);
if (httpHeaderRef.current) {
if (haveyInit) {
dispatch(changeAppStatus({ connectionFailed: false }));
if (typeof window === 'object' && window.PalmSystem) {
dispatch(
changeAppStatus({
// Chromium68 호환성을 위해 Optional Chaining 제거
cursorVisible:
window.PalmSystem &&
window.PalmSystem.cursor &&
window.PalmSystem.cursor.visibility,
})
);
}
dispatch(getHomeMenu());
dispatch(getMyRecommandedKeyword());
dispatch(getMyUpcomingAlertShow());
}
const launchParams = getLaunchParams();
console.log(
'initService...{haveyInit, launchParams}',
haveyInit,
JSON.stringify(launchParams)
);
// pyh TODO: edit or delete later (line 196 ~ 198)
// Chromium68 호환성을 위해 Optional Chaining 제거
if (launchParams && launchParams.bypass) {
dispatch(handleBypassLink(launchParams.bypass));
}
if (launchParams && launchParams.contentTarget) {
dispatch(handleDeepLink(launchParams.contentTarget));
} else {
dispatch(
sendLogTotalRecommend({
contextName: Config.LOG_CONTEXT_NAME.ENTRY,
messageId: Config.LOG_MESSAGE_ID.ENTRY_INFO,
entry_menu: 'App',
})
);
}
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
dispatch(
sendLogTotalRecommend({
contextName: Config.LOG_CONTEXT_NAME.SHOPTIME,
messageId: Config.LOG_MESSAGE_ID.VIEW_CHANGE,
visible: true,
})
);
clearLaunchParams();
}
},
[dispatch]
);
const handleRelaunchEvent = useCallback(() => {
console.log('[App] handleRelaunchEvent triggered');
const launchParams = getLaunchParams();
clearLaunchParams();
if (!launchParams) {
if (introTermsAgreeRef.current) {
initService(false);
}
if (typeof window === 'object' && window.PalmSystem) {
window.PalmSystem.activate();
}
return;
}
/* VUI_DISABLE_START - VUI Intent 처리 비활성화 */
// Voice Intent 처리 (최우선)
// if (launchParams.intent) {
// const { intent, intentParam, languageCode } = launchParams;
// console.log('[App] Voice Intent received:', { intent, intentParam, languageCode });
// // SearchContent 또는 PlayContent intent 처리
// if (intent === 'SearchContent' || intent === 'PlayContent') {
// dispatch(
// pushPanel({
// name: Config.panel_names.SEARCH_PANEL,
// panelInfo: {
// voiceSearch: true, // 음성 검색 플래그
// searchVal: intentParam, // 검색어
// languageCode: languageCode,
// },
// })
// );
// console.log(`[App] Opening SearchPanel with voice query: ${intentParam}`);
// if (typeof window === 'object' && window.PalmSystem) {
// window.PalmSystem.activate();
// }
// return;
// }
// }
/* VUI_DISABLE_END */
// 기존 로직 유지
if (introTermsAgreeRef.current) {
initService(false);
}
if (typeof window === 'object' && window.PalmSystem) {
window.PalmSystem.activate();
}
}, [initService, introTermsAgreeRef, dispatch]);
const visibilityChanged = useCallback(() => {
console.log('document is hidden', document.hidden);
console.log('document.visibilityState= ', document.visibilityState);
if (document.hidden && typeof window === 'object') {
clearTimeout(foreGroundChangeTimer);
} else {
// change to foreground
// set foreground flag using delay time.
clearTimeout(foreGroundChangeTimer);
foreGroundChangeTimer = setTimeout(() => {
console.log(
'visibility changed !!! ==> set to foreground cursorVisible',
// Chromium68 호환성을 위해 Optional Chaining 제거
JSON.stringify(
window.PalmSystem && window.PalmSystem.cursor && window.PalmSystem.cursor.visibility
)
); // eslint-disable-line no-console
if (platform.platformName !== 'webos') {
//for debug
dispatch(
changeAppStatus({
isAppForeground: true,
cursorVisible: !platform.touchscreen,
})
);
} else if (typeof window === 'object') {
dispatch(
changeAppStatus({
isAppForeground: true,
// Chromium68 호환성을 위해 Optional Chaining 제거
cursorVisible:
window.PalmSystem &&
window.PalmSystem.cursor &&
window.PalmSystem.cursor.visibility,
})
);
}
}, 1000);
}
}, [dispatch]);
useEffect(() => {
const keyDownEvent = (event) => {
dispatch(changeAppStatus({ cursorVisible: false }));
Spotlight.setPointerMode(false);
};
// throttle을 사용하여 마우스 이동 이벤트 최적화 (100ms마다 최대 1회 실행)
let lastMoveTime = 0;
const THROTTLE_MS = 100;
const mouseMoveEvent = (event) => {
const now = Date.now();
if (now - lastMoveTime < THROTTLE_MS) {
// throttle 기간 내에는 hideCursor만 재시작
hideCursor.current.start(() => {
dispatch(changeAppStatus({ cursorVisible: false }));
Spotlight.setPointerMode(false);
});
return;
}
lastMoveTime = now;
dispatch(changeAppStatus({ cursorVisible: true }));
Spotlight.setPointerMode(true);
hideCursor.current.start(() => {
dispatch(changeAppStatus({ cursorVisible: false }));
Spotlight.setPointerMode(false);
});
};
if (typeof window === 'object' && window.PalmSystem) {
window.PalmSystem.activate();
window.lunaTest = (service, method, subscribe, parameters) =>
lunaTest(service, method, subscribe, parameters);
}
dispatch(getConnectionStatus());
dispatch(getConnectionInfo());
dispatch(getDeviceId());
dispatch(getHttpHeaderForServiceRequest());
dispatch(getSystemSettings());
document.addEventListener('visibilitychange', visibilityChanged);
document.addEventListener('webOSRelaunch', handleRelaunchEvent);
//local virtual cursorvisiblilty
if (typeof window === 'object' && !window.PalmSystem) {
document.addEventListener('keydown', keyDownEvent, true);
document.addEventListener('mousemove', mouseMoveEvent, true);
document.addEventListener('wheel', mouseMoveEvent, true);
}
return () => {
document.removeEventListener('visibilitychange', visibilityChanged);
document.removeEventListener('webOSRelaunch', handleRelaunchEvent);
if (typeof window === 'object' && !window.PalmSystem) {
document.removeEventListener('keydown', keyDownEvent);
document.removeEventListener('mousemove', mouseMoveEvent);
document.removeEventListener('wheel', mouseMoveEvent);
}
};
}, [dispatch, visibilityChanged, handleRelaunchEvent]);
useEffect(() => {
let userDataChanged = false;
if (JSON.stringify(loginUserDataRef.current) !== JSON.stringify(loginUserData)) {
userDataChanged = true;
}
if (!httpHeader || !deviceId) {
} else if (userDataChanged || httpHeaderRef.current === null) {
//계정정보 변경시 또는 초기 로딩시
if (!httpHeader) {
dispatch(
changeAppStatus({
showLoadingPanel: { show: true, type: 'launching' },
})
);
}
dispatch(checkFirstLaunch());
dispatch(
getHomeTerms({
mbrNo: loginUserData.userNumber,
trmsTpCdList: 'MST00401, MST00402, MST00405', // 선택약관 추가 25.06
})
);
httpHeaderRef.current = httpHeader;
}
loginUserDataRef.current = loginUserData;
}, [httpHeader, deviceId, dispatch, loginUserData]);
useEffect(() => {
if (
webOSVersion &&
Number(webOSVersion) >= 6 &&
deviceCountryCode &&
deviceCountryCode === 'US' &&
deviceId
) {
dispatch(getShoptimeTerms());
}
}, [webOSVersion, deviceId, dispatch, deviceCountryCode]);
// 테스트용 인트로 화면 표시
// useEffect(() => {
// setTimeout(() => {
// console.log("App.js optionalTermsTest 팝업 표시");
// dispatch(setShowPopup({ activePopup: "optionalTermsConfirmBottom" }));
// }, 1000);
// }, [dispatch]);
// 약관 동의 및 선택 약관 팝업 처리
useEffect(() => {
if (introTermsAgree === undefined || termsLoading) {
// 약관 동의 여부 확인 전에는 아무것도 하지 않음
return;
}
console.log('[App.js] deviceRegistered', deviceRegistered);
if (introTermsAgree || deviceRegistered) {
initService(true);
} else {
// 필수 약관에 동의하지 않은 경우
dispatch(pushPanel({ name: Config.panel_names.INTRO_PANEL, panelInfo: {} }));
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
}
}, [introTermsAgree, deviceRegistered, dispatch, initService, termsLoading]);
useEffect(() => {
const launchParmas = getLaunchParams();
const linkTpNm = launchParmas.contentTarget
? launchParmas.contentTarget.split('_')[2] || ''
: Config.LOG_MENU.APP;
const linkTpCd = launchParmas.contentTarget
? launchParmas.contentTarget.split('_')[1] || ''
: '1000';
if (launchParmas.contentTarget) {
dispatch(
setDeepLink({
contentTarget: launchParmas.contentTarget,
isDeepLink: true,
})
);
}
dispatch(setGNBMenu(linkTpNm));
dispatch(
setSecondLayerInfo({
deeplinkId: launchParmas.contentTarget ?? '',
linkTpCd,
logTpNo: Config.LOG_TP_NO.SECOND_LAYER,
})
);
}, [dispatch, initService]);
return (
<ErrorBoundary>
{webOSVersion === '' ? null : Number(webOSVersion) < 4 ? (
<NotSupportedVersion />
) : (
<MainView
initService={initService}
className={
typeof window === 'object' && !window.PalmSystem && !cursorVisible && css.preventMouse
}
/>
)}
<ToastContainer />
{/* <GlobalPopup /> */}
</ErrorBoundary>
);
}
const App = ThemeDecorator({ noAutoFocus: true }, AppBase);
export default App;
export { App, AppBase };