Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aaa8088c82 |
@@ -12,6 +12,7 @@ import {
|
|||||||
buildTextShadow,
|
buildTextShadow,
|
||||||
formatDate,
|
formatDate,
|
||||||
} from "@/lib/media";
|
} from "@/lib/media";
|
||||||
|
import { clampTitle } from "@/lib/title";
|
||||||
import { useTitleSubtitleStyles } from "@/hooks/useTitleSubtitleStyles";
|
import { useTitleSubtitleStyles } from "@/hooks/useTitleSubtitleStyles";
|
||||||
import { useMaterials } from "@/hooks/useMaterials";
|
import { useMaterials } from "@/hooks/useMaterials";
|
||||||
import { useRefAudios } from "@/hooks/useRefAudios";
|
import { useRefAudios } from "@/hooks/useRefAudios";
|
||||||
@@ -19,6 +20,7 @@ import { useBgm } from "@/hooks/useBgm";
|
|||||||
import { useMediaPlayers } from "@/hooks/useMediaPlayers";
|
import { useMediaPlayers } from "@/hooks/useMediaPlayers";
|
||||||
import { useGeneratedVideos } from "@/hooks/useGeneratedVideos";
|
import { useGeneratedVideos } from "@/hooks/useGeneratedVideos";
|
||||||
import { useHomePersistence } from "@/hooks/useHomePersistence";
|
import { useHomePersistence } from "@/hooks/useHomePersistence";
|
||||||
|
import { useTitleInput } from "@/hooks/useTitleInput";
|
||||||
import { useAuth } from "@/contexts/AuthContext";
|
import { useAuth } from "@/contexts/AuthContext";
|
||||||
import { useTask } from "@/contexts/TaskContext";
|
import { useTask } from "@/contexts/TaskContext";
|
||||||
import VideoPreviewModal from "@/components/VideoPreviewModal";
|
import VideoPreviewModal from "@/components/VideoPreviewModal";
|
||||||
@@ -35,13 +37,6 @@ import { PreviewPanel } from "@/components/home/PreviewPanel";
|
|||||||
import { HistoryList } from "@/components/home/HistoryList";
|
import { HistoryList } from "@/components/home/HistoryList";
|
||||||
|
|
||||||
const API_BASE = getApiBaseUrl();
|
const API_BASE = getApiBaseUrl();
|
||||||
const TITLE_MAX_LENGTH = 15;
|
|
||||||
const clampTitle = (value: string) => value.slice(0, TITLE_MAX_LENGTH);
|
|
||||||
const applyTitleLimit = (prev: string, next: string) => {
|
|
||||||
if (next.length <= TITLE_MAX_LENGTH) return next;
|
|
||||||
if (prev.length >= TITLE_MAX_LENGTH) return prev;
|
|
||||||
return next.slice(0, TITLE_MAX_LENGTH);
|
|
||||||
};
|
|
||||||
|
|
||||||
const VOICES = [
|
const VOICES = [
|
||||||
{ id: "zh-CN-YunxiNeural", name: "云溪 (男声-年轻)" },
|
{ id: "zh-CN-YunxiNeural", name: "云溪 (男声-年轻)" },
|
||||||
@@ -121,8 +116,6 @@ export default function Home() {
|
|||||||
|
|
||||||
// 字幕和标题相关状态
|
// 字幕和标题相关状态
|
||||||
const [videoTitle, setVideoTitle] = useState<string>("");
|
const [videoTitle, setVideoTitle] = useState<string>("");
|
||||||
const isTitleComposingRef = useRef(false);
|
|
||||||
const titleCommittedRef = useRef("");
|
|
||||||
const [enableSubtitles, setEnableSubtitles] = useState<boolean>(true);
|
const [enableSubtitles, setEnableSubtitles] = useState<boolean>(true);
|
||||||
const [selectedSubtitleStyleId, setSelectedSubtitleStyleId] = useState<string>("");
|
const [selectedSubtitleStyleId, setSelectedSubtitleStyleId] = useState<string>("");
|
||||||
const [selectedTitleStyleId, setSelectedTitleStyleId] = useState<string>("");
|
const [selectedTitleStyleId, setSelectedTitleStyleId] = useState<string>("");
|
||||||
@@ -316,37 +309,17 @@ export default function Home() {
|
|||||||
selectedRefAudio,
|
selectedRefAudio,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
const syncTitleToPublish = (value: string) => {
|
||||||
if (isTitleComposingRef.current) return;
|
|
||||||
titleCommittedRef.current = videoTitle;
|
|
||||||
}, [videoTitle]);
|
|
||||||
|
|
||||||
const commitTitle = (value: string) => {
|
|
||||||
titleCommittedRef.current = value;
|
|
||||||
setVideoTitle(value);
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
localStorage.setItem(`vigent_${storageKey}_publish_title`, value);
|
localStorage.setItem(`vigent_${storageKey}_publish_title`, value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTitleChange = (value: string) => {
|
const titleInput = useTitleInput({
|
||||||
if (isTitleComposingRef.current) {
|
value: videoTitle,
|
||||||
setVideoTitle(value);
|
onChange: setVideoTitle,
|
||||||
return;
|
onCommit: syncTitleToPublish,
|
||||||
}
|
});
|
||||||
const nextValue = applyTitleLimit(titleCommittedRef.current, value);
|
|
||||||
commitTitle(nextValue);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleTitleCompositionStart = () => {
|
|
||||||
isTitleComposingRef.current = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleTitleCompositionEnd = (value: string) => {
|
|
||||||
isTitleComposingRef.current = false;
|
|
||||||
const nextValue = applyTitleLimit(titleCommittedRef.current, value);
|
|
||||||
commitTitle(nextValue);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 加载素材列表和历史视频
|
// 加载素材列表和历史视频
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -606,11 +579,10 @@ export default function Home() {
|
|||||||
|
|
||||||
// 更新首页标题
|
// 更新首页标题
|
||||||
const nextTitle = clampTitle(data.title || "");
|
const nextTitle = clampTitle(data.title || "");
|
||||||
setVideoTitle(nextTitle);
|
titleInput.commitValue(nextTitle);
|
||||||
|
|
||||||
// 同步到发布页 localStorage
|
// 同步到发布页 localStorage
|
||||||
console.log("[Home] 保存到 publish localStorage - title:", data.title, "tags:", data.tags);
|
console.log("[Home] 保存到 publish localStorage - title:", data.title, "tags:", data.tags);
|
||||||
localStorage.setItem(`vigent_${storageKey}_publish_title`, nextTitle);
|
|
||||||
localStorage.setItem(`vigent_${storageKey}_publish_tags`, JSON.stringify(data.tags || []));
|
localStorage.setItem(`vigent_${storageKey}_publish_tags`, JSON.stringify(data.tags || []));
|
||||||
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -748,9 +720,9 @@ export default function Home() {
|
|||||||
showStylePreview={showStylePreview}
|
showStylePreview={showStylePreview}
|
||||||
onTogglePreview={() => setShowStylePreview((prev) => !prev)}
|
onTogglePreview={() => setShowStylePreview((prev) => !prev)}
|
||||||
videoTitle={videoTitle}
|
videoTitle={videoTitle}
|
||||||
onTitleChange={handleTitleChange}
|
onTitleChange={titleInput.handleChange}
|
||||||
onTitleCompositionStart={handleTitleCompositionStart}
|
onTitleCompositionStart={titleInput.handleCompositionStart}
|
||||||
onTitleCompositionEnd={handleTitleCompositionEnd}
|
onTitleCompositionEnd={titleInput.handleCompositionEnd}
|
||||||
titleStyles={titleStyles}
|
titleStyles={titleStyles}
|
||||||
selectedTitleStyleId={selectedTitleStyleId}
|
selectedTitleStyleId={selectedTitleStyleId}
|
||||||
onSelectTitleStyle={setSelectedTitleStyleId}
|
onSelectTitleStyle={setSelectedTitleStyleId}
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect, useMemo, useRef } from "react";
|
import { useState, useEffect, useMemo } from "react";
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import api from "@/lib/axios";
|
import api from "@/lib/axios";
|
||||||
import { getApiBaseUrl, formatDate, resolveMediaUrl, isAbsoluteUrl } from "@/lib/media";
|
import { getApiBaseUrl, formatDate, resolveMediaUrl, isAbsoluteUrl } from "@/lib/media";
|
||||||
|
import { clampTitle } from "@/lib/title";
|
||||||
import { useAuth } from "@/contexts/AuthContext";
|
import { useAuth } from "@/contexts/AuthContext";
|
||||||
import AccountSettingsDropdown from "@/components/AccountSettingsDropdown";
|
import AccountSettingsDropdown from "@/components/AccountSettingsDropdown";
|
||||||
import VideoPreviewModal from "@/components/VideoPreviewModal";
|
import VideoPreviewModal from "@/components/VideoPreviewModal";
|
||||||
|
import { useTitleInput } from "@/hooks/useTitleInput";
|
||||||
import {
|
import {
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
RotateCcw,
|
RotateCcw,
|
||||||
@@ -25,13 +27,6 @@ const fetcher = (url: string) => api.get(url).then((res) => res.data);
|
|||||||
|
|
||||||
// 动态获取 API 地址:服务端使用 localhost,客户端使用当前域名
|
// 动态获取 API 地址:服务端使用 localhost,客户端使用当前域名
|
||||||
const API_BASE = getApiBaseUrl();
|
const API_BASE = getApiBaseUrl();
|
||||||
const TITLE_MAX_LENGTH = 15;
|
|
||||||
const clampTitle = (value: string) => value.slice(0, TITLE_MAX_LENGTH);
|
|
||||||
const applyTitleLimit = (prev: string, next: string) => {
|
|
||||||
if (next.length <= TITLE_MAX_LENGTH) return next;
|
|
||||||
if (prev.length >= TITLE_MAX_LENGTH) return prev;
|
|
||||||
return next.slice(0, TITLE_MAX_LENGTH);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface Account {
|
interface Account {
|
||||||
platform: string;
|
platform: string;
|
||||||
@@ -54,9 +49,7 @@ export default function PublishPage() {
|
|||||||
const [selectedPlatforms, setSelectedPlatforms] = useState<string[]>([]);
|
const [selectedPlatforms, setSelectedPlatforms] = useState<string[]>([]);
|
||||||
const [title, setTitle] = useState<string>("");
|
const [title, setTitle] = useState<string>("");
|
||||||
const [tags, setTags] = useState<string>("");
|
const [tags, setTags] = useState<string>("");
|
||||||
const isTitleComposingRef = useRef(false);
|
const [isPublishing, setIsPublishing] = useState(false);
|
||||||
const titleCommittedRef = useRef("");
|
|
||||||
const [isPublishing, setIsPublishing] = useState(false);
|
|
||||||
const [publishResults, setPublishResults] = useState<any[]>([]);
|
const [publishResults, setPublishResults] = useState<any[]>([]);
|
||||||
const [scheduleMode, setScheduleMode] = useState<"now" | "scheduled">("now");
|
const [scheduleMode, setScheduleMode] = useState<"now" | "scheduled">("now");
|
||||||
const [publishTime, setPublishTime] = useState<string>("");
|
const [publishTime, setPublishTime] = useState<string>("");
|
||||||
@@ -69,15 +62,10 @@ export default function PublishPage() {
|
|||||||
// 是否已从 localStorage 恢复完成
|
// 是否已从 localStorage 恢复完成
|
||||||
const [isRestored, setIsRestored] = useState(false);
|
const [isRestored, setIsRestored] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
const titleInput = useTitleInput({
|
||||||
if (isTitleComposingRef.current) return;
|
value: title,
|
||||||
titleCommittedRef.current = title;
|
onChange: setTitle,
|
||||||
}, [title]);
|
});
|
||||||
|
|
||||||
const commitTitle = (value: string) => {
|
|
||||||
titleCommittedRef.current = value;
|
|
||||||
setTitle(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 加载账号和视频列表
|
// 加载账号和视频列表
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -100,16 +88,12 @@ export default function PublishPage() {
|
|||||||
|
|
||||||
// 从 localStorage 恢复用户输入(等待认证完成后)
|
// 从 localStorage 恢复用户输入(等待认证完成后)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("[Publish] 恢复检查 - isAuthLoading:", isAuthLoading, "userId:", userId);
|
if (isAuthLoading) return;
|
||||||
if (isAuthLoading) return;
|
|
||||||
|
// 从 localStorage 恢复用户输入(带用户隔离,未登录用户使用 guest)
|
||||||
console.log("[Publish] 开始从 localStorage 恢复数据,storageKey:", storageKey);
|
const savedTitle = localStorage.getItem(`vigent_${storageKey}_publish_title`);
|
||||||
// 从 localStorage 恢复用户输入(带用户隔离,未登录用户使用 guest)
|
const savedTags = localStorage.getItem(`vigent_${storageKey}_publish_tags`);
|
||||||
const savedTitle = localStorage.getItem(`vigent_${storageKey}_publish_title`);
|
|
||||||
const savedTags = localStorage.getItem(`vigent_${storageKey}_publish_tags`);
|
|
||||||
|
|
||||||
console.log("[Publish] localStorage 数据:", { savedTitle, savedTags });
|
|
||||||
|
|
||||||
if (savedTitle) setTitle(clampTitle(savedTitle));
|
if (savedTitle) setTitle(clampTitle(savedTitle));
|
||||||
if (savedTags) {
|
if (savedTags) {
|
||||||
// 兼容 JSON 数组格式(AI 生成)和字符串格式(手动输入)
|
// 兼容 JSON 数组格式(AI 生成)和字符串格式(手动输入)
|
||||||
@@ -125,10 +109,9 @@ export default function PublishPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 恢复完成后才允许保存
|
// 恢复完成后才允许保存
|
||||||
setIsRestored(true);
|
setIsRestored(true);
|
||||||
console.log("[Publish] 恢复完成,isRestored = true");
|
}, [storageKey, isAuthLoading]);
|
||||||
}, [storageKey, isAuthLoading]);
|
|
||||||
|
|
||||||
// 保存用户输入到 localStorage(恢复完成后才保存,未登录用户也可保存)
|
// 保存用户输入到 localStorage(恢复完成后才保存,未登录用户也可保存)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -537,22 +520,9 @@ export default function PublishPage() {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={title}
|
value={title}
|
||||||
onChange={(e) => {
|
onChange={(e) => titleInput.handleChange(e.target.value)}
|
||||||
if (isTitleComposingRef.current) {
|
onCompositionStart={titleInput.handleCompositionStart}
|
||||||
setTitle(e.target.value);
|
onCompositionEnd={(e) => titleInput.handleCompositionEnd(e.currentTarget.value)}
|
||||||
return;
|
|
||||||
}
|
|
||||||
const nextValue = applyTitleLimit(titleCommittedRef.current, e.target.value);
|
|
||||||
commitTitle(nextValue);
|
|
||||||
}}
|
|
||||||
onCompositionStart={() => {
|
|
||||||
isTitleComposingRef.current = true;
|
|
||||||
}}
|
|
||||||
onCompositionEnd={(e) => {
|
|
||||||
isTitleComposingRef.current = false;
|
|
||||||
const nextValue = applyTitleLimit(titleCommittedRef.current, e.currentTarget.value);
|
|
||||||
commitTitle(nextValue);
|
|
||||||
}}
|
|
||||||
placeholder="输入视频标题..."
|
placeholder="输入视频标题..."
|
||||||
className="w-full p-3 bg-black/30 border border-white/10 rounded-xl text-white placeholder-gray-500"
|
className="w-full p-3 bg-black/30 border border-white/10 rounded-xl text-white placeholder-gray-500"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { clampTitle } from "@/lib/title";
|
||||||
const TITLE_MAX_LENGTH = 15;
|
|
||||||
const clampTitle = (value: string) => value.slice(0, TITLE_MAX_LENGTH);
|
|
||||||
|
|
||||||
interface RefAudio {
|
interface RefAudio {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -20,19 +20,15 @@ export const useMaterials = ({
|
|||||||
}: UseMaterialsOptions) => {
|
}: UseMaterialsOptions) => {
|
||||||
const [materials, setMaterials] = useState<Material[]>([]);
|
const [materials, setMaterials] = useState<Material[]>([]);
|
||||||
const [fetchError, setFetchError] = useState<string | null>(null);
|
const [fetchError, setFetchError] = useState<string | null>(null);
|
||||||
const [debugData, setDebugData] = useState<string>("");
|
|
||||||
const [isUploading, setIsUploading] = useState(false);
|
const [isUploading, setIsUploading] = useState(false);
|
||||||
const [uploadProgress, setUploadProgress] = useState(0);
|
const [uploadProgress, setUploadProgress] = useState(0);
|
||||||
const [uploadError, setUploadError] = useState<string | null>(null);
|
const [uploadError, setUploadError] = useState<string | null>(null);
|
||||||
const [uploadData, setUploadData] = useState<string>("");
|
|
||||||
|
|
||||||
const fetchMaterials = useCallback(async () => {
|
const fetchMaterials = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setFetchError(null);
|
setFetchError(null);
|
||||||
setDebugData("Loading...");
|
|
||||||
|
|
||||||
const { data } = await api.get(`/api/materials?t=${new Date().getTime()}`);
|
const { data } = await api.get(`/api/materials?t=${new Date().getTime()}`);
|
||||||
setDebugData(JSON.stringify(data).substring(0, 200));
|
|
||||||
const nextMaterials = data.materials || [];
|
const nextMaterials = data.materials || [];
|
||||||
setMaterials(nextMaterials);
|
setMaterials(nextMaterials);
|
||||||
|
|
||||||
@@ -45,7 +41,6 @@ export const useMaterials = ({
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取素材失败:", error);
|
console.error("获取素材失败:", error);
|
||||||
setFetchError(String(error));
|
setFetchError(String(error));
|
||||||
setDebugData(`Error: ${String(error)}`);
|
|
||||||
}
|
}
|
||||||
}, [selectedMaterial, setSelectedMaterial]);
|
}, [selectedMaterial, setSelectedMaterial]);
|
||||||
|
|
||||||
@@ -94,7 +89,6 @@ export const useMaterials = ({
|
|||||||
setUploadProgress(100);
|
setUploadProgress(100);
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
fetchMaterials();
|
fetchMaterials();
|
||||||
setUploadData("");
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error("Upload failed:", err);
|
console.error("Upload failed:", err);
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
@@ -108,11 +102,9 @@ export const useMaterials = ({
|
|||||||
return {
|
return {
|
||||||
materials,
|
materials,
|
||||||
fetchError,
|
fetchError,
|
||||||
debugData,
|
|
||||||
isUploading,
|
isUploading,
|
||||||
uploadProgress,
|
uploadProgress,
|
||||||
uploadError,
|
uploadError,
|
||||||
uploadData,
|
|
||||||
setUploadError,
|
setUploadError,
|
||||||
fetchMaterials,
|
fetchMaterials,
|
||||||
deleteMaterial,
|
deleteMaterial,
|
||||||
|
|||||||
66
frontend/src/hooks/useTitleInput.ts
Normal file
66
frontend/src/hooks/useTitleInput.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { useCallback, useEffect, useRef } from "react";
|
||||||
|
import { applyTitleLimit, TITLE_MAX_LENGTH } from "@/lib/title";
|
||||||
|
|
||||||
|
interface UseTitleInputOptions {
|
||||||
|
value: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
onCommit?: (value: string) => void;
|
||||||
|
maxLength?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useTitleInput = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
onCommit,
|
||||||
|
maxLength = TITLE_MAX_LENGTH,
|
||||||
|
}: UseTitleInputOptions) => {
|
||||||
|
const isComposingRef = useRef(false);
|
||||||
|
const committedRef = useRef(value);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isComposingRef.current) return;
|
||||||
|
committedRef.current = value;
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
const commitValue = useCallback(
|
||||||
|
(nextValue: string) => {
|
||||||
|
committedRef.current = nextValue;
|
||||||
|
onChange(nextValue);
|
||||||
|
onCommit?.(nextValue);
|
||||||
|
},
|
||||||
|
[onChange, onCommit]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(nextValue: string) => {
|
||||||
|
if (isComposingRef.current) {
|
||||||
|
onChange(nextValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const limited = applyTitleLimit(committedRef.current, nextValue, maxLength);
|
||||||
|
commitValue(limited);
|
||||||
|
},
|
||||||
|
[maxLength, onChange, commitValue]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCompositionStart = useCallback(() => {
|
||||||
|
isComposingRef.current = true;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleCompositionEnd = useCallback(
|
||||||
|
(nextValue: string) => {
|
||||||
|
isComposingRef.current = false;
|
||||||
|
const limited = applyTitleLimit(committedRef.current, nextValue, maxLength);
|
||||||
|
commitValue(limited);
|
||||||
|
},
|
||||||
|
[maxLength, commitValue]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleChange,
|
||||||
|
handleCompositionStart,
|
||||||
|
handleCompositionEnd,
|
||||||
|
commitValue,
|
||||||
|
maxLength,
|
||||||
|
};
|
||||||
|
};
|
||||||
14
frontend/src/lib/title.ts
Normal file
14
frontend/src/lib/title.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export const TITLE_MAX_LENGTH = 15;
|
||||||
|
|
||||||
|
export const clampTitle = (value: string, maxLength: number = TITLE_MAX_LENGTH) =>
|
||||||
|
value.slice(0, maxLength);
|
||||||
|
|
||||||
|
export const applyTitleLimit = (
|
||||||
|
prev: string,
|
||||||
|
next: string,
|
||||||
|
maxLength: number = TITLE_MAX_LENGTH
|
||||||
|
) => {
|
||||||
|
if (next.length <= maxLength) return next;
|
||||||
|
if (prev.length >= maxLength) return prev;
|
||||||
|
return next.slice(0, maxLength);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user