Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 25 additions & 17 deletions src/components/Home/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useEffect } from "react";

import styles from "@/components/Home/Home.module.scss";
import { AppBridgeMessageType } from "@/components/provider/AppBridgeProvider/AppBridgeMessage.types";
Expand All @@ -8,34 +8,31 @@ import Text from "@/components/ui/Text/Text";

import { useRoute } from "@/hooks/common/useRoute";

import { useScanDataStore } from "@/store/useScanDataStore";

export interface ScanResult {
[key: string]: string;
}

const Home = () => {
const { send } = useAppBridge();

interface ScanResult {
[key: string]: string;
}
const { setScanData } = useScanDataStore();

// const [results, setResults] = useState<ScanResult[]>([]);
const [isSuccess, setIsSuccess] = useState<boolean>(false);
const { navigateToReceiptEdit } = useRoute();

useEffect(() => {
if (typeof window !== "undefined") {
window.response =
window.response || ({} as { receiveScanResult: (jsonData: string) => void });

window.response.receiveScanResult = (jsonData: string) => {
window.response = {
receiveScanResult: (jsonData: string) => {
try {
const data: ScanResult[] = JSON.parse(jsonData);
// setResults(data);
console.log(data);
setIsSuccess(true);
setScanData(data);
navigateToReceiptEdit();
} catch (error) {
console.error("Error parsing scan result JSON:", error);
}
};
}
},
};
}, []);

return (
Expand All @@ -51,7 +48,6 @@ const Home = () => {
<div className={styles.HomeImage}>
<img src="/assets/img/img-graphic-logo.png" alt="mainLogo" />
</div>
{isSuccess && <div>성공</div>}
<div className={styles.HomeBottom}>
<IconButton
text="갤러리"
Expand All @@ -63,6 +59,18 @@ const Home = () => {
iconName="camera"
onClick={() => send({ type: AppBridgeMessageType.OPEN_CAMERA, payload: "" })}
/>

<button
onClick={() => {
if (window.response) {
window.response.receiveScanResult(
JSON.stringify([{ sampleKey: "sampleValue" }, { sampleKey2: "sampleValue2" }]),
);
}
}}
>
테스트 데이터 전송
</button>
</div>
</div>
);
Expand Down
116 changes: 74 additions & 42 deletions src/components/ReceiptEdit/ReceiptEdit.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,67 @@
import { useState } from "react";
import { useEffect, useState } from "react";

import styles from "@/components/ReceiptEdit/ReceiptEdit.module.scss";
import Button from "@/components/ui/Button/Button";
import Input from "@/components/ui/Input/Input";
import Text from "@/components/ui/Text/Text";

import { useFocus } from "@/hooks/common/useFocus";
import { useRoute } from "@/hooks/common/useRoute";

import { useCreateReviewStore } from "@/store/useReviewStore";
import { useScanDataStore } from "@/store/useScanDataStore";

const ReceiptEdit = () => {
const { navigateToSelectTag } = useRoute();

const { scanData } = useScanDataStore();

const { setOcrText } = useCreateReviewStore();

const [placeName, setPlaceName] = useState("청담커피 앤 토스트");
const [foodName, setFoodName] = useState("카야토스트+음료세트");
const [formData, setFormData] = useState<{ [key: string]: string }[]>([]);
const [focusState, setFocusState] = useState<{ [key: string]: boolean }>({});

useEffect(() => {
if (Array.isArray(scanData) && scanData.length > 0) {
setFormData(scanData);

const initialFocusState = scanData.reduce(
(acc, data) => {
const keys = Object.keys(data);
keys.forEach((key) => (acc[key] = false));
return acc;
},
{} as { [key: string]: boolean },
);
setFocusState(initialFocusState);
}
}, [scanData]);

const {
isFocus: isPlaceFocus,
onFocus: handlePlaceFocus,
onBlur: handlePlaceBlur,
} = useFocus({});
const { isFocus: isFoodFocus, onFocus: handleFoodFocus, onBlur: handleFoodBlur } = useFocus({});
const handleFocus = (key: string) => {
setFocusState((prevState) => ({ ...prevState, [key]: true }));
};

const handleBlur = (key: string) => {
setFocusState((prevState) => ({ ...prevState, [key]: false }));
};

const handleInputChange = (index: number, key: string, value: string) => {
setFormData((prevData) =>
prevData.map((item, idx) => (idx === index ? { ...item, [key]: value } : item)),
);
};

const handleInfoRightClick = () => {
const ocrText = `${placeName} ${foodName}`;
setOcrText(ocrText);
const formattedText =
formData &&
formData
.map((item) =>
Object.entries(item)
.map(([key, value]) => `${key} ${value}`)
.join(", "),
)
.join(" ");

setOcrText(formattedText);

navigateToSelectTag();
};
Expand All @@ -37,7 +71,9 @@ const ReceiptEdit = () => {
<div className={styles.Top}>
<div className={styles.TitleBox}>
<Text variant="titleM" color="primary" as="h1" truncated>
{placeName}
{formData.length > 0 &&
Object.keys(formData[0]).length > 0 &&
formData[0][Object.keys(formData[0])[0]]}
</Text>
<Text variant="titleM" color="primary" as="h1">
Expand All @@ -48,44 +84,40 @@ const ReceiptEdit = () => {
</Text>

<div className={styles.InfoList}>
<div className={styles.InfoItem}>
<Text variant="bodyXsm" color="secondary">
가게명
</Text>
<Input
placeholder="가게 이름"
value={placeName}
onChange={(e) => setPlaceName(e.target.value)}
onFocus={handlePlaceFocus}
onBlur={handlePlaceBlur}
isFocus={isPlaceFocus}
/>
</div>
<div className={styles.InfoItem}>
<Text variant="bodyXsm" color="secondary">
음식명
</Text>
<Input
placeholder="음식 이름"
value={foodName}
onChange={(e) => setFoodName(e.target.value)}
onFocus={handleFoodFocus}
onBlur={handleFoodBlur}
isFocus={isFoodFocus}
/>
</div>
{formData.map((data, index) => (
<div key={index} className={styles.InfoItem}>
{Object.keys(data).map((key) => (
<div key={key} className={styles.InfoItem}>
<Text variant="bodyXsm" color="secondary">
{key}
</Text>
<Input
placeholder={`${key} 입력`}
value={data[key]}
onFocus={() => handleFocus(key)}
onBlur={() => handleBlur(key)}
isFocus={focusState[key] || false}
onChange={(e) => handleInputChange(index, key, e.target.value)}
/>
</div>
))}
</div>
))}
</div>
</div>

<div className={styles.Bottom}>
{isPlaceFocus || isFoodFocus ? (
<Button text="수정하기" disabled={!placeName || !foodName} />
{Object.values(focusState).some((isFocus) => isFocus) ? (
<Button
text="수정하기"
disabled={formData.some((item) => Object.values(item).some((value) => !value))}
/>
) : (
<>
<Button text="다시 스캔하기" variant="secondary" />
<Button
text="정보가 맞아요"
disabled={!placeName || !foodName}
disabled={formData.some((item) => Object.values(item).some((value) => !value))}
onClick={handleInfoRightClick}
/>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ const iosHandlers = {
};

const androidHandlers = {
[AppBridgeMessageType.OPEN_CAMERA]: (message: string) =>
window.AndroidBridge?.openCamera(message),
[AppBridgeMessageType.OPEN_GALLERY]: (message: string) =>
window.AndroidBridge?.openGallery(message),
[AppBridgeMessageType.SHARE]: (message: string) => window.AndroidBridge?.share(message),
[AppBridgeMessageType.OPEN_CAMERA]: () => window.AndroidBridge?.openCamera(),
[AppBridgeMessageType.OPEN_GALLERY]: () => window.AndroidBridge?.openGallery(),
[AppBridgeMessageType.SHARE]: () => window.AndroidBridge?.share(),
[AppBridgeMessageType.CREATE_REVIEW]: (message: { payload: { json: string } }) =>
window.AndroidBridge?.createReview(message.payload.json),
[AppBridgeMessageType.COPY]: (message: { payload: { json: string } }) =>
Expand Down
17 changes: 7 additions & 10 deletions src/components/provider/WebBridgeProvider/WebBridgeProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import type { ReactNode } from "react";
import { createContext, useContext, useEffect } from "react";

declare global {
interface Window {
response: {
receiveScanResult: (jsonData: string) => void;
};
}
}
// window.response = window.response || {};

interface WebBridgeMessage {
type: string;
payload?: unknown;
Expand Down Expand Up @@ -41,7 +32,13 @@ export function WebBridgeProvider({ children }: WebBridgeProviderProps) {
};

useEffect(() => {
window.response = window.response || {};
if (typeof window !== "undefined" && !window.response) {
window.response = {
receiveScanResult: (jsonData: string) => {
console.log("Received scan result:", jsonData);
},
};
}
}, []);

return <WebBridgeContext.Provider value={{ receive }}>{children}</WebBridgeContext.Provider>;
Expand Down
13 changes: 13 additions & 0 deletions src/store/useScanDataStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { create } from "zustand";

import type { ScanResult } from "@/components/Home/Home";

interface ScanDataStoreProps {
scanData: ScanResult[];
setScanData: (scanData: ScanResult[]) => void;
}

export const useScanDataStore = create<ScanDataStoreProps>((set) => ({
scanData: [],
setScanData: (scanData: ScanResult[]) => set({ scanData }),
}));
9 changes: 6 additions & 3 deletions src/types/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ type MessageHandler<T = void> = {

declare global {
interface Window {
response?: {
receiveScanResult: (jsonData: string) => void;
};
webkit?: {
messageHandlers: {
openCamera: MessageHandler<string>;
Expand All @@ -21,9 +24,9 @@ declare global {
};
};
AndroidBridge?: {
openCamera: (request: string) => void;
openGallery: (request: string) => void;
share: (request: string) => void;
openCamera: () => void;
openGallery: () => void;
share: () => void;
createReview: (json: string) => void;
copy: (json: string) => void;
};
Expand Down