This commit is contained in:
focp212@naver.com
2025-10-13 16:03:56 +09:00
parent eef751dfa2
commit 29eca29222
8 changed files with 85 additions and 78 deletions

View File

@@ -227,7 +227,7 @@ export const FundAccountTransferListWrap = () => {
</div> </div>
</section> </section>
<section className="transaction-list"> <section className="transaction-list pb-86">
{ getListDateGroup() } { getListDateGroup() }
</section> </section>
<div className="apply-row"> <div className="apply-row">

View File

@@ -43,6 +43,7 @@ interface UseAppBridgeReturn {
// 토큰 요청 // 토큰 요청
requestToken: () => Promise<any>; requestToken: () => Promise<any>;
requestRefreshToken: () => Promise<any>;
// 간편 인증 등록 // 간편 인증 등록
registerBiometric: () => Promise<void>; registerBiometric: () => Promise<void>;
@@ -146,15 +147,6 @@ export const useAppBridge = (): UseAppBridgeReturn => {
return appBridge.safeCall(() => appBridge.setStorage(key, value)); return appBridge.safeCall(() => appBridge.setStorage(key, value));
}, [isNativeEnvironment]); }, [isNativeEnvironment]);
/*
const getStorage = useCallback(async <T = unknown>(key: string): Promise<T | null> => {
if (!isNativeEnvironment) {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : null;
}
return appBridge.safeCall(() => appBridge.getStorage(key), null);
}, [isNativeEnvironment]);
*/
const removeStorage = useCallback(async (key: string): Promise<void> => { const removeStorage = useCallback(async (key: string): Promise<void> => {
if (!isNativeEnvironment) { if (!isNativeEnvironment) {
localStorage.removeItem(key); localStorage.removeItem(key);
@@ -177,6 +169,13 @@ export const useAppBridge = (): UseAppBridgeReturn => {
return appBridge.safeCall(() => appBridge.requestToken()); return appBridge.safeCall(() => appBridge.requestToken());
}, [isNativeEnvironment]); }, [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> => { const registerBiometric = useCallback(async (): Promise<void> => {
if (!isNativeEnvironment) { if (!isNativeEnvironment) {
throw new Error('Biometric auth is only available in native environment'); throw new Error('Biometric auth is only available in native environment');
@@ -198,72 +197,7 @@ export const useAppBridge = (): UseAppBridgeReturn => {
} }
return appBridge.safeCall(() => appBridge.closeBiometricRegistrationPopup()); return appBridge.safeCall(() => appBridge.closeBiometricRegistrationPopup());
}, [isNativeEnvironment]); }, [isNativeEnvironment]);
/*
const openCamera = useCallback(async (
options?: { quality?: number; allowEdit?: boolean }
): Promise<string> => {
if (!isNativeEnvironment) {
throw new Error('Camera access is only available in native environment');
}
const result = await appBridge.safeCall(() => appBridge.openCamera(options), '');
return result || '';
}, [isNativeEnvironment]);
const openGallery = useCallback(async (
options?: { multiple?: boolean; maxCount?: number }
): Promise<string[]> => {
if (!isNativeEnvironment) {
throw new Error('Gallery access is only available in native environment');
}
const result = await appBridge.safeCall(() => appBridge.openGallery(options), []);
return result || [];
}, [isNativeEnvironment]);
const getLocation = useCallback(async (
options?: { enableHighAccuracy?: boolean; timeout?: number }
): Promise<LocationInfo> => {
if (!isNativeEnvironment) {
// 웹 환경에서는 HTML5 Geolocation API 사용
return new Promise((resolve, reject) => {
if (!navigator.geolocation) {
reject(new Error('Geolocation is not supported'));
return;
}
navigator.geolocation.getCurrentPosition(
(position) => {
resolve({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy,
timestamp: position.timestamp
});
},
(error) => reject(error),
{
enableHighAccuracy: options?.enableHighAccuracy || false,
timeout: options?.timeout || 10000
}
);
});
}
const result = await appBridge.safeCall(() => appBridge.getLocation(options));
if (!result) {
throw new Error('Failed to get location');
}
return result;
}, [isNativeEnvironment]);
const getContacts = useCallback(async (): Promise<ContactInfo[]> => {
if (!isNativeEnvironment) {
throw new Error('Contact access is only available in native environment');
}
const result = await appBridge.safeCall(() => appBridge.getContacts(), []);
return result || [];
}, [isNativeEnvironment]);
*/
const shareContent = useCallback(async (content: ShareContent): Promise<void> => { const shareContent = useCallback(async (content: ShareContent): Promise<void> => {
if (!isNativeEnvironment) { if (!isNativeEnvironment) {
// 웹 환경에서는 Web Share API 사용 (지원되는 경우) // 웹 환경에서는 Web Share API 사용 (지원되는 경우)
@@ -318,6 +252,7 @@ export const useAppBridge = (): UseAppBridgeReturn => {
shareContent, shareContent,
logout, logout,
requestToken, requestToken,
requestRefreshToken,
registerBiometric, registerBiometric,
openBiometricRegistrationPopup, openBiometricRegistrationPopup,
closeBiometricRegistrationPopup closeBiometricRegistrationPopup

View File

@@ -130,4 +130,10 @@ main.home-main{
} }
.menu-category{ .menu-category{
position: relative; position: relative;
}
.date-group{
margin-bottom: 0;
}
.pb-86{
padding-bottom: 86px;
} }

View File

@@ -37,6 +37,7 @@ export enum BridgeMessageType {
// 토큰 요청 // 토큰 요청
REQUEST_TOKEN = 'requestToken', REQUEST_TOKEN = 'requestToken',
REQUEST_REFRESH_TOKEN = 'requestRefreshToken',
// 카메라/갤러리 // 카메라/갤러리
OPEN_CAMERA = 'openCamera', OPEN_CAMERA = 'openCamera',

View File

@@ -2,6 +2,7 @@ import axios, { AxiosInstance, AxiosResponse, AxiosError, AxiosRequestConfig } f
import { ApiResponse, ApiErrorResponse, FileUploadProgress, FileUploadResponse } from '@/types'; import { ApiResponse, ApiErrorResponse, FileUploadProgress, FileUploadResponse } from '@/types';
import { tokenManager } from './tokenManager'; import { tokenManager } from './tokenManager';
import config from '@/config'; import config from '@/config';
import { useAppBridge } from '@/hooks';
class ApiClient { class ApiClient {
private instance: AxiosInstance; private instance: AxiosInstance;
@@ -11,6 +12,9 @@ class ApiClient {
reject: (error?: unknown) => void; reject: (error?: unknown) => void;
}> = []; }> = [];
private isNativeEnvironment: boolean;
private requestRefreshToken: () => Promise<any>;
constructor() { constructor() {
this.instance = axios.create({ this.instance = axios.create({
baseURL: config.api.baseURL, baseURL: config.api.baseURL,
@@ -21,7 +25,19 @@ class ApiClient {
}, },
}); });
/*
const {
isNativeEnvironment,
openBiometricRegistrationPopup,
requestRefreshToken,
logout
} = useAppBridge();
this.isNativeEnvironment = isNativeEnvironment;
this.requestRefreshToken = requestRefreshToken;
this.setupInterceptors(); this.setupInterceptors();
*/
} }
private setupInterceptors(): void { private setupInterceptors(): void {
@@ -57,7 +73,14 @@ class ApiClient {
originalRequest._retry = true; originalRequest._retry = true;
this.isRefreshing = true; this.isRefreshing = true;
console.log('refreshToken!!');
/*
this.requestRefreshToken().then((token) => {
console.log('requestRefreshToken +[' + JSON.stringify(token) + ']' );
});
try { try {
const newToken = await this.refreshAccessToken(); const newToken = await this.refreshAccessToken();
this.processQueue(null, newToken); this.processQueue(null, newToken);
@@ -72,6 +95,7 @@ class ApiClient {
} finally { } finally {
this.isRefreshing = false; this.isRefreshing = false;
} }
*/
} }
return Promise.reject(this.handleError(error)); return Promise.reject(this.handleError(error));

View File

@@ -157,6 +157,10 @@ class AppBridge {
return this.sendMessage(BridgeMessageType.REQUEST_TOKEN); return this.sendMessage(BridgeMessageType.REQUEST_TOKEN);
} }
async requestRefreshToken(): Promise<any> {
return this.sendMessage(BridgeMessageType.REQUEST_REFRESH_TOKEN);
}
// 공유 관련 // 공유 관련
async shareContent(content: ShareContent): Promise<void> { async shareContent(content: ShareContent): Promise<void> {
return this.sendMessage(BridgeMessageType.SHARE_CONTENT, content); return this.sendMessage(BridgeMessageType.SHARE_CONTENT, content);

View File

@@ -6,6 +6,7 @@ import {
FooterItemActiveKey FooterItemActiveKey
} from '@/entities/common/model/types'; } from '@/entities/common/model/types';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useAppBridge } from '@/hooks';
export const FooterNavigation = ({ export const FooterNavigation = ({
setMenuOn, setMenuOn,
@@ -14,16 +15,34 @@ export const FooterNavigation = ({
}: FooterProps) => { }: FooterProps) => {
const { navigate } = useNavigate(); const { navigate } = useNavigate();
const [isFooterOn, setIsFooterOn] = useState<boolean>(false); const [isFooterOn, setIsFooterOn] = useState<boolean>(false);
const {
isNativeEnvironment,
openBiometricRegistrationPopup,
requestRefreshToken,
logout
} = useAppBridge();
const onClickToNavigate = (path?: string) => { const onClickToNavigate = (path?: string) => {
if(!!path){ if(!!path){
navigate(path); if(path === PATHS.settlement.list){
callRefresh();
}
else{
navigate(path);
}
} }
}; };
const onClickToOpenMenu = () => { const onClickToOpenMenu = () => {
setFavoriteEdit(false); setFavoriteEdit(false);
setMenuOn(true); setMenuOn(true);
}; };
const callRefresh = () => {
requestRefreshToken().then((token) => {
console.log('requestRefreshToken +[' + JSON.stringify(token) + ']' );
});
}
const buttonItems = [ const buttonItems = [
{ {

View File

@@ -119,9 +119,27 @@ export const SubLayout = () => {
} }
setLoginSuccess(true); setLoginSuccess(true);
setHeaderNavigationKey(headerNavigationKey + 1); setHeaderNavigationKey(headerNavigationKey + 1);
reLogin();
}); });
}; };
const reLogin = () => {
console.log('reLogin');
let userInfo = useStore.getState().UserStore.userInfo;
if(userInfo.accessTokenExpiresIn){
let accessTokenExpiresIn = userInfo.accessTokenExpiresIn;
let timer = setTimeout(() => {
if(isNativeEnvironment){
handleRequestToken();
}
else{
handleLogin();
}
}, accessTokenExpiresIn * 1000);
}
};
const handleLogin = useCallback(async () => { const handleLogin = useCallback(async () => {
const userParmas = { const userParmas = {