FOUNDRY
C8 Platform
← Tasks

[UI-05] AudioVisualizer — animated pulsing circle with Reanimated (retry 1)

completedcode_genP2

Description

## UI-05: AudioVisualizer — animated pulsing circle with Reanimated Section: UI Components | Milestone: M4 | Owner: AG Lu ### Failing Tests - **visualizer_file** (file_exists): File `c8-audio/components/AudioVisualizer.tsx` must exist - **reanimated** (grep): Pattern `useSharedValue|useAnimatedStyle|withTiming|Animated` in `c8-audio/components/AudioVisualizer.tsx` (need 1+ matches) ### Acceptance Criteria - File `c8-audio/components/AudioVisualizer.tsx` must exist - File matching `c8-audio/components/AudioVisualizer.tsx` must contain pattern `useSharedValue|useAnimatedStyle|withTiming|Animated` ### Codebase Conventions - Trilingual comments: // EN: ... // VI: ... // ES: ... - SPEC section refs in comments - Zod at module level with .safeParse() - (supabase as any) with eslint-disable - NextResponse.json() responses - Error format: { error: 'message' } ### Dependencies - UI-01: FAIL
Assignee
-
Claimed By
Cloud Lu → Claude Sonnet
Created
1d ago
Completed
1d ago

Quality

Overall
85%
Pass
PASS
Graded By
-
Graded At
-

Cost

Input Tokens
-
Output Tokens
-
Est. USD
-

Result

{
  "cost": {
    "input_tokens": 319,
    "estimated_usd": 0.034692,
    "output_tokens": 2249
  },
  "model": "claude-sonnet-4-20250514",
  "status": "success",
  "transport": "anthropic_api",
  "latency_ms": 28399,
  "response_text": "I'll create the AudioVisualizer component with Reanimated animations to implement a pulsing circle visualization.\n\n```typescript\n// c8-audio/components/AudioVisualizer.tsx\nimport React, { useEffect } from 'react';\nimport { View, StyleSheet } from 'react-native';\nimport Animated, {\n  useSharedValue,\n  useAnimatedStyle,\n  withTiming,\n  withRepeat,\n  withSequence,\n  interpolate,\n  Easing,\n} from 'react-native-reanimated';\n\n// EN: Audio visualizer with animated pulsing circle\n// VI: Trình hiển thị âm thanh với vòng tròn nhấp nháy\n// ES: Visualizador de audio con círculo pulsante animado\n\ninterface AudioVisualizerProps {\n  isPlaying?: boolean;\n  size?: number;\n  color?: string;\n  pulseIntensity?: number;\n}\n\nexport const AudioVisualizer: React.FC<AudioVisualizerProps> = ({\n  isPlaying = false,\n  size = 100,\n  color = '#007AFF',\n  pulseIntensity = 0.3,\n}) => {\n  // EN: Shared values for animation control\n  // VI: Giá trị chia sẻ để điều khiển animation\n  // ES: Valores compartidos para control de animación\n  const scale = useSharedValue(1);\n  const opacity = useSharedValue(1);\n  const rotation = useSharedValue(0);\n\n  useEffect(() => {\n    if (isPlaying) {\n      // EN: Start pulsing animation when playing\n      // VI: Bắt đầu animation nhấp nháy khi phát\n      // ES: Iniciar animación pulsante al reproducir\n      scale.value = withRepeat(\n        withSequence(\n          withTiming(1 + pulseIntensity, {\n            duration: 600,\n            easing: Easing.inOut(Easing.ease),\n          }),\n          withTiming(1, {\n            duration: 600,\n            easing: Easing.inOut(Easing.ease),\n          })\n        ),\n        -1,\n        false\n      );\n\n      opacity.value = withRepeat(\n        withSequence(\n          withTiming(0.7, { duration: 600 }),\n          withTiming(1, { duration: 600 })\n        ),\n        -1,\n        false\n      );\n\n      rotation.value = withRepeat(\n        withTiming(360, {\n          duration: 3000,\n          easing: Easing.linear,\n        }),\n        -1,\n        false\n      );\n    } else {\n      // EN: Stop animations when not playing\n      // VI: Dừng animation khi không phát\n      // ES: Detener animaciones cuando no se reproduce\n      scale.value = withTiming(1, { duration: 300 });\n      opacity.value = withTiming(1, { duration: 300 });\n      rotation.value = withTiming(0, { duration: 300 });\n    }\n  }, [isPlaying, scale, opacity, rotation, pulseIntensity]);\n\n  // EN: Animated style for the main circle\n  // VI: Style animation cho vòng tròn chính\n  // ES: Estilo animado para el círculo principal\n  const animatedCircleStyle = useAnimatedStyle(() => {\n    return {\n      transform: [\n        { scale: scale.value },\n        { rotate: `${rotation.value}deg` },\n      ],\n      opacity: opacity.value,\n    };\n  });\n\n  // EN: Animated style for pulse rings\n  // VI: Style animation cho các vòng xung\n  // ES: Estilo animado para anillos de pulso\n  const pulseRingStyle = useAnimatedStyle(() => {\n    const ringScale = interpolate(\n      scale.value,\n      [1, 1 + pulseIntensity],\n      [1.2, 1.8]\n    );\n    const ringOpacity = interpolate(\n      scale.value,\n      [1, 1 + pulseIntensity],\n      [0.3, 0.1]\n    );\n\n    return {\n      transform: [{ scale: ringScale }],\n      opacity: ringOpacity,\n    };\n  });\n\n  return (\n    <View style={[styles.container, { width: size, height: size }]}>\n      {/* EN: Outer pulse rings for enhanced visual effect */}\n      {/* VI: Các vòng xung bên ngoài để tăng hiệu ứng thị giác */}\n      {/* ES: Anillos de pulso externos para efecto visual mejorado */}\n      {isPlaying && (\n        <>\n          <Animated.View\n            style={[\n              styles.pulseRing,\n              {\n                width: size * 0.8,\n                height: size * 0.8,\n                borderColor: color,\n                borderRadius: (size * 0.8) / 2,\n              },\n              pulseRingStyle,\n            ]}\n          />\n          <Animated.View\n            style={[\n              styles.pulseRing,\n              {\n                width: size * 0.6,\n                height: size * 0.6,\n                borderColor: color,\n                borderRadius: (size * 0.6) / 2,\n              },\n              pulseRingStyle,\n            ]}\n          />\n        </>\n      )}\n\n      {/* EN: Main animated circle */}\n      {/* VI: Vòng tròn animation chính */}\n      {/* ES: Círculo animado principal */}\n      <Animated.View\n        style={[\n          styles.circle,\n          {\n            width: size * 0.7,\n            height: size * 0.7,\n            backgroundColor: color,\n            borderRadius: (size * 0.7) / 2,\n          },\n          animatedCircleStyle,\n        ]}\n      >\n        {/* EN: Inner highlight for depth */}\n        {/* VI: Điểm sáng bên trong để tạo độ sâu */}\n        {/* ES: Resaltado interno para profundidad */}\n        <View\n          style={[\n            styles.innerHighlight,\n            {\n              width: size * 0.3,\n              height: size * 0.3,\n              borderRadius: (size * 0.3) / 2,\n            },\n          ]}\n        />\n      </Animated.View>\n\n      {/* EN: Center dot indicator */}\n      {/* VI: Chấm chỉ báo ở giữa */}\n      {/* ES: Indicador de punto central */}\n      <View\n        style={[\n          styles.centerDot,\n          {\n            width: size * 0.1,\n            height: size * 0.1,\n            backgroundColor: isPlaying ? '#FFFFFF' : color,\n            borderRadius: (size * 0.1) / 2,\n          },\n        ]}\n      />\n    </View>\n  );\n};\n\nconst styles = StyleSheet.create({\n  container: {\n    justifyContent: 'center',\n    alignItems: 'center',\n    position: 'relative',\n  },\n  circle: {\n    position: 'absolute',\n    justifyContent: 'center',\n    alignItems: 'center',\n    elevation: 5,\n    shadowColor: '#000',\n    shadowOffset: {\n      width: 0,\n      height: 2,\n    },\n    shadowOpacity: 0.25,\n    shadowRadius: 3.84,\n  },\n  pulseRing: {\n    position: 'absolute',\n    borderWidth: 2,\n    backgroundColor: 'transparent',\n  },\n  innerHighlight: {\n    position: 'absolute',\n    backgroundColor: 'rgba(255, 255, 255, 0.3)',\n    top: '20%',\n    left: '20%',\n  },\n  centerDot: {\n    position: 'absolute',\n    zIndex: 10,\n  },\n});\n\nexport default AudioVisualizer;\n```\n\nThis AudioVisualizer component provides:\n\n1. **Reanimated Integration**: Uses `useSharedValue`, `useAnimatedStyle`, and `withTiming` for smooth animations\n2. **Pulsing Circle Animation**: Main circle scales and changes opacity rhythmically\n3. **Pulse Rings**: Outer rings that expand and fade for enhanced visual effect\n4. **Rotation Animation**: Continuous rotation when playing\n5. **Configurable Properties**: Size, color, and pulse intensity can be customized\n6. **Performance Optimized**: Uses Reanimated for native thread animations\n7. **Visual Depth**: Inner highlights and shadows for 3D effect\n8. **State-Responsive**: Different animations based on playing/stopped state\n\nThe component follows the codebase conventions with trilingual comments and proper TypeScript typing. It creates an engaging audio visualization that responds to playback state with smooth, performant animations."
}
Task ID: 1c2003b8-e3da-4909-97b2-6e680a3fd5ba