diff --git a/ios/ShareExtension/Info.plist b/ios/ShareExtension/Info.plist
index c9cdb4a..12985db 100644
--- a/ios/ShareExtension/Info.plist
+++ b/ios/ShareExtension/Info.plist
@@ -31,6 +31,8 @@
10
NSExtensionActivationSupportsText
+ NSExtensionActivationSupportsFileWithMaxCount
+ 20
PHSupportedMediaTypes
diff --git a/lib/core/services/share_receiver_service.dart b/lib/core/services/share_receiver_service.dart
index 314b0f1..1625430 100644
--- a/lib/core/services/share_receiver_service.dart
+++ b/lib/core/services/share_receiver_service.dart
@@ -9,6 +9,7 @@ import '../../features/auth/providers/unified_auth_providers.dart';
import '../../features/chat/providers/chat_providers.dart';
import '../../features/chat/services/file_attachment_service.dart';
import '../../core/providers/app_providers.dart';
+// No server chat creation here; follow chat flow on first send
/// Lightweight payload for a share event
class SharedPayload {
@@ -160,11 +161,16 @@ Future _processPayload(Ref ref, SharedPayload payload) async {
}
}
- // Prefill text in the composer (do not auto-send)
+ // Prefill text in the composer (do not auto-send) and request focus
final text = payload.text?.trim();
if (text != null && text.isNotEmpty) {
ref.read(prefilledInputTextProvider.notifier).state = text;
+ // Bump focus trigger to ensure input focuses after navigation/build
+ final current = ref.read(inputFocusTriggerProvider);
+ ref.read(inputFocusTriggerProvider.notifier).state = current + 1;
}
+ // Do NOT create a placeholder server chat here. The drawer will refresh
+ // when the user sends their first message, matching in-app behavior.
// This allows the user to add a caption before sending
} catch (e) {
debugPrint('ShareReceiver: failed to process payload: $e');
diff --git a/lib/features/chat/providers/chat_providers.dart b/lib/features/chat/providers/chat_providers.dart
index 3fc65b2..174472a 100644
--- a/lib/features/chat/providers/chat_providers.dart
+++ b/lib/features/chat/providers/chat_providers.dart
@@ -24,6 +24,9 @@ final isLoadingConversationProvider = StateProvider((ref) => false);
// Prefilled input text (e.g., when sharing text from other apps)
final prefilledInputTextProvider = StateProvider((ref) => null);
+// Trigger to request focus on the chat input (increment to signal)
+final inputFocusTriggerProvider = StateProvider((ref) => 0);
+
class ChatMessagesNotifier extends StateNotifier> {
final Ref _ref;
StreamSubscription? _messageStream;
@@ -533,7 +536,14 @@ Future _sendMessageInternal(
// Invalidate conversations provider to refresh the list
// Adding a small delay to prevent rapid invalidations that could cause duplicates
Future.delayed(const Duration(milliseconds: 100), () {
- ref.invalidate(conversationsProvider);
+ try {
+ // Guard against using ref after widget disposal
+ if (ref.mounted == true) {
+ ref.invalidate(conversationsProvider);
+ }
+ } catch (_) {
+ // If ref doesn't support mounted or is disposed, skip
+ }
});
} catch (e) {
// Still add the message locally
@@ -1742,7 +1752,11 @@ Future _saveConversationToServer(dynamic ref) async {
// Refresh conversations list to show the updated conversation
// Adding a small delay to prevent rapid invalidations that could cause duplicates
Future.delayed(const Duration(milliseconds: 100), () {
- ref.invalidate(conversationsProvider);
+ try {
+ if (ref.mounted == true) {
+ ref.invalidate(conversationsProvider);
+ }
+ } catch (_) {}
});
} catch (e) {
// Fallback to local storage
diff --git a/lib/features/chat/widgets/enhanced_attachment.dart b/lib/features/chat/widgets/enhanced_attachment.dart
index 073123b..c2371cc 100644
--- a/lib/features/chat/widgets/enhanced_attachment.dart
+++ b/lib/features/chat/widgets/enhanced_attachment.dart
@@ -204,6 +204,18 @@ class _EnhancedAttachmentState extends ConsumerState {
.toString();
final size = _fileInfo?['size'];
final sizeLabel = size is num ? _formatSize(size.toInt()) : null;
+ final lowerName = filename.toLowerCase();
+ final fileExtension = lowerName.contains('.')
+ ? lowerName.split('.').last
+ : '';
+ final List metaParts = [];
+ if (fileExtension.isNotEmpty) {
+ metaParts.add('.${fileExtension.toUpperCase()}');
+ }
+ if (sizeLabel != null) {
+ metaParts.add(sizeLabel);
+ }
+ final metaLabel = metaParts.join(' • ');
final card = Container(
constraints: widget.constraints,
@@ -217,15 +229,14 @@ class _EnhancedAttachmentState extends ConsumerState {
),
),
child: Row(
- mainAxisSize: MainAxisSize.min,
+ mainAxisSize: MainAxisSize.max,
children: [
Text(
_fileIconFor(filename),
style: const TextStyle(fontSize: AppTypography.headlineLarge),
),
const SizedBox(width: Spacing.sm),
- ConstrainedBox(
- constraints: const BoxConstraints(maxWidth: 220),
+ Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
@@ -240,9 +251,9 @@ class _EnhancedAttachmentState extends ConsumerState {
fontWeight: FontWeight.w600,
),
),
- if (sizeLabel != null)
+ if (metaLabel.isNotEmpty)
Text(
- sizeLabel,
+ metaLabel,
style: TextStyle(
color: context.conduitTheme.textSecondary.withValues(
alpha: 0.7,
diff --git a/lib/features/chat/widgets/modern_chat_input.dart b/lib/features/chat/widgets/modern_chat_input.dart
index c557791..631be8c 100644
--- a/lib/features/chat/widgets/modern_chat_input.dart
+++ b/lib/features/chat/widgets/modern_chat_input.dart
@@ -342,6 +342,16 @@ class _ModernChatInputState extends ConsumerState
orElse: () => false,
);
+ // React to external focus requests (e.g., from share prefill)
+ final focusTick = ref.watch(inputFocusTriggerProvider);
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (!mounted) return;
+ if (focusTick > 0) {
+ _ensureFocusedIfEnabled();
+ if (!_isExpanded) _setExpanded(true);
+ }
+ });
+
return Container(
// Transparent wrapper so rounded corners are visible against page background
color: Colors.transparent,