-
Notifications
You must be signed in to change notification settings - Fork 14
Show create group dialog when no groups exist #641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8488c74
d498274
35c5525
6f47452
5c5fb46
091d4d5
9feef21
d1279c2
c286da0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,8 +2,13 @@ import 'package:flutter/material.dart'; | |
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||
| import 'package:gap/gap.dart'; | ||
| import 'package:whitenoise/config/extensions/toast_extension.dart'; | ||
| import 'package:whitenoise/config/providers/follows_provider.dart'; | ||
| import 'package:whitenoise/config/providers/group_provider.dart'; | ||
| import 'package:whitenoise/config/providers/user_profile_data_provider.dart'; | ||
| import 'package:whitenoise/domain/models/contact_model.dart'; | ||
| import 'package:whitenoise/src/rust/api/groups.dart'; | ||
| import 'package:whitenoise/ui/chat/chat_management/widgets/create_group_dialog.dart'; | ||
| import 'package:whitenoise/ui/contact_list/new_group_chat_sheet.dart'; | ||
| import 'package:whitenoise/ui/core/themes/assets.dart'; | ||
| import 'package:whitenoise/ui/core/themes/src/app_theme.dart'; | ||
| import 'package:whitenoise/ui/core/ui/wn_avatar.dart'; | ||
|
|
@@ -37,30 +42,48 @@ class _AddToGroupScreenState extends ConsumerState<AddToGroupScreen> { | |
| setState(() { | ||
| _isLoading = true; | ||
| }); | ||
| await ref.read(groupsProvider.notifier).loadGroups(); | ||
|
|
||
| final regularGroups = await ref.read(groupsProvider.notifier).getRegularGroups(); | ||
| if (regularGroups.isEmpty) { | ||
| return; | ||
| } | ||
| try { | ||
| await ref.read(groupsProvider.notifier).loadGroups(); | ||
|
|
||
| final loadTasks = <Future<void>>[]; | ||
| final regularGroups = await ref.read(groupsProvider.notifier).getRegularGroups(); | ||
| if (regularGroups.isEmpty) { | ||
| // Show dialog when no groups exist | ||
| if (mounted) { | ||
| _showCreateGroupDialog(); | ||
| } | ||
| return; | ||
| } | ||
|
|
||
| final loadTasks = <Future<void>>[]; | ||
|
|
||
| for (final group in regularGroups) { | ||
| final existingMembers = ref.read(groupsProvider).groupMembers?[group.mlsGroupId]; | ||
| if (existingMembers == null) { | ||
| loadTasks.add(ref.read(groupsProvider.notifier).loadGroupMembers(group.mlsGroupId)); | ||
| for (final group in regularGroups) { | ||
| final existingMembers = ref.read(groupsProvider).groupMembers?[group.mlsGroupId]; | ||
| if (existingMembers == null) { | ||
| loadTasks.add(ref.read(groupsProvider.notifier).loadGroupMembers(group.mlsGroupId)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (loadTasks.isNotEmpty) { | ||
| await Future.wait(loadTasks); | ||
| } | ||
| if (loadTasks.isNotEmpty) { | ||
| await Future.wait(loadTasks); | ||
| } | ||
|
|
||
| setState(() { | ||
| _regularGroups = regularGroups; | ||
| _isLoading = false; | ||
| }); | ||
| setState(() { | ||
| _regularGroups = regularGroups; | ||
| }); | ||
| } catch (e) { | ||
| // Handle any errors during group loading | ||
| if (mounted) { | ||
| ref.showErrorToast('Failed to load groups: $e'); | ||
| } | ||
| } finally { | ||
| // Always ensure loading state is reset | ||
| if (mounted) { | ||
| setState(() { | ||
| _isLoading = false; | ||
| }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Future<void> _addUserToGroups() async { | ||
|
|
@@ -108,6 +131,68 @@ class _AddToGroupScreenState extends ConsumerState<AddToGroupScreen> { | |
| }); | ||
| } | ||
|
|
||
| void _showCreateGroupDialog() { | ||
| CreateGroupDialog.show( | ||
| context, | ||
| onCreateGroup: () async { | ||
| try { | ||
| // Close the dialog only | ||
| if (Navigator.of(context).canPop()) { | ||
| Navigator.of(context).pop(); | ||
| } | ||
|
|
||
| // Get contact information for the user to be added | ||
| ContactModel? contactToAdd; | ||
| try { | ||
| // First try to get from follows (cached contacts) | ||
| final followsNotifier = ref.read(followsProvider.notifier); | ||
| final existingFollow = followsNotifier.findFollowByPubkey(widget.contactNpub); | ||
|
|
||
| if (existingFollow != null) { | ||
| contactToAdd = ContactModel.fromMetadata( | ||
| pubkey: existingFollow.pubkey, | ||
| metadata: existingFollow.metadata, | ||
| ); | ||
| } else { | ||
| // If not in follows, fetch from user profile data provider | ||
| final userProfileDataNotifier = ref.read(userProfileDataProvider.notifier); | ||
| contactToAdd = await userProfileDataNotifier.getUserProfileData(widget.contactNpub); | ||
| } | ||
| } catch (e) { | ||
|
Comment on lines
+147
to
+161
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Type mismatch and null-safety: ensure ContactModel and never pass [null] to preSelectedContacts.
Apply: - // Get contact information for the user to be added
- ContactModel? contactToAdd;
+ // Get contact information for the user to be added
+ ContactModel? contactToAdd;
try {
// First try to get from follows (cached contacts)
final followsNotifier = ref.read(followsProvider.notifier);
final existingFollow = followsNotifier.findFollowByPubkey(widget.contactNpub);
if (existingFollow != null) {
contactToAdd = ContactModel.fromMetadata(
pubkey: existingFollow.pubkey,
metadata: existingFollow.metadata,
);
} else {
- // If not in follows, fetch from user profile data provider
- final userProfileDataNotifier = ref.read(userProfileDataProvider.notifier);
- contactToAdd = await userProfileDataNotifier.getUserProfileData(widget.contactNpub);
+ // If not in follows, fetch from user profile data provider and convert
+ final userProfileDataNotifier = ref.read(userProfileDataProvider.notifier);
+ final profile = await userProfileDataNotifier.getUserProfileData(widget.contactNpub);
+ if (profile != null) {
+ contactToAdd = ContactModel.fromMetadata(
+ pubkey: widget.contactNpub,
+ metadata: profile.metadata,
+ );
+ }
}
} catch (e) {
- // Create a basic contact model with just the public key
- contactToAdd = ContactModel(
- displayName: 'Unknown User',
- publicKey: widget.contactNpub,
- );
+ // Swallow and fallback below
}
- // Ensure we always have a contact (in case getUserProfileData returns null)
+ // Ensure we always have a contact (in case getUserProfileData returns null)
+ contactToAdd ??= ContactModel(
+ displayName: 'Unknown User',
+ publicKey: widget.contactNpub,
+ );
if (!mounted) return;
await NewGroupChatSheet.show(
context,
- preSelectedContacts: [contactToAdd],
+ preSelectedContacts: [contactToAdd!],
onGroupCreated: (group) {Also applies to: 162-167, 169-176 🤖 Prompt for AI Agents |
||
| // Create a basic contact model with just the public key | ||
| contactToAdd = ContactModel( | ||
| displayName: 'Unknown User', | ||
| publicKey: widget.contactNpub, | ||
| ); | ||
| } | ||
|
|
||
| // Ensure we always have a contact (in case getUserProfileData returns null) | ||
|
|
||
| if (!mounted) return; | ||
|
|
||
| await NewGroupChatSheet.show( | ||
| context, | ||
| preSelectedContacts: [contactToAdd], | ||
| onGroupCreated: (group) { | ||
| // Only pop the AddToGroupScreen if group was created successfully | ||
| if (mounted && group != null) { | ||
| Navigator.of(context).pop(); | ||
| } | ||
| }, | ||
| ); | ||
| } catch (e) { | ||
| if (mounted) { | ||
| ref.showErrorToast('Error creating group: $e'); | ||
| } | ||
| } | ||
| }, | ||
| onCancel: () { | ||
| Navigator.of(context).pop(); | ||
| Navigator.of(context).pop(); | ||
| }, | ||
| ); | ||
| } | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| return Scaffold( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| import 'package:flutter/material.dart'; | ||
| import 'package:flutter_screenutil/flutter_screenutil.dart'; | ||
| import 'package:gap/gap.dart'; | ||
| import 'package:whitenoise/domain/models/contact_model.dart'; | ||
| import 'package:whitenoise/ui/core/themes/assets.dart'; | ||
| import 'package:whitenoise/ui/core/themes/src/extensions.dart'; | ||
| import 'package:whitenoise/ui/core/ui/wn_button.dart'; | ||
| import 'package:whitenoise/ui/core/ui/wn_dialog.dart'; | ||
| import 'package:whitenoise/ui/core/ui/wn_image.dart'; | ||
|
|
||
| class CreateGroupDialog extends StatelessWidget { | ||
| final VoidCallback? onCreateGroup; | ||
| final VoidCallback? onCancel; | ||
| final ContactModel? contactToAdd; | ||
|
|
||
| const CreateGroupDialog({ | ||
| super.key, | ||
| this.onCreateGroup, | ||
| this.onCancel, | ||
| this.contactToAdd, | ||
| }); | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
untreu2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return WnDialog.custom( | ||
| customChild: Column( | ||
| mainAxisSize: MainAxisSize.min, | ||
| children: [ | ||
| Row( | ||
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
| children: [ | ||
| Expanded( | ||
| child: Text( | ||
| 'Create a Group to Continue', | ||
| style: context.textTheme.bodyLarge?.copyWith( | ||
| color: context.colors.primary, | ||
| fontSize: 18.sp, | ||
| fontWeight: FontWeight.w600, | ||
| ), | ||
| ), | ||
untreu2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ), | ||
| GestureDetector( | ||
| onTap: onCancel ?? () => Navigator.of(context).pop(), | ||
| child: WnImage( | ||
| AssetsPaths.icClose, | ||
| size: 16.w, | ||
| color: context.colors.mutedForeground, | ||
| ), | ||
| ), | ||
| ], | ||
| ), | ||
| Gap(8.h), | ||
| Align( | ||
| alignment: Alignment.centerLeft, | ||
| child: Text( | ||
| 'You are not a member of any groups. Make a new group to add someone.', | ||
| style: context.textTheme.bodyMedium?.copyWith( | ||
| color: context.colors.mutedForeground, | ||
| fontSize: 14.sp, | ||
| height: 1.4, | ||
| ), | ||
| textAlign: TextAlign.left, | ||
| ), | ||
| ), | ||
| Gap(16.h), | ||
| Column( | ||
| children: [ | ||
| WnFilledButton( | ||
| onPressed: onCancel ?? () => Navigator.of(context).pop(), | ||
| label: 'Cancel', | ||
| visualState: WnButtonVisualState.secondary, | ||
| size: WnButtonSize.small, | ||
| labelTextStyle: TextStyle( | ||
| fontSize: 16.sp, | ||
| fontWeight: FontWeight.w600, | ||
| ), | ||
| ), | ||
| Gap(12.h), | ||
| WnFilledButton( | ||
| onPressed: onCreateGroup, | ||
| label: 'New Group Chat', | ||
| size: WnButtonSize.small, | ||
| labelTextStyle: TextStyle( | ||
| fontSize: 16.sp, | ||
| fontWeight: FontWeight.w600, | ||
| ), | ||
| ), | ||
| ], | ||
| ), | ||
| ], | ||
| ), | ||
| ); | ||
| } | ||
|
|
||
| static Future<bool?> show( | ||
| BuildContext context, { | ||
| VoidCallback? onCreateGroup, | ||
| VoidCallback? onCancel, | ||
| ContactModel? contactToAdd, | ||
| }) { | ||
| return showDialog<bool>( | ||
| context: context, | ||
| builder: | ||
| (context) => CreateGroupDialog( | ||
| onCreateGroup: onCreateGroup, | ||
| onCancel: onCancel, | ||
| contactToAdd: contactToAdd, | ||
| ), | ||
| ); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Localize user-facing strings with AppLocalizations.
Several strings are hard-coded; move them to l10n.
Example:
Similarly:
Define keys in ARB and replace usages.
Also applies to: 91-92, 118-120, 220-221, 263-264, 296-297
🤖 Prompt for AI Agents