|
|
|
|
@ -98,15 +98,26 @@ class _CopilotChatPageState extends State<CopilotChatPage> {
|
|
|
|
|
setState(() => _inputState = _InputState.idle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _sendRecording() async {
|
|
|
|
|
Future<void> _sendRecording() async {
|
|
|
|
|
if (_recordedFilePath == null) return;
|
|
|
|
|
final file = File(_recordedFilePath!);
|
|
|
|
|
context.read<CopilotProvider>().sendAudio(file, context.settingProvider.language!);
|
|
|
|
|
final language = context.settingProvider.language!;
|
|
|
|
|
final provider = context.read<CopilotProvider>();
|
|
|
|
|
|
|
|
|
|
await _recordedPlayerController.stopPlayer();
|
|
|
|
|
_recorderController.reset();
|
|
|
|
|
_recordedFilePath = null;
|
|
|
|
|
setState(() => _inputState = _InputState.idle);
|
|
|
|
|
_scrollToBottom();
|
|
|
|
|
|
|
|
|
|
final transcribedText = await provider.transcribeAudio(file, language);
|
|
|
|
|
if (!mounted) return;
|
|
|
|
|
|
|
|
|
|
if (transcribedText != null && transcribedText.isNotEmpty) {
|
|
|
|
|
_textController.text = transcribedText;
|
|
|
|
|
_textController.selection = TextSelection.fromPosition(TextPosition(offset: _textController.text.length));
|
|
|
|
|
} else {
|
|
|
|
|
(provider.errorMessage ?? "Failed to transcribe audio. Please try again.").showToast;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
@ -237,11 +248,19 @@ class _CopilotChatPageState extends State<CopilotChatPage> {
|
|
|
|
|
hintStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: AppColor.lightTextColor(context)),
|
|
|
|
|
),
|
|
|
|
|
).expanded,
|
|
|
|
|
IconButton(
|
|
|
|
|
onPressed: _startRecording,
|
|
|
|
|
style: const ButtonStyle(tapTargetSize: MaterialTapTargetSize.shrinkWrap),
|
|
|
|
|
icon: "chat_mic".toSvgAsset(width: 24, height: 24, color: AppColor.icon2Color(context)),
|
|
|
|
|
constraints: const BoxConstraints(),
|
|
|
|
|
Consumer<CopilotProvider>(
|
|
|
|
|
builder: (_, provider, __) => IconButton(
|
|
|
|
|
onPressed: provider.isTranscribing ? null : _startRecording,
|
|
|
|
|
style: const ButtonStyle(tapTargetSize: MaterialTapTargetSize.shrinkWrap),
|
|
|
|
|
icon: provider.isTranscribing
|
|
|
|
|
? const SizedBox(
|
|
|
|
|
height: 20,
|
|
|
|
|
width: 20,
|
|
|
|
|
child: CircularProgressIndicator(color: AppColor.primary10, strokeWidth: 2),
|
|
|
|
|
)
|
|
|
|
|
: "chat_mic".toSvgAsset(width: 24, height: 24, color: AppColor.icon2Color(context)),
|
|
|
|
|
constraints: const BoxConstraints(),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
] else if (_inputState == _InputState.recording) ...[
|
|
|
|
|
AudioWaveforms(
|
|
|
|
|
@ -313,7 +332,7 @@ class _CopilotChatPageState extends State<CopilotChatPage> {
|
|
|
|
|
},
|
|
|
|
|
style: const ButtonStyle(tapTargetSize: MaterialTapTargetSize.shrinkWrap),
|
|
|
|
|
icon: Consumer<CopilotProvider>(
|
|
|
|
|
builder: (_, provider, __) => provider.isStreaming
|
|
|
|
|
builder: (_, provider, __) => (provider.isStreaming || provider.isTranscribing)
|
|
|
|
|
? const SizedBox(
|
|
|
|
|
height: 24,
|
|
|
|
|
width: 24,
|
|
|
|
|
|