diff --git a/app/lib/desktop/pages/conversations/desktop_conversations_page.dart b/app/lib/desktop/pages/conversations/desktop_conversations_page.dart index 6718e5ccd8..11d4bfeae2 100644 --- a/app/lib/desktop/pages/conversations/desktop_conversations_page.dart +++ b/app/lib/desktop/pages/conversations/desktop_conversations_page.dart @@ -21,6 +21,7 @@ import 'widgets/desktop_conversation_card.dart'; import 'widgets/desktop_daily_score_widget.dart'; import 'widgets/desktop_empty_conversations.dart'; import 'widgets/desktop_goals_widget.dart'; +import 'widgets/desktop_processing_conversation_widget.dart'; import 'widgets/desktop_recording_widget.dart'; import 'widgets/desktop_search_result_header.dart'; import 'widgets/desktop_search_widget.dart'; @@ -311,9 +312,13 @@ class _DesktopConversationsPageState extends State opacity: _fadeAnimation, child: Container( padding: const EdgeInsets.fromLTRB(32, 24, 32, 24), - child: DesktopRecordingWidget( - hasConversations: true, - onStartRecording: _showExpandedRecordingView, + child: Column( + children: [ + DesktopRecordingWidget( + hasConversations: true, + onStartRecording: _showExpandedRecordingView, + ), + ], ), ), ), @@ -351,6 +356,17 @@ class _DesktopConversationsPageState extends State ), ), ), + if (convoProvider.processingConversations.isNotEmpty) + SliverToBoxAdapter( + child: FadeTransition( + opacity: _fadeAnimation, + child: Padding( + padding: const EdgeInsets.fromLTRB(32, 0, 32, 24), + child: getDesktopProcessingConversationsWidget( + convoProvider.processingConversations), + ), + ), + ), // Search result header (only show if there are conversations in system) if (hasAnyConversationsInSystem) diff --git a/app/lib/desktop/pages/conversations/widgets/desktop_processing_conversation_widget.dart b/app/lib/desktop/pages/conversations/widgets/desktop_processing_conversation_widget.dart new file mode 100644 index 0000000000..4de36b103e --- /dev/null +++ b/app/lib/desktop/pages/conversations/widgets/desktop_processing_conversation_widget.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; + +import 'package:omi/backend/schema/conversation.dart'; +import 'package:omi/utils/l10n_extensions.dart'; +import 'package:omi/utils/responsive/responsive_helper.dart'; + +class DesktopProcessingConversationWidget extends StatelessWidget { + final ServerConversation? conversation; + + const DesktopProcessingConversationWidget({ + super.key, + this.conversation, + }); + + @override + Widget build(BuildContext context) { + return Container( + width: double.maxFinite, + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16), + decoration: BoxDecoration( + color: ResponsiveHelper.backgroundTertiary.withValues(alpha: 0.4), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: ResponsiveHelper.backgroundTertiary.withValues(alpha: 0.6), + width: 1, + ), + ), + child: Row( + children: [ + // Shimmer emoji placeholder (matches DesktopConversationCard 44x44 rounded square) + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: ResponsiveHelper.backgroundTertiary.withValues(alpha: 0.8), + borderRadius: BorderRadius.circular(12), + ), + child: const Center( + child: SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator( + strokeWidth: 2, + color: ResponsiveHelper.textTertiary, + ), + ), + ), + ), + const SizedBox(width: 14), + // Title and subtitle placeholders + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + context.l10n.processing, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w500, + color: ResponsiveHelper.textTertiary, + height: 1.3, + ), + ), + const SizedBox(height: 6), + // Shimmer bar for subtitle + Container( + width: 80, + height: 10, + decoration: BoxDecoration( + color: ResponsiveHelper.backgroundTertiary.withValues(alpha: 0.6), + borderRadius: BorderRadius.circular(5), + ), + ), + ], + ), + ), + ], + ), + ); + } +} + +/// Widget to display processing conversations in a list +Widget getDesktopProcessingConversationsWidget(List conversations) { + if (conversations.isEmpty) { + return const SizedBox.shrink(); + } + + // Show only the first (most recent) processing conversation + return DesktopProcessingConversationWidget(conversation: conversations.first); +}