Skip to content

Commit

Permalink
Merge pull request #39 from causyj/dev/frameImg
Browse files Browse the repository at this point in the history
[DEV] frame 페이지 이미지 불러오기
  • Loading branch information
yugenius0213 authored Oct 15, 2024
2 parents 18e9b54 + b9e43d9 commit 3f4f0cf
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 76 deletions.
20 changes: 20 additions & 0 deletions src/app/api/waiting/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { NextRequest, NextResponse } from 'next/server';

export const GET = async (request: NextRequest) => {
const { searchParams } = new URL(request.url);
const searchCode = searchParams.get('code') || '';

const response = await fetch(
`${process.env.NEXT_PUBLIC_SERVER_ADDRESS}:${process.env.NEXT_PUBLIC_SERVER_PORT}/api/photo-request/status`,
{
headers: {
Authorization: `Bearer ${searchCode}`,
},
},
);
const jsonData = await response.json();

return NextResponse.json(jsonData, {
status: jsonData.code,
});
};
1 change: 0 additions & 1 deletion src/app/concept/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {

export default function SelectConceptView() {
const [selectedBox, setSelectedBox] = useAtom(selectedBoxAtom);

const handleBoxClick = (box: GenderStateUnion) => {
if (selectedBox === box) {
setSelectedBox(gender.none);
Expand Down
43 changes: 30 additions & 13 deletions src/app/frame/page.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,45 @@
'use client';

import { useAtom } from 'jotai';
import dynamic from 'next/dynamic';
import Image from 'next/image';
import Link from 'next/link';
import { useState } from 'react';
import PreviousPage from '@/components/PreviousPage';
import { BASIC_FRAME_DATA, PREMIUM_FRAME_DATA } from '@/constants';
import { ROUTE_TYPES } from '@/interfaces';
import {
createdPhotoAtomWithStorage,
selectedPhotoAtomWithStorage,
} from '@/store/atoms/atomWithStorage';
import SVGDownload from '@/styles/icons/download.svg';
import SVGGoToList from '@/styles/icons/gotolist.svg';
import DownloadImage from '@/utils/DownloadImage';
import SelectFrame from './_components/SelectFrame';

export default function FrameSelectView() {
export function FrameSelectView() {
const [colorOfCircle, setColorOfCircle] = useState<string>('');
const [isPremiumSelected, setIsPremiumSelected] = useState<boolean>(false);

const [selectedPhoto, setSelectedPhoto] = useAtom(
selectedPhotoAtomWithStorage,
);
const frametype = isPremiumSelected ? PREMIUM_FRAME_DATA : BASIC_FRAME_DATA;
const frameWidth = colorOfCircle === '/premiumframe2.png' ? 283 : 239;
const frameHeight = colorOfCircle === '/premiumframe2.png' ? 332 : 290;
const imageSrc = '/resultsample.png'; // 생성된 이미지(임시)
const [createdPhoto] = useAtom(createdPhotoAtomWithStorage);
const imageSrc = selectedPhoto === '' ? createdPhoto : selectedPhoto;

const onCaptureClick = () => {
DownloadImage({ colorOfCircle, imageSrc, frameWidth, frameHeight });
};

return (
<div className="flex w-full flex-col items-center justify-center">
<div className="flex w-full flex-row justify-between px-4">
<PreviousPage target={ROUTE_TYPES.HOME} />
<PreviousPage
target={ROUTE_TYPES.HOME}
onClick={() => setSelectedPhoto('')}
/>

<Link href="/list" className="pr-4">
<SVGGoToList />
</Link>
Expand Down Expand Up @@ -56,14 +68,16 @@ export default function FrameSelectView() {
<div
className={`relative flex ${colorOfCircle === '/premiumframe2.png' ? 'h-[332px] w-[283px]' : 'h-[290px] w-[239px]'} justify-center`}
>
<Image
src={imageSrc}
alt="Sample Image"
width={206}
height={206}
priority
className="absolute mt-4"
/>
{imageSrc && (
<Image
src={imageSrc}
alt="Sample Image"
width={206}
height={206}
priority
className="absolute mt-4"
/>
)}
{colorOfCircle && colorOfCircle !== '' && (
<Image
src={colorOfCircle}
Expand Down Expand Up @@ -99,3 +113,6 @@ export default function FrameSelectView() {
</div>
);
}
export default dynamic(() => Promise.resolve(FrameSelectView), {
ssr: false,
});
9 changes: 4 additions & 5 deletions src/app/list/_components/ImageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,25 @@ import Image from 'next/image';
import MyButton from '@/components/MyButton';
import { ROUTE_TYPES } from '@/interfaces';
import { ListWithDataProps } from '@/interfaces/profile-list.interface';
import { selectedPhotoAtom } from '@/store/atoms/selectedPhotoAtom';
import { selectedPhotoAtomWithStorage } from '@/store/atoms/atomWithStorage';

export default function ImageList({ list }: ListWithDataProps): JSX.Element {
const [selectedPhoto, setSelectedPhoto] = useAtom(selectedPhotoAtom);

const [selectedPhoto, setSelectedPhoto] = useAtom(
selectedPhotoAtomWithStorage,
);
const handlePhotoClick = (selectTime: string) => {
if (selectedPhoto === selectTime) {
setSelectedPhoto('');
} else {
setSelectedPhoto(selectTime);
}
};

return (
<div>
<p className="mb-5 text-center font-sfpro text-sm text-white">
편집할 사진을 선택해서 <br />
이미지 프레임을 선택해 주세요!
</p>

<div className="h-96 overflow-y-auto">
<div className="grid grid-cols-2 gap-4">
{list.map((item) => (
Expand Down
34 changes: 26 additions & 8 deletions src/app/list/page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
'use client';

import { useSetAtom } from 'jotai/index';
import dynamic from 'next/dynamic';
import { useEffect, useState } from 'react';
import PreviousPage from '@/components/PreviousPage';
import { LOGIN_ERROR_CHECK_MSG, LOGIN_ERROR_MSG } from '@/constants';
import {
GENERATION_ERROR_CHECK_MSG,
IMG_LIST_ERROR_MSG,
NO_GENERATED_IMAGE_MSG,
} from '@/constants';
import { createdPhotoAtomWithStorage } from '@/store/atoms/atomWithStorage';
import {
errorCheckMessageAtom,
errorMessageAtom,
} from '@/store/atoms/errorMessageAtom';
import EmptyList from './_components/EmptyList';
import ImageList from './_components/ImageList';

export default function ListView() {
export function ListView() {
const [list, setList] = useState<string[]>([]);
const [loading, setLoading] = useState(false);
const setErrorMessage = useSetAtom(errorMessageAtom);
const setErrorCheckMessage = useSetAtom(errorCheckMessageAtom);

const setCreatedPhoto = useSetAtom(createdPhotoAtomWithStorage);
const storedToken = window.sessionStorage.getItem('accessToken') || '';

useEffect(() => {
Expand All @@ -28,18 +34,27 @@ export default function ListView() {
.then(async (data) => {
if (data.code === 200) {
setList(data.data);
if (data.data.length > 0) {
setCreatedPhoto(data.data[0]);
}
} else {
setErrorMessage(LOGIN_ERROR_MSG);
setErrorCheckMessage(LOGIN_ERROR_CHECK_MSG);
setErrorMessage(NO_GENERATED_IMAGE_MSG);
setErrorCheckMessage(GENERATION_ERROR_CHECK_MSG);
}
})
.catch(() => {
setErrorMessage(LOGIN_ERROR_MSG);
setErrorCheckMessage(LOGIN_ERROR_CHECK_MSG);
setErrorMessage(IMG_LIST_ERROR_MSG);
setErrorCheckMessage(GENERATION_ERROR_CHECK_MSG);
})
.finally(() => setLoading(false));
}
}, [storedToken, setErrorCheckMessage, setErrorMessage, setList]);
}, [
setCreatedPhoto,
setErrorCheckMessage,
setErrorMessage,
setList,
storedToken,
]);

return (
<div className="flex w-full flex-col justify-start bg-background">
Expand All @@ -52,3 +67,6 @@ export default function ListView() {
</div>
);
}
export default dynamic(() => Promise.resolve(ListView), {
ssr: false,
});
138 changes: 92 additions & 46 deletions src/app/waiting/page.tsx
Original file line number Diff line number Diff line change
@@ -1,74 +1,120 @@
'use client';

import { useSetAtom } from 'jotai';
import dynamic from 'next/dynamic';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { useEffect, useRef, useState } from 'react';
import CopyToClipboard from 'react-copy-to-clipboard';
import {
GENERATION_ERROR_CHECK_MSG,
GENERATION_STATUS_ERROR_MSG,
IMG_NOT_READY_MSG,
} from '@/constants';
import { ROUTE_TYPES } from '@/interfaces';
import {
errorCheckMessageAtom,
errorMessageAtom,
} from '@/store/atoms/errorMessageAtom';
import SVGLink from '@/styles/icons/link.svg';

export default function WaitingView() {
export function WaitingView() {
const [url, setUrl] = useState<string>('');
const copyRef = useRef<HTMLButtonElement>(null);
const setErrorMessage = useSetAtom(errorMessageAtom);
const setErrorCheckMessage = useSetAtom(errorCheckMessageAtom);
const [photoMade, setPhotoMade] = useState<string | null>(null);
const router = useRouter();
const storedToken = window.sessionStorage.getItem('accessToken') || '';

useEffect(() => {
if (typeof window !== 'undefined') {
setUrl(window.location.href);
}
setUrl(window.location.href);
}, []);

const copyToClipboardFunc = () => {
if (copyRef.current) {
copyRef.current.click();
}
};
useEffect(() => {
if (storedToken !== '') {
fetch(
`${process.env.NEXT_PUBLIC_CLIENT_ADDRESS}:${process.env.NEXT_PUBLIC_CLIENT_PORT}/api/status?code=${storedToken}`,
)
.then((response) => response.json())
.then(async (data) => {
if (data.code === 200) {
setPhotoMade(data.data);
} else {
setErrorMessage(IMG_NOT_READY_MSG);
setErrorCheckMessage(GENERATION_ERROR_CHECK_MSG);
}
})
.catch(() => {
setErrorMessage(GENERATION_STATUS_ERROR_MSG);
setErrorCheckMessage(GENERATION_ERROR_CHECK_MSG);
});
}
}, [setErrorCheckMessage, setErrorMessage, storedToken]);

useEffect(() => {
if (photoMade !== null) {
router.push(ROUTE_TYPES.FRAME);
}
}, [photoMade, router]);
return (
<div className="flex w-full flex-col items-center gap-y-8">
<div className="text-center text-xl">
프로필 생성 중 <br />
조금만 기다려 주세요!
</div>

<Image
src="/unlocked-profile.png"
alt="Sample Image"
width={205}
height={205}
priority
/>
<div>
<div className="flex w-full flex-col items-center gap-y-8">
<div className="text-center text-xl">
프로필 생성 중 <br />
조금만 기다려 주세요!
</div>

<div className="text-center font-sfpro text-2xs font-bold text-white">
AI 프로필이 완성되면 <br />
이메일로 알림을 보내드립니다
</div>
<Image
src="/unlocked-profile.png"
alt="Sample Image"
width={205}
height={205}
priority
/>

<div className="flex h-[124px] w-full flex-col items-center gap-y-2 rounded-[20px] bg-white p-4 text-center">
<div className="font-sfpro text-2xs font-bold">
링크를 복사해두면 완성된 프로필을 <br />더 편하게 받아볼 수 있어요
<div className="text-center font-sfpro text-2xs font-bold text-white">
AI 프로필이 완성되면 <br />
이메일로 알림을 보내드립니다
</div>
<button
type="button"
onClick={copyToClipboardFunc}
className="flex h-12 flex-row items-center justify-center gap-1 rounded-full bg-primary-darkblue px-6 font-cafe24 text-xl text-white"
>
링크복사
<SVGLink />
</button>
{url && (
<CopyToClipboard
text={url}
onCopy={() => {
alert('클립보드에 복사되었습니다.');
}}

<div className="flex h-[124px] w-full flex-col items-center gap-y-2 rounded-[20px] bg-white p-4 text-center">
<div className="font-sfpro text-2xs font-bold">
링크를 복사해두면 완성된 프로필을 <br />더 편하게 받아볼 수 있어요
</div>
<button
type="button"
onClick={copyToClipboardFunc}
className="flex h-12 flex-row items-center justify-center gap-1 rounded-full bg-primary-darkblue px-6 font-cafe24 text-xl text-white"
>
<button
type="button"
ref={copyRef}
style={{ display: 'none' }}
aria-label="링크 복사 버튼"
/>
</CopyToClipboard>
)}
링크복사
<SVGLink />
</button>
{url && (
<CopyToClipboard
text={url}
onCopy={() => {
alert('클립보드에 복사되었습니다.');
}}
>
<button
type="button"
ref={copyRef}
style={{ display: 'none' }}
aria-label="링크 복사 버튼"
/>
</CopyToClipboard>
)}
</div>
</div>
</div>
);
}
export default dynamic(() => Promise.resolve(WaitingView), {
ssr: false,
});
4 changes: 2 additions & 2 deletions src/components/PreviousPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import Link from 'next/link';
import { PreviousPageProps } from '@/interfaces';
import SVGPrevious from '@/styles/icons/previous.svg';

export default function PreviousPage({ target }: PreviousPageProps) {
export default function PreviousPage({ target, onClick }: PreviousPageProps) {
return (
<Link href={target}>
<Link href={target} onClick={onClick}>
<SVGPrevious />
</Link>
);
Expand Down
Loading

0 comments on commit 3f4f0cf

Please sign in to comment.