diff --git a/.gitignore b/.gitignore index f579352..2391545 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ dist db .vscode out -.vite_cache \ No newline at end of file +.vite_cache +.codegpt \ No newline at end of file diff --git a/packages/page/src/api/dto/letter.dto.ts b/packages/page/src/api/dto/letter.dto.ts index 4a65dac..8ae8633 100644 --- a/packages/page/src/api/dto/letter.dto.ts +++ b/packages/page/src/api/dto/letter.dto.ts @@ -12,6 +12,10 @@ export interface MetaDetail extends MetaDefault { angle?: string; } +export interface MetaText extends MetaDefault { + font: string; +} + export interface PrepareRequest { thumbnailMeta: MetaDefault; @@ -20,6 +24,8 @@ export interface PrepareRequest { backgroundMeta: MetaDefault; componentMetas: MetaDetail[]; + + textMetas: MetaText[]; } export interface PrepareResponse { @@ -27,6 +33,7 @@ export interface PrepareResponse { letterUrl: string; backgroundUrl: string; componentUrls: string[]; + textUrls: string[]; expires: number; sessionKey: string; } diff --git a/packages/page/src/api/image.api.ts b/packages/page/src/api/image.api.ts index 9f06eba..1845ec7 100644 --- a/packages/page/src/api/image.api.ts +++ b/packages/page/src/api/image.api.ts @@ -3,14 +3,10 @@ import apiClient from '../common/http.client'; import ApiResponse from '../common/response'; import useErrorStore from '../store/error.store'; -interface GetPresignedUrlResponse {} - const useImageApi = () => { - const [presignedUrl, setPresignedUrl] = useState(); + const [presignedUrl, setPresignedUrl] = useState(); const getPresignedUrl = async (path: string) => { - const response = await apiClient.get>( - `/image/${path}`, - ); + const response = await apiClient.get>(`/image/${path}`); setPresignedUrl(response.data); return response.data; }; diff --git a/packages/page/src/components/image/presigned/presigned.image.tsx b/packages/page/src/components/image/presigned/presigned.image.tsx index ea0ef84..a685649 100644 --- a/packages/page/src/components/image/presigned/presigned.image.tsx +++ b/packages/page/src/components/image/presigned/presigned.image.tsx @@ -1,40 +1,167 @@ import useImageApi from '@/api/image.api'; -import { Image } from '@mantine/core'; -import { useEffect } from 'react'; +import { Container, Image, Text } from '@mantine/core'; +import useMoveResize from '../move/move.resize.hook'; +import { useEffect, useState } from 'react'; interface PresignedImageProps { path: string; - width?: string; - height?: string; + width?: string | number; + height?: string | number; position?: 'relative' | 'absolute' | 'fixed'; - x?: string; - y?: string; + x?: string | number; + y?: string | number; + movable?: boolean; + onUpdate?: (data: { + size: { width: number; height: number }; + position: { x: number; y: number }; + }) => void; } const PresignedImage = ({ path, width = '100%', height = '100%', - position, - x, - y, + position = 'relative', + x = 0, + y = 0, + movable = false, + onUpdate, }: PresignedImageProps) => { const { presignedUrl, getPresignedUrl } = useImageApi(); + const { size, position: pos, handleMouseDown, init } = useMoveResize(); + const [contentType, setContentType] = useState(''); + const [textContent, setTextContent] = useState(''); + useEffect(() => { - if (path) { - getPresignedUrl(path); - } + const fetchData = async () => { + if (path) { + const url = await getPresignedUrl(path); + if (!url) return; + const response = await fetch(url, { + method: 'GET', + }); + const type = response.headers.get('Content-Type') || ''; + setContentType(type); + if (type === 'text/plain') { + const text = await response.text(); + setTextContent(text); + } + } + }; + fetchData(); }, [path]); + + const extractNumber = (value: string | number) => { + if (typeof value === 'string') { + const match = value.match(/(\d+)/); + return match ? parseInt(match[0], 10) : 0; + } + return value; + }; + + const initialX = extractNumber(x); + const initialY = extractNumber(y); + + useEffect(() => { + if (movable) { + const initialWidth = typeof width === 'string' ? 200 : (width as number); + const initialHeight = + typeof height === 'string' ? 200 : (height as number); + const initialX = extractNumber(x); + const initialY = extractNumber(y); + init( + { width: initialWidth, height: initialHeight }, + { x: initialX, y: initialY }, + ); + } + }, [movable]); + + useEffect(() => { + if (movable && onUpdate) { + onUpdate({ size, position: pos }); + } + }, [size, pos]); + + const renderContent = () => { + if (contentType === 'text/plain') { + return ( + + {textContent} + + ); + } + + return ( + {path} + ); + }; + + if (!movable) { + if (contentType === 'text/plain') { + return ( + + {textContent} + + ); + } + + return ( + + ); + } + return ( - + + {renderContent()} +
+ ); }; diff --git a/packages/page/src/pages/page/create.tsx b/packages/page/src/pages/page/create.tsx index 0b78fd2..48973ba 100644 --- a/packages/page/src/pages/page/create.tsx +++ b/packages/page/src/pages/page/create.tsx @@ -96,13 +96,32 @@ const CreatePage = () => { 'x-amz-meta-height': `${files[idx].size.height}`, 'x-amz-meta-width': `${files[idx].size.width}`, 'x-amz-meta-angle': '0', - 'x-amz-meta-z': '0', + 'x-amz-meta-z': `${idx}`, 'x-amz-meta-x': `${files[idx].position.x}`, 'x-amz-meta-y': `${files[idx].position.y}`, }, }); }), ); + + await Promise.all( + prepareUrls.textUrls.map(async (textUrl, idx) => { + await fetch(textUrl, { + method: 'PUT', + body: texts[idx].text, + headers: { + 'Content-Type': 'text/plain', + 'x-amz-meta-session': prepareUrls.sessionKey, + 'x-amz-meta-height': `${texts[idx].size.height}`, + 'x-amz-meta-width': `${texts[idx].size.width}`, + 'x-amz-meta-angle': '0', + 'x-amz-meta-z': `${idx}`, + 'x-amz-meta-x': `${texts[idx].position.x}`, + 'x-amz-meta-y': `${texts[idx].position.y}`, + }, + }); + }), + ); postAddLetter({ category: 'LT001', title: '테스트', @@ -152,6 +171,17 @@ const CreatePage = () => { angle: '0', }; }), + textMetas: textData.map((text, idx) => { + return { + width: text.size.width.toString(), + height: text.size.height.toString(), + font: 'Noto Sans KR', + x: text.position.x.toString(), + y: text.position.y.toString(), + z: idx.toString(), + angle: '0', + }; + }), }); return result; }; diff --git a/packages/page/src/pages/page/letter/modify/[id].tsx b/packages/page/src/pages/page/letter/modify/[id].tsx index 61fc5cf..8022fd3 100644 --- a/packages/page/src/pages/page/letter/modify/[id].tsx +++ b/packages/page/src/pages/page/letter/modify/[id].tsx @@ -32,6 +32,7 @@ const ModifyLetterPage = () => { /> {letterDetail.components?.map((component) => (