Skip to content

Commit

Permalink
0.0.9v 배포 (#226)
Browse files Browse the repository at this point in the history
* feat: mypage ui 생성

* feat: mypage 모달 생성, 앨범 기능 추가

* feat: rename ATopics and add BTopics

* fix: b nav item to

* feat: add b topics background color

* fix: separate onVote

* fix: remove transition from  body

* feat: add backgroundColor as optional

* feat: implements BTopicChoice

* feat: implements BTopicCard

* fix: render BTopicCard

* feat: load routes lazily

* fix: routes missing

* feat: separate loading component

* feat: 프로필 이미지 s3 업로드 및 프로필 이미지 변경 api 연동

* feat: separate loading component

* feat: add BackButton component

* fix: make choiceSlide box-sizing to content-box

* feat: add B side detail page

* feat: navigate to detail page on click card

* feat: add @sentry/react

* feat: init sentry

* feat: thrwo Error for testing

* fix: 삭제하기 액션모달 사용을 위한 수정

* feat: 프로필 이미지 삭제하기 기능추가

delete api 연동 및 삭제하기 모달 추가

* Theme Color를 Layout에서 동적으로 수정 (#210)

* chore: add react-helmet

* feat: change theme color in layout

* fix: google redirect url

* fix: signup 화면 디자인 불일치 수정 (#213)

* fix: make DefaultButton extends ButtonHTMLAttributes

* feat: replace to DefaultButton

* fix: radio button selected style

* feat: navigate to home after consent to term

* fix: separate login url retrieve to function

* feat: add useOAuth to authroize commonly

* fix: redirect user to login page on fail

* feat: replace to useOAuth

* feat: add admin login button

* fix: 토픽 작성자는 투표 불가 (#217)

* chore: add react-toastify

* feat: add zIndex type to theme

* feat: Toast container

* feat: show toast and reset slide

* fix: make handleVote return Promise<boolean>

* A 사이드 토픽 카드 텍스트 영역 및 소수점 이슈 해결 (#218)

* fix: round percentage

* fix: make progress content hidden with ellipsis

* fix: integrate to topic

* fix: show toast on vote my topic

* fix: 유저가 작성한 토픽인 경우 조회 가능하도록 수정 (#219)

* fix: get method to throw error

* fix: check if member created topic

* fiX: remove console.log

* refactor: remove another console.logs

* 알림 화면에 뒤로가기 버튼 추가 (#220)

* comment에 choice 추가해서 선택지 반영 (#222)

---------

Co-authored-by: chaeyoung103 <[email protected]>
Co-authored-by: CHAEYOUNG SONG <[email protected]>
  • Loading branch information
3 people authored Feb 19, 2024
1 parent 4ed3857 commit 747e186
Show file tree
Hide file tree
Showing 31 changed files with 400 additions and 141 deletions.
1 change: 0 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
<meta name="description" content="My Awesome App description" />
<meta name="theme-color" content="#242036" />
<title>AB - 세상의 모든 질문, AB로 답하다</title>
<style>
@font-face {
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@
"framer-motion": "^10.16.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0",
"react-hook-form": "^7.49.2",
"react-modal": "^3.16.1",
"react-router-dom": "^6.16.0",
"react-toastify": "^10.0.4",
"react-uid": "^2.3.3",
"styled-components": "^6.0.8",
"styled-normalize": "^8.0.7",
Expand All @@ -42,6 +44,7 @@
"@tanstack/eslint-plugin-query": "^5.0.5",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@types/react-helmet": "^6.1.11",
"@types/react-modal": "^3.16.3",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
Expand Down
3 changes: 3 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useEffect } from 'react';
import { StyleSheetManager, ThemeProvider } from 'styled-components';

import { StyledToastConatiner } from '@components/commons/Toast/Toast';

import GlobalStyle from '@styles/global';
import { theme } from '@styles/theme';

Expand All @@ -22,6 +24,7 @@ const App = () => {
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={theme}>
<GlobalStyle />
<StyledToastConatiner limit={1} />
<Router />
</ThemeProvider>
</QueryClientProvider>
Expand Down
11 changes: 8 additions & 3 deletions src/apis/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Fetch {
this.baseURL = import.meta.env.VITE_API_BASE_URL;
}

async get<T>(path: string, qs?: Record<string, any>): Promise<T> {
async get<TData>(path: string, qs?: Record<string, any>) {
const queryString = qs ? `?${new URLSearchParams(qs).toString()}` : '';
const response = await fetch(`${this.baseURL}${path}${queryString}`, {
method: 'GET',
Expand All @@ -26,8 +26,13 @@ class Fetch {
...(this.accessToken && { Authorization: `Bearer ${this.accessToken}` }),
},
});
const data: T = await response.json();
return data;
const data = await response.json();

if (!response.ok) {
throw new ResponseError(data);
}

return data as TData;
}

async post<TData>({
Expand Down
10 changes: 3 additions & 7 deletions src/apis/oauth/kakao.ts → src/apis/oauth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,22 @@ import { OAuthLoginRequest, OAuthResponse } from '@interfaces/api/oauth';

import client from '@apis/fetch';

export const kakaoLogin = async (authorizeCode: string) => {
export const OAuthLogin = async (type: 'kakao' | 'google' | 'apple', authorizeCode: string) => {
const body: OAuthLoginRequest = {
type: 'BY_CODE',
code: authorizeCode,
redirect_uri: import.meta.env.VITE_KAKAO_REDIRECT_URI,
redirect_uri: import.meta.env[`VITE_${type.toUpperCase()}_REDIRECT_URI`],
id_token: null,
provider: null,
};

const response = await client.post<OAuthResponse>({
path: '/oauth/kakao/authorize',
path: `/oauth/${type}/authorize`,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
body,
});

if (response.accessToken) {
client.setAccessToken(response.accessToken);
}

return response;
};
1 change: 1 addition & 0 deletions src/assets/styles/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const zIndex = {
};

export type ColorsTypes = typeof colors;
export type ZIndexTypes = typeof zIndex;

export const theme: DefaultTheme = {
colors,
Expand Down
38 changes: 21 additions & 17 deletions src/components/A/ATopicCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,47 +14,51 @@ import { colors } from '@styles/theme';

import { getDateDiff } from '@utils/date';

interface AlphaTopicCardProps extends TopicResponse {
interface AlphaTopicCardProps {
topic: TopicResponse;
onVote: (topicId: number, side: 'CHOICE_A' | 'CHOICE_B') => void;
chip?: 'popular' | 'close';
onVote: (topicId: number, side: 'CHOICE_A' | 'CHOICE_B') => void;
}

const AlphaTopicCard = React.memo((props: AlphaTopicCardProps) => {
const AlphaTopicCard = React.memo(({ topic, onVote, chip }: AlphaTopicCardProps) => {
const { BottomSheet: CommentSheet, toggleSheet } = useBottomSheet({});
const [A, B] = props.choices;
const [A, B] = topic.choices;
const roundedPercentageA = Math.round((A.voteCount / topic.voteCount) * 100);
const roundedPercentageB = 100 - roundedPercentageA;

const handleCommentChipClick = () => {
toggleSheet();
};

const handleVote = (side: 'CHOICE_A' | 'CHOICE_B') => {
props.onVote(props.topicId, side);
onVote(topic.topicId, side);
};

return (
<>
<Col padding={'20px'}>
{props.chip && (
{chip && (
<Row style={{ marginBottom: 12 }}>
<Chip tintColor={'#D3FF9C'} label={'실시간 인기 토픽'} />
</Row>
)}
<Row justifyContent={'space-between'} style={{ marginBottom: 14 }}>
<Text size={18} weight={500} color={colors.white}>
{props.topicTitle}
{topic.topicTitle}
</Text>
<button> - </button>
</Row>
<Col gap={5} style={{ marginBottom: 14 }}>
<ProgressBar
revealed={props.selectedOption !== null}
highlighted={props.selectedOption === 'CHOICE_A'}
revealed={topic.selectedOption !== null}
highlighted={topic.selectedOption === 'CHOICE_A'}
title={A.content.text || ''}
percentage={(A.voteCount / props.voteCount) * 100}
percentage={roundedPercentageA}
onClick={() => handleVote('CHOICE_A')}
left={() => (
<Text
color={props.selectedOption === 'CHOICE_A' ? colors.A_80 : colors.A_40}
color={topic.selectedOption === 'CHOICE_A' ? colors.A_80 : colors.A_40}
size={24}
weight={900}
>
Expand All @@ -63,14 +67,14 @@ const AlphaTopicCard = React.memo((props: AlphaTopicCardProps) => {
)}
/>
<ProgressBar
revealed={props.selectedOption !== null}
highlighted={props.selectedOption === 'CHOICE_B'}
revealed={topic.selectedOption !== null}
highlighted={topic.selectedOption === 'CHOICE_B'}
title={B.content.text || ''}
percentage={(B.voteCount / props.voteCount) * 100}
percentage={roundedPercentageB}
onClick={() => handleVote('CHOICE_B')}
left={() => (
<Text
color={props.selectedOption === 'CHOICE_B' ? colors.B_80 : colors.B_40}
color={topic.selectedOption === 'CHOICE_B' ? colors.B_80 : colors.B_40}
size={24}
weight={900}
>
Expand All @@ -81,13 +85,13 @@ const AlphaTopicCard = React.memo((props: AlphaTopicCardProps) => {
</Col>
<Row justifyContent={'space-between'} alignItems={'center'}>
<Text size={13} color={colors.white_40}>
{getDateDiff(props.createdAt)}
{getDateDiff(topic.createdAt)}
</Text>
<CommentChip count={props.commentCount} onClick={handleCommentChipClick} />
<CommentChip count={topic.commentCount} onClick={handleCommentChipClick} />
</Row>
</Col>
<CommentSheet>
<TopicComments topic={props} />
<TopicComments topic={topic} />
</CommentSheet>
</>
);
Expand Down
18 changes: 14 additions & 4 deletions src/components/Home/ChoiceSlider/ChoiceSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Choice } from '@interfaces/api/topic';
import { getScreenWidth } from '@utils/screenWidth';

interface ChoiceSliderProps {
onVote: (choiceOption: Choice['choiceOption']) => void;
onVote: (choiceOption: Choice['choiceOption']) => Promise<boolean>;
choices: Choice[];
}

Expand All @@ -28,17 +28,27 @@ const ChoiceSlider = ({ onVote, choices }: ChoiceSliderProps) => {
translateX: -(screenWidth / 2 + 7.5 + screenWidth),
opacity: 0,
},
reset: {
translateX: 0,
opacity: 1,
},
};

const handleDragEnd = (_: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {
const handleDragEnd = async (_: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {
if (info.velocity.x > 0 && info.offset.x > screenWidth / 2 + 7.5) {
// A 슬라이드
controls.start('A');
onVote(choices[0].choiceOption);
const result = await onVote(choices[0].choiceOption);
if (!result) {
controls.start('reset');
}
} else if (info.velocity.x < 0 && info.offset.x < -(screenWidth / 2 + 7.5)) {
// B 슬라이드
controls.start('B');
onVote(choices[1].choiceOption);
const result = await onVote(choices[1].choiceOption);
if (!result) {
controls.start('reset');
}
}
};

Expand Down
12 changes: 9 additions & 3 deletions src/components/Home/Comment/Comment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ProfileImg from '@components/commons/ProfileImg/ProfileImg';
import Text from '@components/commons/Text/Text';
import useModal from '@hooks/useModal/useModal';
import { CommentResponse } from '@interfaces/api/comment';
import { Choice } from '@interfaces/api/topic';

import { useAuthStore } from '@store/auth';

Expand All @@ -18,9 +19,10 @@ import Thumbs from './Thumbs';

interface CommentProps {
comment: CommentResponse;
choices: Choice[];
}

const Comment = React.memo(({ comment }: CommentProps) => {
const Comment = React.memo(({ comment, choices }: CommentProps) => {
const { Modal, toggleModal } = useModal('action');
const reactMutation = useReactComment(comment.topicId, comment.commentId);
const memberId = useAuthStore((store) => store.memberId);
Expand Down Expand Up @@ -79,8 +81,12 @@ const Comment = React.memo(({ comment }: CommentProps) => {
{'·'} {distanceText}
</Text>
</Row>
<Text size={14} color={colors.A} weight={600}>
{comment.writersVotedOption}
<Text
size={14}
color={comment.writersVotedOption === 'CHOICE_A' ? colors.A_60 : colors.B_60}
weight={600}
>
{choices[comment.writersVotedOption === 'CHOICE_A' ? 0 : 1].content.text}
</Text>
</Col>
</Row>
Expand Down
36 changes: 27 additions & 9 deletions src/components/Home/TopicCard/TopicCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useLatestComment } from '@apis/comment/useComment';
import useVoteTopic from '@apis/topic/useVoteTopic';
import ProfileImg from '@components/commons/ProfileImg/ProfileImg';
import Text from '@components/commons/Text/Text';
import { Toast } from '@components/commons/Toast/Toast';
import ChoiceSlider from '@components/Home/ChoiceSlider/ChoiceSlider';
import CommentBox from '@components/Home/CommentBox/CommentBox';
import Timer from '@components/Home/Timer/Timer';
Expand All @@ -14,10 +15,14 @@ import useBottomSheet from '@hooks/useBottomSheet/useBottomSheet';
import { LatestComment } from '@interfaces/api/comment';
import { Choice, TopicResponse } from '@interfaces/api/topic';

import { useAuthStore } from '@store/auth';

import { colors } from '@styles/theme';

import { LeftDoubleArrowIcon, RightDoubleArrowIcon } from '@icons/index';

import { ResponseError } from '@apis/fetch';

import TopicComments from '../TopicComments/TopicComments';

import {
Expand All @@ -35,12 +40,15 @@ interface TopicCardProps {

const TopicCard = ({ topic }: TopicCardProps) => {
const [, setSearchParams] = useSearchParams();
const memberId = useAuthStore((store) => store.memberId);
const isMyTopic = topic.author.id === memberId;

const swiperSlide = useSwiperSlide();
const { BottomSheet: CommentSheet, toggleSheet } = useBottomSheet({});
const voteMutation = useVoteTopic();
const { data: latestCommentData, isSuccess } = useLatestComment(
topic.topicId,
topic.selectedOption !== null
topic.selectedOption !== null || isMyTopic
);
const [latestComment, setLatestComment] = useState<LatestComment | undefined>();

Expand All @@ -62,18 +70,28 @@ const TopicCard = ({ topic }: TopicCardProps) => {
}, [isSuccess]);

const handleOnClickCommentBox = () => {
if (topic.selectedOption !== null) {
if (isMyTopic || topic.selectedOption !== null) {
toggleSheet();
}
};

const handleOnVote = async (choiceOption: Choice['choiceOption']) => {
const data = await voteMutation.mutateAsync({
topicId: topic.topicId,
choiceOption: choiceOption,
votedAt: new Date().getTime() / 1000,
});
setLatestComment(data.latestComment);
try {
const data = await voteMutation.mutateAsync({
topicId: topic.topicId,
choiceOption: choiceOption,
votedAt: new Date().getTime() / 1000,
});
setLatestComment(data.latestComment);
return true;
} catch (error) {
if (error instanceof ResponseError) {
if (error.errorData.abCode === 'VOTED_BY_AUTHOR') {
Toast.error('토픽을 작성한 사람은 투표할 수 없어요');
}
}
return false;
}
};

return (
Expand Down Expand Up @@ -124,7 +142,7 @@ const TopicCard = ({ topic }: TopicCardProps) => {
</SelectTextContainer>
<CommentBox
side={topic.topicSide}
hasVoted={topic.selectedOption !== null}
hasVoted={topic.selectedOption !== null || isMyTopic}
topicId={topic.topicId}
commentCount={0}
voteCount={0}
Expand Down
4 changes: 3 additions & 1 deletion src/components/Home/TopicComments/TopicComments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ const TopicComments = memo(({ topic }: TopicCommentsProps) => {
</Row>
</TopicCommentsHeader>
<CommentsContainer>
{comments?.map((comment) => <Comment key={comment.commentId} comment={comment} />)}
{comments?.map((comment) => (
<Comment key={comment.commentId} comment={comment} choices={topic.choices} />
))}
</CommentsContainer>
<CommentInputContainer>
<CommentInput
Expand Down
2 changes: 1 addition & 1 deletion src/components/Login/LoginButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const LoginButton = ({
buttonText,
}: LoginButtonProps) => (
<Button onClick={onClick} style={{ backgroundColor }}>
<Row padding={'15px 20px'} justifyContent={'space-between'}>
<Row padding={'15px 20px'} justifyContent={'space-between'} alignItems="center">
<Icon />
<Text size={16} color={color} weight={'bold'}>
{buttonText}
Expand Down
Loading

0 comments on commit 747e186

Please sign in to comment.