This commit is contained in:
focp212@naver.com
2025-10-27 16:57:47 +09:00
4 changed files with 98 additions and 19 deletions

View File

@@ -53,6 +53,12 @@ interface UseAppBridgeReturn {
openBiometricRegistrationPopup: () => Promise<boolean>; openBiometricRegistrationPopup: () => Promise<boolean>;
// 간편 인증 등록 팝업 닫기 // 간편 인증 등록 팝업 닫기
closeBiometricRegistrationPopup: () => Promise<void>; closeBiometricRegistrationPopup: () => Promise<void>;
// 푸시 알림 권한 확인
isPushNotificationEnabled: () => Promise<boolean>;
// 앱 설정 화면 열기
openAppSettings: () => Promise<void>;
} }
export const useAppBridge = (): UseAppBridgeReturn => { export const useAppBridge = (): UseAppBridgeReturn => {
@@ -229,6 +235,21 @@ export const useAppBridge = (): UseAppBridgeReturn => {
return appBridge.safeCall(() => appBridge.shareContent(content)); return appBridge.safeCall(() => appBridge.shareContent(content));
}, [isNativeEnvironment]); }, [isNativeEnvironment]);
const isPushNotificationEnabled = useCallback(async (): Promise<boolean> => {
if (!isNativeEnvironment) {
return true;
}
const result = await appBridge.safeCall(() => appBridge.isPushNotificationEnabled(), false);
return result || false;
}, [isNativeEnvironment]);
const openAppSettings = useCallback(async (): Promise<void> => {
if (!isNativeEnvironment) {
return;
}
return appBridge.safeCall(() => appBridge.openAppSettings());
}, [isNativeEnvironment]);
return { return {
isNativeEnvironment, isNativeEnvironment,
isAndroid, isAndroid,
@@ -249,6 +270,8 @@ export const useAppBridge = (): UseAppBridgeReturn => {
requestRefreshToken, requestRefreshToken,
registerBiometric, registerBiometric,
openBiometricRegistrationPopup, openBiometricRegistrationPopup,
closeBiometricRegistrationPopup closeBiometricRegistrationPopup,
isPushNotificationEnabled,
openAppSettings
}; };
}; };

View File

@@ -12,9 +12,12 @@ import {
useSetFooterMode useSetFooterMode
} from '@/widgets/sub-layout/use-sub-layout'; } from '@/widgets/sub-layout/use-sub-layout';
import { ChangeEvent, useEffect, useState } from 'react'; import { ChangeEvent, useEffect, useState } from 'react';
import { useAppBridge } from '@/hooks/useAppBridge';
export const SettingPage = () => { export const SettingPage = () => {
let userInfo = useStore.getState().UserStore.userInfo; let userInfo = useStore.getState().UserStore.userInfo;
const { isPushNotificationEnabled, openAppSettings } = useAppBridge();
useSetHeaderTitle('설정'); useSetHeaderTitle('설정');
useSetHeaderType(HeaderType.LeftArrow); useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(false); useSetFooterMode(false);
@@ -27,6 +30,7 @@ export const SettingPage = () => {
const {mutateAsync: appAlarmFind} = useAppAlarmFindMutation(); const {mutateAsync: appAlarmFind} = useAppAlarmFindMutation();
const {mutateAsync: appAlarmConsent} = useAppAlarmConsentMutation(); const {mutateAsync: appAlarmConsent} = useAppAlarmConsentMutation();
const [pushNotificationEnabled, setPushNotificationEnabled] = useState<boolean>(false);
const [alarmSetting, setAlarmSetting] = useState<Record<string, boolean>>({ const [alarmSetting, setAlarmSetting] = useState<Record<string, boolean>>({
'21': false, '21': false,
'11': false, '11': false,
@@ -41,6 +45,16 @@ export const SettingPage = () => {
window.open('https://www.nicevan.co.kr/privacy-policy', '_blank'); window.open('https://www.nicevan.co.kr/privacy-policy', '_blank');
}; };
const checkPushNotificationStatus = () => {
isPushNotificationEnabled().then((enabled) => {
setPushNotificationEnabled(enabled);
});
};
const onClickPushNotificationToggle = () => {
openAppSettings();
};
const callAppAlarmFind = () => { const callAppAlarmFind = () => {
if(userInfo.usrid){ if(userInfo.usrid){
let params: AppAlarmFindParams = { let params: AppAlarmFindParams = {
@@ -88,6 +102,26 @@ export const SettingPage = () => {
useEffect(() => { useEffect(() => {
callAppAlarmFind(); callAppAlarmFind();
checkPushNotificationStatus();
// 앱이 포어그라운드로 돌아올 때 푸시 알림 권한 상태 재확인
const handleVisibilityChange = () => {
if (document.visibilityState === 'visible') {
checkPushNotificationStatus();
}
};
const handleFocus = () => {
checkPushNotificationStatus();
};
document.addEventListener('visibilitychange', handleVisibilityChange);
window.addEventListener('focus', handleFocus);
return () => {
document.removeEventListener('visibilitychange', handleVisibilityChange);
window.removeEventListener('focus', handleFocus);
};
}, []); }, []);
return ( return (
@@ -96,8 +130,8 @@ export const SettingPage = () => {
<div className="sub-wrap"> <div className="sub-wrap">
<div className="settings-header"> <div className="settings-header">
<div className="settings-title"> </div> <div className="settings-title"> </div>
<label className="settings-switch"> <label className="settings-switch" onClick={onClickPushNotificationToggle}>
<input type="checkbox" /> <input type="checkbox" checked={pushNotificationEnabled} readOnly />
<span className="slider"></span> <span className="slider"></span>
</label> </label>
</div> </div>
@@ -112,6 +146,7 @@ export const SettingPage = () => {
<input <input
type="checkbox" type="checkbox"
checked={ alarmSetting['21'] } checked={ alarmSetting['21'] }
disabled={ !pushNotificationEnabled }
onChange={ (e: ChangeEvent<HTMLInputElement>) => callAppAlarmConsent(e.target.checked, '21') } onChange={ (e: ChangeEvent<HTMLInputElement>) => callAppAlarmConsent(e.target.checked, '21') }
/> />
<span className="slider"></span> <span className="slider"></span>
@@ -123,6 +158,7 @@ export const SettingPage = () => {
<input <input
type="checkbox" type="checkbox"
checked={ alarmSetting['11'] } checked={ alarmSetting['11'] }
disabled={ !pushNotificationEnabled }
onChange={ (e: ChangeEvent<HTMLInputElement>) => callAppAlarmConsent(e.target.checked, '11') } onChange={ (e: ChangeEvent<HTMLInputElement>) => callAppAlarmConsent(e.target.checked, '11') }
/> />
<span className="slider"></span> <span className="slider"></span>
@@ -134,6 +170,7 @@ export const SettingPage = () => {
<input <input
type="checkbox" type="checkbox"
checked={ alarmSetting['31'] } checked={ alarmSetting['31'] }
disabled={ !pushNotificationEnabled }
onChange={ (e: ChangeEvent<HTMLInputElement>) => callAppAlarmConsent(e.target.checked, '31') } onChange={ (e: ChangeEvent<HTMLInputElement>) => callAppAlarmConsent(e.target.checked, '31') }
/> />
<span className="slider"></span> <span className="slider"></span>
@@ -145,6 +182,7 @@ export const SettingPage = () => {
<input <input
type="checkbox" type="checkbox"
checked={ alarmSetting['41'] } checked={ alarmSetting['41'] }
disabled={ !pushNotificationEnabled }
onChange={ (e: ChangeEvent<HTMLInputElement>) => callAppAlarmConsent(e.target.checked, '41') } onChange={ (e: ChangeEvent<HTMLInputElement>) => callAppAlarmConsent(e.target.checked, '41') }
/> />
<span className="slider"></span> <span className="slider"></span>
@@ -160,6 +198,7 @@ export const SettingPage = () => {
<input <input
type="checkbox" type="checkbox"
checked={ alarmSetting['61'] } checked={ alarmSetting['61'] }
disabled={ !pushNotificationEnabled }
onChange={ (e: ChangeEvent<HTMLInputElement>) => callAppAlarmConsent(e.target.checked, '61') } onChange={ (e: ChangeEvent<HTMLInputElement>) => callAppAlarmConsent(e.target.checked, '61') }
/> />
<span className="slider"></span> <span className="slider"></span>
@@ -171,6 +210,7 @@ export const SettingPage = () => {
<input <input
type="checkbox" type="checkbox"
checked={ alarmSetting['62'] } checked={ alarmSetting['62'] }
disabled={ !pushNotificationEnabled }
onChange={ (e: ChangeEvent<HTMLInputElement>) => callAppAlarmConsent(e.target.checked, '62') } onChange={ (e: ChangeEvent<HTMLInputElement>) => callAppAlarmConsent(e.target.checked, '62') }
/> />
<span className="slider"></span> <span className="slider"></span>

View File

@@ -65,7 +65,13 @@ export enum BridgeMessageType {
GET_LANGUAGE = 'getLanguage', GET_LANGUAGE = 'getLanguage',
// 메시지 카운트 업데이트 // 메시지 카운트 업데이트
UPDATE_MESSAGE_COUNT = 'updateMessageCount' UPDATE_MESSAGE_COUNT = 'updateMessageCount',
// 푸시 알림 권한 확인
IS_PUSH_NOTIFICATION_ENABLED = 'isPushNotificationEnabled',
// 앱 설정 화면 열기
OPEN_APP_SETTINGS = 'openAppSettings'
} }
export interface DeviceInfo { export interface DeviceInfo {

View File

@@ -195,6 +195,16 @@ class AppBridge {
return this.sendMessage(BridgeMessageType.UPDATE_MESSAGE_COUNT, { count }); return this.sendMessage(BridgeMessageType.UPDATE_MESSAGE_COUNT, { count });
} }
// 푸시 알림 권한 확인
async isPushNotificationEnabled(): Promise<boolean> {
return this.sendMessage(BridgeMessageType.IS_PUSH_NOTIFICATION_ENABLED);
}
// 앱 설정 화면 열기
async openAppSettings(): Promise<void> {
return this.sendMessage(BridgeMessageType.OPEN_APP_SETTINGS);
}
// 네이티브 환경 체크 // 네이티브 환경 체크
isNativeEnvironment(): boolean { isNativeEnvironment(): boolean {
return !!( return !!(