Skip to content

Commit

Permalink
Merge pull request #80 from teamViNO/feature-057
Browse files Browse the repository at this point in the history
Feature 057
  • Loading branch information
whistleJs authored Feb 15, 2024
2 parents 250567e + 5313f57 commit c6d86ea
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import { IVideoSubHeading } from '@/models/video';

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

import { toastListState } from '@/stores/toast';
import {
summaryFindKeywordCountState,
summarySearchIndexState,
summaryVideoState,
} from '@/stores/summary';

import { getSearchIndex } from '@/utils/summary';

type Props = {
Expand All @@ -32,6 +34,7 @@ const ChangeKeywordModal = ({ onChange, onClose }: Props) => {
const [summaryVideo, setSummaryVideo] = useRecoilState(summaryVideoState);
const findKeywordCount = useRecoilValue(summaryFindKeywordCountState);
const [searchIndex, setSearchIndex] = useRecoilState(summarySearchIndexState);
const [toastList, setToastList] = useRecoilState(toastListState);

const [holdPosition, setHoldPosition] = useState({ x: -1, y: -1 });
const [position, setPosition] = useState({ x: 0, y: 0 });
Expand All @@ -45,6 +48,11 @@ const ChangeKeywordModal = ({ onChange, onClose }: Props) => {
const { result } = (await getVideoAPI(summaryVideo.video_id)).data;

setSummaryVideo(result);

setToastList([
...toastList,
{ id: Date.now(), content: '단어 변경이 완료되었어요!' },
]);
} catch (e) {
console.error(e);
}
Expand Down Expand Up @@ -101,37 +109,72 @@ const ChangeKeywordModal = ({ onChange, onClose }: Props) => {
setPosition({ x, y });
};

const handleClickChangeAllButton = () => {
const handleClickChangeAllButton = async () => {
if (!summaryVideo) return;

const subHeading = summaryVideo.subHeading.map(({ content, ...others }) => {
return {
content: content.replace(new RegExp(keyword, 'g'), replaceKeyword),
...others,
};
});
const regex = new RegExp(keyword, 'g');

const subHeading = summaryVideo.subHeading.map(
({ name, content, ...others }) => {
return {
name: name.replace(regex, replaceKeyword),
content: content.replace(regex, replaceKeyword),
...others,
};
},
);

await updateSubHeading(subHeading);

updateSubHeading(subHeading);
onClose();
};

const handleClickChangeButton = () => {
const handleClickChangeButton = async () => {
if (!summaryVideo) return;

const regex = new RegExp(keyword, 'g');
const subHeadingList = [...summaryVideo.subHeading];
const subHeadingList: IVideoSubHeading[] = [];
let index = -1;

for (const i in summaryVideo.subHeading) {
const { content } = summaryVideo.subHeading[i];
for (const subHeading of summaryVideo.subHeading) {
const splittedName = subHeading.name.split(keyword);
const splittedContent = subHeading.content.split(keyword);
let name = '';
let content = '';

// 제목 탐색 (탐색 로직 같음)
for (let i = 0; i < splittedName.length; i++) {
if (i === splittedName.length - 1) {
name += splittedName[splittedName.length - 1];
break;
}
index++;

if (index === searchIndex) {
name += splittedName[i] + replaceKeyword;
} else {
name += splittedName[i] + keyword;
}
}

if (regex.test(content)) index++;
if (index === searchIndex) {
subHeadingList[i].content = content.replace(regex, replaceKeyword);
break;
// 컨텐츠 탐색 (탐색 로직 같음)
for (let i = 0; i < splittedContent.length; i++) {
if (i === splittedContent.length - 1) {
content += splittedContent[splittedContent.length - 1];
break;
}
index++;

if (index === searchIndex) {
content += splittedContent[i] + replaceKeyword;
} else {
content += splittedContent[i] + keyword;
}
}

subHeadingList.push({ ...subHeading, name, content });
}

updateSubHeading(subHeadingList);
await updateSubHeading(subHeadingList);
};

useEffect(() => {
Expand Down Expand Up @@ -233,11 +276,17 @@ const ChangeKeywordModal = ({ onChange, onClose }: Props) => {
<div style={{ marginTop: 32, gap: 12, flexDirection: 'row' }}>
<button
className="transform all"
disabled={findKeywordCount === 0}
onClick={handleClickChangeAllButton}
>
전체 바꾸기
</button>
<button className="transform" onClick={handleClickChangeButton}>

<button
className="transform"
disabled={findKeywordCount === 0}
onClick={handleClickChangeButton}
>
바꾸기
</button>
</div>
Expand Down
31 changes: 26 additions & 5 deletions src/components/SummaryPage/SummaryScriptBox/SummaryScriptBox.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';

import ModifyIcon from '@/assets/icons/modify.svg?react';
Expand Down Expand Up @@ -41,36 +41,50 @@ const SummaryScriptBox = () => {
const [focusId, setFocusId] = useState(1);
const [keyword, setKeyword] = useState('');

const handleChangeKeyword = (keyword: string) => {
const updateFindKeywordCount = useCallback(() => {
if (keyword === '') {
setFindKeywordCount(0);
} else {
setFindKeywordCount(
summaryVideo.subHeading.reduce(
(total, { content }) => total + (content.split(keyword).length - 1),
(total, { name, content }) =>
total +
(name.split(keyword).length - 1) +
(content.split(keyword).length - 1),
0,
),
);
}
}, [summaryVideo, keyword, setFindKeywordCount]);

const handleChangeKeyword = (keyword: string) => {
setKeyword(keyword);
updateFindKeywordCount();
};

const formattedScriptList = useMemo(() => {
return summaryVideo.subHeading.map(({ content, ...others }) => {
return summaryVideo.subHeading.map(({ name, content, ...others }) => {
if ((searchIsOpen || transformModalIsOpen) && keyword !== '') {
name = name
.split(keyword)
.map((s) => escapeHTML(s))
.join(`<mark>${escapeHTML(keyword)}</mark>`);

content = content
.split(keyword)
.map((s) => escapeHTML(s))
.join(`<mark>${escapeHTML(keyword)}</mark>`);
} else {
name = escapeHTML(name);
content = escapeHTML(content);
}

name = name.replace(/\n/g, '<br>');
content = content.replace(/\n/g, '<br>');

return {
content,
name,
...others,
};
});
Expand Down Expand Up @@ -101,6 +115,10 @@ const SummaryScriptBox = () => {
});
}, [searchIsOpen, transformModalIsOpen, keyword, searchIndex]);

useEffect(() => {
updateFindKeywordCount();
}, [updateFindKeywordCount]);

return (
<ScriptBox style={{ width }}>
<div className="tools">
Expand Down Expand Up @@ -137,7 +155,10 @@ const SummaryScriptBox = () => {
<PlayIcon width={36} height={36} />
</span>

<span className="script-title">{script.name}</span>
<span
className="script-title"
dangerouslySetInnerHTML={{ __html: script.name }}
/>
</div>

<span className="script-badge">
Expand Down
13 changes: 7 additions & 6 deletions src/components/common/ToastList/ToastItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { useRecoilState } from 'recoil';
import { useSetRecoilState } from 'recoil';

import { IToast, toastListState } from '@/stores/toast';

Expand All @@ -8,23 +8,24 @@ type Props = {
};

const ToastItem = ({ toast }: Props) => {
const [list, setList] = useRecoilState(toastListState);
const setList = useSetRecoilState(toastListState);
const [isShow, setIsShow] = useState(true);

useEffect(() => {
const removeTimer = setTimeout(() => {
setList(list.filter((item) => item.id !== toast.id));
}, 1000 * 4);
setList((list) => list.filter((item) => item.id !== toast.id));
}, 1000 * 3);

const hideTimer = setTimeout(() => {
setIsShow(false);
}, 1000 * 3.5);
}, 1000 * 2.5);

return () => {
clearTimeout(hideTimer);
clearTimeout(removeTimer);
};
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
<div className={`toast ${isShow ? 'show' : 'hide'}`}>{toast.content}</div>
Expand Down
3 changes: 3 additions & 0 deletions src/components/common/ToastList/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ export const Container = styled.div`
left: 50%;
bottom: 60px;
transform: translateX(-50%);
display: flex;
flex-direction: column;
gap: 8px;
& > .toast {
display: flex;
Expand Down
39 changes: 25 additions & 14 deletions src/styles/SummaryPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ export const ScriptBox = styled.div`
& span.script-title {
color: ${(props) => props.theme.color.gray500};
${(props) => props.theme.typography.Subheader1}
${(props) => props.theme.typography.Subheader1};
}
& span.play-button {
Expand All @@ -263,18 +263,6 @@ export const ScriptBox = styled.div`
font-weight: 600;
line-height: 2;
color: #5d5b5b;
& > mark {
padding: 4px 8px;
border-radius: 4px;
background-color: #d2f1b4;
line-height: 1.6;
color: ${(props) => props.theme.color.gray500};
&.active {
background-color: #a4de6b;
}
}
}
& div.resize-thumb {
Expand All @@ -285,6 +273,18 @@ export const ScriptBox = styled.div`
height: 100%;
cursor: ew-resize;
}
& mark {
padding: 4px 8px;
border-radius: 4px;
background-color: #d2f1b4;
line-height: 1.6;
color: ${(props) => props.theme.color.gray500};
&.active {
background-color: #a4de6b;
}
}
`;

export const Dropdown = styled.div`
Expand Down Expand Up @@ -487,14 +487,25 @@ export const ModalContainer = styled(BlurBackground)`
border-radius: 12px;
background-color: ${(props) => props.theme.color.gray500};
color: white;
transition: 0.1s;
cursor: pointer;
${(props) => props.theme.typography.Body1};
&.all {
background-color: white;
background-color: white !important;
border: solid 1.5px ${(props) => props.theme.color.gray200};
color: ${(props) => props.theme.color.gray400};
}
&:disabled {
background-color: ${(props) => props.theme.color.gray200};
color: ${(props) => props.theme.color.gray300};
cursor: not-allowed;
&.all {
color: ${(props) => props.theme.color.gray300};
}
}
}
`;

Expand Down

0 comments on commit c6d86ea

Please sign in to comment.