mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
tts/vc pop up signup modal after 2 generations w/ 2 minute local storage ttl
This commit is contained in:
1 parent
7a2c5c3fc5
commit
ecdf4c51a8
4 files changed
+108
-24
No files matched your search
+2
-2
@@ -80,6 +80,6 @@
|
||||
}
|
||||
|
||||
.fy-modal-scrollable {
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
export function setLocalStorageItem(
|
||||
key: string,
|
||||
value: string,
|
||||
ttl: number
|
||||
): void {
|
||||
const now = new Date();
|
||||
const item = {
|
||||
value: value,
|
||||
expiry: now.getTime() + ttl,
|
||||
};
|
||||
localStorage.setItem(key, JSON.stringify(item));
|
||||
}
|
||||
|
||||
export function getLocalStorageItem(key: string): string | null {
|
||||
const itemStr = localStorage.getItem(key);
|
||||
if (!itemStr) {
|
||||
return null;
|
||||
}
|
||||
const item = JSON.parse(itemStr);
|
||||
const now = new Date();
|
||||
if (now.getTime() > item.expiry) {
|
||||
localStorage.removeItem(key);
|
||||
return null;
|
||||
}
|
||||
return item.value;
|
||||
}
|
||||
@@ -8,7 +8,13 @@ import {
|
||||
Panel,
|
||||
TextArea,
|
||||
} from "components/common";
|
||||
import { useDebounce, useInferenceJobs, useLocalize, useModal } from "hooks";
|
||||
import {
|
||||
useDebounce,
|
||||
useInferenceJobs,
|
||||
useLocalize,
|
||||
useModal,
|
||||
useSession,
|
||||
} from "hooks";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faDeleteLeft,
|
||||
@@ -33,6 +39,7 @@ import { useTtsStore } from "hooks";
|
||||
import ExploreTts from "../ExploreVoices";
|
||||
import { AITools } from "components/marketing";
|
||||
import VoicePickerPreview from "../VoicePickerPreview";
|
||||
import { getLocalStorageItem, setLocalStorageItem } from "utils/localStorage";
|
||||
|
||||
interface Props {
|
||||
sessionSubscriptionsWrapper: any;
|
||||
@@ -41,6 +48,7 @@ interface Props {
|
||||
export default function NewTTS({ sessionSubscriptionsWrapper }: Props) {
|
||||
const { enqueueInferenceJob } = useInferenceJobs();
|
||||
const { modalState, open, close } = useModal();
|
||||
const { loggedIn, loggedInOrModal } = useSession();
|
||||
const [search, searchSet] = useState("");
|
||||
const [updated, updatedSet] = useState(false);
|
||||
const { selectedVoice, setSelectedVoice, text, setText } = useTtsStore();
|
||||
@@ -54,10 +62,10 @@ export default function NewTTS({ sessionSubscriptionsWrapper }: Props) {
|
||||
|
||||
const searchChange =
|
||||
(setUpdate = true) =>
|
||||
({ target }: { target: any }) => {
|
||||
if (setUpdate) updatedSet(true);
|
||||
searchSet(target.value);
|
||||
};
|
||||
({ target }: { target: any }) => {
|
||||
if (setUpdate) updatedSet(true);
|
||||
searchSet(target.value);
|
||||
};
|
||||
|
||||
const handleResultSelect = (data: any) => {
|
||||
setSelectedVoice(data);
|
||||
@@ -112,6 +120,23 @@ export default function NewTTS({ sessionSubscriptionsWrapper }: Props) {
|
||||
const handleSpeak = async (ev: React.FormEvent<HTMLFormElement>) => {
|
||||
ev.preventDefault();
|
||||
|
||||
const generationCountKey = "generationCountTTS";
|
||||
const promptShownKey = "promptShownTTS";
|
||||
const ttl = 2 * 60 * 1000; // 2 minutes in milliseconds
|
||||
let generationCount = parseInt(
|
||||
getLocalStorageItem(generationCountKey) || "0"
|
||||
);
|
||||
const promptShown = getLocalStorageItem(promptShownKey);
|
||||
|
||||
// Show sign up prompt after 2 generations, and dont show again until local storage expires
|
||||
if (!loggedIn && generationCount >= 2 && !promptShown) {
|
||||
loggedInOrModal({
|
||||
loginMessage: "Login to keep your generated audio history",
|
||||
signupMessage: "Sign up to keep your generated audio history",
|
||||
});
|
||||
setLocalStorageItem(promptShownKey, "true", ttl);
|
||||
}
|
||||
|
||||
if (!selectedVoice || !text) return;
|
||||
|
||||
setIsGenerating(true);
|
||||
@@ -139,13 +164,16 @@ export default function NewTTS({ sessionSubscriptionsWrapper }: Props) {
|
||||
} catch (error) {
|
||||
// @ts-ignore
|
||||
window.dataLayer.push({
|
||||
"event": "enqueue_failure",
|
||||
"page": "/tts",
|
||||
"user_id": "$user_id"
|
||||
event: "enqueue_failure",
|
||||
page: "/tts",
|
||||
user_id: "$user_id",
|
||||
});
|
||||
console.error("Unexpected error:", error);
|
||||
setIsGenerating(false);
|
||||
}
|
||||
|
||||
generationCount += 1;
|
||||
setLocalStorageItem(generationCountKey, generationCount.toString(), ttl);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -207,10 +235,11 @@ export default function NewTTS({ sessionSubscriptionsWrapper }: Props) {
|
||||
<div className="d-flex gap-2 align-items-center w-100">
|
||||
<div className="flex-grow-1">
|
||||
<Label
|
||||
label={`${selectedVoice
|
||||
label={`${
|
||||
selectedVoice
|
||||
? t("label.selected")
|
||||
: t("label.select")
|
||||
}`}
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,7 +8,13 @@ import {
|
||||
Label,
|
||||
Panel,
|
||||
} from "components/common";
|
||||
import { useDebounce, useInferenceJobs, useLocalize, useModal } from "hooks";
|
||||
import {
|
||||
useDebounce,
|
||||
useInferenceJobs,
|
||||
useLocalize,
|
||||
useModal,
|
||||
useSession,
|
||||
} from "hooks";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faSearch,
|
||||
@@ -36,6 +42,7 @@ import VCUploadComponent from "./VCUploadComponent";
|
||||
import VCPitchShiftComponent from "./VCPitchShiftComponent";
|
||||
import VCPitchEstimateMethodComponent from "./VCPitchEstimateMethodComponent";
|
||||
import { SessionVoiceConversionResultsList } from "v2/view/_common/SessionVoiceConversionResultsList";
|
||||
import { getLocalStorageItem, setLocalStorageItem } from "utils/localStorage";
|
||||
|
||||
interface Props {
|
||||
sessionSubscriptionsWrapper: any;
|
||||
@@ -44,6 +51,7 @@ interface Props {
|
||||
export default function NewVC({ sessionSubscriptionsWrapper }: Props) {
|
||||
const { enqueueInferenceJob } = useInferenceJobs();
|
||||
const { modalState, open, close } = useModal();
|
||||
const { loggedIn, loggedInOrModal } = useSession();
|
||||
const [search, searchSet] = useState("");
|
||||
const [updated, updatedSet] = useState(false);
|
||||
const {
|
||||
@@ -73,10 +81,10 @@ export default function NewVC({ sessionSubscriptionsWrapper }: Props) {
|
||||
|
||||
const searchChange =
|
||||
(setUpdate = true) =>
|
||||
({ target }: { target: any }) => {
|
||||
if (setUpdate) updatedSet(true);
|
||||
searchSet(target.value);
|
||||
};
|
||||
({ target }: { target: any }) => {
|
||||
if (setUpdate) updatedSet(true);
|
||||
searchSet(target.value);
|
||||
};
|
||||
|
||||
const handleResultSelect = (data: any) => {
|
||||
setSelectedVoice(data);
|
||||
@@ -144,6 +152,23 @@ export default function NewVC({ sessionSubscriptionsWrapper }: Props) {
|
||||
const handleConvert = async (ev: React.FormEvent<HTMLFormElement>) => {
|
||||
ev.preventDefault();
|
||||
|
||||
const generationCountKey = "generationCountVC";
|
||||
const promptShownKey = "promptShownVC";
|
||||
const ttl = 2 * 60 * 1000; // 2 minutes in milliseconds
|
||||
let generationCount = parseInt(
|
||||
getLocalStorageItem(generationCountKey) || "0"
|
||||
);
|
||||
const promptShown = getLocalStorageItem(promptShownKey);
|
||||
|
||||
// Show sign up prompt after 2 generations, and dont show again until local storage expires
|
||||
if (!loggedIn && generationCount >= 2 && !promptShown) {
|
||||
loggedInOrModal({
|
||||
loginMessage: "Login to keep your generated audio history",
|
||||
signupMessage: "Sign up to keep your generated audio history",
|
||||
});
|
||||
setLocalStorageItem(promptShownKey, "true", ttl);
|
||||
}
|
||||
|
||||
if (!selectedVoice || !mediaUploadToken) return;
|
||||
|
||||
setIsGenerating(true);
|
||||
@@ -179,9 +204,9 @@ export default function NewVC({ sessionSubscriptionsWrapper }: Props) {
|
||||
} else {
|
||||
// @ts-ignore
|
||||
window.dataLayer.push({
|
||||
"event": "enqueue_failure",
|
||||
"page": "/voice-conversion",
|
||||
"user_id": "$user_id"
|
||||
event: "enqueue_failure",
|
||||
page: "/voice-conversion",
|
||||
user_id: "$user_id",
|
||||
});
|
||||
console.error("Error queuing VC:", "failed to enqueue");
|
||||
setIsGenerating(false);
|
||||
@@ -190,6 +215,9 @@ export default function NewVC({ sessionSubscriptionsWrapper }: Props) {
|
||||
console.error("Unexpected error:", error);
|
||||
setIsGenerating(false);
|
||||
}
|
||||
|
||||
generationCount += 1;
|
||||
setLocalStorageItem(generationCountKey, generationCount.toString(), ttl);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -251,10 +279,11 @@ export default function NewVC({ sessionSubscriptionsWrapper }: Props) {
|
||||
<div className="d-flex gap-2 align-items-center w-100">
|
||||
<div className="flex-grow-1">
|
||||
<Label
|
||||
label={`${selectedVoice
|
||||
? t("label.selected")
|
||||
: t("label.select")
|
||||
}`}
|
||||
label={`${
|
||||
selectedVoice
|
||||
? t("label.selected")
|
||||
: t("label.select")
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in new issue
Block a user