import { View, Text, Pressable, ActivityIndicator } from "react-native"; import { StyleSheet, useUnistyles } from "react-native-unistyles"; import { X, ArrowUp, RefreshCcw, Check, Mic } from "lucide-react-native"; import { VolumeMeter } from "./volume-meter"; import { FOOTER_HEIGHT } from "@/constants/layout"; import type { DictationStatus } from "@/hooks/use-dictation"; interface DictationControlsProps { volume: number; duration: number; transcript?: string; isRecording: boolean; isProcessing: boolean; status: DictationStatus; onStart: () => void; onCancel: () => void; onAccept: () => void; onAcceptAndSend: () => void; onRetry?: () => void; onDiscard?: () => void; disabled?: boolean; } function formatDuration(seconds: number): string { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins.toString().padStart(2, "0")}:${secs .toString() .padStart(2, "0")}`; } export function DictationControls({ volume, duration, isRecording, isProcessing, status, onStart, onCancel, onAccept, onAcceptAndSend, onRetry, onDiscard, disabled = false, }: DictationControlsProps) { const { theme } = useUnistyles(); const isFailed = status === "failed"; const showActiveState = isRecording || isProcessing || isFailed; const actionsDisabled = isProcessing; const handleCancel = isFailed && onDiscard ? onDiscard : onCancel; if (!showActiveState) { return ( ); } return ( {formatDuration(duration)} {actionsDisabled ? ( ) : isFailed ? ( ) : ( <> )} ); } /** * Full-width overlay variant for the agent input footer. * Uses blue background with white icons. */ export function DictationOverlay({ volume, duration, isRecording, isProcessing, status, errorText, onCancel, onAccept, onAcceptAndSend, onRetry, onDiscard, }: Omit & { errorText?: string }) { const { theme } = useUnistyles(); const isFailed = status === "failed"; const showActiveState = isRecording || isProcessing || isFailed; const actionsDisabled = isProcessing; const handleCancel = isFailed && onDiscard ? onDiscard : onCancel; if (!showActiveState) { return null; } return ( {formatDuration(duration)} {isFailed ? ( {errorText ? `Dictation failed: ${errorText}` : "Dictation failed. Tap retry."} ) : null} {actionsDisabled ? ( ) : isFailed ? ( ) : ( <> )} ); } const BUTTON_SIZE = 32; const styles = StyleSheet.create((theme) => ({ micButton: { width: BUTTON_SIZE, height: BUTTON_SIZE, borderRadius: theme.borderRadius.full, alignItems: "center", justifyContent: "center", borderWidth: theme.borderWidth[1], borderColor: theme.colors.border, backgroundColor: theme.colors.surface0, }, activeContainer: { flexDirection: "row", alignItems: "center", gap: theme.spacing[3], }, meterWrapper: { width: 80, alignItems: "center", justifyContent: "center", }, timerText: { fontSize: theme.fontSize.sm, fontWeight: theme.fontWeight.semibold, fontVariant: ["tabular-nums"], }, actionGroup: { flexDirection: "row", gap: theme.spacing[2], }, actionButton: { width: BUTTON_SIZE, height: BUTTON_SIZE, borderRadius: theme.borderRadius.full, alignItems: "center", justifyContent: "center", borderWidth: theme.borderWidth[1], }, actionButtonCancel: { borderColor: theme.colors.border, backgroundColor: theme.colors.surface0, }, actionButtonSecondary: { borderColor: theme.colors.border, backgroundColor: theme.colors.surface0, }, actionButtonConfirm: { borderColor: theme.colors.foreground, backgroundColor: theme.colors.foreground, }, buttonDisabled: { opacity: 0.4, }, loadingContainer: { width: BUTTON_SIZE, height: BUTTON_SIZE, alignItems: "center", justifyContent: "center", }, statusLabel: { fontSize: theme.fontSize.xs, fontWeight: theme.fontWeight.semibold, }, })); const OVERLAY_BUTTON_SIZE = 44; const OVERLAY_VERTICAL_PADDING = (FOOTER_HEIGHT - OVERLAY_BUTTON_SIZE) / 2; const overlayStyles = StyleSheet.create((theme) => ({ container: { flexDirection: "row", alignItems: "center", width: "100%", borderRadius: theme.borderRadius["2xl"], justifyContent: "space-between", paddingHorizontal: theme.spacing[4], paddingVertical: OVERLAY_VERTICAL_PADDING, height: FOOTER_HEIGHT, }, cancelButton: { width: OVERLAY_BUTTON_SIZE, height: OVERLAY_BUTTON_SIZE, borderRadius: theme.borderRadius.full, backgroundColor: "rgba(0, 0, 0, 0.15)", alignItems: "center", justifyContent: "center", }, centerContainer: { flex: 1, flexDirection: "column", alignItems: "center", justifyContent: "center", gap: theme.spacing[2], }, meterRow: { flexDirection: "row", alignItems: "center", justifyContent: "center", gap: theme.spacing[4], }, timerText: { fontSize: theme.fontSize.xl, fontWeight: theme.fontWeight.semibold, fontVariant: ["tabular-nums"], }, transcriptText: { fontSize: theme.fontSize.sm, fontWeight: theme.fontWeight.normal, textAlign: "center", paddingHorizontal: theme.spacing[2], opacity: 0.9, }, actionButtonsContainer: { flexDirection: "row", alignItems: "center", gap: theme.spacing[2], }, actionButton: { width: OVERLAY_BUTTON_SIZE, height: OVERLAY_BUTTON_SIZE, borderRadius: theme.borderRadius.full, alignItems: "center", justifyContent: "center", }, buttonDisabled: { opacity: 0.5, }, loadingContainer: { width: OVERLAY_BUTTON_SIZE, height: OVERLAY_BUTTON_SIZE, alignItems: "center", justifyContent: "center", }, }));