fix: sing reaction only starts when recording begins, adjust animation timing

- Add onRecordingStart/onRecordingStop callbacks to InlineSingCard
- Move singing reaction trigger from card open to actual recording start
- Reduce sing bounce animation movement (6px → 3px for baby, 4px → 2px for egg)
- Slow down sing bounce animation (0.4s → 0.5s for baby, 0.5s → 0.6s for egg)
- Change Record button label to Sing
This commit is contained in:
filemon
2026-03-16 19:29:50 -03:00
parent 04112110f7
commit d59ba03cc6
4 changed files with 36 additions and 14 deletions
@@ -29,6 +29,10 @@ interface InlineSingCardProps {
onConfirm: () => Promise<void>;
/** Called when user closes the sing card */
onClose: () => void;
/** Called when recording starts (for Blobbi reaction) */
onRecordingStart?: () => void;
/** Called when recording stops (for Blobbi reaction) */
onRecordingStop?: () => void;
/** Whether publishing is in progress */
isPublishing: boolean;
}
@@ -62,6 +66,8 @@ function getSupportedAudioMimeType(): string | undefined {
export function InlineSingCard({
onConfirm,
onClose,
onRecordingStart,
onRecordingStop,
isPublishing,
}: InlineSingCardProps) {
// Recording state
@@ -218,6 +224,9 @@ export function InlineSingCard({
setRecordingState('recording');
setRecordingDuration(0);
// Notify parent that recording started (for Blobbi reaction)
onRecordingStart?.();
timerRef.current = setInterval(() => {
setRecordingDuration(prev => prev + 1);
}, 1000);
@@ -236,7 +245,7 @@ export function InlineSingCard({
}
setRecordingState('error');
}
}, []);
}, [onRecordingStart]);
// Stop recording
const stopRecording = useCallback(() => {
@@ -248,7 +257,10 @@ export function InlineSingCard({
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') {
mediaRecorderRef.current.stop();
}
}, []);
// Notify parent that recording stopped (for Blobbi reaction)
onRecordingStop?.();
}, [onRecordingStop]);
// Handle preview playback
const handlePreview = useCallback(() => {
@@ -384,7 +396,7 @@ export function InlineSingCard({
className="rounded-full px-6 bg-purple-500 hover:bg-purple-600"
>
<Mic className="size-4 mr-2" />
Record
Sing
</Button>
)}
+5 -5
View File
@@ -47,20 +47,20 @@
}
}
/* Bouncy animation for singing - more energetic than sway */
/* Bouncy animation for singing - more energetic than sway but still cute */
@keyframes egg-bounce {
0%,
100% {
transform: translateY(0) rotate(0deg);
}
25% {
transform: translateY(-4px) rotate(2deg);
transform: translateY(-2px) rotate(1deg);
}
50% {
transform: translateY(0) rotate(0deg);
}
75% {
transform: translateY(-4px) rotate(-2deg);
transform: translateY(-2px) rotate(-1deg);
}
}
@@ -70,8 +70,8 @@
}
.animate-egg-bounce {
/* Egg bounce is gentler than baby/adult (0.5s vs 0.4s) */
animation: egg-bounce 0.5s ease-in-out infinite;
/* Egg sing bounce - gentler than baby/adult (0.6s vs 0.5s) */
animation: egg-bounce 0.6s ease-in-out infinite;
}
.animate-egg-warmth {
+4 -4
View File
@@ -183,13 +183,13 @@
transform: translateY(0) rotate(0deg);
}
25% {
transform: translateY(-6px) rotate(3deg);
transform: translateY(-3px) rotate(1.5deg);
}
50% {
transform: translateY(0) rotate(0deg);
}
75% {
transform: translateY(-6px) rotate(-3deg);
transform: translateY(-3px) rotate(-1.5deg);
}
}
@@ -199,8 +199,8 @@
}
.animate-blobbi-bounce {
/* Baby/adult bounce is livelier than egg (0.4s vs 0.5s) */
animation: blobbi-bounce 0.4s ease-in-out infinite;
/* Baby/adult sing bounce - smoother and cuter than before (0.5s) */
animation: blobbi-bounce 0.5s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
+12 -2
View File
@@ -725,9 +725,8 @@ function BlobbiDashboard({
setShowTrackPickerModal(true);
} else if (action === 'sing') {
// Open the inline sing card directly
// Note: Singing reaction starts when recording actually begins (via onRecordingStart)
setInlineActivity(createSingActivity());
// Start singing reaction animation
setBlobbiReaction('singing');
}
};
@@ -782,6 +781,15 @@ function BlobbiDashboard({
setBlobbiReaction('idle');
};
// Handle sing recording state changes (for Blobbi reaction)
const handleSingRecordingStart = () => {
setBlobbiReaction('singing');
};
const handleSingRecordingStop = () => {
setBlobbiReaction('idle');
};
// Handle opening track picker to change track (from inline player)
const handleChangeTrack = () => {
setShowTrackPickerModal(true);
@@ -976,6 +984,8 @@ function BlobbiDashboard({
<InlineSingCard
onConfirm={handleConfirmSing}
onClose={handleCloseInlineActivity}
onRecordingStart={handleSingRecordingStart}
onRecordingStop={handleSingRecordingStop}
isPublishing={isDirectActionPending}
/>
</div>