Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Deploy] Ver1.0.0 릴리즈 #361

Merged
merged 12 commits into from
Dec 14, 2023
138 changes: 61 additions & 77 deletions README.md

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions packages/client/src/pages/Home/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,10 @@ export default function Home() {
<>
<Outlet />
{status === 'new' && <CoachMarker isFirst={true} />}
{isSwitching !== 'end' && (
<WarpScreen isSwitching={isSwitching} setIsSwitching={setIsSwitching} />
)}
{text && <Toast type={type}>{text}</Toast>}

<WarpScreen isSwitching={isSwitching} setIsSwitching={setIsSwitching} />

<UpperBar />
<UnderBar />

Expand Down
2 changes: 2 additions & 0 deletions packages/client/src/shared/lib/types/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export interface PostData {
images: string[];
like_cnt?: number;
}

export type TextStateTypes = 'DEFAULT' | 'INVALID' | 'OVER';
113 changes: 96 additions & 17 deletions packages/client/src/widgets/postModal/ui/PostModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { useLocation, useNavigate, useParams } from 'react-router-dom';
import remarkGfm from 'remark-gfm';
import { instance } from 'shared/apis';
import { useCheckNickName, useFetch, useRefresh } from 'shared/hooks';
import { PostData } from 'shared/lib';
import { PostData, TextStateTypes } from 'shared/lib';
import { useCameraStore, useToastStore, useViewStore } from 'shared/store';
import { AlertDialog, Button, Input, Modal, TextArea } from 'shared/ui';
import { deletePost } from '../api/deletePost';
import ImageSlider from './ImageSlider';
import { Caption } from 'shared/styles';
import { css } from '@emotion/react';

export default function PostModal() {
const [deleteModal, setDeleteModal] = useState(false);
Expand All @@ -19,6 +21,8 @@ export default function PostModal() {
const [title, setTitle] = useState('');
const [isDeleteButtonDisabled, setIsDeleteButtonDisabled] = useState(false);
const [isSaveButtonDisabled, setIsSaveButtonDisabled] = useState(false);
const [titleState, setTitleState] = useState<TextStateTypes>('DEFAULT');
const [contentState, setContentState] = useState<TextStateTypes>('DEFAULT');

const { setToast } = useToastStore();
const { setView } = useViewStore();
Expand All @@ -44,7 +48,6 @@ export default function PostModal() {
const formData = new FormData();
formData.append('title', title);
formData.append('content', content);

try {
await instance({
url: `/post/${postId}`,
Expand Down Expand Up @@ -105,6 +108,8 @@ export default function PostModal() {
buttonType="CTA-icon"
type="button"
onClick={() => {
if (title === '') return setTitleState('INVALID');
if (content === '') return setContentState('INVALID');
setIsEdit(false);
handleEditSave();
}}
Expand Down Expand Up @@ -160,21 +165,44 @@ export default function PostModal() {
)}
{isEdit ? (
<TextContainer style={{ height: '100%' }}>
<Input
id={'postTitle'}
placeholder="제목"
style={{ marginBottom: '30px', height: '25%' }}
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<TextArea
value={content}
onChange={(content) => {
setContent(content);
}}
width="100%"
height="75%"
/>
<TitleContainer>
<TitleInput
id={'postTitle'}
state={titleState}
placeholder="제목"
style={{ height: '25%' }}
value={title}
onChange={(e) => {
if (e.target.value.length > 20) {
setTitleState('OVER');
return;
}
setTitleState('DEFAULT');
setTitle(e.target.value);
}}
autoComplete="off"
/>
{titleState === 'INVALID' && (
<Message>제목을 입력해주세요.</Message>
)}
{titleState === 'OVER' && (
<Message>제목은 20자 까지 입력 가능합니다.</Message>
)}
</TitleContainer>
<ContentContainer state={contentState}>
<TextArea
value={content}
onChange={(content) => {
setContentState('DEFAULT');
setContent(content);
}}
width="100%"
height="100%"
/>
{contentState === 'INVALID' && (
<Message>내용을 입력해주세요.</Message>
)}
</ContentContainer>
</TextContainer>
) : (
<TextContainer>
Expand Down Expand Up @@ -203,6 +231,45 @@ export default function PostModal() {
);
}

const ContentContainer = styled.div<{ state: TextStateTypes }>`
border: 1px solid;
border-radius: 4px;
height: 75%;
${({ state, theme: { colors } }) => {
if (state === 'DEFAULT') return;

return css`
border-color: ${colors.text.warning};

&:focus {
border-color: ${colors.text.warning};
}

&:hover {
border-color: ${colors.text.warning};
}
`;
}};
`;

const TitleInput = styled(Input)<{ state: TextStateTypes }>`
${({ state, theme: { colors } }) => {
if (state === 'DEFAULT') return;

return css`
border-color: ${colors.text.warning};

&:focus {
border-color: ${colors.text.warning};
}

&:hover {
border-color: ${colors.text.warning};
}
`;
}};
`;

const PostModalLayout = styled(Modal)`
transform: translate(-10%, -50%);
`;
Expand Down Expand Up @@ -263,3 +330,15 @@ const ButtonContainer = styled.div`
display: flex;
gap: 8px;
`;

const Message = styled.p`
position: absolute;
margin: 4px 0 0 0;
color: ${({ theme: { colors } }) => colors.text.warning};

${Caption}
`;

const TitleContainer = styled.div`
margin-bottom: 30px;
`;
7 changes: 5 additions & 2 deletions packages/client/src/widgets/warpScreen/WarpScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ export default function WarpScreen({ isSwitching, setIsSwitching }: PropsType) {
zIndex: 999,
backgroundColor: theme.colors.background.bdp04,
};
if (isSwitching === 'fade') return <FadeoutScreen />;

if (isSwitching === 'end') return null;

if (isSwitching === 'fade')
return <FadeoutScreen onAnimationEnd={() => setIsSwitching('end')} />;

return (
<Canvas camera={camera} style={canvasStyle}>
Expand All @@ -50,7 +54,6 @@ export default function WarpScreen({ isSwitching, setIsSwitching }: PropsType) {
</EffectComposer>

<ambientLight intensity={AMBIENT_LIGHT_INTENSITY} />

<BrightSphere />
<SpaceWarp setIsSwitching={setIsSwitching} />
</Canvas>
Expand Down
6 changes: 3 additions & 3 deletions packages/client/src/widgets/warpScreen/ui/SpaceWarp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from '../lib/constants';
import { WarpStateType } from 'shared/lib';

const geSpaceWarpLinesInfo = () => {
const getSpaceWarpLinesInfo = () => {
const positions = Array.from({ length: SPACE_WARP_LINES_NUM }, () => {
const x = getRandomFloat(SPACE_WARP_XZ_MIN, SPACE_WARP_XZ_MAX);
const y = getRandomFloat(SPACE_WARP_Y_MIN, SPACE_WARP_Y_MAX);
Expand All @@ -38,10 +38,10 @@ interface PropsType {
}

export default function SpaceWarp({ setIsSwitching }: PropsType) {
const [positions, colors] = useMemo(() => geSpaceWarpLinesInfo(), []);
const [positions, colors] = useMemo(() => getSpaceWarpLinesInfo(), []);

useFrame((state, delta) => {
if (state.camera.position.y <= 0) {
if (state.camera.position.y <= SPACE_WARP_Y_MIN) {
state.scene.background = new THREE.Color(0xffffff);
setIsSwitching('fade');
return;
Expand Down
3 changes: 1 addition & 2 deletions packages/client/src/widgets/writingModal/ui/WritingModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { usePostStore, useViewStore } from 'shared/store';
import { Caption } from 'shared/styles';
import { AlertDialog, Button, Input, Modal, TextArea } from 'shared/ui';
import Images from './Images';

type TextStateTypes = 'DEFAULT' | 'INVALID' | 'OVER';
import { TextStateTypes } from 'shared/lib';

export default function WritingModal() {
const [titleState, setTitleState] = useState<TextStateTypes>('DEFAULT');
Expand Down
4 changes: 2 additions & 2 deletions packages/server/src/board/dto/create-board.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { IsJSON, IsNotEmpty, IsString, MaxLength } from 'class-validator';
export class CreateBoardDto {
@IsNotEmpty({ message: '게시글 제목은 필수 입력입니다.' })
@IsString({ message: '게시글 제목은 문자열로 입력해야 합니다.' })
@MaxLength(20, { message: '게시글 제목은 20자 이내로 입력해야 합니다.' })
@MaxLength(40, { message: '게시글 제목은 20자 이내로 입력해야 합니다.' })
@ApiProperty({
description: '게시글 제목',
example: 'test title',
Expand All @@ -14,7 +14,7 @@ export class CreateBoardDto {

@IsNotEmpty({ message: '게시글 내용은 필수 입력입니다.' })
@IsString({ message: '게시글 내용은 문자열로 입력해야 합니다.' })
@MaxLength(1000, { message: '게시글 내용은 1000자 이내로 입력해야 합니다.' })
@MaxLength(2200, { message: '게시글 내용은 1000자 이내로 입력해야 합니다.' })
@ApiProperty({
description: '게시글 내용',
example: 'test content',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ export class HttpExceptionFilter {
const response = context.getResponse();
const method = request.method;
const status = exception.getStatus();
const errorResponse = exception.getResponse() as any;

const now = new Date();
const korTime = new Date(now.getTime() + 9 * 3600 * 1000);
const exceptionData = {
path: `${method} ${request.url}`,
error: `${status} ${exception.name}`,
message: exception.message,
message: errorResponse.message,
timestamp: korTime,
};
const saveException = new this.exceptionModel(exceptionData);
Expand Down
6 changes: 5 additions & 1 deletion packages/server/src/interceptor/log.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ export class LogInterceptor implements NestInterceptor {
errLog += ` ${userString}`;
}
Logger.error(errLog);
Logger.error(error);
Logger.error(
`${error.getResponse()['statusCode']} ${
error.getResponse()['error']
} - ${error.getResponse()['message']}`,
);
throw error;
}),
tap(() => {
Expand Down