Files
nice-app-web/src/hooks/useAppBridge.tsx
Jay Sheen 5afc15e861 알림 수신 설정 푸시 권한 연동 구현
- 앱브리지에 isPushNotificationEnabled, openAppSettings 메서드 추가
- 설정 페이지에서 푸시 알림 권한 상태에 따라 알림 수신 설정 토글 표시
- 알림 수신 설정 토글 클릭 시 앱 설정 화면으로 이동
- 푸시 권한이 꺼져있으면 하위 알림 토글들(11, 21, 31, 41, 61, 62) 비활성화
- 앱이 포어그라운드로 돌아올 때 푸시 권한 상태 재확인하여 UI 업데이트

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 16:49:59 +09:00

277 lines
8.9 KiB
TypeScript

import { useState, useEffect, useCallback } from 'react';
import { appBridge } from '@/utils/appBridge';
import { DeviceInfo, ShareContent } from '@/types';
import { LoginResponse } from '@/entities/user/model/types';
interface UseAppBridgeReturn {
isNativeEnvironment: boolean;
isAndroid: boolean;
isIOS: boolean;
deviceInfo: DeviceInfo | null;
// 네비게이션
navigateBack: () => Promise<void>;
navigateTo: (path: string) => Promise<void>;
navigateToLogin: () => Promise<void>;
closeWebView: () => Promise<void>;
// 알림
showToast: (message: string, duration?: number) => Promise<void>;
showAlert: (title: string, message: string) => Promise<void>;
showConfirm: (title: string, message: string) => Promise<boolean>;
// 저장소
setStorage: (key: string, value: unknown) => Promise<void>;
// getStorage: <T = unknown>(key: string) => Promise<T | null>;
removeStorage: (key: string) => Promise<void>;
/*
// 미디어
openCamera: (options?: { quality?: number; allowEdit?: boolean }) => Promise<string>;
openGallery: (options?: { multiple?: boolean; maxCount?: number }) => Promise<string[]>;
// 위치
getLocation: (options?: { enableHighAccuracy?: boolean; timeout?: number }) => Promise<LocationInfo>;
// 연락처
getContacts: () => Promise<ContactInfo[]>;
*/
// 공유
shareContent: (content: ShareContent) => Promise<void>;
// 로그아웃
logout: () => Promise<void>;
// 토큰 요청
requestToken: () => Promise<LoginResponse>;
requestRefreshToken: () => Promise<any>;
// 간편 인증 등록
registerBiometric: () => Promise<void>;
// 간편 인증 등록 팝업 열기
openBiometricRegistrationPopup: () => Promise<boolean>;
// 간편 인증 등록 팝업 닫기
closeBiometricRegistrationPopup: () => Promise<void>;
// 푸시 알림 권한 확인
isPushNotificationEnabled: () => Promise<boolean>;
// 앱 설정 화면 열기
openAppSettings: () => Promise<void>;
}
export const useAppBridge = (): UseAppBridgeReturn => {
const [deviceInfo, setDeviceInfo] = useState<DeviceInfo | null>(null);
const isNativeEnvironment = appBridge.isNativeEnvironment();
const isAndroid = appBridge.isAndroid();
const isIOS = appBridge.isIOS();
useEffect(() => {
if (isNativeEnvironment) {
appBridge.safeCall(
() => appBridge.getDeviceInfo(),
undefined,
(error) => console.warn('Failed to get device info:', error)
).then(info => {
if (info) setDeviceInfo(info);
});
}
}, [isNativeEnvironment]);
const navigateBack = useCallback(async (): Promise<void> => {
if (!isNativeEnvironment) {
window.history.back();
return;
}
return appBridge.safeCall(() => appBridge.navigateBack());
}, [isNativeEnvironment]);
const navigateTo = useCallback(async (path: string): Promise<void> => {
if (!isNativeEnvironment) {
window.location.href = path;
return;
}
return appBridge.safeCall(() => appBridge.navigateTo(path));
}, [isNativeEnvironment]);
const closeWebView = useCallback(async (): Promise<void> => {
if (!isNativeEnvironment) {
window.close();
return;
}
return appBridge.safeCall(() => appBridge.closeWebView());
}, [isNativeEnvironment]);
const navigateToLogin = useCallback(async (): Promise<void> => {
if (!isNativeEnvironment) {
// 웹 환경에서는 로그인 페이지로 이동
window.location.href = '/login';
return;
}
// 네이티브 환경에서는 로그인 화면으로 이동
return appBridge.safeCall(() => appBridge.navigateTo('/login'));
}, [isNativeEnvironment]);
const showToast = useCallback(async (message: string, duration = 3000): Promise<void> => {
if (!isNativeEnvironment) {
// 웹 환경에서는 간단한 토스트 메시지 구현
const toast = document.createElement('div');
toast.className = 'fixed top-4 right-4 bg-gray-800 text-white px-4 py-2 rounded-md z-50 animate-fade-in';
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
document.body.removeChild(toast);
}, duration);
return;
}
return appBridge.safeCall(() => appBridge.showToast(message, duration));
}, [isNativeEnvironment]);
const showAlert = useCallback(async (title: string, message: string): Promise<void> => {
if (!isNativeEnvironment) {
alert(`${title}\n\n${message}`);
return;
}
return appBridge.safeCall(() => appBridge.showAlert(title, message));
}, [isNativeEnvironment]);
const showConfirm = useCallback(async (title: string, message: string): Promise<boolean> => {
if (!isNativeEnvironment) {
return confirm(`${title}\n\n${message}`);
}
const result = await appBridge.safeCall(() => appBridge.showConfirm(title, message), false);
return result || false;
}, [isNativeEnvironment]);
const setStorage = useCallback(async (key: string, value: unknown): Promise<void> => {
if (!isNativeEnvironment) {
localStorage.setItem(key, JSON.stringify(value));
return;
}
return appBridge.safeCall(() => appBridge.setStorage(key, value));
}, [isNativeEnvironment]);
const removeStorage = useCallback(async (key: string): Promise<void> => {
if (!isNativeEnvironment) {
localStorage.removeItem(key);
return;
}
return appBridge.safeCall(() => appBridge.removeStorage(key));
}, [isNativeEnvironment]);
const logout = useCallback(async (): Promise<void> => {
if (!isNativeEnvironment) {
throw new Error('Logout is only available in native environment');
}
return appBridge.safeCall(() => appBridge.logout());
}, [isNativeEnvironment]);
const requestToken = useCallback(async (): Promise<any> => {
if (!isNativeEnvironment) {
throw new Error('Token request is only available in native environment');
}
return appBridge.safeCall(() => appBridge.requestToken());
}, [isNativeEnvironment]);
const requestRefreshToken = useCallback(async (): Promise<any> => {
if (!isNativeEnvironment) {
throw new Error('Token refresh request is only available in native environment');
}
return appBridge.safeCall(() => appBridge.requestRefreshToken());
}, [isNativeEnvironment]);
const registerBiometric = useCallback(async (): Promise<void> => {
if (!isNativeEnvironment) {
throw new Error('Biometric auth is only available in native environment');
}
return appBridge.safeCall(() => appBridge.registerBiometric());
}, [isNativeEnvironment]);
const openBiometricRegistrationPopup = useCallback(async (): Promise<boolean> => {
if (!isNativeEnvironment) {
return false;
}
const result = await appBridge.safeCall(() => appBridge.openBiometricRegistrationPopup(), false);
return result || false;
}, [isNativeEnvironment]);
const closeBiometricRegistrationPopup = useCallback(async (): Promise<void> => {
if (!isNativeEnvironment) {
return;
}
return appBridge.safeCall(() => appBridge.closeBiometricRegistrationPopup());
}, [isNativeEnvironment]);
const shareContent = useCallback(async (content: ShareContent): Promise<void> => {
if (!isNativeEnvironment) {
// 웹 환경에서는 Web Share API 사용 (지원되는 경우)
if (navigator.share) {
try {
await navigator.share({
title: content.title,
text: content.text,
...(content.url && { url: content.url })
});
return;
} catch (error) {
console.warn('Web Share API failed:', error);
}
}
// 폴백: 클립보드에 복사
const shareText = `${content.title}\n${content.text}${content.url ? `\n${content.url}` : ''}`;
if (navigator.clipboard) {
await navigator.clipboard.writeText(shareText);
alert('클립보드에 복사되었습니다.');
} else {
throw new Error('Sharing is not supported');
}
return;
}
return appBridge.safeCall(() => appBridge.shareContent(content));
}, [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 {
isNativeEnvironment,
isAndroid,
isIOS,
deviceInfo,
navigateBack,
navigateTo,
navigateToLogin,
closeWebView,
showToast,
showAlert,
showConfirm,
setStorage,
removeStorage,
shareContent,
logout,
requestToken,
requestRefreshToken,
registerBiometric,
openBiometricRegistrationPopup,
closeBiometricRegistrationPopup,
isPushNotificationEnabled,
openAppSettings
};
};