Update API key usage and credit checking logic for comic generation
This commit is contained in:
@@ -70,11 +70,11 @@ export function ApiKeyModal({ isOpen, onClose, onSubmit }: ApiKeyModalProps) {
|
||||
: "Add your API key to continue"}
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription className="text-center text-muted-foreground">
|
||||
{existingKey
|
||||
? "Update your Together API key or add a new one. You can also delete your existing key."
|
||||
: "Your first page was free! Add your Together API key to generate more pages."}
|
||||
</DialogDescription>
|
||||
<DialogDescription className="text-center text-muted-foreground">
|
||||
{existingKey
|
||||
? "Update your Together API key or add a new one. You can also delete your existing key."
|
||||
: "You've used your weekly credit! Add your Together API key for unlimited generation."}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4 mt-4">
|
||||
|
||||
@@ -11,6 +11,7 @@ import { COMIC_STYLES } from "@/lib/constants";
|
||||
import { useKeyboardShortcut } from "@/hooks/use-keyboard-shortcut";
|
||||
import { useApiKey } from "@/hooks/use-api-key";
|
||||
import { isContentPolicyViolation } from "@/lib/utils";
|
||||
import { ApiKeyModal } from "@/components/api-key-modal";
|
||||
|
||||
interface ComicCreationFormProps {
|
||||
prompt: string;
|
||||
@@ -23,11 +24,14 @@ interface ComicCreationFormProps {
|
||||
setIsLoading: (loading: boolean) => void;
|
||||
}
|
||||
|
||||
const DEFAULT_STYLE = 'noir';
|
||||
const STYLE_STORAGE_KEY = 'comic-style-preference';
|
||||
|
||||
export function ComicCreationForm({
|
||||
prompt,
|
||||
setPrompt,
|
||||
style,
|
||||
setStyle,
|
||||
style: initialStyle,
|
||||
setStyle: setParentStyle,
|
||||
characterFiles,
|
||||
setCharacterFiles,
|
||||
isLoading,
|
||||
@@ -39,11 +43,22 @@ export function ComicCreationForm({
|
||||
const { uploadToS3 } = useS3Upload();
|
||||
const { isSignedIn, isLoaded } = useAuth();
|
||||
const { openSignIn } = useClerk();
|
||||
const [apiKey] = useApiKey();
|
||||
const [apiKey, setApiKey] = useApiKey();
|
||||
const hasApiKey = !!apiKey;
|
||||
const [previews, setPreviews] = useState<string[]>([]);
|
||||
const [showPreview, setShowPreview] = useState<number | null>(null);
|
||||
const [showStyleDropdown, setShowStyleDropdown] = useState(false);
|
||||
const [creditsRemaining, setCreditsRemaining] = useState<number | null>(null);
|
||||
const [showApiModal, setShowApiModal] = useState(false);
|
||||
|
||||
// Initialize style with saved preference or default
|
||||
const [style, setStyle] = useState(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const saved = localStorage.getItem(STYLE_STORAGE_KEY);
|
||||
return saved || initialStyle || DEFAULT_STYLE;
|
||||
}
|
||||
return initialStyle || DEFAULT_STYLE;
|
||||
});
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
@@ -78,6 +93,38 @@ export function ComicCreationForm({
|
||||
}
|
||||
}, []); // Run only on mount
|
||||
|
||||
// Save style to localStorage and sync with parent
|
||||
useEffect(() => {
|
||||
localStorage.setItem(STYLE_STORAGE_KEY, style);
|
||||
setParentStyle(style);
|
||||
}, [style, setParentStyle]);
|
||||
|
||||
// Fetch credits on mount
|
||||
useEffect(() => {
|
||||
if (isSignedIn && !hasApiKey) {
|
||||
const fetchCredits = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/check-credits', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ hasApiKey: false }),
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok) {
|
||||
setCreditsRemaining(data.creditsRemaining);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching credits:', error);
|
||||
}
|
||||
};
|
||||
fetchCredits();
|
||||
} else if (hasApiKey) {
|
||||
setCreditsRemaining(null); // Unlimited
|
||||
}
|
||||
}, [isSignedIn, hasApiKey]);
|
||||
|
||||
// Keyboard shortcut for form submission
|
||||
useKeyboardShortcut(() => {
|
||||
if (!isLoading && prompt.trim()) {
|
||||
@@ -151,15 +198,33 @@ export function ComicCreationForm({
|
||||
setLoadingStep(0);
|
||||
|
||||
try {
|
||||
if (!apiKey) {
|
||||
toast({
|
||||
title: "API key required",
|
||||
description: "Please add your API key to generate comics.",
|
||||
variant: "destructive",
|
||||
duration: 3000,
|
||||
// Check credits
|
||||
const hasApiKey = !!apiKey;
|
||||
if (!hasApiKey) {
|
||||
const creditsResponse = await fetch('/api/check-credits', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ hasApiKey }),
|
||||
});
|
||||
setIsLoading(false);
|
||||
return;
|
||||
const creditsData = await creditsResponse.json();
|
||||
|
||||
if (!creditsResponse.ok) {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Failed to check credits",
|
||||
variant: "destructive",
|
||||
});
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (creditsData.creditsRemaining === 0) {
|
||||
setShowApiModal(true);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const characterUploads = await Promise.all(
|
||||
@@ -174,7 +239,7 @@ export function ComicCreationForm({
|
||||
},
|
||||
body: JSON.stringify({
|
||||
prompt,
|
||||
apiKey,
|
||||
...(apiKey && { apiKey }),
|
||||
style,
|
||||
characterImages: characterUploads,
|
||||
}),
|
||||
@@ -214,6 +279,11 @@ export function ComicCreationForm({
|
||||
}
|
||||
};
|
||||
|
||||
const handleApiKeySubmit = (key: string) => {
|
||||
setApiKey(key);
|
||||
setShowApiModal(false);
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
const isEnter = e.key === "Enter" || e.key === "\n" || e.keyCode === 13;
|
||||
const isModifierPressed = e.shiftKey || e.ctrlKey || e.metaKey; // metaKey for Cmd on Mac
|
||||
@@ -418,7 +488,7 @@ export function ComicCreationForm({
|
||||
{hasApiKey ? (
|
||||
<>Using your API key (~$0.01 per comic)</>
|
||||
) : (
|
||||
<>1 credit weekly</>
|
||||
<>{creditsRemaining !== null ? `${creditsRemaining} credit${creditsRemaining === 1 ? '' : 's'} remaining` : 'Checking credits...'}</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -431,6 +501,12 @@ export function ComicCreationForm({
|
||||
</SignInButton>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ApiKeyModal
|
||||
isOpen={showApiModal}
|
||||
onClose={() => setShowApiModal(false)}
|
||||
onSubmit={handleApiKeySubmit}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user