refactor: ux

This commit is contained in:
cogwheel0
2025-08-21 16:15:27 +05:30
parent dc166e2347
commit 6dedc1924c
8 changed files with 76 additions and 445 deletions

View File

@@ -50,6 +50,8 @@ PODS:
- SDWebImage (5.21.1): - SDWebImage (5.21.1):
- SDWebImage/Core (= 5.21.1) - SDWebImage/Core (= 5.21.1)
- SDWebImage/Core (5.21.1) - SDWebImage/Core (5.21.1)
- share_plus (0.0.1):
- Flutter
- shared_preferences_foundation (0.0.1): - shared_preferences_foundation (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
@@ -71,6 +73,7 @@ DEPENDENCIES:
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- record_ios (from `.symlinks/plugins/record_ios/ios`) - record_ios (from `.symlinks/plugins/record_ios/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
@@ -100,6 +103,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin" :path: ".symlinks/plugins/path_provider_foundation/darwin"
record_ios: record_ios:
:path: ".symlinks/plugins/record_ios/ios" :path: ".symlinks/plugins/record_ios/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation: shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin" :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite_darwin: sqflite_darwin:
@@ -121,6 +126,7 @@ SPEC CHECKSUMS:
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
record_ios: fee1c924aa4879b882ebca2b4bce6011bcfc3d8b record_ios: fee1c924aa4879b882ebca2b4bce6011bcfc3d8b
SDWebImage: f29024626962457f3470184232766516dee8dfea SDWebImage: f29024626962457f3470184232766516dee8dfea
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4

View File

@@ -9,7 +9,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_animate/flutter_animate.dart';
import 'dart:io' show Platform, File; import 'dart:io' show Platform, File;
import 'dart:async'; import 'dart:async';
import 'package:path/path.dart' as path;
import '../../../core/providers/app_providers.dart'; import '../../../core/providers/app_providers.dart';
import '../providers/chat_providers.dart'; import '../providers/chat_providers.dart';
import '../../../core/utils/debug_logger.dart'; import '../../../core/utils/debug_logger.dart';
@@ -247,11 +246,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
if (selectedModel == null) { if (selectedModel == null) {
debugPrint('DEBUG: No model selected'); debugPrint('DEBUG: No model selected');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Please select a model first')),
);
}
return; return;
} }
@@ -262,16 +256,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
); );
if (!isOnline && !isReviewerMode) { if (!isOnline && !isReviewerMode) {
debugPrint('DEBUG: Offline - cannot send message'); debugPrint('DEBUG: Offline - cannot send message');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text(
'You\'re offline. Message will be sent when connection is restored.',
),
backgroundColor: context.conduitTheme.warning,
),
);
}
return; return;
} }
@@ -328,22 +312,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
}); });
} catch (e) { } catch (e) {
debugPrint('DEBUG: Message send error: $e'); debugPrint('DEBUG: Message send error: $e');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text(
'Message failed to send. Check your connection and try again.',
),
backgroundColor: context.conduitTheme.error,
action: SnackBarAction(
label: 'Retry',
textColor: Colors.white,
onPressed: () => _handleMessageSend(text, selectedModel),
),
duration: const Duration(seconds: 6),
),
);
}
} }
} }
@@ -353,12 +321,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
if (!isAvailable) { if (!isAvailable) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Voice input unavailable. Check permissions.'),
backgroundColor: context.conduitTheme.warning,
),
);
return; return;
} }
@@ -386,20 +348,11 @@ class _ChatPageState extends ConsumerState<ChatPage> {
final fileUploadCapableModels = ref.read(fileUploadCapableModelsProvider); final fileUploadCapableModels = ref.read(fileUploadCapableModelsProvider);
if (fileUploadCapableModels.isEmpty) { if (fileUploadCapableModels.isEmpty) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Selected model does not support file upload'),
backgroundColor: context.conduitTheme.error,
),
);
return; return;
} }
final fileService = ref.read(fileAttachmentServiceProvider); final fileService = ref.read(fileAttachmentServiceProvider);
if (fileService == null) { if (fileService == null) {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('File service unavailable')));
return; return;
} }
@@ -411,12 +364,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
final currentFiles = ref.read(attachedFilesProvider); final currentFiles = ref.read(attachedFilesProvider);
if (!validateFileCount(currentFiles.length, files.length, 10)) { if (!validateFileCount(currentFiles.length, files.length, 10)) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Maximum 10 files allowed'),
backgroundColor: context.conduitTheme.error,
),
);
return; return;
} }
@@ -425,14 +372,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
final fileSize = await file.length(); final fileSize = await file.length();
if (!validateFileSize(fileSize, 20)) { if (!validateFileSize(fileSize, 20)) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'File ${path.basename(file.path)} exceeds 20MB limit',
),
backgroundColor: context.conduitTheme.error,
),
);
return; return;
} }
} }
@@ -451,23 +390,13 @@ class _ChatPageState extends ConsumerState<ChatPage> {
}, },
onError: (error) { onError: (error) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( debugPrint('Upload failed: $error');
SnackBar(
content: Text('Upload failed: $error'),
backgroundColor: context.conduitTheme.error,
),
);
}, },
); );
} }
} catch (e) { } catch (e) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( debugPrint('File selection failed: $e');
SnackBar(
content: Text('File selection failed: $e'),
backgroundColor: context.conduitTheme.error,
),
);
} }
} }
@@ -480,21 +409,12 @@ class _ChatPageState extends ConsumerState<ChatPage> {
final visionCapableModels = ref.read(visionCapableModelsProvider); final visionCapableModels = ref.read(visionCapableModelsProvider);
if (visionCapableModels.isEmpty) { if (visionCapableModels.isEmpty) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Selected model does not support image inputs'),
backgroundColor: context.conduitTheme.error,
),
);
return; return;
} }
final fileService = ref.read(fileAttachmentServiceProvider); final fileService = ref.read(fileAttachmentServiceProvider);
if (fileService == null) { if (fileService == null) {
debugPrint('DEBUG: File service is null - cannot proceed'); debugPrint('DEBUG: File service is null - cannot proceed');
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('File service unavailable')));
return; return;
} }
@@ -515,12 +435,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
// Validate file size (default 20MB limit like OpenWebUI) // Validate file size (default 20MB limit like OpenWebUI)
if (!validateFileSize(imageSize, 20)) { if (!validateFileSize(imageSize, 20)) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Image size exceeds 20MB limit'),
backgroundColor: context.conduitTheme.error,
),
);
return; return;
} }
@@ -528,12 +442,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
final currentFiles = ref.read(attachedFilesProvider); final currentFiles = ref.read(attachedFilesProvider);
if (!validateFileCount(currentFiles.length, 1, 10)) { if (!validateFileCount(currentFiles.length, 1, 10)) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Maximum 10 files allowed'),
backgroundColor: context.conduitTheme.error,
),
);
return; return;
} }
@@ -556,23 +464,11 @@ class _ChatPageState extends ConsumerState<ChatPage> {
onError: (error) { onError: (error) {
debugPrint('DEBUG: Image upload error: $error'); debugPrint('DEBUG: Image upload error: $error');
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Image upload failed: $error'),
backgroundColor: context.conduitTheme.error,
),
);
}, },
); );
} catch (e) { } catch (e) {
debugPrint('DEBUG: Image attachment error: $e'); debugPrint('DEBUG: Image attachment error: $e');
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Image attachment failed: $e'),
backgroundColor: context.conduitTheme.error,
),
);
} }
} }
@@ -586,14 +482,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
_showScrollToBottom = false; _showScrollToBottom = false;
}); });
} }
// Show success message
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('New chat started'),
duration: Duration(seconds: 2),
),
);
} }
void _showChatsListOverlay() { void _showChatsListOverlay() {
@@ -1029,20 +917,11 @@ class _ChatPageState extends ConsumerState<ChatPage> {
void _copyMessage(String content) { void _copyMessage(String content) {
Clipboard.setData(ClipboardData(text: content)); Clipboard.setData(ClipboardData(text: content));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Copied to clipboard'),
duration: Duration(seconds: 2),
),
);
} }
void _regenerateMessage(dynamic message) async { void _regenerateMessage(dynamic message) async {
final selectedModel = ref.read(selectedModelProvider); final selectedModel = ref.read(selectedModelProvider);
if (selectedModel == null) { if (selectedModel == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Please select a model first')),
);
return; return;
} }
@@ -1051,9 +930,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
final messageIndex = messages.indexOf(message); final messageIndex = messages.indexOf(message);
if (messageIndex <= 0 || messages[messageIndex - 1].role != 'user') { if (messageIndex <= 0 || messages[messageIndex - 1].role != 'user') {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Cannot regenerate this message')),
);
return; return;
} }
@@ -1068,40 +944,13 @@ class _ChatPageState extends ConsumerState<ChatPage> {
userMessage.content, userMessage.content,
userMessage.attachmentIds, userMessage.attachmentIds,
); );
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Regenerating...'),
duration: Duration(seconds: 2),
),
);
}
} catch (e) { } catch (e) {
if (mounted) { debugPrint('Regenerate failed: $e');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Failed to regenerate message. Try again or check your connection.',
),
backgroundColor: context.conduitTheme.error,
action: SnackBarAction(
label: 'Retry',
textColor: Colors.white,
onPressed: () => _regenerateMessage(message),
),
duration: const Duration(seconds: 6),
),
);
}
} }
} }
void _editMessage(dynamic message) async { void _editMessage(dynamic message) async {
if (message.role != 'user') { if (message.role != 'user') {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Only user messages can be edited')),
);
return; return;
} }
@@ -1170,25 +1019,11 @@ class _ChatPageState extends ConsumerState<ChatPage> {
if (selectedModel != null) { if (selectedModel != null) {
await sendMessage(ref, result, null); await sendMessage(ref, result, null);
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Message updated'),
duration: Duration(seconds: 2),
),
);
}
} }
} }
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to edit message: $e'),
backgroundColor: context.conduitTheme.error,
),
);
}
} }
} }
@@ -1197,16 +1032,10 @@ class _ChatPageState extends ConsumerState<ChatPage> {
void _likeMessage(dynamic message) { void _likeMessage(dynamic message) {
// TODO: Implement message liking // TODO: Implement message liking
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Message liked!')));
} }
void _dislikeMessage(dynamic message) { void _dislikeMessage(dynamic message) {
// TODO: Implement message disliking // TODO: Implement message disliking
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Message disliked!')));
} }
Widget _buildEmptyState(ThemeData theme) { Widget _buildEmptyState(ThemeData theme) {
@@ -1755,14 +1584,7 @@ class _ChatPageState extends ConsumerState<ChatPage> {
// ref.read(chatMessagesProvider.notifier).removeMessage(selectedMessage.id); // ref.read(chatMessagesProvider.notifier).removeMessage(selectedMessage.id);
// } // }
_clearSelection(); _clearSelection();
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Messages removed'),
duration: Duration(seconds: 2),
),
);
}
} }
}); });
} }
@@ -2249,28 +2071,14 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> {
_isListening = false; _isListening = false;
}); });
_elapsedTimer?.cancel(); _elapsedTimer?.cancel();
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Voice input error: $error'),
backgroundColor: context.conduitTheme.error,
),
);
}
}, },
); );
} catch (e) { } catch (e) {
setState(() { setState(() {
_isListening = false; _isListening = false;
}); });
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to start voice input: $e'),
backgroundColor: context.conduitTheme.error,
),
);
}
} }
} }
@@ -2304,12 +2112,6 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> {
setState(() => _isListening = false); setState(() => _isListening = false);
} catch (e) { } catch (e) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Transcription failed: $e'),
backgroundColor: context.conduitTheme.error,
),
);
setState(() => _isListening = false); setState(() => _isListening = false);
} finally { } finally {
if (mounted) setState(() => _isTranscribing = false); if (mounted) setState(() => _isTranscribing = false);

View File

@@ -313,9 +313,11 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
), ),
), ),
// Action buttons below the message content (always visible) // Action buttons below the message content (only after streaming completes)
const SizedBox(height: Spacing.sm), if (!widget.isStreaming) ...[
_buildActionButtons(), const SizedBox(height: Spacing.sm),
_buildActionButtons(),
],
], ],
), ),
) )
@@ -387,15 +389,23 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
child: EnhancedImageAttachment( child: EnhancedImageAttachment(
attachmentId: widget.message.attachmentIds![0], attachmentId: widget.message.attachmentIds![0],
isMarkdownFormat: true, isMarkdownFormat: true,
constraints: const BoxConstraints(maxWidth: 500, maxHeight: 400), constraints: const BoxConstraints(
disableAnimation: widget.isStreaming, // Disable animation during streaming maxWidth: 500,
maxHeight: 400,
),
disableAnimation:
widget.isStreaming, // Disable animation during streaming
), ),
) )
: Wrap( : Wrap(
key: ValueKey('multi_images_${widget.message.attachmentIds!.join('_')}'), key: ValueKey(
'multi_images_${widget.message.attachmentIds!.join('_')}',
),
spacing: Spacing.sm, spacing: Spacing.sm,
runSpacing: Spacing.sm, runSpacing: Spacing.sm,
children: widget.message.attachmentIds!.map<Widget>((attachmentId) { children: widget.message.attachmentIds!.map<Widget>((
attachmentId,
) {
return EnhancedImageAttachment( return EnhancedImageAttachment(
key: ValueKey('attachment_$attachmentId'), key: ValueKey('attachment_$attachmentId'),
attachmentId: attachmentId, attachmentId: attachmentId,
@@ -404,7 +414,8 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
maxWidth: imageCount == 2 ? 245 : 160, maxWidth: imageCount == 2 ? 245 : 160,
maxHeight: imageCount == 2 ? 245 : 160, maxHeight: imageCount == 2 ? 245 : 160,
), ),
disableAnimation: widget.isStreaming, // Disable animation during streaming disableAnimation:
widget.isStreaming, // Disable animation during streaming
); );
}).toList(), }).toList(),
), ),
@@ -415,7 +426,7 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
if (widget.message.files == null || widget.message.files!.isEmpty) { if (widget.message.files == null || widget.message.files!.isEmpty) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
// Filter for image files // Filter for image files
final imageFiles = widget.message.files! final imageFiles = widget.message.files!
.where((file) => file['type'] == 'image') .where((file) => file['type'] == 'image')
@@ -439,24 +450,31 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
builder: (context) { builder: (context) {
final imageUrl = imageFiles[0]['url'] as String?; final imageUrl = imageFiles[0]['url'] as String?;
if (imageUrl == null) return const SizedBox.shrink(); if (imageUrl == null) return const SizedBox.shrink();
return EnhancedImageAttachment( return EnhancedImageAttachment(
attachmentId: imageUrl, // Pass URL directly as it handles URLs attachmentId:
imageUrl, // Pass URL directly as it handles URLs
isMarkdownFormat: true, isMarkdownFormat: true,
constraints: const BoxConstraints(maxWidth: 500, maxHeight: 400), constraints: const BoxConstraints(
disableAnimation: widget.isStreaming, // Disable animation during streaming maxWidth: 500,
maxHeight: 400,
),
disableAnimation: widget
.isStreaming, // Disable animation during streaming
); );
}, },
), ),
) )
: Wrap( : Wrap(
key: ValueKey('gen_multi_${imageFiles.map((f) => f['url']).join('_')}'), key: ValueKey(
'gen_multi_${imageFiles.map((f) => f['url']).join('_')}',
),
spacing: Spacing.sm, spacing: Spacing.sm,
runSpacing: Spacing.sm, runSpacing: Spacing.sm,
children: imageFiles.map<Widget>((file) { children: imageFiles.map<Widget>((file) {
final imageUrl = file['url'] as String?; final imageUrl = file['url'] as String?;
if (imageUrl == null) return const SizedBox.shrink(); if (imageUrl == null) return const SizedBox.shrink();
return EnhancedImageAttachment( return EnhancedImageAttachment(
key: ValueKey('gen_attachment_$imageUrl'), key: ValueKey('gen_attachment_$imageUrl'),
attachmentId: imageUrl, // Pass URL directly attachmentId: imageUrl, // Pass URL directly
@@ -465,7 +483,8 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
maxWidth: imageCount == 2 ? 245 : 160, maxWidth: imageCount == 2 ? 245 : 160,
maxHeight: imageCount == 2 ? 245 : 160, maxHeight: imageCount == 2 ? 245 : 160,
), ),
disableAnimation: widget.isStreaming, // Disable animation during streaming disableAnimation:
widget.isStreaming, // Disable animation during streaming
); );
}).toList(), }).toList(),
), ),

View File

@@ -725,9 +725,8 @@ class FullScreenImageViewer extends ConsumerWidget {
await SharePlus.instance.share(ShareParams(files: [XFile(file.path)])); await SharePlus.instance.share(ShareParams(files: [XFile(file.path)]));
} catch (e) { } catch (e) {
ScaffoldMessenger.of( // Swallowing UI feedback per requirements; keep a log for debugging
context, debugPrint('Failed to share image: $e');
).showSnackBar(const SnackBar(content: Text('Failed to share image')));
} }
} }
} }

View File

@@ -307,36 +307,13 @@ class CopyOptionsSheet extends ConsumerWidget {
if (content != null) { if (content != null) {
await Clipboard.setData(ClipboardData(text: content)); await Clipboard.setData(ClipboardData(text: content));
if (context.mounted) { if (context.mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'${messages.length} messages copied to clipboard',
),
backgroundColor: AppTheme.success,
),
);
}
} }
} else { } else {
if (context.mounted) { if (context.mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to copy messages: ${result.error}'),
backgroundColor: AppTheme.error,
),
);
}
} }
} catch (e) { } catch (e) {
if (context.mounted) { if (context.mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error copying messages: $e'),
backgroundColor: AppTheme.error,
),
);
}
} }
} }
} }
@@ -652,21 +629,10 @@ class MoreOptionsSheet extends ConsumerWidget {
controller.clear(); controller.clear();
setState(() {}); // Refresh the dialog setState(() {}); // Refresh the dialog
if (context.mounted) { if (context.mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Tag "$tag" added')),
);
}
} }
} catch (e) { } catch (e) {
if (context.mounted) { if (context.mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to add tag: $e'),
backgroundColor: AppTheme.error,
),
);
}
} }
} }
}, },
@@ -730,28 +696,10 @@ class MoreOptionsSheet extends ConsumerWidget {
); );
setState(() {}); // Refresh the dialog setState(() {}); // Refresh the dialog
if (context.mounted) { if (context.mounted) {}
ScaffoldMessenger.of(
context,
).showSnackBar(
SnackBar(
content: Text('Tag "$tag" removed'),
),
);
}
} }
} catch (e) { } catch (e) {
if (context.mounted) { if (context.mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Failed to remove tag: $e',
),
backgroundColor:
context.conduitTheme.error,
),
);
}
} }
}, },
), ),
@@ -818,23 +766,12 @@ class MoreOptionsSheet extends ConsumerWidget {
ref.invalidate(archivedConversationsProvider); ref.invalidate(archivedConversationsProvider);
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Conversation archived')),
);
// Navigate back or clear current conversation // Navigate back or clear current conversation
Navigator.of(context).popUntil((route) => route.isFirst); Navigator.of(context).popUntil((route) => route.isFirst);
} }
} }
} catch (e) { } catch (e) {
if (context.mounted) { if (context.mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to archive conversation: $e'),
backgroundColor: AppTheme.error,
),
);
}
} }
} }
} }
@@ -848,7 +785,7 @@ class MoreOptionsSheet extends ConsumerWidget {
confirmText: 'Delete', confirmText: 'Delete',
isDestructive: true, isDestructive: true,
); );
if (confirmed == true && context.mounted) { if (confirmed == true && context.mounted) {
_deleteMessages(context, ref); _deleteMessages(context, ref);
} }
@@ -880,23 +817,12 @@ class MoreOptionsSheet extends ConsumerWidget {
ref.read(chatMessagesProvider.notifier).clearMessages(); ref.read(chatMessagesProvider.notifier).clearMessages();
if (context.mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Conversation deleted')),
);
// Navigate back to conversation list // Navigate back to conversation list
Navigator.of(context).popUntil((route) => route.isFirst); Navigator.of(context).popUntil((route) => route.isFirst);
} }
} }
} catch (e) { } catch (e) {
if (context.mounted) { if (context.mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to delete conversation: $e'),
backgroundColor: AppTheme.error,
),
);
}
} }
} }
} }

View File

@@ -280,6 +280,8 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
autofocus: false, autofocus: false,
maxLines: _isExpanded ? null : 1, maxLines: _isExpanded ? null : 1,
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
textCapitalization:
TextCapitalization.sentences,
textInputAction: TextInputAction.newline, textInputAction: TextInputAction.newline,
showCursor: true, showCursor: true,
cursorColor: context.conduitTheme.inputText, cursorColor: context.conduitTheme.inputText,

View File

@@ -213,20 +213,9 @@ class _TagManagementDialogState extends ConsumerState<TagManagementDialog> {
ref.invalidate(conversationsProvider); ref.invalidate(conversationsProvider);
_tagController.clear(); _tagController.clear();
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Tag "$tag" added')));
}
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error adding tag: $e'),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
}
} finally { } finally {
setState(() => _isAdding = false); setState(() => _isAdding = false);
} }
@@ -240,20 +229,9 @@ class _TagManagementDialogState extends ConsumerState<TagManagementDialog> {
await api.removeTagFromConversation(widget.conversation.id, tag); await api.removeTagFromConversation(widget.conversation.id, tag);
ref.invalidate(conversationsProvider); ref.invalidate(conversationsProvider);
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Tag "$tag" removed')));
}
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error removing tag: $e'),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
}
} }
} }
} }

View File

@@ -1295,12 +1295,7 @@ class _ChatsListPageState extends ConsumerState<ChatsListPage>
String name, String name,
BuildContext dialogContext, BuildContext dialogContext,
) async { ) async {
// Store theme values and messenger before async operation // Begin async operation
final theme = context.conduitTheme;
final textInverseColor = theme.textInverse;
final successColor = theme.success;
final errorColor = theme.error;
final messenger = ScaffoldMessenger.of(context);
try { try {
final api = ref.read(apiServiceProvider); final api = ref.read(apiServiceProvider);
@@ -1312,33 +1307,9 @@ class _ChatsListPageState extends ConsumerState<ChatsListPage>
if (mounted && dialogContext.mounted) { if (mounted && dialogContext.mounted) {
Navigator.pop(dialogContext); Navigator.pop(dialogContext);
} }
if (context.mounted) { if (context.mounted) {}
messenger.showSnackBar(
SnackBar(
content: Text(
'Folder "$name" created',
style: AppTypography.bodyMediumStyle.copyWith(
color: textInverseColor,
),
),
backgroundColor: successColor,
),
);
}
} catch (e) { } catch (e) {
if (context.mounted) { if (context.mounted) {}
messenger.showSnackBar(
SnackBar(
content: Text(
'Failed to create folder: $e',
style: AppTypography.bodyMediumStyle.copyWith(
color: textInverseColor,
),
),
backgroundColor: errorColor,
),
);
}
} }
} }
@@ -1352,35 +1323,11 @@ class _ChatsListPageState extends ConsumerState<ChatsListPage>
// Refresh conversations list // Refresh conversations list
ref.invalidate(conversationsProvider); ref.invalidate(conversationsProvider);
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
newPinnedState ? 'Chat pinned' : 'Chat unpinned',
style: AppTypography.bodyMediumStyle.copyWith(
color: context.conduitTheme.textInverse,
),
),
backgroundColor: context.conduitTheme.success,
),
);
}
} }
} catch (e) { } catch (e) {
DebugLogger.error('Error toggling pin', e); DebugLogger.error('Error toggling pin', e);
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Failed to ${conversation.pinned == true ? 'unpin' : 'pin'} chat',
style: AppTypography.bodyMediumStyle.copyWith(
color: context.conduitTheme.textInverse,
),
),
backgroundColor: context.conduitTheme.error,
),
);
}
} }
} }
@@ -1393,35 +1340,11 @@ class _ChatsListPageState extends ConsumerState<ChatsListPage>
// Refresh conversations list // Refresh conversations list
ref.invalidate(conversationsProvider); ref.invalidate(conversationsProvider);
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Chat archived',
style: AppTypography.bodyMediumStyle.copyWith(
color: context.conduitTheme.textInverse,
),
),
backgroundColor: context.conduitTheme.success,
),
);
}
} }
} catch (e) { } catch (e) {
DebugLogger.error('Error archiving conversation', e); DebugLogger.error('Error archiving conversation', e);
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Failed to archive chat',
style: AppTypography.bodyMediumStyle.copyWith(
color: context.conduitTheme.textInverse,
),
),
backgroundColor: context.conduitTheme.error,
),
);
}
} }
} }
@@ -1446,35 +1369,11 @@ class _ChatsListPageState extends ConsumerState<ChatsListPage>
// Refresh conversations list // Refresh conversations list
ref.invalidate(conversationsProvider); ref.invalidate(conversationsProvider);
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Chat deleted',
style: AppTypography.bodyMediumStyle.copyWith(
color: context.conduitTheme.textInverse,
),
),
backgroundColor: context.conduitTheme.success,
),
);
}
} }
} catch (e) { } catch (e) {
DebugLogger.error('Error deleting conversation', e); DebugLogger.error('Error deleting conversation', e);
if (mounted) { if (mounted) {}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Failed to delete chat',
style: AppTypography.bodyMediumStyle.copyWith(
color: context.conduitTheme.textInverse,
),
),
backgroundColor: context.conduitTheme.error,
),
);
}
} }
} }
} }