diff --git a/src/components/Home/Home.tsx b/src/components/Home/Home.tsx index a5711c7..8770f4e 100644 --- a/src/components/Home/Home.tsx +++ b/src/components/Home/Home.tsx @@ -15,25 +15,45 @@ export interface ScanResult { } const Home = () => { - const { send } = useAppBridge(); + const { send, receive } = useAppBridge(); const { setScanData } = useScanDataStore(); const { navigateToReceiptEdit } = useRoute(); + const handleScanResult = (jsonData: string) => { + try { + const data: ScanResult[] = JSON.parse(jsonData); + + receive({ + type: AppBridgeMessageType.RECEIVE_SCAN_RESULT, + payload: data, + }); + + setScanData(data); + + navigateToReceiptEdit(); + } catch (error) { + console.error("스캔 결과 JSON 파싱 오류:", error); + alert("스캔 데이터를 처리하는 중 오류가 발생했습니다. 다시 시도해 주세요."); + } + }; + useEffect(() => { - window.response = { - receiveScanResult: (jsonData: string) => { - try { - const data: ScanResult[] = JSON.parse(jsonData); - setScanData(data); - navigateToReceiptEdit(); - } catch (error) { - console.error("Error parsing scan result JSON:", error); - } - }, + const responseHandler = { + receiveScanResult: handleScanResult, }; - }, []); + + if (typeof window !== "undefined") { + window.response = Object.assign({}, window.response, responseHandler); + } + + return () => { + if (typeof window !== "undefined") { + delete window.response; + } + }; + }, [receive, navigateToReceiptEdit, setScanData]); return (
@@ -59,18 +79,6 @@ const Home = () => { iconName="camera" onClick={() => send({ type: AppBridgeMessageType.OPEN_CAMERA, payload: "" })} /> - -
); diff --git a/src/components/provider/AppBridgeProvider/AppBridgeMessage.types.ts b/src/components/provider/AppBridgeProvider/AppBridgeMessage.types.ts index db83f86..8b8d7c2 100644 --- a/src/components/provider/AppBridgeProvider/AppBridgeMessage.types.ts +++ b/src/components/provider/AppBridgeProvider/AppBridgeMessage.types.ts @@ -4,6 +4,8 @@ export enum AppBridgeMessageType { SHARE = "share", CREATE_REVIEW = "createReview", COPY = "copy", + RECEIVE_SCAN_RESULT = "receiveScanResult", + RECEIVE_GENERATED_REVIEW = "receiveGeneratedReview", } export type AppBridgeMessage = @@ -11,7 +13,9 @@ export type AppBridgeMessage = | OpenGalleryMessage | ShareMessage | CreateReviewMessage - | CopyMessage; + | CopyMessage + | ReceiveScanResultMessage + | ReceiveGeneratedReviewMessage; export interface OpenCameraMessage { type: AppBridgeMessageType.OPEN_CAMERA; @@ -43,3 +47,15 @@ export interface CopyMessage { review: string; }; } + +export interface ReceiveScanResultMessage { + type: AppBridgeMessageType.RECEIVE_SCAN_RESULT; + payload: Array<{ [key: string]: string }>; +} + +export interface ReceiveGeneratedReviewMessage { + type: AppBridgeMessageType.RECEIVE_GENERATED_REVIEW; + payload: { + result: string; + }; +} diff --git a/src/components/provider/AppBridgeProvider/AppBridgeProvider.tsx b/src/components/provider/AppBridgeProvider/AppBridgeProvider.tsx index b109d02..eaa10c5 100644 --- a/src/components/provider/AppBridgeProvider/AppBridgeProvider.tsx +++ b/src/components/provider/AppBridgeProvider/AppBridgeProvider.tsx @@ -1,5 +1,5 @@ import type { ReactNode } from "react"; -import { createContext, useContext } from "react"; +import { createContext, useContext, useEffect } from "react"; import type { AppBridgeMessage } from "@/components/provider/AppBridgeProvider/AppBridgeMessage.types"; import { @@ -8,17 +8,22 @@ import { } from "@/components/provider/AppBridgeProvider/convertToNativeMessage"; import { useUserAgent } from "@/components/provider/UserAgentProvider"; +import { useScanDataStore } from "@/store/useScanDataStore"; + interface AppBridgeProviderProps { children: ReactNode; } interface AppBridge { send: (message: AppBridgeMessage) => void; + receive: (message: AppBridgeMessage) => void; } export const AppBridgeContext = createContext(null); export function AppBridgeProvider({ children }: AppBridgeProviderProps) { + const { setScanData } = useScanDataStore(); + const userAgent = useUserAgent(); const isIOS = userAgent.isIOS; @@ -32,7 +37,41 @@ export function AppBridgeProvider({ children }: AppBridgeProviderProps) { } }; - return {children}; + const receive = (message: AppBridgeMessage) => { + try { + if (isIOS) return convertToIOSAppBridge(message); + return convertToAndroidAppBridge(message); + } catch { + alert("App Bridge API called: " + message.type); + } + }; + + useEffect(() => { + if (typeof window !== "undefined") { + window.response = { + receiveScanResult: (jsonData: string) => { + try { + const data = JSON.parse(jsonData); + setScanData(data); + } catch (error) { + console.error("Invalid JSON data for scan result:", error); + } + }, + receiveGeneratedReview: (jsonData: string) => { + try { + const data = JSON.parse(jsonData); + alert(`Generated Review: ${data.review}`); + } catch (error) { + console.error("Invalid JSON data for generated review:", error); + } + }, + }; + } + }, []); + + return ( + {children} + ); } export function useAppBridge() { diff --git a/src/components/provider/AppBridgeProvider/convertToNativeMessage.ts b/src/components/provider/AppBridgeProvider/convertToNativeMessage.ts index 4844dc2..4c5e8c4 100644 --- a/src/components/provider/AppBridgeProvider/convertToNativeMessage.ts +++ b/src/components/provider/AppBridgeProvider/convertToNativeMessage.ts @@ -12,6 +12,10 @@ const iosHandlers = { window.webkit?.messageHandlers.createReview.postMessage(message.payload.json), [AppBridgeMessageType.COPY]: (message: { payload: { json: string } }) => window.webkit?.messageHandlers.copy.postMessage(message.payload.json), + [AppBridgeMessageType.RECEIVE_SCAN_RESULT]: (message: { payload: { result: string } }) => + window.response?.receiveScanResult(message.payload.result), + [AppBridgeMessageType.RECEIVE_GENERATED_REVIEW]: (message: { payload: { result: string } }) => + window.response?.receiveGeneratedReview(message.payload.result), }; const androidHandlers = { @@ -22,6 +26,10 @@ const androidHandlers = { window.AndroidBridge?.createReview(message.payload.json), [AppBridgeMessageType.COPY]: (message: { payload: { json: string } }) => window.AndroidBridge?.copy(message.payload.json), + [AppBridgeMessageType.RECEIVE_SCAN_RESULT]: (message: { payload: { result: string } }) => + window.response?.receiveScanResult(message.payload.result), + [AppBridgeMessageType.RECEIVE_GENERATED_REVIEW]: (message: { payload: { result: string } }) => + window.response?.receiveGeneratedReview(message.payload.result), }; export function convertToIOSAppBridge(message: AppBridgeMessage) { diff --git a/src/components/provider/WebBridgeProvider/WebBridgeMessage.types.ts b/src/components/provider/WebBridgeProvider/WebBridgeMessage.types.ts deleted file mode 100644 index 1bed8c0..0000000 --- a/src/components/provider/WebBridgeProvider/WebBridgeMessage.types.ts +++ /dev/null @@ -1,9 +0,0 @@ -export enum WebBridgeMessageType { - RECEIVE_SCAN_RESULT = "RECEIVE_SCAN_RESULT", -} - -export type WebBridgeMessage = ReceiveScanResultMessage; - -export interface ReceiveScanResultMessage { - type: WebBridgeMessageType.RECEIVE_SCAN_RESULT; -} diff --git a/src/components/provider/WebBridgeProvider/WebBridgeProvider.tsx b/src/components/provider/WebBridgeProvider/WebBridgeProvider.tsx deleted file mode 100644 index b87b997..0000000 --- a/src/components/provider/WebBridgeProvider/WebBridgeProvider.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import type { ReactNode } from "react"; -import { createContext, useContext, useEffect } from "react"; - -interface WebBridgeMessage { - type: string; - payload?: unknown; -} - -interface WebBridge { - receive: (message: WebBridgeMessage) => void; -} - -interface WebBridgeProviderProps { - children: ReactNode; -} - -export const WebBridgeContext = createContext(null); - -export function WebBridgeProvider({ children }: WebBridgeProviderProps) { - const receive = (message: WebBridgeMessage) => { - try { - if (typeof window !== "undefined" && window.response) { - if (typeof window.response.receiveScanResult === "function") { - window.response.receiveScanResult(JSON.stringify(message.payload)); - } else { - console.warn("window.response.receiveScanResult is not available."); - } - } - } catch (error) { - console.error("WebBridge API call failed:", error); - } - }; - - useEffect(() => { - if (typeof window !== "undefined" && !window.response) { - window.response = { - receiveScanResult: (jsonData: string) => { - console.log("Received scan result:", jsonData); - }, - }; - } - }, []); - - return {children}; -} - -export function useWebBridge() { - const webBridge = useContext(WebBridgeContext); - - if (webBridge == null) { - throw new Error("Wrap Web Bridge Provider"); - } - - return webBridge; -} diff --git a/src/main.tsx b/src/main.tsx index 92ac95a..3a49d45 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -8,7 +8,6 @@ import AppRouter from "@/router/AppRouter"; import { AppBridgeProvider } from "@/components/provider/AppBridgeProvider/AppBridgeProvider"; import ReactQueryClientProvider from "@/components/provider/ReactQueryClientProvider"; import { UserAgentProvider } from "@/components/provider/UserAgentProvider"; -import { WebBridgeProvider } from "@/components/provider/WebBridgeProvider/WebBridgeProvider"; import "@/styles/reset.scss"; import "@/styles/global.scss"; @@ -18,10 +17,8 @@ ReactDom.createRoot(document.getElementById("root")!).render( - - - - + + diff --git a/src/types/global.d.ts b/src/types/global.d.ts index c8e9919..b3924ce 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -13,6 +13,7 @@ declare global { interface Window { response?: { receiveScanResult: (jsonData: string) => void; + receiveGeneratedReview: (jsonData: string) => void; }; webkit?: { messageHandlers: {