diff --git a/src/components/SummaryPage/SummaryScriptBox/ChangeKeyword/ChangeKeywordModal.tsx b/src/components/SummaryPage/SummaryScriptBox/ChangeKeyword/ChangeKeywordModal.tsx
index 24c8c4e..4de590d 100644
--- a/src/components/SummaryPage/SummaryScriptBox/ChangeKeyword/ChangeKeywordModal.tsx
+++ b/src/components/SummaryPage/SummaryScriptBox/ChangeKeyword/ChangeKeywordModal.tsx
@@ -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 = {
@@ -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 });
@@ -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);
}
@@ -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(() => {
@@ -233,11 +276,17 @@ const ChangeKeywordModal = ({ onChange, onClose }: Props) => {
-
diff --git a/src/components/SummaryPage/SummaryScriptBox/SummaryScriptBox.tsx b/src/components/SummaryPage/SummaryScriptBox/SummaryScriptBox.tsx
index 8b67257..24a95a8 100644
--- a/src/components/SummaryPage/SummaryScriptBox/SummaryScriptBox.tsx
+++ b/src/components/SummaryPage/SummaryScriptBox/SummaryScriptBox.tsx
@@ -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';
@@ -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(`${escapeHTML(keyword)}`);
+
content = content
.split(keyword)
.map((s) => escapeHTML(s))
.join(`${escapeHTML(keyword)}`);
} else {
+ name = escapeHTML(name);
content = escapeHTML(content);
}
+ name = name.replace(/\n/g, '
');
content = content.replace(/\n/g, '
');
return {
content,
+ name,
...others,
};
});
@@ -101,6 +115,10 @@ const SummaryScriptBox = () => {
});
}, [searchIsOpen, transformModalIsOpen, keyword, searchIndex]);
+ useEffect(() => {
+ updateFindKeywordCount();
+ }, [updateFindKeywordCount]);
+
return (
@@ -137,7 +155,10 @@ const SummaryScriptBox = () => {
-
{script.name}
+
diff --git a/src/components/common/ToastList/ToastItem.tsx b/src/components/common/ToastList/ToastItem.tsx
index 3c856b7..ab673ac 100644
--- a/src/components/common/ToastList/ToastItem.tsx
+++ b/src/components/common/ToastList/ToastItem.tsx
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
-import { useRecoilState } from 'recoil';
+import { useSetRecoilState } from 'recoil';
import { IToast, toastListState } from '@/stores/toast';
@@ -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 (
{toast.content}
diff --git a/src/components/common/ToastList/style.ts b/src/components/common/ToastList/style.ts
index c880bc7..3197dd9 100644
--- a/src/components/common/ToastList/style.ts
+++ b/src/components/common/ToastList/style.ts
@@ -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;
diff --git a/src/styles/SummaryPage.ts b/src/styles/SummaryPage.ts
index 0c9e379..6eec058 100644
--- a/src/styles/SummaryPage.ts
+++ b/src/styles/SummaryPage.ts
@@ -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 {
@@ -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 {
@@ -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`
@@ -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};
+ }
+ }
}
`;