Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5df2417
feat: 알람 컨테이너 UI 구현, 알람 리스트 thunk로 불러옴
live-small Aug 8, 2022
8303eb7
fix: alarm list 응답 타입, index.d.ts 파일에서 관리하도록 수정
live-small Aug 8, 2022
b771cf5
feat: 알람 아이템 > 알람보낸 프로필 UI 구현
live-small Aug 8, 2022
d653f62
feat: 알림 item 하나로 통일, 로딩처리, 알람없는 경우 처리
live-small Aug 10, 2022
7b650e7
feat: 알람 내 게시글 썸네일 클릭 시 해당 게시글로 이동
live-small Aug 10, 2022
d5682fe
fix: 알람 item UI 일부 수정
live-small Aug 10, 2022
deb3f1c
refactor: div 대신 ul,li 태그 이용
live-small Aug 11, 2022
48a352a
Merge branch 'develop' into feature/alarm
live-small Aug 30, 2022
f00d848
Merge branch 'develop' into feature/alarm
live-small Aug 30, 2022
766843e
feat: hashtag, mentions 링크,스타일링 적용
live-small Aug 30, 2022
5e067ed
fix: useOnView 수정: parameter, useEffect deps
live-small Sep 13, 2022
9b72f22
fix: Alarm 무한스크롤을 위해 Alarm type 수정
live-small Sep 13, 2022
f28db0c
feat: Alarm 무한스크롤 구현 - alarm list 컴포넌트에서 observing 붙임
live-small Sep 13, 2022
c390cff
fix: 불필요한 log 제거
live-small Sep 13, 2022
f987f73
Merge branch 'develop' into feature/alarm
live-small Sep 13, 2022
c09bec6
live-small Oct 3, 2022
020fd94
feat: 알람>팔로우, 팔로잉 버튼 UI, 모달 핸들러 구현
live-small Oct 3, 2022
73790af
fix: alarm type 중복되는 타입 묶어서 확장하는 방향으로 수정
live-small Oct 3, 2022
ef91700
feat: 알람 없을 때 UI 수정
live-small Oct 3, 2022
cb8722a
fix: 알람 모달상태값 isClickAlarm -> isAlarmOn
live-small Oct 3, 2022
0efa59a
fix: 안쓰는 컴포넌트 import 삭제
live-small Oct 3, 2022
d6e43f3
fix: 알람버튼 cursor pointer
live-small Oct 3, 2022
a62da14
fix: 공통 컨벤션으로 수정
live-small Dec 27, 2022
54e36fc
fix: Alarm 무한스크롤 구현 시 필요한 상태값, 컴포넌트에서 관리하도록 수정
live-small Dec 28, 2022
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
37 changes: 37 additions & 0 deletions src/@type/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ declare module ModalType {
| "report"
| "articleMenu"
| "shareWith"
| "alarmUnfollowing"
| null;

interface ModalPositionProps {
Expand Down Expand Up @@ -460,3 +461,39 @@ declare module EditType {

type modalType = "image" | "gender" | null;
}

declare module AlarmType {
interface AlarmItem {
content: AlarmContent[];
totalPages: number;
currentPage: number;
}

type AlarmContent = PostAlarm | FollowAlarm;

interface CommonAlarm {
id: number;
type: "COMMENT" | "LIKE_POST" | "MENTION_POST";
message: string;
agent: {
id: number;
username: string;
name: string;
image: CommonType.ImageInfo;
hasStory: false;
};
createdDate: string;
}
interface PostAlarm extends CommonAlarm {
postId: number;
postImageUrl: string;
content: string;
mentionsOfContent: string[];
hashtagsOfContent: string[];
}

interface FollowAlarm extends CommonAlarm {
type: "FOLLOW";
following: boolean;
}
}
16 changes: 16 additions & 0 deletions src/app/store/ducks/alarm/alarmSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createSlice } from "@reduxjs/toolkit";

export interface AlarmStateProps {}

const initialState: AlarmStateProps = {};

const alarmSlice = createSlice({
name: " alarm",
initialState,
reducers: {},
extraReducers: (build) => {},
});

export const alarmAction = alarmSlice.actions;

export const alarmReducer = alarmSlice.reducer;
22 changes: 22 additions & 0 deletions src/app/store/ducks/alarm/alarmThunk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { authorizedCustomAxios } from "customAxios";
import { createAsyncThunk } from "@reduxjs/toolkit";

export const loadAlarmList = createAsyncThunk<
AlarmType.AlarmItem,
{ page: number }
>("alarm/loadList", async (payload, ThunkOptions) => {
try {
const config = {
params: {
page: payload.page,
size: 10,
},
};
const {
data: { data },
} = await authorizedCustomAxios.get(`/alarms`, config);
return { ...data, currentPage: payload.page };
} catch (error) {
ThunkOptions.rejectWithValue(error);
}
});
7 changes: 7 additions & 0 deletions src/app/store/ducks/modal/modalSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ const modalSlice = createSlice({
name: "modal",
initialState,
reducers: {
setModalUsernameAndImageUrl: (
state,
action: PayloadAction<{ nickname: string; imageUrl: string }>,
) => {
state.memberNickname = action.payload.nickname;
state.memberImageUrl = action.payload.imageUrl;
},
startModal: (
state,
action: PayloadAction<ModalType.ModalStateProps>,
Expand Down
2 changes: 2 additions & 0 deletions src/app/store/store.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { alarmReducer } from "./ducks/alarm/alarmSlice";
import { configureStore } from "@reduxjs/toolkit";
import { authReducer } from "app/store/ducks/auth/authSlice";
import { homeReducer } from "app/store/ducks/home/homeSlice";
Expand All @@ -18,6 +19,7 @@ export const store = configureStore({
profile: profileReducer,
edit: editReducer,
common: commonReducer,
alarm: alarmReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
Expand Down
40 changes: 32 additions & 8 deletions src/components/Common/Header/NavItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,19 @@ import { ReactComponent as DirectActive } from "assets/Svgs/direct-active.svg";
import { ReactComponent as NewArticle } from "assets/Svgs/new-article.svg";
import { ReactComponent as NewArticleActive } from "assets/Svgs/new-article-active.svg";

import { ReactComponent as Map } from "assets/Svgs/map.svg";
import { ReactComponent as MapActive } from "assets/Svgs/map-active.svg";

Comment on lines -11 to -13
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안쓰는 이미지라서 삭제했습니다 @kimyoungyin
아마 저희가 저 기능 구현안하기로해서 작업안한거 맞죠..?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kimyoungyin 영인님 이 부분도 봐주신거 맞나용?

import { ReactComponent as Heart } from "assets/Svgs/heart.svg";
import { ReactComponent as HeartActive } from "assets/Svgs/heart-active.svg";

import { NavLink, Link } from "react-router-dom";
import { NavLink } from "react-router-dom";
import { useAppDispatch, useAppSelector } from "app/store/Hooks";
import { selectView } from "app/store/ducks/direct/DirectSlice";
import { uploadActions } from "app/store/ducks/upload/uploadSlice";
import Upload from "components/Common/Header/Upload";
import SubNav from "./SubNav";

import { useRef, useState } from "react";
import useOutsideClick from "hooks/useOutsideClick";
import Alarm from "components/Common/Header/alarm";

const Container = styled.div`
flex: 1 0 0%;
Expand Down Expand Up @@ -71,14 +70,22 @@ const AvatarWrapper = styled(NavItemWrapper)<{ isSubnavModalOn: boolean }>`

const NavItems = () => {
const [isSubnavModalOn, setIsSubnavMoalOn] = useState(false);
const [isAlarmOn, setIsAlarmOn] = useState(false);

const dispatch = useAppDispatch();
const isUploading = useAppSelector(({ upload }) => upload.isUploading);
const userInfo = useAppSelector((state) => state.auth.userInfo);

// setting
const navContainerRef = useRef<HTMLDivElement | null>(null);
const subModalControllerRef = useRef<HTMLDivElement | null>(null);
const subModalControllerRef = useRef<HTMLImageElement | null>(null);
useOutsideClick(navContainerRef, setIsSubnavMoalOn, subModalControllerRef);

// alarm
const alarmContainerRef = useRef<HTMLDivElement | null>(null);
const alarmModalControllerRef = useRef<HTMLSpanElement | null>(null);
useOutsideClick(alarmContainerRef, setIsAlarmOn, alarmModalControllerRef);

Comment on lines +85 to +88
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 두 Ref는 useOutsideClick을 위해 생성한 걸까요?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네네 맞아요

const navItems = [
{
id: "홈",
Expand Down Expand Up @@ -127,8 +134,19 @@ const NavItems = () => {
{
id: "피드 활동",
path: "/",
component: <Heart />,
activeComponent: <HeartActive />,
component: (
<span ref={alarmModalControllerRef}>
<Heart onClick={() => setIsAlarmOn(!isAlarmOn)} />
</span>
),
activeComponent: (
<span ref={alarmModalControllerRef} style={{ width: `22px` }}>
<HeartActive onClick={() => setIsAlarmOn(!isAlarmOn)} />
{isAlarmOn && (
<Alarm alarmContainerRef={alarmContainerRef} />
)}
</span>
),
},
];

Expand All @@ -144,6 +162,12 @@ const NavItems = () => {
? navItem.activeComponent
: navItem.component}
</div>
) : navItem.id === "피드 활동" ? (
<div>
{isAlarmOn
? navItem.activeComponent
: navItem.component}
</div>
) : (
<NavLink to={navItem.path}>
{navItem.component}
Expand All @@ -154,7 +178,6 @@ const NavItems = () => {

<AvatarWrapper isSubnavModalOn={isSubnavModalOn}>
<div
ref={subModalControllerRef}
onClick={() => {
setIsSubnavMoalOn(!isSubnavModalOn);
}}
Expand All @@ -166,6 +189,7 @@ const NavItems = () => {
data-testid="user-avatar"
draggable="false"
src={userInfo?.memberImageUrl}
ref={subModalControllerRef}
/>
</div>
{isSubnavModalOn && (
Expand Down
49 changes: 49 additions & 0 deletions src/components/Common/Header/alarm/AlarmList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import AlarmItem from "components/Common/Header/alarm/alarmType/AlarmItem";
import FollowAlarm from "components/Common/Header/alarm/alarmType/FollowAlarm";
import useOnView from "hooks/useOnView";
import { useEffect, useRef } from "react";
import styled from "styled-components";

const Container = styled.ul`
display: flex;
flex-direction: column;

.alarm-item {
padding: 12px 16px;
}
`;

export default function AlarmList({
alarmList,
onLoadExtraAlarm,
}: {
alarmList: AlarmType.AlarmContent[];
onLoadExtraAlarm: () => void;
}) {
const lastAlarmItemRef = useRef<HTMLLIElement>(null);
const isVisible = useOnView(lastAlarmItemRef);

useEffect(() => {
isVisible && onLoadExtraAlarm();
}, [isVisible]);

return (
<Container>
{alarmList.map((alarm, index) => (
<li
className="alarm-item"
key={alarm.id}
ref={
index === alarmList.length - 4 ? lastAlarmItemRef : null
}
>
{alarm.type === "FOLLOW" ? (
<FollowAlarm alarm={alarm} />
) : (
<AlarmItem alarm={alarm} />
)}
</li>
))}
</Container>
);
}
25 changes: 25 additions & 0 deletions src/components/Common/Header/alarm/AlarmProfile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Link } from "react-router-dom";
import styled from "styled-components";

const Container = styled.div`
img {
height: 44px;
width: 44px;
border-radius: 50%;
}
`;

export default function AlarmProfile({
agent,
}: Pick<AlarmType.AlarmContent, "agent">) {
return (
<Container>
<Link to={`/profile/${agent.username}`}>
<img
src={agent.image.imageUrl}
alt={`${agent.username}님의 이미지`}
></img>
</Link>
</Container>
);
}
71 changes: 71 additions & 0 deletions src/components/Common/Header/alarm/alarmType/AlarmItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from "react";
import styled from "styled-components";
import AlarmProfile from "components/Common/Header/alarm/AlarmProfile";
import { Link } from "react-router-dom";
import useGapText from "hooks/useGapText";
import { removeRefer } from "components/Common/Header/alarm/utils";
import StringFragmentWithMentionOrHashtagLink from "components/Common/StringFragmentWithMentionOrHashtagLink";

const Container = styled.div`
display: flex;
flex: 1;

.alarm {
margin: 0 12px;
flex: 1;

a {
text-decoration: none;

.username {
font-weight: 700;
}
}

.create-date {
color: ${(props) => props.theme.font.gray};
}
}

.relative-image {
display: flex;
flex-direction: column;
justify-content: center;

img {
height: 40px;
width: 40px;
}
}
`;

export default function AlarmItem({ alarm }: { alarm: AlarmType.PostAlarm }) {
const alarmMessage = removeRefer(alarm.message);
// 무한스크롤
// 컴포넌트 언마운트 -> alarm창 닫도록

return (
<Container>
<AlarmProfile agent={alarm.agent} />
<div className="alarm">
<Link to={`/profile/${alarm.agent.username}`}>
<span className="username">{alarm.agent.username}</span>
</Link>
{alarmMessage}
<StringFragmentWithMentionOrHashtagLink
str={alarm.content}
mentions={alarm.mentionsOfContent}
hashtags={alarm.hashtagsOfContent}
/>{" "}
<span className="create-date">
{useGapText(alarm.createdDate)}
</span>
</div>
<div className="relative-image">
<Link to={`/p/${alarm.postId}`}>
<img src={alarm.postImageUrl} alt={"이미지 썸네일"}></img>
</Link>
</div>
</Container>
);
}
Loading