Skip to content

Commit

Permalink
Merge pull request #198 from team-offonoff/feat/b-side-page
Browse files Browse the repository at this point in the history
feat: B 사이드 토픽 홈 화면 구현
  • Loading branch information
Jinho1011 authored Feb 14, 2024
2 parents 4f28590 + 566d213 commit 5ff3a6e
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 17 deletions.
2 changes: 2 additions & 0 deletions src/assets/styles/global.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const GlobalStyle = styled.createGlobalStyle`
body {
font-size: 1.6rem;
background-color: ${(props) => props.theme.colors.navy};
/* transition: 0.2s; */
}
html,
Expand Down
10 changes: 2 additions & 8 deletions src/components/A/ATopicCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,19 @@ import { getDateDiff } from '@utils/date';

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

const AlphaTopicCard = React.memo((props: AlphaTopicCardProps) => {
const { BottomSheet: CommentSheet, toggleSheet } = useBottomSheet({});
const voteMutation = useVoteTopic({ side: 'TOPIC_A', sort: 'createdAt,DESC' });
const [A, B] = props.choices;

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

const handleVote = (side: 'CHOICE_A' | 'CHOICE_B') => {
if (props.selectedOption === null) {
voteMutation.mutate({
topicId: props.topicId,
choiceOption: side,
votedAt: new Date().getTime() / 1000,
});
}
props.onVote(props.topicId, side);
};

return (
Expand Down
21 changes: 21 additions & 0 deletions src/components/B/BTopicCard.styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { styled } from 'styled-components';

const CardContainer = styled.div`
display: flex;
flex-direction: column;
background-color: ${({ theme }) => theme.colors.navy2_60};
backdrop-filter: blur(1.5px);
border-radius: 10px;
`;

const CardFooter = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
padding: 9px 22px;
background-color: rgb(100 81 155 / 60%);
border-bottom-right-radius: 10px;
border-bottom-left-radius: 10px;
`;

export { CardContainer, CardFooter };
88 changes: 88 additions & 0 deletions src/components/B/BTopicCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React from 'react';

import CommentChip from '@components/commons/Chip/CommentChip';
import { Col, Row } from '@components/commons/Flex/Flex';
import Text from '@components/commons/Text/Text';
import { TopicResponse } from '@interfaces/api/topic';

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

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

import { CardContainer, CardFooter } from './BTopicCard.styles';
import BTopicChoice from './BTopicChoice';

interface BTopicCardProps {
topic: TopicResponse;
}

const BTopicCard = ({ topic }: BTopicCardProps) => {
return (
<>
<CardContainer>
<Col padding={'12px 22px 20px'}>
<Row justifyContent={'space-between'} alignItems={'center'} style={{ marginBottom: 2 }}>
<Row gap={10}>
<Text size={13} color={colors.purple}>
{topic.keyword?.keywordName}
</Text>
<Text size={13} color={colors.white_40}>
{getDateDiff(topic.createdAt)}
</Text>
</Row>
{topic.selectedOption && (
<Text
size={13}
weight={600}
color={colors.purple}
noWrap
style={{
borderRadius: 30,
padding: '2px 12px',
backgroundColor: colors.purple_30,
}}
>
선택완료
</Text>
)}
</Row>
<Row justifyContent={'space-between'} alignItems={'center'} style={{ marginBottom: 17 }}>
<Text size={18} weight={600} color={colors.white}>
{topic.topicTitle}
</Text>
<button>-</button>
</Row>
<Row gap={5}>
<BTopicChoice
side={topic.choices[0].choiceOption}
content={topic.choices[0].content.text || ''}
/>
<BTopicChoice
side={topic.choices[1].choiceOption}
content={topic.choices[1].content.text || ''}
/>
</Row>
</Col>
<CardFooter>
<Row gap={5}>
<Text size={13} weight={600} color={colors.white_80}>
선택
</Text>
<Text size={13} weight={600} color={colors.white_40}>
{topic.voteCount.toLocaleString()}
</Text>
</Row>
<CommentChip
count={topic.commentCount}
backgroundColor={colors.black_20}
onClick={function (): void {
throw new Error('Function not implemented.');
}}
/>
</CardFooter>
</CardContainer>
</>
);
};

export default BTopicCard;
43 changes: 43 additions & 0 deletions src/components/B/BTopicChoice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import styled from 'styled-components';

import Text from '@components/commons/Text/Text';

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

interface BTopicChoiceProps {
side: 'CHOICE_A' | 'CHOICE_B';
content: string;
}

const BTopicChoice = ({ side, content }: BTopicChoiceProps) => {
return (
<ChoiceContainer>
<Text size={13} weight={600} color={colors.white_40} align={'center'}>
{content}
</Text>
<Text
size={20}
weight={900}
color={side === 'CHOICE_A' ? colors.A_60 : colors.B_60}
style={{ position: 'absolute', top: 0, left: 6 }}
>
{side === 'CHOICE_A' ? 'A' : 'B'}
</Text>
</ChoiceContainer>
);
};

const ChoiceContainer = styled.div`
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 72px;
padding: 0 20px;
background-color: ${({ theme }) => theme.colors.navy2};
border-radius: 10px;
`;

export default BTopicChoice;
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const BottomNavigation = () => {
)}
</StyledNavLink>
<Empty />
<StyledNavLink to={'/b'}>
<StyledNavLink to={'/topics/b'}>
{({ isActive }) => (
<>
{isActive ? <BFillLogoIcon /> : <BStrokeLogoIcon />}
Expand Down
11 changes: 8 additions & 3 deletions src/components/commons/Chip/CommentChip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@ import Text from '../Text/Text';
interface CommentChipProps {
count: number;
onClick: () => void;
backgroundColor?: string;
}

const CommentChip = ({ count, onClick }: CommentChipProps) => {
const CommentChip = ({ count, onClick, backgroundColor }: CommentChipProps) => {
return (
<Container type="button" onClick={onClick}>
<Container
type="button"
onClick={onClick}
style={{ backgroundColor: backgroundColor || colors.black_40 }}
>
<CommentIcon width={18} height={18} />
<Text size={13} weight={400} color={colors.white}>
댓글
Expand All @@ -29,10 +34,10 @@ export default CommentChip;

const Container = styled.button`
display: flex;
flex-shrink: 0;
gap: 5px;
align-items: center;
justify-content: center;
padding: 2px 10px;
background-color: ${({ theme }) => theme.colors.black_40};
border-radius: 20px;
`;
3 changes: 2 additions & 1 deletion src/components/commons/Layout/Layout.styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ export const Main = styled.main`
max-width: 512px;
height: calc(var(--vh, 1vh) * 100);
margin: 0 auto;
background-color: ${(props) => props.theme.colors.navy};
/* background-color: ${(props) => props.theme.colors.navy}; */
&::-webkit-scrollbar {
display: none;
Expand Down
15 changes: 13 additions & 2 deletions src/routes/A/ATopics.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ChangeEvent, useState } from 'react';

import useTopics from '@apis/topic/useTopics';
import useVoteTopic from '@apis/topic/useVoteTopic';
import ATopicCard from '@components/A/ATopicCard';
import { Col, Row } from '@components/commons/Flex/Flex';
import Layout from '@components/commons/Layout/Layout';
Expand All @@ -13,8 +14,9 @@ import { ALogoIcon, UpDownChevronIcon } from '@icons/index';

import { Container } from './ATopics.styles';

const AlphaTopics = () => {
const ATopics = () => {
const { data } = useTopics({ side: 'TOPIC_A', sort: 'createdAt,DESC' });
const voteMutation = useVoteTopic({ side: 'TOPIC_A', sort: 'createdAt,DESC' });
const [topicFilter, setTopicFilter] = useState('진행중');
const [isMineOnly, setIsMineOnly] = useState(false);
const [isLatest, setIsLatest] = useState(true);
Expand All @@ -25,6 +27,14 @@ const AlphaTopics = () => {
setTopicFilter(e.target.value);
};

const handleVote = (topicId: number, side: 'CHOICE_A' | 'CHOICE_B') => {
voteMutation.mutate({
topicId: topicId,
choiceOption: side,
votedAt: new Date().getTime() / 1000,
});
};

return (
<Layout
hasBottomNavigation
Expand Down Expand Up @@ -88,6 +98,7 @@ const AlphaTopics = () => {
selectedOption={topic.selectedOption}
commentCount={topic.commentCount}
createdAt={topic.createdAt}
onVote={handleVote}
/>
);
})}
Expand All @@ -97,4 +108,4 @@ const AlphaTopics = () => {
);
};

export default AlphaTopics;
export default ATopics;
5 changes: 5 additions & 0 deletions src/routes/B/BTopics.styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { styled } from 'styled-components';

export const Container = styled.div`
height: 100%;
`;
94 changes: 94 additions & 0 deletions src/routes/B/BTopics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React, { ChangeEvent, useLayoutEffect, useState } from 'react';

import useTopics from '@apis/topic/useTopics';
import BTopicCard from '@components/B/BTopicCard';
import { Row, Col } from '@components/commons/Flex/Flex';
import Layout from '@components/commons/Layout/Layout';
import Text from '@components/commons/Text/Text';
import ToggleSwitch from '@components/commons/ToggleSwitch/ToggleSwitch';

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

import { BFillLogoIcon, UpDownChevronIcon } from '@icons/index';

import { Container } from './BTopics.styles';

const BTopics = () => {
const { data } = useTopics({ side: 'TOPIC_B', sort: 'createdAt,DESC' });
const [topicFilter, setTopicFilter] = useState('진행중');
const [isMineOnly, setIsMineOnly] = useState(false);
const [isLatest, setIsLatest] = useState(true);

const topics = data?.pages.flatMap((page) => page.data);

const handleTopicStatusChange = (e: ChangeEvent<HTMLInputElement>) => {
setTopicFilter(e.target.value);
};

/**
* set body background color to dark and restore it on cleanup
*/
useLayoutEffect(() => {
const prevColor = document.body.style.backgroundColor;
document.body.style.backgroundColor = '#0e0d16';

return () => {
document.body.style.backgroundColor = prevColor;
};
}, []);

return (
<Layout
hasBottomNavigation
HeaderLeft={<BFillLogoIcon width={30} height={30} fill={colors.white} />}
HeaderCenter={
<ToggleSwitch value={topicFilter} onChange={handleTopicStatusChange}>
<ToggleSwitch.Option value={'진행중'}>
<Text size={15} weight={500} color={'inherit'}>
진행중
</Text>
</ToggleSwitch.Option>
<ToggleSwitch.Option value={'종료된'}>
<Text size={15} weight={500} color={'inherit'}>
종료된
</Text>
</ToggleSwitch.Option>
</ToggleSwitch>
}
>
<Container>
<Row justifyContent={'flex-end'} gap={12} padding="15px 20px">
<button onClick={() => setIsMineOnly((prev) => !prev)}>
<Row alignItems="center" gap={6}>
<div
style={{
width: '14px',
height: '14px',
borderRadius: '50%',
border: '1px solid',
borderColor: isMineOnly ? colors.white : colors.white_40,
}}
/>
<Text size={13} color={isMineOnly ? colors.white : colors.white_40}>
내 토픽만
</Text>
</Row>
</button>
<button onClick={() => setIsLatest((prev) => !prev)}>
<Row alignItems="center" gap={6}>
<UpDownChevronIcon stroke={isLatest ? colors.white : colors.white_40} />
<Text size={13} color={isLatest ? colors.white : colors.white_40}>
최신순
</Text>
</Row>
</button>
</Row>
<Col padding={'0 20px 100px'} gap={30}>
{topics?.map((topic) => <BTopicCard key={topic.topicId} topic={topic} />)}
</Col>
</Container>
</Layout>
);
};

export default BTopics;
Loading

0 comments on commit 5ff3a6e

Please sign in to comment.