Skip to content

Commit

Permalink
Merge pull request #68 from teamViNO/feature-057
Browse files Browse the repository at this point in the history
feature-057: 영상 요약 페이지 API 연동 중
  • Loading branch information
whistleJs authored Feb 13, 2024
2 parents 8b4bc62 + 81cd8e2 commit e031173
Show file tree
Hide file tree
Showing 13 changed files with 221 additions and 133 deletions.
21 changes: 19 additions & 2 deletions src/apis/videos.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { APIResponse } from '@/models/config/axios';
import { IVideo, VideoVersionType } from '@/models/video';
import { APIBaseResponse, APIResponse } from '@/models/config/axios';
import { IVideo, UpdateVideoRequest, VideoVersionType } from '@/models/video';

import axios from './config/instance';
import axiosInstance from './config/instance';
Expand Down Expand Up @@ -34,3 +34,20 @@ export const getVideoById = async (
const response = await axiosInstance.get(`/videos/${videoId}`);
return response.data;
};

export const updateVideoAPI = (
videoId: string | number,
data: UpdateVideoRequest,
) => {
return axios.patch<APIResponse<IVideo>>(PREFIX + `/${videoId}`, data);
};

export const createVideoSummaryAPI = (videoId: number, content: string[]) => {
return axios.post<APIBaseResponse>(PREFIX + `/${videoId}/newSummary`, {
content,
});
};

export const deleteVideoSummaryAPI = (summaryId: number) => {
return axios.delete<APIBaseResponse>(PREFIX + `/${summaryId}/deleteSummary`);
};
2 changes: 1 addition & 1 deletion src/components/Home/InsightVideos.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const InsightVideos: React.FC<InsightVideosProps> = ({
const [categoryItems] = useState<IVideoProps[]>([]);
const [checkedItems, setCheckedItems] = useState<number[]>([]);

const onFileClick = (e: React.MouseEvent<HTMLSpanElement>) => {
const onFileClick = (e: React.MouseEvent) => {
e.stopPropagation();
// 비디오 카테고리로 저장 API 호출 후 이런 인사이트는 어때요 API 재호출로 최신화하기
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
import { useRecoilValue } from 'recoil';

import { Dropdown } from '@/styles/SummaryPage';

import DropdownItem from './DropdownItem';
import { useRecoilValue } from 'recoil';
import { categoryState } from '@/stores/category';
import React from 'react';
import { ISelectedCategoryProps } from 'types/category';

interface ICategoryDropdownProp {
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
handleSelectCategory: ({ name, categoryId }: ISelectedCategoryProps) => void;
}
import DropdownItem from './DropdownItem';

type Props = {
onSelect: (categoryId: number) => void;
};

const CategoryDropdown = ({
setIsOpen,
handleSelectCategory,
}: ICategoryDropdownProp) => {
const CategoryDropdown = ({ onSelect }: Props) => {
const categories = useRecoilValue(categoryState);

return (
<Dropdown onClick={(e) => e.stopPropagation()}>
<ul>
{categories.map((category) => (
<DropdownItem
key={category.categoryId}
category={category}
setIsOpen={setIsOpen}
handleSelectCategory={handleSelectCategory}
onSelect={onSelect}
/>
))}
</ul>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
import { useState } from 'react';
import { IFolderProps } from 'types/category';

import DownIcon from '@/assets/icons/down.svg?react';
import { IFolderProps, ISelectedCategoryProps } from 'types/category';

import { DropdownTopCategoryName } from '@/styles/SummaryPage';

interface ICategoryDropdownProp {
type Props = {
category: IFolderProps;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
handleSelectCategory: ({ name, categoryId }: ISelectedCategoryProps) => void;
}

interface IItemClickProps {
name: string;
categoryId: number;
}
onSelect: (categoryId: number) => void;
};

const DropdownItem = ({
category,
setIsOpen,
handleSelectCategory,
}: ICategoryDropdownProp) => {
const DropdownItem = ({ category, onSelect }: Props) => {
const [isShow, setIsShow] = useState(false);

const dynamicStyles = {
Expand All @@ -31,11 +22,6 @@ const DropdownItem = ({
},
};

const handleItemClick = async ({ name, categoryId }: IItemClickProps) => {
handleSelectCategory({ name, categoryId });
setIsOpen(false);
};

return (
<>
<li>
Expand All @@ -45,14 +31,8 @@ const DropdownItem = ({
style={dynamicStyles.icon}
onClick={() => setIsShow(!isShow)}
/>
<DropdownTopCategoryName
onClick={() =>
handleItemClick({
name: category.name,
categoryId: category.categoryId,
})
}
>

<DropdownTopCategoryName onClick={() => onSelect(category.categoryId)}>
{category.name}
</DropdownTopCategoryName>
</li>
Expand All @@ -61,12 +41,7 @@ const DropdownItem = ({
{category.subFolders.map((subFolder) => (
<li
key={subFolder.categoryId}
onClick={() =>
handleItemClick({
name: subFolder.name,
categoryId: subFolder.categoryId,
})
}
onClick={() => onSelect(subFolder.categoryId)}
>
{subFolder.name}
</li>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,69 @@
import { useState } from 'react';
import { useRecoilValue } from 'recoil';

import DownIcon from '@/assets/icons/down.svg?react';
import OpenFileIcon from '@/assets/icons/open-file.svg?react';

import useOutsideClick from '@/hooks/useOutsideClick';

import { categoryState } from '@/stores/category';
import { userTokenState } from '@/stores/user';

import { CategoryDropdown } from './CategoryDropdown';
import { ISelectedCategoryProps } from 'types/category';

interface ICategorySelectBoxProps {
selectedCategory: ISelectedCategoryProps;
handleSelectCategory: ({ name, categoryId }: ISelectedCategoryProps) => void;
onFileClick?: (e: React.MouseEvent<HTMLSpanElement>) => void;
}
type Props = {
selectedCategoryId?: number;
onSelect: (categoryId: number) => void;
onFileClick?: (e: React.MouseEvent) => void;
};

const CategorySelectBox = ({
selectedCategory,
handleSelectCategory,
selectedCategoryId,
onSelect,
onFileClick,
}: ICategorySelectBoxProps) => {
const [isLogin] = useState(true);
}: Props) => {
const userToken = useRecoilValue(userTokenState);
const categories = useRecoilValue(categoryState);

const [isOpen, setIsOpen] = useState(false);

const selectedCategory =
selectedCategoryId &&
categories
.reduce(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(acc: any[], category) => [
...acc,
{ ...category },
...category.subFolders,
],
[],
)
.find((category) => category.categoryId === selectedCategoryId);

// 다른 영역 클릭 시 dropdown 안보여지게 하기
const [ref] = useOutsideClick<HTMLDivElement>(() => {
setIsOpen(false);
});

const handleBoxClick = () => {
if (!isLogin) return;
if (!userToken) return;

setIsOpen(!isOpen);
};

const handleSelect = (categoryId: number) => {
onSelect(categoryId);
setIsOpen(false);
};

return (
<div ref={ref} style={{ display: 'flex', gap: 8 }} onClick={handleBoxClick}>
<div style={{ position: 'relative', flex: '1 1 auto' }}>
<div className="select-box">
<span>
{isLogin
? selectedCategory.name
{userToken
? selectedCategory
? selectedCategory.name
: '어떤 카테고리에 넣을까요?'
: '로그인하고 요약한 영상을 아카이빙해요!'}
Expand All @@ -47,17 +72,12 @@ const CategorySelectBox = ({
<DownIcon width={18} height={18} />
</div>

{isOpen && (
<CategoryDropdown
setIsOpen={setIsOpen}
handleSelectCategory={handleSelectCategory}
/>
)}
{isOpen && <CategoryDropdown onSelect={handleSelect} />}
</div>

<span
className={`icon-button ${!isLogin && 'disabled'} ${
selectedCategory.name ? 'selected' : 'not-selected'
className={`icon-button ${!userToken && 'disabled'} ${
selectedCategory ? 'selected' : 'not-selected'
}`}
onClick={onFileClick}
>
Expand Down
51 changes: 48 additions & 3 deletions src/components/SummaryPage/SummaryDetailBox/NoteBox/NoteBox.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { useRecoilValue } from 'recoil';

import {
createVideoSummaryAPI,
deleteVideoSummaryAPI,
updateVideoAPI,
} from '@/apis/videos';

import PlusIcon from '@/assets/icons/plus.svg?react';

import useIndex from '@/hooks/useIndex';

import { IVideoSummary } from '@/models/video';

import { summaryVideoState } from '@/stores/summary';

import NoteItem from './NoteItem';

const NoteBox = () => {
type Props = {
onRefresh: () => void;
};

const NoteBox = ({ onRefresh }: Props) => {
const summaryVideo = useRecoilValue(summaryVideoState);
const [editableIndex, setEditableIndex, setDisableIndex] = useIndex();

Expand All @@ -20,6 +32,38 @@ const NoteBox = () => {
}
};

const handleUpdateNote = async (summary: IVideoSummary) => {
if (!summaryVideo || editableIndex === null) return;

try {
if (summary.content === '') {
await deleteVideoSummaryAPI(summary.id);

setDisableIndex();
} else {
await updateVideoAPI(summaryVideo.video_id, { summary: [summary] });

handleActiveEditable(editableIndex + 1);
}

onRefresh();
} catch (e) {
console.error(e);
}
};

const handleCreateNote = async (content: string) => {
if (!summaryVideo || content === '') return;

try {
await createVideoSummaryAPI(summaryVideo.video_id, [content]);

onRefresh();
} catch (e) {
console.error(e);
}
};

return (
<div style={{ position: 'relative', marginTop: 40 }}>
<div className="note-box">
Expand All @@ -30,16 +74,17 @@ const NoteBox = () => {
isEditable={editableIndex === index}
onDisableEditable={setDisableIndex}
onActiveEditable={() => handleActiveEditable(index)}
onActiveNextEditable={() => handleActiveEditable(index + 1)}
onEdit={(content) => handleUpdateNote({ id: summary.id, content })}
/>
))}

{/* 추가 */}
{editableIndex === -1 && (
<NoteItem
summary={{ id: 0, content: '' }}
summary={{ id: -1, content: '' }}
isEditable={editableIndex === -1}
onDisableEditable={setDisableIndex}
onEdit={handleCreateNote}
/>
)}

Expand Down
11 changes: 8 additions & 3 deletions src/components/SummaryPage/SummaryDetailBox/NoteBox/NoteItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ type Props = {
isEditable: boolean;
onDisableEditable: () => void;
onActiveEditable?: () => void;
onActiveNextEditable?: () => void;
onEdit: (content: string) => void;
};

const NoteItem = ({
summary,
isEditable,
onDisableEditable,
onActiveEditable,
onActiveNextEditable,
onEdit,
}: Props) => {
const textareaRef = useRef<HTMLTextAreaElement>(null);
const [noteText, setNoteText] = useState(summary.content);
Expand All @@ -30,7 +30,12 @@ const NoteItem = ({
} else if (e.key === 'Enter') {
e.preventDefault();

onActiveNextEditable && onActiveNextEditable();
onEdit(noteText);

// 이어서 생성할 수 있도록
if (summary.id === -1) {
setNoteText('');
}
}
};

Expand Down
Loading

0 comments on commit e031173

Please sign in to comment.