Skip to content
Open
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
30 changes: 30 additions & 0 deletions frontend/src/components/@common/RefreshIcon/RefreshIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useTheme } from '@emotion/react';
import { SVGProps } from 'react';

type Props = {
fill?: string;
size?: number;
} & SVGProps<SVGSVGElement>;

const RefreshIcon = ({ fill, size = 22, ...rest }: Props) => {
const theme = useTheme();
const iconFill = fill ?? theme.color.gray[500];

return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...rest}
>
<path
d="M12 6v3l4-4-4-4v3c-4.42 0-8 3.58-8 8 0 1.57.46 3.03 1.24 4.26L6.7 14.8c-.45-.83-.7-1.79-.7-2.8 0-3.31 2.69-6 6-6zm6.76 1.74L17.3 9.2c.44.84.7 1.79.7 2.8 0 3.31-2.69 6-6 6v-3l-4 4 4 4v-3c4.42 0 8-3.58 8-8 0-1.57-.46-3.03-1.24-4.26z"
fill={iconFill}
/>
</svg>
);
};

export default RefreshIcon;
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,44 @@ export const Wrapper = styled.section`
gap: 10px;
`;

export const HeadlineRow = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
`;

export const RandomButton = styled.button`
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
flex-shrink: 0;

height: 32px;
padding: 0 12px;
border: none;
border-radius: 16px;
background: ${({ theme }) => theme.color.point[50]};
cursor: pointer;
transition: all 0.15s ease;

color: ${({ theme }) => theme.color.point[400]};
font-size: 14px;
font-weight: 600;

&:active {
transform: scale(0.95);
background: ${({ theme }) => theme.color.point[100]};
}

&:disabled {
cursor: default;
opacity: 0.5;
transform: none;
}
`;

export const ProgressWrapper = styled.div`
display: flex;
justify-content: flex-end;
Expand Down
47 changes: 45 additions & 2 deletions frontend/src/features/entry/pages/EntryNamePage/EntryNamePage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import useLazyFetch from '@/apis/rest/useLazyFetch';
import BackButton from '@/components/@common/BackButton/BackButton';
import Button from '@/components/@common/Button/Button';
import RefreshIcon from '@/components/@common/RefreshIcon/RefreshIcon';
import Headline3 from '@/components/@common/Headline3/Headline3';
import Input from '@/components/@common/Input/Input';
import ProgressCounter from '@/components/@common/ProgressCounter/ProgressCounter';
Expand All @@ -9,6 +10,7 @@ import { useIdentifier } from '@/contexts/Identifier/IdentifierContext';
import { usePlayerType } from '@/contexts/PlayerType/PlayerTypeContext';
import { useReplaceNavigate } from '@/hooks/useReplaceNavigate';
import Layout from '@/layouts/Layout';
import { useTheme } from '@emotion/react';
import { useRef, useState } from 'react';
import { useRoomManagement } from './hooks/useRoomManagement';
import * as S from './EntryNamePage.styled';
Expand All @@ -19,6 +21,10 @@ type PlayerNameCheckResponse = {
exist: boolean;
};

type RandomNicknameResponse = {
nickname: string;
};

const EntryNamePage = () => {
const [name, setName] = useState('');
const navigate = useReplaceNavigate();
Expand All @@ -27,15 +33,40 @@ const EntryNamePage = () => {
const { showToast } = useToast();
const buttonRef = useRef<HTMLButtonElement>(null);
const { proceedToRoom, isLoading } = useRoomManagement();
const theme = useTheme();

const checkGuestNameQuery = new URLSearchParams({
joinCode: joinCode ?? '',
guestName: name,
}).toString();

const { execute: checkGuestName } = useLazyFetch<PlayerNameCheckResponse>({
endpoint: `/rooms/check-guestName?joinCode=${joinCode}&guestName=${name}`,
endpoint: `/rooms/check-guestName?${checkGuestNameQuery}`,
errorDisplayMode: 'toast',
});

const randomNicknameEndpoint = joinCode
? `/rooms/nickname/random?${new URLSearchParams({ joinCode }).toString()}`
: `/rooms/nickname/random`;

const { execute: fetchRandomNickname, loading: isRandomLoading } =
useLazyFetch<RandomNicknameResponse>({
endpoint: randomNicknameEndpoint,
errorDisplayMode: 'toast',
});

const handleNavigateToHome = () => {
navigate('/');
};

const handleRandomNickname = async () => {
const response = await fetchRandomNickname();
if (response?.nickname) {
const truncatedNickname = response.nickname.slice(0, MAX_NAME_LENGTH);
setName(truncatedNickname);
}
};

const handleProceedToRoom = async () => {
if (playerType === 'GUEST') {
const response = await checkGuestName();
Expand All @@ -60,7 +91,19 @@ const EntryNamePage = () => {
<Layout.TopBar left={<BackButton onClick={handleNavigateToHome} />} />
<Layout.Content>
<S.Container>
<Headline3>닉네임을 입력해주세요</Headline3>
<S.HeadlineRow>
<Headline3>닉네임을 입력해주세요</Headline3>
<S.RandomButton
type="button"
onClick={handleRandomNickname}
disabled={isRandomLoading}
aria-label="닉네임 자동 생성"
data-testid="random-nickname-button"
>
<RefreshIcon size={16} fill={theme.color.point[400]} />
<span>자동 생성</span>
</S.RandomButton>
</S.HeadlineRow>
<S.Wrapper>
<Input
value={name}
Expand Down
Loading