Skip to content

Commit 36d25b8

Browse files
authored
Merge branch 'master' into 477-past-npub-icon-button-in-profile-search
2 parents 512d077 + 806c5b4 commit 36d25b8

File tree

3 files changed

+230
-21
lines changed

3 files changed

+230
-21
lines changed

lib/ui/chat/chat_management/add_to_group_screen.dart

Lines changed: 103 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ import 'package:flutter/material.dart';
22
import 'package:flutter_riverpod/flutter_riverpod.dart';
33
import 'package:gap/gap.dart';
44
import 'package:whitenoise/config/extensions/toast_extension.dart';
5+
import 'package:whitenoise/config/providers/follows_provider.dart';
56
import 'package:whitenoise/config/providers/group_provider.dart';
7+
import 'package:whitenoise/config/providers/user_profile_data_provider.dart';
8+
import 'package:whitenoise/domain/models/contact_model.dart';
69
import 'package:whitenoise/src/rust/api/groups.dart';
10+
import 'package:whitenoise/ui/chat/chat_management/widgets/create_group_dialog.dart';
11+
import 'package:whitenoise/ui/contact_list/new_group_chat_sheet.dart';
712
import 'package:whitenoise/ui/core/themes/assets.dart';
813
import 'package:whitenoise/ui/core/themes/src/app_theme.dart';
914
import 'package:whitenoise/ui/core/ui/wn_avatar.dart';
@@ -37,30 +42,48 @@ class _AddToGroupScreenState extends ConsumerState<AddToGroupScreen> {
3742
setState(() {
3843
_isLoading = true;
3944
});
40-
await ref.read(groupsProvider.notifier).loadGroups();
4145

42-
final regularGroups = await ref.read(groupsProvider.notifier).getRegularGroups();
43-
if (regularGroups.isEmpty) {
44-
return;
45-
}
46+
try {
47+
await ref.read(groupsProvider.notifier).loadGroups();
4648

47-
final loadTasks = <Future<void>>[];
49+
final regularGroups = await ref.read(groupsProvider.notifier).getRegularGroups();
50+
if (regularGroups.isEmpty) {
51+
// Show dialog when no groups exist
52+
if (mounted) {
53+
_showCreateGroupDialog();
54+
}
55+
return;
56+
}
57+
58+
final loadTasks = <Future<void>>[];
4859

49-
for (final group in regularGroups) {
50-
final existingMembers = ref.read(groupsProvider).groupMembers?[group.mlsGroupId];
51-
if (existingMembers == null) {
52-
loadTasks.add(ref.read(groupsProvider.notifier).loadGroupMembers(group.mlsGroupId));
60+
for (final group in regularGroups) {
61+
final existingMembers = ref.read(groupsProvider).groupMembers?[group.mlsGroupId];
62+
if (existingMembers == null) {
63+
loadTasks.add(ref.read(groupsProvider.notifier).loadGroupMembers(group.mlsGroupId));
64+
}
5365
}
54-
}
5566

56-
if (loadTasks.isNotEmpty) {
57-
await Future.wait(loadTasks);
58-
}
67+
if (loadTasks.isNotEmpty) {
68+
await Future.wait(loadTasks);
69+
}
5970

60-
setState(() {
61-
_regularGroups = regularGroups;
62-
_isLoading = false;
63-
});
71+
setState(() {
72+
_regularGroups = regularGroups;
73+
});
74+
} catch (e) {
75+
// Handle any errors during group loading
76+
if (mounted) {
77+
ref.showErrorToast('Failed to load groups: $e');
78+
}
79+
} finally {
80+
// Always ensure loading state is reset
81+
if (mounted) {
82+
setState(() {
83+
_isLoading = false;
84+
});
85+
}
86+
}
6487
}
6588

6689
Future<void> _addUserToGroups() async {
@@ -108,6 +131,68 @@ class _AddToGroupScreenState extends ConsumerState<AddToGroupScreen> {
108131
});
109132
}
110133

134+
void _showCreateGroupDialog() {
135+
CreateGroupDialog.show(
136+
context,
137+
onCreateGroup: () async {
138+
try {
139+
// Close the dialog only
140+
if (Navigator.of(context).canPop()) {
141+
Navigator.of(context).pop();
142+
}
143+
144+
// Get contact information for the user to be added
145+
ContactModel? contactToAdd;
146+
try {
147+
// First try to get from follows (cached contacts)
148+
final followsNotifier = ref.read(followsProvider.notifier);
149+
final existingFollow = followsNotifier.findFollowByPubkey(widget.contactNpub);
150+
151+
if (existingFollow != null) {
152+
contactToAdd = ContactModel.fromMetadata(
153+
pubkey: existingFollow.pubkey,
154+
metadata: existingFollow.metadata,
155+
);
156+
} else {
157+
// If not in follows, fetch from user profile data provider
158+
final userProfileDataNotifier = ref.read(userProfileDataProvider.notifier);
159+
contactToAdd = await userProfileDataNotifier.getUserProfileData(widget.contactNpub);
160+
}
161+
} catch (e) {
162+
// Create a basic contact model with just the public key
163+
contactToAdd = ContactModel(
164+
displayName: 'Unknown User',
165+
publicKey: widget.contactNpub,
166+
);
167+
}
168+
169+
// Ensure we always have a contact (in case getUserProfileData returns null)
170+
171+
if (!mounted) return;
172+
173+
await NewGroupChatSheet.show(
174+
context,
175+
preSelectedContacts: [contactToAdd],
176+
onGroupCreated: (group) {
177+
// Only pop the AddToGroupScreen if group was created successfully
178+
if (mounted && group != null) {
179+
Navigator.of(context).pop();
180+
}
181+
},
182+
);
183+
} catch (e) {
184+
if (mounted) {
185+
ref.showErrorToast('Error creating group: $e');
186+
}
187+
}
188+
},
189+
onCancel: () {
190+
Navigator.of(context).pop();
191+
Navigator.of(context).pop();
192+
},
193+
);
194+
}
195+
111196
@override
112197
Widget build(BuildContext context) {
113198
return Scaffold(
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_screenutil/flutter_screenutil.dart';
3+
import 'package:gap/gap.dart';
4+
import 'package:whitenoise/domain/models/contact_model.dart';
5+
import 'package:whitenoise/ui/core/themes/assets.dart';
6+
import 'package:whitenoise/ui/core/themes/src/extensions.dart';
7+
import 'package:whitenoise/ui/core/ui/wn_button.dart';
8+
import 'package:whitenoise/ui/core/ui/wn_dialog.dart';
9+
import 'package:whitenoise/ui/core/ui/wn_image.dart';
10+
11+
class CreateGroupDialog extends StatelessWidget {
12+
final VoidCallback? onCreateGroup;
13+
final VoidCallback? onCancel;
14+
final ContactModel? contactToAdd;
15+
16+
const CreateGroupDialog({
17+
super.key,
18+
this.onCreateGroup,
19+
this.onCancel,
20+
this.contactToAdd,
21+
});
22+
23+
@override
24+
Widget build(BuildContext context) {
25+
return WnDialog.custom(
26+
customChild: Column(
27+
mainAxisSize: MainAxisSize.min,
28+
children: [
29+
Row(
30+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
31+
children: [
32+
Expanded(
33+
child: Text(
34+
'Create a Group to Continue',
35+
style: context.textTheme.bodyLarge?.copyWith(
36+
color: context.colors.primary,
37+
fontSize: 18.sp,
38+
fontWeight: FontWeight.w600,
39+
),
40+
),
41+
),
42+
GestureDetector(
43+
onTap: onCancel ?? () => Navigator.of(context).pop(),
44+
child: WnImage(
45+
AssetsPaths.icClose,
46+
size: 16.w,
47+
color: context.colors.mutedForeground,
48+
),
49+
),
50+
],
51+
),
52+
Gap(8.h),
53+
Align(
54+
alignment: Alignment.centerLeft,
55+
child: Text(
56+
'You are not a member of any groups. Make a new group to add someone.',
57+
style: context.textTheme.bodyMedium?.copyWith(
58+
color: context.colors.mutedForeground,
59+
fontSize: 14.sp,
60+
height: 1.4,
61+
),
62+
textAlign: TextAlign.left,
63+
),
64+
),
65+
Gap(16.h),
66+
Column(
67+
children: [
68+
WnFilledButton(
69+
onPressed: onCancel ?? () => Navigator.of(context).pop(),
70+
label: 'Cancel',
71+
visualState: WnButtonVisualState.secondary,
72+
size: WnButtonSize.small,
73+
labelTextStyle: TextStyle(
74+
fontSize: 16.sp,
75+
fontWeight: FontWeight.w600,
76+
),
77+
),
78+
Gap(12.h),
79+
WnFilledButton(
80+
onPressed: onCreateGroup,
81+
label: 'New Group Chat',
82+
size: WnButtonSize.small,
83+
labelTextStyle: TextStyle(
84+
fontSize: 16.sp,
85+
fontWeight: FontWeight.w600,
86+
),
87+
),
88+
],
89+
),
90+
],
91+
),
92+
);
93+
}
94+
95+
static Future<bool?> show(
96+
BuildContext context, {
97+
VoidCallback? onCreateGroup,
98+
VoidCallback? onCancel,
99+
ContactModel? contactToAdd,
100+
}) {
101+
return showDialog<bool>(
102+
context: context,
103+
builder:
104+
(context) => CreateGroupDialog(
105+
onCreateGroup: onCreateGroup,
106+
onCancel: onCancel,
107+
contactToAdd: contactToAdd,
108+
),
109+
);
110+
}
111+
}

lib/ui/contact_list/new_group_chat_sheet.dart

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,28 @@ import 'package:whitenoise/utils/clipboard_utils.dart';
1919

2020
class NewGroupChatSheet extends ConsumerStatefulWidget {
2121
final ValueChanged<Group?>? onGroupCreated;
22+
final List<ContactModel>? preSelectedContacts;
2223

23-
const NewGroupChatSheet({super.key, this.onGroupCreated});
24+
const NewGroupChatSheet({super.key, this.onGroupCreated, this.preSelectedContacts});
2425

2526
@override
2627
ConsumerState<NewGroupChatSheet> createState() => _NewGroupChatSheetState();
2728

28-
static Future<void> show(BuildContext context, {ValueChanged<Group?>? onGroupCreated}) {
29+
static Future<void> show(
30+
BuildContext context, {
31+
ValueChanged<Group?>? onGroupCreated,
32+
List<ContactModel>? preSelectedContacts,
33+
}) {
2934
return WnBottomSheet.show(
3035
context: context,
3136
title: 'New group chat',
3237
blurSigma: 8.0,
3338
transitionDuration: const Duration(milliseconds: 400),
34-
builder: (context) => NewGroupChatSheet(onGroupCreated: onGroupCreated),
39+
builder:
40+
(context) => NewGroupChatSheet(
41+
onGroupCreated: onGroupCreated,
42+
preSelectedContacts: preSelectedContacts,
43+
),
3544
);
3645
}
3746
}
@@ -45,6 +54,10 @@ class _NewGroupChatSheetState extends ConsumerState<NewGroupChatSheet> {
4554
void initState() {
4655
super.initState();
4756
_searchController.addListener(_onSearchChanged);
57+
// Add pre-selected contacts to the selection
58+
if (widget.preSelectedContacts != null) {
59+
_selectedContacts.addAll(widget.preSelectedContacts!);
60+
}
4861
}
4962

5063
@override

0 commit comments

Comments
 (0)