|
@@ -16,6 +16,8 @@ import Popup from "@/components/popup/popup";
|
|
|
import PopupRecorder, { ECloneStatus } from "./components/popup-recorder/index";
|
|
|
import PopupTryout from "./components/popup-tryout/index";
|
|
|
import style from "./index.module.less";
|
|
|
+import { useVoiceStore } from "@/store/voiceStore";
|
|
|
+import { EVoiceStatus, TVoiceItem } from "@/types/voice";
|
|
|
|
|
|
interface Props {
|
|
|
value?: ICharacter;
|
|
@@ -35,95 +37,16 @@ export default ({ profileId, onPlay }: Props) => {
|
|
|
const [voiceName, setVoiceName] = useState("");
|
|
|
const [voiceIndex, setVoiceIndex] = useState(-1);
|
|
|
|
|
|
- const { saveCharacter, fetchCharacter, fetchVoiceCloneHistory } =
|
|
|
- useCharacterStore();
|
|
|
+ // const { saveCharacter, fetchCharacter, fetchVoiceCloneHistory } =
|
|
|
+ // useCharacterStore();
|
|
|
+ const {getVoices} = useVoiceStore()
|
|
|
+ const voices = useVoiceStore(state => state.voices)
|
|
|
const character = useCharacterStore((state) => state.character);
|
|
|
- const voiceList = useCharacterStore((state) => state.voiceList);
|
|
|
+ const myVoices = useVoiceStore(state => state.myVoices)
|
|
|
const appConfig = useAppStore((state) => state.appConfig);
|
|
|
|
|
|
- const createVoiceNameText = (voiceName: string) => {
|
|
|
- const i = voiceList.findIndex((item) => item.voiceName === voiceName);
|
|
|
- console.log(voiceName, i);
|
|
|
- if (i === -1) return "";
|
|
|
- return `克隆声音 0${i + 1}`;
|
|
|
- };
|
|
|
- // 克隆列表操作
|
|
|
- const renderRightColumn = (item?: TEntityVoiceCloneRecord) => {
|
|
|
- if (item?.status == CloneVoiceStatus.CloneVoiceStatusExpired) {
|
|
|
- return <WemetaRadio disabled></WemetaRadio>;
|
|
|
- }
|
|
|
-
|
|
|
- if (item?.status == CloneVoiceStatus.CloneVoiceStatusSuccess) {
|
|
|
- const notSystemVoice = (character?.voice !== character?.defaultSystemVoice)
|
|
|
- const isSameVoice = (item.voiceName === character?.voice)
|
|
|
- return (
|
|
|
- <WemetaRadio
|
|
|
- checked={ isSameVoice && notSystemVoice}
|
|
|
- ></WemetaRadio>
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- if (item?.status == CloneVoiceStatus.CloneVoiceStatusUnconfirmed) {
|
|
|
- return (
|
|
|
- <View className="text-14 bg-primary text-black leading-22 px-12 py-6 rounded-28 active:pressed-button">
|
|
|
- 试听
|
|
|
- </View>
|
|
|
- );
|
|
|
- }
|
|
|
- return <></>;
|
|
|
- };
|
|
|
-
|
|
|
- // 克隆状态
|
|
|
- const renderCloneStatus = (item: TEntityVoiceCloneRecord) => {
|
|
|
- if (item.status === CloneVoiceStatus.CloneVoiceStatusSuccess) {
|
|
|
- return (
|
|
|
- <View className={`text-12 leading-20 text-gray-45`}>
|
|
|
- {item.createdAt && formatDateFull(new Date(item.createdAt))}
|
|
|
- </View>
|
|
|
- );
|
|
|
- }
|
|
|
- if (item.status === CloneVoiceStatus.CloneVoiceStatusUnconfirmed) {
|
|
|
- return <View className={`text-12 leading-20 text-orange`}>试听确认</View>;
|
|
|
- }
|
|
|
-
|
|
|
- if (item.status == CloneVoiceStatus.CloneVoiceStatusExpired) {
|
|
|
- return <View className={`text-12 leading-20 text-orange`}>已过期</View>;
|
|
|
- }
|
|
|
- return <View>当前时间</View>;
|
|
|
- };
|
|
|
-
|
|
|
- const renderCloneList = () => {
|
|
|
- if (!voiceList.length) {
|
|
|
- return <EmptyData></EmptyData>;
|
|
|
- }
|
|
|
- return (
|
|
|
- <CardList>
|
|
|
- {voiceList.map((item, _index) => {
|
|
|
- return (
|
|
|
- <CardListItem
|
|
|
- rightRenderer={() => {
|
|
|
- return renderRightColumn(item);
|
|
|
- }}
|
|
|
- onClick={() => handleSelect(item, _index)}
|
|
|
- >
|
|
|
- <View className="flex items-center gap-16">
|
|
|
- <View className={style.listIcon}>
|
|
|
- <Image src={IconWave} className={style.iconImage}></Image>
|
|
|
- </View>
|
|
|
- <View className="flex flex-col gap-4">
|
|
|
- <View className={style.ListItemText}>
|
|
|
- {/* deprecated getCloneVoiceIdentifier */}
|
|
|
- {item.voiceAlias ?? getCloneVoiceIdentifier(_index + 1)}
|
|
|
- </View>
|
|
|
- {renderCloneStatus(item)}
|
|
|
- </View>
|
|
|
- </View>
|
|
|
- </CardListItem>
|
|
|
- );
|
|
|
- })}
|
|
|
- </CardList>
|
|
|
- );
|
|
|
- };
|
|
|
+
|
|
|
+
|
|
|
|
|
|
const handleSelect = (item: TEntityVoiceCloneRecord, index: number) => {
|
|
|
setVoiceIndex(index);
|
|
@@ -138,10 +61,7 @@ export default ({ profileId, onPlay }: Props) => {
|
|
|
voiceIndex: index,
|
|
|
});
|
|
|
}
|
|
|
- saveCharacter({
|
|
|
- profileId: profileId,
|
|
|
- voice: item.voiceName,
|
|
|
- });
|
|
|
+ // save
|
|
|
}
|
|
|
|
|
|
if (item.status == CloneVoiceStatus.CloneVoiceStatusUnconfirmed) {
|
|
@@ -158,8 +78,8 @@ export default ({ profileId, onPlay }: Props) => {
|
|
|
|
|
|
const fetchVoiceList = async () => {
|
|
|
if (profileId) {
|
|
|
- const r = await fetchVoiceCloneHistory(profileId);
|
|
|
- const result = r.find((item) => item.status === "pending");
|
|
|
+ await getVoices();
|
|
|
+ const result = voices.find((item) => !item.isSystem && item.status === 'processing');
|
|
|
if (result) {
|
|
|
intervalRef.current = setTimeout(() => fetchVoiceList(), 3000);
|
|
|
} else {
|
|
@@ -170,7 +90,7 @@ export default ({ profileId, onPlay }: Props) => {
|
|
|
|
|
|
const handleSureAction = async () => {
|
|
|
setShow(false);
|
|
|
- await voiceCloneConfirm(voiceList[voiceIndex].voiceName!);
|
|
|
+ await voiceCloneConfirm(myVoices[voiceIndex].voiceName!);
|
|
|
fetchVoiceList();
|
|
|
};
|
|
|
|
|
@@ -194,26 +114,22 @@ export default ({ profileId, onPlay }: Props) => {
|
|
|
|
|
|
const calcRemainCloneNum = () => {
|
|
|
if (appConfig?.maxCloneNum) {
|
|
|
- const clonedNum = voiceList.filter(
|
|
|
- (item) =>
|
|
|
- item.status === CloneVoiceStatus.CloneVoiceStatusSuccess ||
|
|
|
- item.status === CloneVoiceStatus.CloneVoiceStatusUnconfirmed,
|
|
|
- ).length;
|
|
|
- const remainNum = appConfig.maxCloneNum - clonedNum;
|
|
|
- return remainNum < 0 ? 0 : remainNum;
|
|
|
+ // const clonedNum = myVoices.filter(
|
|
|
+ // (item) =>
|
|
|
+ // item.status === CloneVoiceStatus.CloneVoiceStatusSuccess ||
|
|
|
+ // item.status === CloneVoiceStatus.CloneVoiceStatusUnconfirmed,
|
|
|
+ // ).length;
|
|
|
+ // const remainNum = appConfig.maxCloneNum - clonedNum;
|
|
|
+ // return remainNum < 0 ? 0 : remainNum;
|
|
|
}
|
|
|
- return voiceList.length;
|
|
|
+ return myVoices.length;
|
|
|
};
|
|
|
|
|
|
- const initPage = async (profileId: string) => {
|
|
|
- await fetchCharacter(profileId);
|
|
|
- fetchVoiceCloneHistory(profileId);
|
|
|
- character?.voice && setVoiceName(character.voice);
|
|
|
+ const initPage = async () => {
|
|
|
+
|
|
|
};
|
|
|
|
|
|
- useEffect(() => {
|
|
|
- profileId && initPage(profileId);
|
|
|
- }, [profileId]);
|
|
|
+
|
|
|
|
|
|
// 清除定时器
|
|
|
useEffect(() => {
|
|
@@ -221,6 +137,73 @@ export default ({ profileId, onPlay }: Props) => {
|
|
|
stopTimer();
|
|
|
};
|
|
|
}, []);
|
|
|
+
|
|
|
+
|
|
|
+ // 克隆列表右侧操作栏
|
|
|
+ const renderRightColumn = (item: TVoiceItem) => {
|
|
|
+ if (item.status == EVoiceStatus.DONE) {
|
|
|
+ return <WemetaRadio></WemetaRadio>;
|
|
|
+ }
|
|
|
+
|
|
|
+ return <></>;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 克隆状态栏
|
|
|
+ const renderCloneStatus = (item: TVoiceItem) => {
|
|
|
+ if (item.status === EVoiceStatus.DONE) {
|
|
|
+ return (
|
|
|
+ <View className={`text-12 leading-20 text-gray-45`}>
|
|
|
+ {item.createTime && formatDateFull(new Date(item.createTime))}
|
|
|
+ </View>
|
|
|
+ );
|
|
|
+ }
|
|
|
+ if (item.status === EVoiceStatus.DOING) {
|
|
|
+ return <View className={`text-12 leading-20 text-orange`}>生成中...</View>;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (item.status == EVoiceStatus.FAILED) {
|
|
|
+ return <View className={`text-12 leading-20 text-orange`}>生成错误</View>;
|
|
|
+ }
|
|
|
+ return <View>...</View>;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 渲染克隆列表
|
|
|
+ const renderCloneList = () => {
|
|
|
+ if (!myVoices.length) {
|
|
|
+ return <EmptyData></EmptyData>;
|
|
|
+ }
|
|
|
+ return (
|
|
|
+ <View className="py-12 px-16 gap-">
|
|
|
+ {myVoices.map((item, _index) => {
|
|
|
+ return (
|
|
|
+ <CardListItem
|
|
|
+ className="py-16"
|
|
|
+ underline
|
|
|
+ leftRenderer={()=> {
|
|
|
+ return <View className={style.listIcon}>
|
|
|
+ <Image src={IconWave} className={style.iconImage}></Image>
|
|
|
+ </View>
|
|
|
+ }}
|
|
|
+ rightRenderer={() => {
|
|
|
+ return renderRightColumn(item);
|
|
|
+ }}
|
|
|
+ onClick={() => handleSelect(item, _index)}
|
|
|
+ >
|
|
|
+ <View className="flex items-center gap-16">
|
|
|
+ <View className="flex flex-col gap-4">
|
|
|
+ <View className={style.ListItemText}>
|
|
|
+ {item.voiceName}
|
|
|
+ </View>
|
|
|
+ {renderCloneStatus(item)}
|
|
|
+ </View>
|
|
|
+ </View>
|
|
|
+ </CardListItem>
|
|
|
+ );
|
|
|
+ })}
|
|
|
+ </View>
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
return (
|
|
|
<View className={`flex flex-col`}>
|
|
|
<View className="flex-1">
|
|
@@ -242,7 +225,7 @@ export default ({ profileId, onPlay }: Props) => {
|
|
|
setShow={setShow}
|
|
|
setCloneStatus={(status) => handleCloneStatus(status)}
|
|
|
voiceName={
|
|
|
- popupType == "reclone" ? voiceList[voiceIndex].voiceName! : ""
|
|
|
+ popupType == "reclone" ? myVoices[voiceIndex].voiceName! : ""
|
|
|
}
|
|
|
></PopupRecorder>
|
|
|
)}
|
|
@@ -251,7 +234,7 @@ export default ({ profileId, onPlay }: Props) => {
|
|
|
show={show}
|
|
|
onSure={handleSureAction}
|
|
|
onReclone={handleRecloneAction}
|
|
|
- voiceName={voiceList[voiceIndex].voiceName!}
|
|
|
+ voiceName={myVoices[voiceIndex].voiceName!}
|
|
|
showName={getCloneVoiceIdentifier(voiceIndex + 1)}
|
|
|
></PopupTryout>
|
|
|
)}
|