diff --git a/assets/svg/ic_delete_group.svg b/assets/svg/ic_delete_group.svg
new file mode 100644
index 00000000..4187677c
--- /dev/null
+++ b/assets/svg/ic_delete_group.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/svg/ic_pencil.svg b/assets/svg/ic_pencil.svg
new file mode 100644
index 00000000..d66ed484
--- /dev/null
+++ b/assets/svg/ic_pencil.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/svg/ic_trust_deactivated.svg b/assets/svg/ic_trust_deactivated.svg
new file mode 100644
index 00000000..b3cef1bc
--- /dev/null
+++ b/assets/svg/ic_trust_deactivated.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/app.dart b/lib/app.dart
index a8963946..b2d16f34 100644
--- a/lib/app.dart
+++ b/lib/app.dart
@@ -9,6 +9,7 @@ import 'package:atsign_atmosphere_pro/view_models/desktop_groups_screen_provider
import 'package:atsign_atmosphere_pro/view_models/file_download_checker.dart';
import 'package:atsign_atmosphere_pro/desktop_routes/desktop_routes.dart';
import 'package:atsign_atmosphere_pro/view_models/file_progress_provider.dart';
+import 'package:atsign_atmosphere_pro/view_models/groups_provider.dart';
import 'package:atsign_atmosphere_pro/view_models/internet_connectivity_checker.dart';
import 'package:atsign_atmosphere_pro/view_models/my_files_provider.dart';
import 'package:atsign_atmosphere_pro/view_models/side_bar_provider.dart';
@@ -72,6 +73,9 @@ class _MyAppState extends State {
ChangeNotifierProvider(create: (context) => DesktopAddGroupProvider()),
ChangeNotifierProvider(create: (context) => ContactProvider()),
ChangeNotifierProvider(create: (context) => NotificationService()),
+ ChangeNotifierProvider(
+ create: (context) => GroupsProvider(),
+ ),
],
child: MaterialApp(
builder: (BuildContext context, Widget? child) {
diff --git a/lib/desktop_routes/desktop_route_names.dart b/lib/desktop_routes/desktop_route_names.dart
index 4d0fd7b2..deb0294a 100644
--- a/lib/desktop_routes/desktop_route_names.dart
+++ b/lib/desktop_routes/desktop_route_names.dart
@@ -27,4 +27,6 @@ class DesktopRoutes {
'desktop_group_right_initial';
static const String DESKTOP_NEW_GROUP = 'desktop_new_group';
static const String DESKTOP_GROUP_DETAIL = 'desktop_group_detail';
+ static const String DESKTOP_ADD_OR_REMOVE_CONTACTS =
+ 'desktop_add_or_remove_contacts';
}
diff --git a/lib/desktop_routes/desktop_routes.dart b/lib/desktop_routes/desktop_routes.dart
index c7327718..8f736176 100644
--- a/lib/desktop_routes/desktop_routes.dart
+++ b/lib/desktop_routes/desktop_routes.dart
@@ -5,6 +5,7 @@ import 'package:atsign_atmosphere_pro/desktop_screens/desktop_download_all_files
import 'package:atsign_atmosphere_pro/desktop_screens/desktop_home/desktop_home.dart';
import 'package:atsign_atmosphere_pro/desktop_screens/trusted_sender/desktop_empty_trusted_sender.dart';
import 'package:atsign_atmosphere_pro/desktop_screens_new/contacts_screen/desktop_contact_screen.dart';
+import 'package:atsign_atmosphere_pro/desktop_screens_new/groups_screen/desktop_add_or_remove_contacts_screen.dart';
import 'package:atsign_atmosphere_pro/desktop_screens_new/groups_screen/desktop_groups_screen.dart';
import 'package:atsign_atmosphere_pro/desktop_screens_new/settings_screen/blocked_contacts.dart';
import 'package:atsign_atmosphere_pro/desktop_screens_new/settings_screen/settings_desktop.dart';
@@ -71,6 +72,9 @@ class DesktopSetupRoutes {
DesktopRoutes.DEKSTOP_CONTACTS_SCREEN: (context) {
return DesktopContactScreen();
},
+ DesktopRoutes.DESKTOP_ADD_OR_REMOVE_CONTACTS: (context) {
+ return DesktopAddOrRemoveContactsScreen();
+ },
DesktopRoutes.DESKTOP_DOWNLOAD_ALL: (context) {
return DesktopDownloadAllFiles();
},
diff --git a/lib/desktop_screens_new/groups_screen/desktop_add_or_remove_contacts_screen.dart b/lib/desktop_screens_new/groups_screen/desktop_add_or_remove_contacts_screen.dart
new file mode 100644
index 00000000..2b6e34ba
--- /dev/null
+++ b/lib/desktop_screens_new/groups_screen/desktop_add_or_remove_contacts_screen.dart
@@ -0,0 +1,309 @@
+import 'dart:typed_data';
+
+import 'package:at_contact/at_contact.dart';
+import 'package:at_contacts_group_flutter/models/group_contacts_model.dart';
+import 'package:at_contacts_group_flutter/services/group_service.dart';
+import 'package:at_contacts_group_flutter/utils/text_constants.dart';
+import 'package:at_contacts_group_flutter/widgets/confirmation_dialog.dart';
+import 'package:at_onboarding_flutter/at_onboarding_flutter.dart';
+import 'package:atsign_atmosphere_pro/data_models/enums/group_card_state.dart';
+import 'package:atsign_atmosphere_pro/desktop_routes/desktop_route_names.dart';
+import 'package:atsign_atmosphere_pro/desktop_routes/desktop_routes.dart';
+import 'package:atsign_atmosphere_pro/desktop_screens_new/groups_screen/widgets/desktop_cover_image_picker.dart';
+import 'package:atsign_atmosphere_pro/desktop_screens_new/groups_screen/widgets/desktop_group_contacts_list.dart';
+import 'package:atsign_atmosphere_pro/screens/common_widgets/custom_toast.dart';
+import 'package:atsign_atmosphere_pro/utils/colors.dart';
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:atsign_atmosphere_pro/utils/vectors.dart';
+import 'package:atsign_atmosphere_pro/view_models/desktop_groups_screen_provider.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:provider/provider.dart';
+
+class DesktopAddOrRemoveContactsScreen extends StatefulWidget {
+ const DesktopAddOrRemoveContactsScreen();
+
+ @override
+ State createState() =>
+ _DesktopAddOrRemoveContactsScreenState();
+}
+
+class _DesktopAddOrRemoveContactsScreenState
+ extends State {
+ final _groupService = GroupService();
+ late DesktopGroupsScreenProvider _groupsProvider =
+ context.read();
+ bool isLoading = false;
+
+ @override
+ void initState() {
+ super.initState();
+ WidgetsBinding.instance.addPostFrameCallback((_) async {
+ await _groupService.fetchGroupsAndContacts(isDesktop: true);
+
+ _groupService.selectedGroupContacts =
+ _groupsProvider.selectedAtGroup?.members
+ ?.map(
+ (e) => GroupContactsModel(
+ contact: e,
+ contactType: ContactsType.CONTACT,
+ ),
+ )
+ .toList() ??
+ [];
+ _groupService.selectedContactsSink
+ .add(_groupService.selectedGroupContacts);
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: Colors.white,
+ body: Row(
+ children: [
+ Expanded(
+ flex: 572,
+ child: buildHalfLeftWidget(),
+ ),
+ Expanded(
+ flex: 441,
+ child: buildHalfRightWidget(),
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget buildHalfLeftWidget() {
+ return Container(
+ decoration: BoxDecoration(
+ color: ColorConstants.background,
+ borderRadius: BorderRadius.horizontal(
+ right: Radius.circular(47),
+ ),
+ ),
+ child: ListView(
+ children: [
+ SizedBox(height: 40),
+ Padding(
+ padding: const EdgeInsets.only(left: 60),
+ child: buildAppbar(),
+ ),
+ SizedBox(height: 20),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 72),
+ child: DesktopGroupContactsList(
+ asSelectionScreen: true,
+ initialData: _groupsProvider.selectedAtGroup?.members
+ ?.map(
+ (e) => GroupContactsModel(
+ contact: e,
+ contactType: ContactsType.CONTACT,
+ ),
+ )
+ .toList(),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget buildAppbar() {
+ return Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ InkWell(
+ onTap: () async {
+ await DesktopSetupRoutes.nested_push(
+ DesktopRoutes.DESKTOP_GROUP,
+ );
+ },
+ child: SizedBox(
+ width: 24,
+ height: 24,
+ child: Center(
+ child: SvgPicture.asset(
+ AppVectors.icBack,
+ width: 8,
+ height: 16,
+ fit: BoxFit.cover,
+ ),
+ ),
+ ),
+ ),
+ SizedBox(width: 28),
+ Text(
+ 'Add or Remove Contacts',
+ style: CustomTextStyles.blackW50020,
+ )
+ ],
+ );
+ }
+
+ Widget buildHalfRightWidget() {
+ return Stack(
+ children: [
+ ListView(
+ padding: const EdgeInsets.fromLTRB(56, 40, 24, 0),
+ children: [
+ Text(
+ _groupsProvider.selectedGroupName ?? '',
+ style: CustomTextStyles.blackW50020,
+ ),
+ SizedBox(height: 28),
+ DesktopCoverImagePicker(
+ selectedImage: _groupsProvider.selectedGroupImage,
+ isEdit: false,
+ ),
+ SizedBox(height: 52),
+ StreamBuilder>(
+ stream: _groupService.selectedContactsStream,
+ initialData: _groupsProvider.selectedAtGroup?.members
+ ?.map(
+ (e) => GroupContactsModel(
+ contact: e,
+ contactType: ContactsType.CONTACT,
+ ),
+ )
+ .toList(),
+ builder: (context, snapshot) {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Center(
+ child: Text(
+ '${snapshot.data?.length} ${(snapshot.data?.length ?? 0) > 1 ? 'Members' : 'Member'}',
+ style: CustomTextStyles.raisinBlackW50015,
+ ),
+ ),
+ const SizedBox(height: 28),
+ DesktopGroupContactsList(
+ showMembersOnly: true,
+ asSelectionScreen: false,
+ initialData: snapshot.data,
+ showSelectedBorder: false,
+ ),
+ ],
+ );
+ },
+ ),
+ ],
+ ),
+ Positioned(
+ bottom: 0,
+ left: 0,
+ right: 0,
+ child: Container(
+ color: Colors.white,
+ padding: const EdgeInsets.fromLTRB(56, 16, 24, 52),
+ child: InkWell(
+ onTap: () async {
+ await updateMembers(context);
+ },
+ child: Container(
+ alignment: Alignment.center,
+ padding: EdgeInsets.symmetric(vertical: 16),
+ decoration: BoxDecoration(
+ color: ColorConstants.orange,
+ borderRadius: BorderRadius.circular(135),
+ ),
+ child: isLoading
+ ? SizedBox(
+ width: 20,
+ height: 20,
+ child: CircularProgressIndicator(
+ color: Colors.white,
+ ),
+ )
+ : Text(
+ 'Update Group',
+ style: CustomTextStyles.whiteBold16,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+
+ Future updateMembers(BuildContext context) async {
+ if (isLoading) return;
+ setState(() {
+ isLoading = true;
+ });
+
+ if (_groupService.selectedGroupContacts.isEmpty) {
+ await showMyDialog(context, _groupsProvider.selectedAtGroup!);
+ } else {
+ AtGroup group = _groupsProvider.selectedAtGroup!;
+ group.members = _groupService.selectedGroupContacts
+ .where((e) =>
+ e!.contactType == ContactsType.CONTACT && e.contact != null)
+ .map((e) => e!.contact!)
+ .toSet();
+ final result = await GroupService().updateGroup(group);
+ if (result is AtGroup) {
+ _groupService.selectedGroupContacts = [];
+ await _groupsProvider.setSelectedAtGroup(result);
+ await _groupService.fetchGroupsAndContacts(isDesktop: true);
+ await DesktopSetupRoutes.nested_push(
+ DesktopRoutes.DESKTOP_GROUP,
+ );
+ } else if (result != null) {
+ if (result.runtimeType == AlreadyExistsException) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(TextConstants().GROUP_ALREADY_EXISTS)));
+ } else if (result.runtimeType == InvalidAtSignException) {
+ ScaffoldMessenger.of(context)
+ .showSnackBar(SnackBar(content: Text(result.content)));
+ } else {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(TextConstants().SERVICE_ERROR)));
+ }
+ } else {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(TextConstants().SERVICE_ERROR)));
+ }
+ }
+ setState(() {
+ isLoading = false;
+ });
+ }
+
+ Future showMyDialog(BuildContext context, AtGroup group) async {
+ return showDialog(
+ context: context,
+ barrierDismissible: true,
+ builder: (BuildContext context) {
+ Uint8List? groupPicture;
+ if (group.groupPicture != null) {
+ List intList = group.groupPicture.cast();
+ groupPicture = Uint8List.fromList(intList);
+ }
+ return ConfirmationDialog(
+ title: '${group.displayName}',
+ heading: 'Are you sure you want to delete this group?',
+ onYesPressed: () async {
+ var result = await GroupService().deleteGroup(group);
+
+ if (!mounted) return;
+ if (result != null && result) {
+ Navigator.pop(context);
+ await _groupsProvider.setSelectedAtGroup(null);
+ _groupsProvider.setGroupCardState(GroupCardState.disable);
+ await DesktopSetupRoutes.nested_push(
+ DesktopRoutes.DESKTOP_GROUP,
+ );
+ } else {
+ CustomToast().show(TextConstants().SERVICE_ERROR, context);
+ }
+ },
+ image: groupPicture,
+ );
+ },
+ );
+ }
+}
diff --git a/lib/desktop_screens_new/groups_screen/desktop_groups_screen.dart b/lib/desktop_screens_new/groups_screen/desktop_groups_screen.dart
index 39d975f1..08a2bb86 100644
--- a/lib/desktop_screens_new/groups_screen/desktop_groups_screen.dart
+++ b/lib/desktop_screens_new/groups_screen/desktop_groups_screen.dart
@@ -84,8 +84,8 @@ class _DesktopGroupsScreenState extends State {
snapshot.data!,
key: UniqueKey(),
expandIndex: GroupService().expandIndex ?? 0,
- onExpand: (value) {
- provider.setSelectedAtGroup(value);
+ onExpand: (value) async {
+ await provider.setSelectedAtGroup(value);
provider.setGroupCardState(GroupCardState.expanded);
},
onAdd: () {
@@ -111,8 +111,8 @@ class _DesktopGroupsScreenState extends State {
children: [
Expanded(
child: InkWell(
- onTap: () {
- groupsProvider.setSelectedAtGroup(null);
+ onTap: () async {
+ await groupsProvider.setSelectedAtGroup(null);
groupsProvider
.setGroupCardState(GroupCardState.disable);
},
@@ -135,7 +135,7 @@ class _DesktopGroupsScreenState extends State {
return DesktopAddGroup(
onDoneTap: (value) async {
if (value) await GroupService().getAllGroupsDetails();
- groupsProvider.setSelectedAtGroup(null);
+ await groupsProvider.setSelectedAtGroup(null);
groupsProvider.setGroupCardState(GroupCardState.disable);
},
);
@@ -143,7 +143,7 @@ class _DesktopGroupsScreenState extends State {
return DesktopGroupsDetail(
onBackArrowTap: (value) async {
if (value) await GroupService().getAllGroupsDetails();
- groupsProvider.setSelectedAtGroup(null);
+ await groupsProvider.setSelectedAtGroup(null);
groupsProvider.setGroupCardState(GroupCardState.disable);
},
);
diff --git a/lib/desktop_screens_new/groups_screen/widgets/desktop_cover_image_picker.dart b/lib/desktop_screens_new/groups_screen/widgets/desktop_cover_image_picker.dart
index 54194a67..963c80f5 100644
--- a/lib/desktop_screens_new/groups_screen/widgets/desktop_cover_image_picker.dart
+++ b/lib/desktop_screens_new/groups_screen/widgets/desktop_cover_image_picker.dart
@@ -7,16 +7,16 @@ import 'package:flutter_svg/flutter_svg.dart';
class DesktopCoverImagePicker extends StatelessWidget {
final Uint8List? selectedImage;
- final Function() onPickImage;
+ final Function()? onPickImage;
final bool isEdit;
- final Function() onCancel;
+ final Function()? onCancel;
const DesktopCoverImagePicker({
Key? key,
this.selectedImage,
- required this.onPickImage,
+ this.onPickImage,
required this.isEdit,
- required this.onCancel,
+ this.onCancel,
}) : super(key: key);
@override
@@ -24,14 +24,13 @@ class DesktopCoverImagePicker extends StatelessWidget {
return InkWell(
onTap: () async {
if (isEdit) {
- onPickImage.call();
+ onPickImage?.call();
}
},
- child: selectedImage != null && selectedImage!.isNotEmpty
- ? SizedBox(
- width: 360,
- height: 88,
- child: Stack(
+ child: SizedBox(
+ height: 120,
+ child: selectedImage != null && selectedImage!.isNotEmpty
+ ? Stack(
alignment: Alignment.center,
fit: StackFit.expand,
children: [
@@ -84,31 +83,30 @@ class DesktopCoverImagePicker extends StatelessWidget {
),
),
],
+ )
+ : Container(
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(5),
+ color: ColorConstants.pickerBackgroundColor,
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ 'Insert Cover Image',
+ style: CustomTextStyles.orangeW50014,
+ ),
+ const SizedBox(height: 8),
+ SvgPicture.asset(
+ AppVectors.icDesktopImage,
+ width: 48,
+ height: 32,
+ ),
+ ],
+ ),
),
- )
- : Container(
- padding: const EdgeInsets.fromLTRB(108, 12, 108, 16),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(5),
- color: ColorConstants.pickerBackgroundColor,
- ),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text(
- 'Insert Cover Image',
- style: CustomTextStyles.orangeW50014,
- ),
- const SizedBox(height: 8),
- SvgPicture.asset(
- AppVectors.icDesktopImage,
- width: 48,
- height: 32,
- fit: BoxFit.fitWidth,
- ),
- ],
- ),
- ),
+ ),
);
}
}
diff --git a/lib/desktop_screens_new/groups_screen/widgets/desktop_custom_list_tile.dart b/lib/desktop_screens_new/groups_screen/widgets/desktop_custom_list_tile.dart
index 9ca9f6fe..351cd38a 100644
--- a/lib/desktop_screens_new/groups_screen/widgets/desktop_custom_list_tile.dart
+++ b/lib/desktop_screens_new/groups_screen/widgets/desktop_custom_list_tile.dart
@@ -32,6 +32,8 @@ class DesktopCustomListTile extends StatefulWidget {
final bool selectSingle;
final ValueChanged>? selectedList;
final bool isTrusted;
+ final List selectedContact;
+ final bool showSelectedBorder;
const DesktopCustomListTile({
Key? key,
@@ -42,6 +44,8 @@ class DesktopCustomListTile extends StatefulWidget {
this.selectSingle = false,
this.selectedList,
required this.isTrusted,
+ this.selectedContact = const [],
+ required this.showSelectedBorder,
}) : super(key: key);
@override
@@ -63,7 +67,7 @@ class _DesktopCustomListTileState extends State {
_groupService = GroupService();
// ignore: omit_local_variable_types
- getIsSelectedValue(_groupService.selectedGroupContacts);
+ getIsSelectedValue(widget.selectedContact);
super.initState();
}
@@ -113,110 +117,104 @@ class _DesktopCustomListTileState extends State {
Widget build(BuildContext context) {
getNameAndImage();
- return StreamBuilder>(
- initialData: _groupService.selectedGroupContacts,
- stream: _groupService.selectedContactsStream,
- builder: (context, snapshot) {
- getIsSelectedValue(_groupService.selectedGroupContacts);
-
- return InkWell(
- onTap: () {
- if (widget.asSelectionTile) {
- if (widget.selectSingle) {
- _groupService.selectedGroupContacts = [];
+ return InkWell(
+ onTap: () {
+ if (widget.asSelectionTile) {
+ if (widget.selectSingle) {
+ _groupService.selectedGroupContacts = [];
+ _groupService.addGroupContact(widget.item);
+ widget.selectedList!([widget.item]);
+ Navigator.pop(context);
+ } else if (!widget.selectSingle) {
+ if (mounted) {
+ setState(() {
+ if (isSelected) {
+ _groupService.removeGroupContact(widget.item);
+ } else {
_groupService.addGroupContact(widget.item);
- widget.selectedList!([widget.item]);
- Navigator.pop(context);
- } else if (!widget.selectSingle) {
- if (mounted) {
- setState(() {
- if (isSelected) {
- _groupService.removeGroupContact(widget.item);
- } else {
- _groupService.addGroupContact(widget.item);
- }
- isSelected = !isSelected;
- });
- }
}
- } else {
- widget.onTap!();
- }
- },
- child: Container(
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(12),
- border: Border.all(
- color:
- isSelected ? ColorConstants.orange : Colors.transparent,
- width: 2,
- ),
- ),
- child: Row(
- children: [
- (isLoading)
- ? const CircularProgressIndicator()
- : ClipRRect(
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(10),
- bottomLeft: Radius.circular(10),
- ),
- child: (image != null)
- ? Image.memory(
- image!,
- width: 72,
- height: 72,
- fit: BoxFit.cover,
- )
- : ContactInitial(
- size: 72,
- borderRadius: 0,
- initials: (initials ?? 'UG'),
- ),
- ),
- const SizedBox(width: 16),
- Expanded(
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 20),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- widget.item?.contact?.atSign ??
- '${widget.item?.group?.members?.length} Members',
- style: TextStyle(
- color: Colors.black,
- fontSize: 13.toFont,
- fontWeight: FontWeight.w600,
- ),
- ),
- if ((widget.item?.contact?.tags?["nickname"] ?? '')
- .isNotEmpty)
- Text(
- widget.item?.contact?.tags?["nickname"],
- style: TextStyle(
- color: Colors.black,
- fontSize: 10.toFont,
- fontWeight: FontWeight.w400,
- ),
- ),
- ],
- ),
+ isSelected = !isSelected;
+ });
+ }
+ }
+ } else {
+ widget.onTap!();
+ }
+ },
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(12),
+ border: Border.all(
+ color: isSelected && widget.showSelectedBorder
+ ? ColorConstants.orange
+ : Colors.transparent,
+ width: 2,
+ ),
+ ),
+ child: Row(
+ children: [
+ (isLoading)
+ ? const CircularProgressIndicator()
+ : ClipRRect(
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(10),
+ bottomLeft: Radius.circular(10),
),
+ child: (image != null)
+ ? Image.memory(
+ image!,
+ width: 72,
+ height: 72,
+ fit: BoxFit.cover,
+ )
+ : ContactInitial(
+ size: 72,
+ borderRadius: 0,
+ initials: (initials ?? 'UG'),
+ ),
),
- if (widget.isTrusted)
- SvgPicture.asset(
- AppVectors.icTrust,
- width: 24,
- height: 20,
- color: ColorConstants.orange,
+ const SizedBox(width: 16),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 20),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ widget.item?.contact?.atSign ??
+ '${widget.item?.group?.members?.length} Members',
+ style: TextStyle(
+ color: Colors.black,
+ fontSize: 13.toFont,
+ fontWeight: FontWeight.w600,
+ ),
),
- const SizedBox(width: 24),
- ],
+ if ((widget.item?.contact?.tags?["nickname"] ?? '')
+ .isNotEmpty)
+ Text(
+ widget.item?.contact?.tags?["nickname"],
+ style: TextStyle(
+ color: Colors.black,
+ fontSize: 10.toFont,
+ fontWeight: FontWeight.w400,
+ ),
+ ),
+ ],
+ ),
),
),
- );
- });
+ if (widget.isTrusted)
+ SvgPicture.asset(
+ AppVectors.icTrust,
+ width: 24,
+ height: 20,
+ color: ColorConstants.orange,
+ ),
+ const SizedBox(width: 24),
+ ],
+ ),
+ ),
+ );
}
}
diff --git a/lib/desktop_screens_new/groups_screen/widgets/desktop_group_contacts_list.dart b/lib/desktop_screens_new/groups_screen/widgets/desktop_group_contacts_list.dart
index e5c97509..efbfabe3 100644
--- a/lib/desktop_screens_new/groups_screen/widgets/desktop_group_contacts_list.dart
+++ b/lib/desktop_screens_new/groups_screen/widgets/desktop_group_contacts_list.dart
@@ -5,7 +5,6 @@ import 'package:at_contacts_flutter/utils/text_strings.dart';
import 'package:at_contacts_group_flutter/models/group_contacts_model.dart';
import 'package:at_contacts_group_flutter/services/group_service.dart';
import 'package:at_contacts_group_flutter/widgets/add_contacts_group_dialog.dart';
-import 'package:at_contacts_group_flutter/widgets/horizontal_circular_list.dart';
import 'package:atsign_atmosphere_pro/desktop_screens_new/groups_screen/widgets/desktop_custom_list_tile.dart';
import 'package:atsign_atmosphere_pro/desktop_screens_new/groups_screen/widgets/icon_button_widget.dart';
import 'package:atsign_atmosphere_pro/utils/colors.dart';
@@ -13,6 +12,7 @@ import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
import 'package:atsign_atmosphere_pro/utils/vectors.dart';
import 'package:atsign_atmosphere_pro/view_models/desktop_groups_screen_provider.dart';
import 'package:atsign_atmosphere_pro/view_models/trusted_sender_view_model.dart';
+import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:flutter_svg/flutter_svg.dart';
@@ -20,16 +20,16 @@ import 'package:provider/provider.dart';
class DesktopGroupContactsList extends StatefulWidget {
final bool asSelectionScreen;
- final bool singleSelection;
- final bool showContacts;
final List? initialData;
+ final bool showMembersOnly;
+ final bool showSelectedBorder;
const DesktopGroupContactsList({
Key? key,
this.asSelectionScreen = false,
- this.singleSelection = false,
- this.showContacts = true,
this.initialData,
+ this.showMembersOnly = false,
+ this.showSelectedBorder = true,
}) : super(key: key);
@override
@@ -51,11 +51,12 @@ class _DesktopGroupContactsListState extends State {
groupProvider = context.read();
trustedProvider = context.read();
searchController = TextEditingController();
- WidgetsBinding.instance.addPostFrameCallback((_) {
+ WidgetsBinding.instance.addPostFrameCallback((_) async {
groupProvider.setSearchContactText('');
if (groupProvider.showTrustedContacts) {
groupProvider.setShowTrustedContacts();
}
+ _groupService.selectedContactsSink.add(widget.initialData ?? []);
});
super.initState();
}
@@ -66,8 +67,6 @@ class _DesktopGroupContactsListState extends State {
@override
void dispose() {
- _groupService.selectedGroupContacts = [];
- _groupService.selectedContactsSink.add(_groupService.selectedGroupContacts);
super.dispose();
}
@@ -77,33 +76,29 @@ class _DesktopGroupContactsListState extends State {
builder: (context, provider, child) {
return Column(
children: [
- Row(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Expanded(
- child: buildSearchField(),
- ),
- const SizedBox(width: 12),
- IconButtonWidget(
- icon: AppVectors.icTrust,
- backgroundColor: ColorConstants.iconButtonColor,
- isSelected: provider.showTrustedContacts,
- onTap: () {
- provider.setShowTrustedContacts();
- },
- )
- ],
- ),
- const SizedBox(height: 20),
- widget.asSelectionScreen ? HorizontalCircularList() : Container(),
- (widget.initialData ?? []).isNotEmpty
- ? buildContactsList(provider.showTrustedContacts
- ? widget.initialData
- ?.where((e) => trustedProvider.trustedContacts.any(
- (element) => element.atSign == e?.contact?.atSign))
- .toList()
- : widget.initialData)
+ if (!widget.showMembersOnly) ...[
+ Row(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Expanded(
+ child: buildSearchField(),
+ ),
+ const SizedBox(width: 12),
+ IconButtonWidget(
+ icon: AppVectors.icTrust,
+ backgroundColor: ColorConstants.iconButtonColor,
+ isSelected: provider.showTrustedContacts,
+ onTap: () {
+ provider.setShowTrustedContacts();
+ },
+ )
+ ],
+ ),
+ const SizedBox(height: 20),
+ ],
+ !widget.asSelectionScreen
+ ? buildContactsList(widget.initialData)
: StreamBuilder>(
stream: _groupService.allContactsStream,
initialData: _groupService.allContacts,
@@ -140,14 +135,16 @@ class _DesktopGroupContactsListState extends State {
],
);
} else {
- return buildContactsList(provider.showTrustedContacts
- ? trustedProvider.trustedContacts
- .map((e) => GroupContactsModel(
- contact: e,
- contactType: ContactsType.CONTACT,
- ))
- .toList()
- : snapshot.data);
+ return buildContactsList(
+ provider.showTrustedContacts
+ ? trustedProvider.trustedContacts
+ .map((e) => GroupContactsModel(
+ contact: e,
+ contactType: ContactsType.CONTACT,
+ ))
+ .toList()
+ : snapshot.data,
+ );
}
}
})
@@ -160,6 +157,7 @@ class _DesktopGroupContactsListState extends State {
// filtering contacts and groups
var _filteredList = [];
_filteredList = getAllContactList(data ?? []);
+ bool isFirst = true;
if (_filteredList.isEmpty) {
return Center(
@@ -175,7 +173,6 @@ class _DesktopGroupContactsListState extends State {
// renders contacts according to the initial alphabet
return ListView.builder(
- padding: EdgeInsets.only(bottom: 80.toHeight),
itemCount: 27,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
@@ -200,31 +197,91 @@ class _DesktopGroupContactsListState extends State {
}
if (contactsForAlphabet.isEmpty) {
- return Container();
+ Widget separator = SizedBox();
+ if (widget.showMembersOnly &&
+ getContactsForAlphabets(
+ _filteredList,
+ String.fromCharCode(alphabetIndex + 1 + 65).toUpperCase(),
+ alphabetIndex + 1,
+ ).isNotEmpty) {
+ if (isFirst &&
+ getContactsForAlphabets(
+ _filteredList,
+ String.fromCharCode(alphabetIndex -1 + 65).toUpperCase(),
+ alphabetIndex - 1,
+ ).isNotEmpty) {
+ isFirst = false;
+ } else {
+ separator = Padding(
+ padding: const EdgeInsets.symmetric(vertical: 12),
+ child: Divider(
+ color: ColorConstants.boxGrey,
+ thickness: 1.toHeight,
+ height: 0,
+ ),
+ );
+ }
+ } else if (!widget.showMembersOnly &&
+ getContactsForAlphabets(
+ _filteredList,
+ String.fromCharCode(alphabetIndex + 1 + 65).toUpperCase(),
+ alphabetIndex + 1,
+ ).isNotEmpty) {
+ if (isFirst &&
+ getContactsForAlphabets(
+ _filteredList,
+ String.fromCharCode(alphabetIndex -1 + 65).toUpperCase(),
+ alphabetIndex - 1,
+ ).isNotEmpty) {
+ isFirst = false;
+ } else {
+ separator = SizedBox(height: 16);
+ }
+ }
+ return separator;
}
return Column(
+ mainAxisSize: MainAxisSize.min,
children: [
- Row(
- children: [
- Text(
- currentChar,
- style: TextStyle(
- color: ColorConstants.darkSliver,
- fontSize: 20.toFont,
- fontWeight: FontWeight.bold,
+ if (!widget.showMembersOnly) ...[
+ Row(
+ children: [
+ Text(
+ currentChar,
+ style: TextStyle(
+ color: ColorConstants.darkSliver,
+ fontSize: 20.toFont,
+ fontWeight: FontWeight.bold,
+ ),
),
- ),
- SizedBox(width: 16.toWidth),
- Expanded(
- child: Divider(
- color: ColorConstants.boxGrey,
- height: 1.toHeight,
+ SizedBox(width: 16.toWidth),
+ Expanded(
+ child: Divider(
+ color: ColorConstants.boxGrey,
+ thickness: 1.toHeight,
+ height: 0,
+ ),
),
+ ],
+ ),
+ SizedBox(height: 16),
+ ],
+ contactListBuilder(contactsForAlphabet),
+ if (widget.showMembersOnly &&
+ getContactsForAlphabets(
+ _filteredList,
+ String.fromCharCode(alphabetIndex + 1 + 65).toUpperCase(),
+ alphabetIndex + 1,
+ ).isNotEmpty)
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 12),
+ child: Divider(
+ color: ColorConstants.boxGrey,
+ thickness: 1.toHeight,
+ height: 0,
),
- ],
- ),
- contactListBuilder(contactsForAlphabet)
+ ),
],
);
},
@@ -324,47 +381,83 @@ class _DesktopGroupContactsListState extends State {
List contactsForAlphabet,
) {
return ListView.separated(
- padding: const EdgeInsets.symmetric(horizontal: 28),
+ padding: EdgeInsets.symmetric(
+ horizontal: widget.showMembersOnly ? 0 : 28,
+ ),
itemCount: contactsForAlphabet.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
- separatorBuilder: (context, _) => Divider(
- color: ColorConstants.dividerColor,
- height: 1.toHeight,
+ separatorBuilder: (context, _) => Padding(
+ padding: const EdgeInsets.symmetric(vertical: 12),
+ child: Divider(
+ color: ColorConstants.boxGrey,
+ thickness: 1.toHeight,
+ height: 0,
+ ),
),
itemBuilder: (context, index) {
- return Padding(
- padding: const EdgeInsets.only(
- right: 24,
- bottom: 12,
- top: 12,
- ),
- child: (contactsForAlphabet[index]!.contact != null)
- ? Slidable(
- endActionPane: ActionPane(
- motion: const ScrollMotion(),
- extentRatio: 0.25,
- children: [
- SlidableAction(
- label: TextStrings().block,
- backgroundColor: ColorConstants.inputFieldColor,
- icon: Icons.block,
- onPressed: (context) async {
- blockUnblockContact(
- contactsForAlphabet[index]!.contact!);
+ return (contactsForAlphabet[index]!.contact != null)
+ ? Slidable(
+ endActionPane: ActionPane(
+ motion: const ScrollMotion(),
+ extentRatio: 0.25,
+ children: [
+ SlidableAction(
+ label: TextStrings().block,
+ backgroundColor: ColorConstants.inputFieldColor,
+ icon: Icons.block,
+ onPressed: (context) async {
+ blockUnblockContact(
+ contactsForAlphabet[index]!.contact!);
+ },
+ ),
+ SlidableAction(
+ label: TextStrings().delete,
+ backgroundColor: Colors.red,
+ icon: Icons.delete,
+ onPressed: (context) async {
+ deleteAtSign(contactsForAlphabet[index]!.contact!);
+ },
+ ),
+ ],
+ ),
+ child: StreamBuilder>(
+ initialData: widget.initialData,
+ stream: _groupService.selectedContactsStream,
+ builder: (context, snapshot) {
+ return DesktopCustomListTile(
+ key: UniqueKey(),
+ onTap: () {},
+ asSelectionTile: widget.asSelectionScreen,
+ selectSingle: false,
+ item: contactsForAlphabet[index],
+ selectedList: (s) {
+ setSelectedContactsList(s);
},
- ),
- SlidableAction(
- label: TextStrings().delete,
- backgroundColor: Colors.red,
- icon: Icons.delete,
- onPressed: (context) async {
- deleteAtSign(contactsForAlphabet[index]!.contact!);
+ selectedContact: snapshot.data ?? [],
+ onTrailingPressed: () {
+ if (contactsForAlphabet[index]!.contact != null) {
+ Navigator.pop(context);
+
+ _groupService
+ .addGroupContact(contactsForAlphabet[index]);
+ setSelectedContactsList(
+ _groupService.selectedGroupContacts);
+ }
},
- ),
- ],
- ),
- child: DesktopCustomListTile(
+ isTrusted: trustedProvider.trustedContacts.any(
+ (element) =>
+ element.atSign ==
+ contactsForAlphabet[index]?.contact?.atSign),
+ showSelectedBorder: widget.showSelectedBorder,
+ );
+ }),
+ )
+ : StreamBuilder>(
+ initialData: widget.initialData,
+ stream: _groupService.selectedContactsStream,
+ builder: (context, snapshot) {
+ return DesktopCustomListTile(
key: UniqueKey(),
onTap: () {},
asSelectionTile: widget.asSelectionScreen,
@@ -373,8 +466,9 @@ class _DesktopGroupContactsListState extends State {
selectedList: (s) {
setSelectedContactsList(s);
},
+ selectedContact: snapshot.data ?? [],
onTrailingPressed: () {
- if (contactsForAlphabet[index]!.contact != null) {
+ if (contactsForAlphabet[index]!.group != null) {
Navigator.pop(context);
_groupService
@@ -386,31 +480,9 @@ class _DesktopGroupContactsListState extends State {
isTrusted: trustedProvider.trustedContacts.any((element) =>
element.atSign ==
contactsForAlphabet[index]?.contact?.atSign),
- ),
- )
- : DesktopCustomListTile(
- key: UniqueKey(),
- onTap: () {},
- asSelectionTile: widget.asSelectionScreen,
- selectSingle: false,
- item: contactsForAlphabet[index],
- selectedList: (s) {
- setSelectedContactsList(s);
- },
- onTrailingPressed: () {
- if (contactsForAlphabet[index]!.group != null) {
- Navigator.pop(context);
-
- _groupService.addGroupContact(contactsForAlphabet[index]);
- setSelectedContactsList(
- _groupService.selectedGroupContacts);
- }
- },
- isTrusted: trustedProvider.trustedContacts.any((element) =>
- element.atSign ==
- contactsForAlphabet[index]?.contact?.atSign),
- ),
- );
+ showSelectedBorder: widget.showSelectedBorder,
+ );
+ });
},
);
}
diff --git a/lib/desktop_screens_new/groups_screen/widgets/desktop_groups_detail.dart b/lib/desktop_screens_new/groups_screen/widgets/desktop_groups_detail.dart
index 86cfe7f4..f82d0d6c 100644
--- a/lib/desktop_screens_new/groups_screen/widgets/desktop_groups_detail.dart
+++ b/lib/desktop_screens_new/groups_screen/widgets/desktop_groups_detail.dart
@@ -10,13 +10,12 @@ import 'package:at_contacts_group_flutter/utils/text_constants.dart';
import 'package:at_contacts_group_flutter/widgets/confirmation_dialog.dart';
import 'package:at_contacts_group_flutter/widgets/custom_toast.dart';
import 'package:atsign_atmosphere_pro/data_models/enums/group_card_state.dart';
+import 'package:atsign_atmosphere_pro/desktop_routes/desktop_route_names.dart';
import 'package:atsign_atmosphere_pro/desktop_routes/desktop_routes.dart';
import 'package:atsign_atmosphere_pro/desktop_screens_new/groups_screen/widgets/desktop_cover_image_picker.dart';
import 'package:atsign_atmosphere_pro/desktop_screens_new/groups_screen/widgets/desktop_custom_app_bar.dart';
-import 'package:atsign_atmosphere_pro/desktop_screens_new/groups_screen/widgets/desktop_floating_add_contact_button.dart';
import 'package:atsign_atmosphere_pro/desktop_screens_new/groups_screen/widgets/desktop_group_contacts_list.dart';
import 'package:atsign_atmosphere_pro/desktop_screens_new/groups_screen/widgets/desktop_group_name_text_field.dart';
-import 'package:atsign_atmosphere_pro/desktop_screens_new/groups_screen/widgets/icon_button_widget.dart';
import 'package:atsign_atmosphere_pro/services/picker_service.dart';
import 'package:atsign_atmosphere_pro/services/snackbar_service.dart';
import 'package:atsign_atmosphere_pro/utils/colors.dart';
@@ -92,49 +91,63 @@ class _DesktopGroupsDetailState extends State {
titleTextStyle: CustomTextStyles.blackW50020,
leadingIcon: InkWell(
onTap: provider.isEditing
- ? () {
- provider.setIsEditing(false);
+ ? () async {
+ await provider.setIsEditing(false);
if (provider.isAddingContacts) {
provider.setIsAddingContact();
}
resetGroupName();
}
: () => widget.onBackArrowTap(false),
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 20),
- child: SvgPicture.asset(
- AppVectors.icBack,
- width: 8,
- height: 20,
+ child: SizedBox(
+ height: 24,
+ width: 24,
+ child: Center(
+ child: SvgPicture.asset(
+ AppVectors.icBack,
+ width: 8,
+ height: 20,
+ fit: BoxFit.cover,
+ ),
),
),
),
showLeadingIcon: true,
- showTrailingIcon: provider.isEditing,
- trailingIcon: InkWell(
- onTap: () async {
- await updateGroup();
- },
- child: Container(
- padding:
- const EdgeInsets.symmetric(horizontal: 52, vertical: 8),
- margin: const EdgeInsets.only(right: 28),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(46),
- color: ColorConstants.orange,
- ),
- child: Text(
- 'Save',
- style: CustomTextStyles.whiteW50015,
- ),
- ),
- ),
+ showTrailingIcon: true,
+ onTrailingIconPressed: provider.isEditing
+ ? () async {
+ await updateGroup();
+ }
+ : () {
+ groupProvider.setShowEditOptionsStatus();
+ },
+ trailingIcon: provider.isEditing
+ ? Container(
+ padding:
+ const EdgeInsets.symmetric(horizontal: 52, vertical: 8),
+ margin: const EdgeInsets.only(right: 28),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(46),
+ color: ColorConstants.orange,
+ ),
+ child: Text(
+ 'Save',
+ style: CustomTextStyles.whiteW50015,
+ ),
+ )
+ : SvgPicture.asset(
+ AppVectors.icOptions,
+ width: 16,
+ fit: BoxFit.fitWidth,
+ color: provider.showEditOptions
+ ? ColorConstants.orange
+ : Colors.black,
+ ),
),
body: Stack(
children: [
ListView(
- padding:
- const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
+ padding: const EdgeInsets.fromLTRB(20, 16, 20, 132),
children: [
if (provider.isEditing) ...[
DesktopGroupNameTextField(
@@ -155,7 +168,7 @@ class _DesktopGroupsDetailState extends State {
bgColor: ColorConstants.redAlert,
);
} else {
- provider.setSelectedGroupImage(
+ await provider.setSelectedGroupImage(
await File(details.files.first.path)
.readAsBytes(),
);
@@ -178,13 +191,21 @@ class _DesktopGroupsDetailState extends State {
),
),
SizedBox(height: provider.isEditing ? 16 : 20),
- buildDetailOptions(
- isAddingContacts: provider.isAddingContacts,
- isEditing: provider.isEditing,
- ),
- const SizedBox(height: 12),
+ if (!provider.isEditing) buildTransferFileButton(),
+ const SizedBox(height: 36),
+ provider.isEditing
+ ? SizedBox(height: 8)
+ : Center(
+ child: Text(
+ '${provider.selectedAtGroup?.members?.length} ${(provider.selectedAtGroup?.members?.length ?? 0) > 1 ? 'Members' : 'Member'}',
+ style: CustomTextStyles.raisinBlackW50015,
+ ),
+ ),
+ const SizedBox(height: 28),
DesktopGroupContactsList(
- asSelectionScreen: provider.isAddingContacts,
+ asSelectionScreen: false,
+ showMembersOnly: !provider.isEditing,
+ showSelectedBorder: provider.isEditing,
initialData: provider.isAddingContacts
? []
: provider.selectedAtGroup?.members
@@ -195,8 +216,32 @@ class _DesktopGroupsDetailState extends State {
),
],
),
- if (provider.isAddingContacts)
- const DesktopFloatingAddContactButton(),
+ if (!provider.isEditing)
+ Positioned(
+ bottom: 0,
+ left: 0,
+ right: 0,
+ child: Container(
+ color: Colors.white,
+ padding: const EdgeInsets.fromLTRB(28, 16, 28, 52),
+ child: buildEditMembers(),
+ ),
+ ),
+ if (provider.showEditOptions) ...[
+ Positioned.fill(
+ child: InkWell(
+ onTap: () {
+ groupProvider.setShowEditOptionsStatus();
+ },
+ ),
+ ),
+ Positioned(
+ top: 8,
+ left: 20,
+ right: 20,
+ child: buildEditOptionsDialog(),
+ ),
+ ],
],
),
),
@@ -204,111 +249,152 @@ class _DesktopGroupsDetailState extends State {
});
}
- Widget buildDetailOptions({
- required bool isEditing,
- required bool isAddingContacts,
- }) {
- return isEditing
- ? Row(
- mainAxisSize: MainAxisSize.min,
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- IconButtonWidget(
- icon: AppVectors.icDesktopAdd,
- isSelected: isAddingContacts,
- onTap: () {
- groupProvider.setIsAddingContact();
- },
- backgroundColor: ColorConstants.iconButtonColor,
- ),
- const SizedBox(width: 20),
- IconButtonWidget(
- icon: AppVectors.icShare,
- padding:
- const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
- onTap: () {},
- backgroundColor: ColorConstants.iconButtonColor,
- ),
- const SizedBox(width: 20),
- IconButtonWidget(
- icon: AppVectors.icDelete,
- onTap: () async {
- await showMyDialog(
- context,
- groupProvider.selectedAtGroup!,
- );
- },
- backgroundColor: ColorConstants.iconButtonColor,
- ),
- ],
- )
- : Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- InkWell(
- onTap: () async {
- Provider.of(context, listen: false)
- .selectedContacts = [
- GroupContactsModel(
- group: groupProvider.selectedAtGroup,
- contactType: ContactsType.GROUP,
- ),
- ];
- Provider.of(context, listen: false)
- .notify();
- await DesktopSetupRoutes.nested_pop();
- },
- child: Container(
- padding: const EdgeInsets.symmetric(vertical: 12),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(5),
- color: Colors.black,
- ),
- child: Center(
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text(
- "Transfer File",
- style: CustomTextStyles.whiteBold12,
- ),
- const SizedBox(width: 12),
- SvgPicture.asset(
- AppVectors.icTransfer,
- width: 16,
- height: 12,
- fit: BoxFit.cover,
- ),
- ],
- ),
+ Widget buildEditMembers() {
+ return InkWell(
+ onTap: () async {
+ await DesktopSetupRoutes.nested_push(
+ DesktopRoutes.DESKTOP_ADD_OR_REMOVE_CONTACTS,
+ );
+ },
+ child: Container(
+ alignment: Alignment.center,
+ padding: EdgeInsets.symmetric(vertical: 20),
+ decoration: BoxDecoration(
+ color: ColorConstants.editMembersButtonColor,
+ borderRadius: BorderRadius.circular(10),
+ ),
+ child: Text(
+ 'Edit Members',
+ style: CustomTextStyles.dimGrayW50015,
+ ),
+ ),
+ );
+ }
+
+ Widget buildEditOptionsDialog() {
+ return Container(
+ clipBehavior: Clip.antiAlias,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(7),
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(0.25),
+ offset: Offset(0, 4),
+ blurRadius: 18,
+ ),
+ ],
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ InkWell(
+ onTap: () async {
+ await groupProvider.setIsEditing(true);
+ groupProvider.setShowEditOptionsStatus();
+ },
+ child: Padding(
+ padding: EdgeInsets.symmetric(vertical: 12),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ 'Edit Group',
+ style: CustomTextStyles.blackW40015,
),
- ),
+ SizedBox(width: 12),
+ SvgPicture.asset(
+ AppVectors.icPencil,
+ width: 16,
+ height: 16,
+ fit: BoxFit.cover,
+ )
+ ],
),
- const SizedBox(height: 20),
- Row(
- mainAxisSize: MainAxisSize.min,
+ ),
+ ),
+ Divider(
+ height: 0,
+ color: Colors.black,
+ thickness: 1,
+ ),
+ InkWell(
+ onTap: () async {
+ groupProvider.setShowEditOptionsStatus();
+ await showMyDialog(
+ context,
+ groupProvider.selectedAtGroup!,
+ );
+ },
+ child: Padding(
+ padding: EdgeInsets.symmetric(vertical: 12),
+ child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
- IconButtonWidget(
- icon: AppVectors.icEdit,
- backgroundColor: ColorConstants.iconButtonColor,
- onTap: () => groupProvider.setIsEditing(true),
- ),
- const SizedBox(width: 24),
- IconButtonWidget(
- icon: AppVectors.icDelete,
- backgroundColor: ColorConstants.iconButtonColor,
- onTap: () async {
- await showMyDialog(
- context,
- groupProvider.selectedAtGroup!,
- );
- },
+ Text(
+ 'Delete Group',
+ style: CustomTextStyles.blackW40015,
),
+ SizedBox(width: 12),
+ SvgPicture.asset(
+ AppVectors.icDeleteGroup,
+ width: 16,
+ height: 16,
+ fit: BoxFit.cover,
+ )
],
),
+ ),
+ )
+ ],
+ ),
+ );
+ }
+
+ Widget buildTransferFileButton() {
+ return InkWell(
+ onTap: () async {
+ Provider.of(context, listen: false)
+ .selectedContacts = [
+ GroupContactsModel(
+ group: groupProvider.selectedAtGroup,
+ contactType: ContactsType.GROUP,
+ ),
+ ];
+ Provider.of(context, listen: false).notify();
+ await DesktopSetupRoutes.nested_pop();
+ },
+ child: Container(
+ padding: const EdgeInsets.symmetric(vertical: 16),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ color: Colors.black,
+ ),
+ child: Center(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ "Transfer File",
+ style: CustomTextStyles.whiteBold12,
+ ),
+ const SizedBox(width: 12),
+ SizedBox(
+ width: 20,
+ height: 20,
+ child: SvgPicture.asset(
+ AppVectors.icSendGroup,
+ width: 16,
+ height: 16,
+ fit: BoxFit.cover,
+ color: Colors.white,
+ ),
+ ),
],
- );
+ ),
+ ),
+ ),
+ );
}
Future showMyDialog(BuildContext context, AtGroup group) async {
@@ -330,7 +416,7 @@ class _DesktopGroupsDetailState extends State {
if (!mounted) return;
if (result != null && result) {
Navigator.of(context).pop();
- groupProvider.setSelectedAtGroup(null);
+ await groupProvider.setSelectedAtGroup(null);
groupProvider.setGroupCardState(GroupCardState.disable);
} else {
CustomToast().show(TextConstants().SERVICE_ERROR, context);
diff --git a/lib/screens/group_contacts_screen/widgets/group_contact_list_tile.dart b/lib/screens/common_widgets/contact_list_tile.dart
similarity index 100%
rename from lib/screens/group_contacts_screen/widgets/group_contact_list_tile.dart
rename to lib/screens/common_widgets/contact_list_tile.dart
diff --git a/lib/screens/common_widgets/cover_image_picker.dart b/lib/screens/common_widgets/cover_image_picker.dart
new file mode 100644
index 00000000..0869e1e4
--- /dev/null
+++ b/lib/screens/common_widgets/cover_image_picker.dart
@@ -0,0 +1,114 @@
+import 'dart:typed_data';
+
+import 'package:atsign_atmosphere_pro/utils/colors.dart';
+import 'package:atsign_atmosphere_pro/utils/images.dart';
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:atsign_atmosphere_pro/utils/vectors.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+class CoverImagePicker extends StatelessWidget {
+ final Function() onTap;
+ final Uint8List? groupImage;
+ final double height;
+ final EdgeInsetsGeometry margin;
+ final bool showOptions;
+ final Function()? onCancel;
+
+ const CoverImagePicker({
+ required this.onTap,
+ required this.groupImage,
+ required this.height,
+ this.margin = const EdgeInsets.symmetric(horizontal: 28),
+ this.showOptions = false,
+ this.onCancel,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return InkWell(
+ onTap: onTap,
+ child: Container(
+ height: height,
+ width: double.infinity,
+ margin: margin,
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ color: ColorConstants.dividerContextMenuColor,
+ ),
+ child: Stack(
+ fit: StackFit.expand,
+ children: [
+ groupImage != null
+ ? ClipRRect(
+ borderRadius: BorderRadius.circular(10),
+ child: Image.memory(
+ groupImage!,
+ fit: BoxFit.cover,
+ ),
+ )
+ : Column(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ "Insert Cover Image",
+ style: CustomTextStyles.greyW50014,
+ ),
+ SizedBox(height: 8),
+ Image.asset(
+ ImageConstants.icImage,
+ width: 48,
+ height: 32,
+ ),
+ ],
+ ),
+ if (showOptions)
+ Positioned(
+ top: 12,
+ right: 12,
+ left: 12,
+ child: Row(
+ children: [
+ if (groupImage != null)
+ InkWell(
+ onTap: onCancel,
+ child: Container(
+ padding: const EdgeInsets.all(8),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ color: Colors.white54.withOpacity(0.5),
+ ),
+ child: SvgPicture.asset(
+ AppVectors.icCancel,
+ width: 16,
+ height: 16,
+ color: Colors.black,
+ fit: BoxFit.cover,
+ ),
+ ),
+ ),
+ Spacer(),
+ Container(
+ padding: const EdgeInsets.all(8),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ color: Colors.white54.withOpacity(0.5),
+ ),
+ child: SvgPicture.asset(
+ AppVectors.icEdit,
+ width: 16,
+ height: 16,
+ fit: BoxFit.cover,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/screens/contact_new_version/contact_detail_screen.dart b/lib/screens/contact_new_version/contact_detail_screen.dart
index 7ea37aba..2abf445b 100644
--- a/lib/screens/contact_new_version/contact_detail_screen.dart
+++ b/lib/screens/contact_new_version/contact_detail_screen.dart
@@ -156,46 +156,39 @@ class _ContactDetailScreenState extends State {
SizedBox(height: 4),
Flexible(
child: isEditNickname
- ? Row(
- children: [
- Flexible(
- child: TextField(
- maxLines: 1,
- decoration: InputDecoration(
- contentPadding: EdgeInsets.only(
- left: 16,
- ),
- hintText: 'Enter Nickname',
- hintStyle: TextStyle(
- fontSize: 14.toFont,
- fontWeight: FontWeight.w500,
- color: ColorConstants.textBlack,
- ),
- border: OutlineInputBorder(
- borderRadius:
- BorderRadius.circular(5),
- borderSide: BorderSide.none,
- ),
- labelStyle: TextStyle(
- fontSize: 14.toFont,
- ),
- fillColor: Colors.white,
- filled: true,
- suffixIcon: InkWell(
- onTap: () {
- nicknameController.clear();
- },
- child: Icon(
- Icons.clear,
- color: Colors.black,
- size: 16,
- ),
- ),
- ),
- controller: nicknameController,
+ ? TextField(
+ maxLines: 1,
+ decoration: InputDecoration(
+ contentPadding: EdgeInsets.only(
+ left: 16,
+ ),
+ hintText: 'Enter Nickname',
+ hintStyle: TextStyle(
+ fontSize: 14.toFont,
+ fontWeight: FontWeight.w500,
+ color: ColorConstants.textBlack,
+ ),
+ border: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(5),
+ borderSide: BorderSide.none,
+ ),
+ labelStyle: TextStyle(
+ fontSize: 14.toFont,
+ ),
+ fillColor: Colors.white,
+ filled: true,
+ suffixIcon: InkWell(
+ onTap: () {
+ nicknameController.clear();
+ },
+ child: Icon(
+ Icons.clear,
+ color: Colors.black,
+ size: 16,
),
),
- ],
+ ),
+ controller: nicknameController,
)
: Text(
widget.contact.tags?['nickname'] ??
diff --git a/lib/screens/contact_new_version/contact_screen.dart b/lib/screens/contact_new_version/contact_screen.dart
index 22fc314b..39857f80 100644
--- a/lib/screens/contact_new_version/contact_screen.dart
+++ b/lib/screens/contact_new_version/contact_screen.dart
@@ -1,5 +1,4 @@
import 'package:at_common_flutter/services/size_config.dart';
-import 'package:at_contacts_group_flutter/screens/group_view/group_view.dart';
import 'package:at_contacts_group_flutter/services/group_service.dart';
import 'package:atsign_atmosphere_pro/data_models/enums/contact_type.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/app_bar_custom.dart';
@@ -10,6 +9,7 @@ import 'package:atsign_atmosphere_pro/screens/contact_new_version/contact_detail
import 'package:atsign_atmosphere_pro/screens/contact_new_version/create_group_screen.dart';
import 'package:atsign_atmosphere_pro/screens/contact_new_version/widget/contact_skeleton_loading_widget.dart';
import 'package:atsign_atmosphere_pro/screens/contact_new_version/widget/list_contact_widget.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/group_contacts_screen.dart';
import 'package:atsign_atmosphere_pro/utils/colors.dart';
import 'package:atsign_atmosphere_pro/view_models/contact_provider.dart';
import 'package:atsign_atmosphere_pro/view_models/create_group_provider.dart';
@@ -271,14 +271,20 @@ class _ContactScreenState extends State
.addPostFrameCallback((_) async {
_groupService.groupViewSink.add(group);
});
- await Navigator.push(
+ final result = await Navigator.push(
context,
MaterialPageRoute(
- builder: (context) => GroupView(
+ builder: (context) => GroupContactsScreen(
group: group,
),
),
);
+
+ if (result ?? false) {
+ setState(() => isReloading = !isReloading);
+ await _groupService.fetchGroupsAndContacts();
+ setState(() => isReloading = !isReloading);
+ }
},
onTapAddButton: () async {
final result = await showModalBottomSheet(
diff --git a/lib/screens/contact_new_version/create_group_screen.dart b/lib/screens/contact_new_version/create_group_screen.dart
index f289a51f..69a8bffc 100644
--- a/lib/screens/contact_new_version/create_group_screen.dart
+++ b/lib/screens/contact_new_version/create_group_screen.dart
@@ -2,17 +2,16 @@ import 'package:at_common_flutter/at_common_flutter.dart';
import 'package:at_commons/at_commons.dart';
import 'package:at_contact/at_contact.dart';
import 'package:at_contacts_group_flutter/at_contacts_group_flutter.dart';
+import 'package:atsign_atmosphere_pro/screens/common_widgets/cover_image_picker.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/custom_toast.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/input_widget.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/search_widget.dart';
import 'package:atsign_atmosphere_pro/screens/contact_new_version/widget/list_contact_widget.dart';
import 'package:atsign_atmosphere_pro/utils/colors.dart';
-import 'package:atsign_atmosphere_pro/utils/images.dart';
import 'package:atsign_atmosphere_pro/utils/text_strings.dart';
import 'package:atsign_atmosphere_pro/utils/vectors.dart';
import 'package:atsign_atmosphere_pro/view_models/create_group_provider.dart';
import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
@@ -115,7 +114,14 @@ class _CreateGroupScreenState extends State {
},
),
),
- _buildImage(value.selectedImageByteData),
+ SizedBox(height: 16),
+ CoverImagePicker(
+ onTap: () async {
+ await _provider.selectCoverImage();
+ },
+ groupImage: value.selectedImageByteData,
+ height: 88,
+ ),
Padding(
padding: const EdgeInsets.only(
top: 22,
@@ -254,48 +260,4 @@ class _CreateGroupScreenState extends State {
);
});
}
-
- Widget _buildImage(Uint8List? selectedImage) {
- return InkWell(
- onTap: () async {
- await _provider.selectCoverImage();
- },
- child: Container(
- height: 89,
- width: double.infinity,
- margin: const EdgeInsets.fromLTRB(27, 14, 27, 0),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(10),
- color: Color(0xFFECECEC),
- ),
- child: selectedImage != null
- ? ClipRRect(
- borderRadius: BorderRadius.circular(10),
- child: Image.memory(
- selectedImage,
- fit: BoxFit.cover,
- ),
- )
- : Center(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text(
- "Insert Cover Image",
- style: TextStyle(
- fontSize: 14,
- fontWeight: FontWeight.w500,
- color: ColorConstants.grey,
- ),
- ),
- SizedBox(height: 8),
- Image.asset(
- ImageConstants.icImage,
- ),
- ],
- ),
- ),
- ),
- );
- }
}
diff --git a/lib/screens/group_contacts/group_contacts_screen.dart b/lib/screens/group_contacts/group_contacts_screen.dart
new file mode 100644
index 00000000..f67bb1de
--- /dev/null
+++ b/lib/screens/group_contacts/group_contacts_screen.dart
@@ -0,0 +1,357 @@
+import 'package:at_contact/at_contact.dart';
+import 'package:at_contacts_group_flutter/models/group_contacts_model.dart';
+import 'package:atsign_atmosphere_pro/screens/common_widgets/cover_image_picker.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_app_bar.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_custom_button.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_edit_options_widget.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_manage_members_widget.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_member_list_view.dart';
+import 'package:atsign_atmosphere_pro/services/picker_service.dart';
+import 'package:atsign_atmosphere_pro/utils/app_utils.dart';
+import 'package:atsign_atmosphere_pro/utils/colors.dart';
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:atsign_atmosphere_pro/utils/vectors.dart';
+import 'package:atsign_atmosphere_pro/view_models/groups_provider.dart';
+import 'package:atsign_atmosphere_pro/view_models/welcome_screen_view_model.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:provider/provider.dart';
+import 'package:tuple/tuple.dart';
+
+class GroupContactsScreen extends StatefulWidget {
+ final AtGroup group;
+
+ const GroupContactsScreen({required this.group});
+
+ @override
+ State createState() => _GroupContactsScreenState();
+}
+
+class _GroupContactsScreenState extends State {
+ late TextEditingController groupNameController =
+ TextEditingController(text: _groupsProvider.atGroup?.groupName ?? '');
+ late final _groupsProvider = context.read();
+ late final _welcomeProvider = context.read();
+
+ @override
+ void initState() {
+ super.initState();
+ WidgetsBinding.instance.addPostFrameCallback((_) async {
+ await _groupsProvider.init(widget.group);
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return PopScope(
+ canPop: false,
+ onPopInvoked: (didPop) async {
+ if (didPop) {
+ return;
+ }
+ if (_groupsProvider.isEditingName) {
+ groupNameController.text = _groupsProvider.atGroup?.groupName ?? '';
+ _groupsProvider.setIsEditingName();
+ } else if (_groupsProvider.isEditingImage) {
+ await _groupsProvider
+ .setGroupPicture(_groupsProvider.atGroup?.groupPicture);
+ _groupsProvider.setIsEditingImage();
+ } else {
+ Navigator.pop(
+ context,
+ _groupsProvider.needRefresh,
+ );
+ _groupsProvider.reset();
+ }
+ },
+ child: Scaffold(
+ backgroundColor: ColorConstants.background,
+ appBar: GroupsAppBar(
+ title: context.watch().isEditingName
+ ? 'Edit'
+ : _groupsProvider.atGroup?.groupName ?? '',
+ onBack: () async {
+ if (_groupsProvider.isEditingName) {
+ groupNameController.text =
+ _groupsProvider.atGroup?.groupName ?? '';
+ _groupsProvider.setIsEditingName();
+ } else if (_groupsProvider.isEditingImage) {
+ await _groupsProvider
+ .setGroupPicture(_groupsProvider.atGroup?.groupPicture);
+ _groupsProvider.setIsEditingImage();
+ } else {
+ Navigator.pop(
+ context,
+ _groupsProvider.needRefresh,
+ );
+ _groupsProvider.reset();
+ }
+ },
+ actions: [
+ Selector>(
+ builder: (context, value, child) {
+ return value.item1 || value.item2
+ ? SvgPicture.asset(
+ AppVectors.icOptions,
+ width: 16,
+ fit: BoxFit.cover,
+ color: Colors.black,
+ )
+ : InkWell(
+ onTap: () {
+ _groupsProvider.setShowEditOptions();
+ },
+ child: SvgPicture.asset(
+ AppVectors.icPencil,
+ width: 24,
+ height: 24,
+ fit: BoxFit.cover,
+ color: value.item3
+ ? ColorConstants.orangeColor
+ : Colors.black,
+ ),
+ );
+ },
+ selector: (_, p) => Tuple3(
+ p.isEditingName,
+ p.isEditingImage,
+ p.showEditOptions,
+ ),
+ ),
+ ],
+ ),
+ body: SafeArea(
+ child: Stack(
+ children: [
+ Column(
+ children: [
+ SizedBox(height: 20),
+ Selector>(
+ builder: (context, value, child) {
+ return value.item1
+ ? buildEditGroupNameWidget()
+ : CoverImagePicker(
+ showOptions: value.item2,
+ onTap: () async {
+ if (value.item2) {
+ await PickerService.pickImage(
+ onPickedImage: (result) async {
+ if (result.isNotEmpty) {
+ await AppUtils.checkGroupImageSize(
+ image: result,
+ onSatisfy: (value) {
+ _groupsProvider
+ .setGroupPicture(result);
+ },
+ );
+ }
+ },
+ );
+ }
+ },
+ groupImage: value.item3,
+ height: 120,
+ onCancel: () async {
+ await _groupsProvider.setGroupPicture(null);
+ },
+ );
+ },
+ selector: (_, p) => Tuple3(
+ p.isEditingName,
+ p.isEditingImage,
+ p.groupPicture,
+ ),
+ ),
+ Selector>(
+ builder: (context, value, child) {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ SizedBox(
+ height: value.item1 || value.item2 ? 24 : 12,
+ ),
+ value.item1 || value.item2
+ ? Selector>(
+ builder: (context, value, child) {
+ return GroupsCustomButton(
+ title: 'Save',
+ onTap: () async {
+ if (value.item2) {
+ await _groupsProvider.updateGroupName(
+ context,
+ name: groupNameController.text,
+ );
+ return;
+ }
+ if (value.item3) {
+ await _groupsProvider
+ .updateGroupPicture(context);
+ return;
+ }
+ },
+ borderRadius: 5,
+ isLoading: value.item1,
+ );
+ },
+ selector: (_, p) => Tuple3(
+ p.isLoading,
+ p.isEditingName,
+ p.isEditingImage,
+ ),
+ )
+ : GroupsCustomButton(
+ title: 'Transfer File',
+ suffix: SvgPicture.asset(
+ AppVectors.icTransfer,
+ width: 24,
+ height: 16,
+ fit: BoxFit.cover,
+ ),
+ onTap: () {
+ _welcomeProvider.selectedContacts = [
+ GroupContactsModel(
+ group: _groupsProvider.atGroup,
+ contactType: ContactsType.GROUP,
+ ),
+ ];
+ Navigator.pop(
+ context,
+ _groupsProvider.needRefresh,
+ );
+ _welcomeProvider
+ .changeBottomNavigationIndex(0);
+ },
+ borderRadius: 7,
+ ),
+ ],
+ );
+ },
+ selector: (_, p) => Tuple2(
+ p.isEditingName,
+ p.isEditingImage,
+ ),
+ ),
+ Expanded(
+ child: Selector>(
+ builder: (context, value, child) {
+ return GroupsMemberListView(
+ members: value,
+ );
+ },
+ selector: (_, p) => p.atGroup?.members ?? {},
+ ),
+ ),
+ ],
+ ),
+ Selector(
+ builder: (context, value, child) {
+ return Visibility(
+ visible: value,
+ child: Positioned(
+ top: 0,
+ bottom: 0,
+ right: 0,
+ left: 0,
+ child: InkWell(
+ onTap: () {
+ _groupsProvider.setShowEditOptions();
+ },
+ ),
+ ),
+ );
+ },
+ selector: (_, p) => p.showEditOptions,
+ ),
+ Selector(
+ builder: (context, value, child) {
+ return Visibility(
+ visible: value,
+ child: Container(
+ padding: EdgeInsets.only(top: 20, left: 20, right: 20),
+ child: GroupsEditOptionsWidget(
+ onEditName: () {
+ _groupsProvider.setShowEditOptions();
+ _groupsProvider.setIsEditingName();
+ },
+ onCoverImage: () {
+ _groupsProvider.setShowEditOptions();
+ _groupsProvider.setIsEditingImage();
+ },
+ onManageMembers: () {
+ _groupsProvider.setShowEditOptions();
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => GroupsManageMembersWidget(),
+ ),
+ );
+ },
+ onDelete: () async {
+ await _groupsProvider
+ .showDeleteConfirmDialog(context);
+ },
+ ),
+ ),
+ );
+ },
+ selector: (_, p) => p.showEditOptions,
+ )
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget buildEditGroupNameWidget() {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 28),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Text(
+ 'Group Name',
+ style: CustomTextStyles.blackW5008,
+ ),
+ SizedBox(height: 4),
+ TextField(
+ maxLines: 1,
+ style: CustomTextStyles.blackW50014,
+ decoration: InputDecoration(
+ contentPadding: EdgeInsets.only(
+ left: 28,
+ top: 16,
+ bottom: 16,
+ ),
+ border: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(5),
+ borderSide: BorderSide.none,
+ ),
+ fillColor: Colors.white,
+ filled: true,
+ isDense: true,
+ suffix: InkWell(
+ onTap: () {
+ groupNameController.clear();
+ },
+ child: Container(
+ padding: EdgeInsets.symmetric(horizontal: 12),
+ child: SvgPicture.asset(
+ AppVectors.icCancel,
+ color: Colors.black,
+ height: 8,
+ width: 8,
+ fit: BoxFit.cover,
+ ),
+ ),
+ ),
+ ),
+ controller: groupNameController,
+ )
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/screens/group_contacts/widgets/groups_app_bar.dart b/lib/screens/group_contacts/widgets/groups_app_bar.dart
new file mode 100644
index 00000000..dcad88ee
--- /dev/null
+++ b/lib/screens/group_contacts/widgets/groups_app_bar.dart
@@ -0,0 +1,61 @@
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:atsign_atmosphere_pro/utils/vectors.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+class GroupsAppBar extends StatelessWidget implements PreferredSizeWidget {
+ final String title;
+ final List actions;
+ final Function()? onBack;
+
+ const GroupsAppBar({
+ required this.title,
+ this.actions = const [],
+ this.onBack,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return SafeArea(
+ child: Padding(
+ padding: const EdgeInsets.only(
+ left: 24,
+ right: 24,
+ top: 36,
+ ),
+ child: Row(
+ children: [
+ SizedBox(width: 8),
+ InkWell(
+ onTap: onBack ??
+ () {
+ Navigator.pop(context);
+ },
+ child: SizedBox(
+ width: 24,
+ height: 24,
+ child: SvgPicture.asset(
+ AppVectors.icBack,
+ height: 20,
+ width: 8,
+ fit: BoxFit.cover,
+ alignment: Alignment.center,
+ ),
+ ),
+ ),
+ SizedBox(width: 32),
+ Text(
+ title,
+ style: CustomTextStyles.desktopPrimaryW50018,
+ ),
+ Spacer(),
+ ...actions
+ ],
+ ),
+ ),
+ );
+ }
+
+ @override
+ Size get preferredSize => Size.fromHeight(64);
+}
diff --git a/lib/screens/group_contacts/widgets/groups_custom_button.dart b/lib/screens/group_contacts/widgets/groups_custom_button.dart
new file mode 100644
index 00000000..a6cb4b8c
--- /dev/null
+++ b/lib/screens/group_contacts/widgets/groups_custom_button.dart
@@ -0,0 +1,60 @@
+import 'package:atsign_atmosphere_pro/utils/colors.dart';
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:flutter/material.dart';
+
+class GroupsCustomButton extends StatelessWidget {
+ final Function() onTap;
+ final bool isLoading;
+ final String title;
+ final double spaceSize;
+ final Widget? suffix;
+ final double borderRadius;
+
+ const GroupsCustomButton({
+ required this.onTap,
+ this.isLoading = false,
+ required this.title,
+ this.spaceSize = 28,
+ this.suffix,
+ required this.borderRadius,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ height: 52,
+ width: double.infinity,
+ decoration: BoxDecoration(
+ color: ColorConstants.raisinBlack,
+ borderRadius: BorderRadius.circular(borderRadius),
+ ),
+ margin: EdgeInsets.symmetric(horizontal: 28),
+ child: InkWell(
+ onTap: onTap,
+ child: Center(
+ child: isLoading
+ ? SizedBox(
+ width: 20,
+ height: 20,
+ child: CircularProgressIndicator(
+ color: Colors.white,
+ ),
+ )
+ : Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ title,
+ style: CustomTextStyles.whiteBold(size: 14),
+ ),
+ if (suffix != null) ...[
+ SizedBox(width: spaceSize),
+ suffix!,
+ ]
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/screens/group_contacts/widgets/groups_edit_options_widget.dart b/lib/screens/group_contacts/widgets/groups_edit_options_widget.dart
new file mode 100644
index 00000000..43f0fbe5
--- /dev/null
+++ b/lib/screens/group_contacts/widgets/groups_edit_options_widget.dart
@@ -0,0 +1,102 @@
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:flutter/material.dart';
+
+class GroupsEditOptionsWidget extends StatelessWidget {
+ final Function() onEditName;
+ final Function() onCoverImage;
+ final Function() onManageMembers;
+ final Function() onDelete;
+
+ const GroupsEditOptionsWidget({
+ required this.onEditName,
+ required this.onCoverImage,
+ required this.onManageMembers,
+ required this.onDelete,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ clipBehavior: Clip.antiAlias,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(7),
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(0.25),
+ blurRadius: 18,
+ offset: Offset(0, 4),
+ )
+ ],
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ InkWell(
+ onTap: onEditName,
+ child: SizedBox(
+ height: 56,
+ child: Center(
+ child: Text(
+ 'Edit Name',
+ style: CustomTextStyles.blackW40017,
+ ),
+ ),
+ ),
+ ),
+ Divider(
+ height: 0,
+ thickness: 1,
+ color: Colors.black,
+ ),
+ InkWell(
+ onTap: onCoverImage,
+ child: SizedBox(
+ height: 56,
+ child: Center(
+ child: Text(
+ 'Edit Cover Image',
+ style: CustomTextStyles.blackW40017,
+ ),
+ ),
+ ),
+ ),
+ Divider(
+ height: 0,
+ thickness: 1,
+ color: Colors.black,
+ ),
+ InkWell(
+ onTap: onManageMembers,
+ child: SizedBox(
+ height: 56,
+ child: Center(
+ child: Text(
+ 'Manage Members',
+ style: CustomTextStyles.blackW40017,
+ ),
+ ),
+ ),
+ ),
+ Divider(
+ height: 0,
+ thickness: 1,
+ color: Colors.black,
+ ),
+ InkWell(
+ onTap: onDelete,
+ child: SizedBox(
+ height: 56,
+ child: Center(
+ child: Text(
+ 'Delete',
+ style: CustomTextStyles.blackW40017,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/screens/group_contacts/widgets/groups_manage_members_widget.dart b/lib/screens/group_contacts/widgets/groups_manage_members_widget.dart
new file mode 100644
index 00000000..65e86ca4
--- /dev/null
+++ b/lib/screens/group_contacts/widgets/groups_manage_members_widget.dart
@@ -0,0 +1,342 @@
+import 'package:at_common_flutter/services/size_config.dart';
+import 'package:at_contact/at_contact.dart';
+import 'package:at_contacts_group_flutter/services/group_service.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_app_bar.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_member_item_widget.dart';
+import 'package:atsign_atmosphere_pro/utils/colors.dart';
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:atsign_atmosphere_pro/utils/vectors.dart';
+import 'package:atsign_atmosphere_pro/view_models/groups_provider.dart';
+import 'package:atsign_atmosphere_pro/view_models/trusted_sender_view_model.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:provider/provider.dart';
+import 'package:tuple/tuple.dart';
+
+class GroupsManageMembersWidget extends StatefulWidget {
+ const GroupsManageMembersWidget();
+
+ @override
+ State createState() =>
+ _GroupsManageMembersWidgetState();
+}
+
+class _GroupsManageMembersWidgetState extends State {
+ TextEditingController searchController = TextEditingController();
+ late TrustedContactProvider trustedContactProvider =
+ context.read();
+ late GroupsProvider groupsProvider = context.read();
+
+ final _groupService = GroupService();
+
+ @override
+ void initState() {
+ super.initState();
+ WidgetsBinding.instance.addPostFrameCallback((_) async {
+ await _groupService.fetchGroupsAndContacts();
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: ColorConstants.background,
+ appBar: GroupsAppBar(title: 'Manage Members'),
+ body: SafeArea(
+ child: Stack(
+ children: [
+ Column(
+ children: [
+ SizedBox(height: 28),
+ Padding(
+ padding: EdgeInsets.only(left: 36, right: 20),
+ child: buildSearchWidget(),
+ ),
+ SizedBox(height: 12),
+ Expanded(
+ child: Selector>(
+ builder: (context, value, child) {
+ return ListView.separated(
+ physics: ClampingScrollPhysics(),
+ padding: EdgeInsets.only(
+ left: 32,
+ right: 32,
+ bottom: 112,
+ ),
+ itemBuilder: (context, index) {
+ return buildContactList(
+ filterContacts(
+ data: _groupService.allContacts
+ .where((e) => e?.contact != null)
+ .map((e) => e!.contact!)
+ .toList(),
+ searchKeyword: value.item1,
+ showTrustedMembers: value.item2,
+ ),
+ )[index];
+ },
+ separatorBuilder: (context, index) {
+ return SizedBox(height: 12);
+ },
+ itemCount: buildContactList(
+ filterContacts(
+ data: _groupService.allContacts
+ .where((e) => e?.contact != null)
+ .map((e) => e!.contact!)
+ .toList(),
+ searchKeyword: value.item1,
+ showTrustedMembers: value.item2,
+ ),
+ ).length,
+ );
+ },
+ selector: (_, p) => Tuple2(
+ p.searchKeyword,
+ p.showTrustedMembers,
+ ),
+ ),
+ ),
+ ],
+ ),
+ Positioned(
+ bottom: 28,
+ left: 48,
+ right: 48,
+ child: buildSelectContactButton(),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ List filterContacts(
+ {required List data,
+ required bool showTrustedMembers,
+ required String searchKeyword}) {
+ List sampleList = data;
+
+ if (showTrustedMembers) {
+ sampleList = sampleList
+ .where((element) => trustedContactProvider.trustedContacts
+ .any((e) => e.atSign == element.atSign))
+ .map((e) => e)
+ .toList();
+ } else if (searchKeyword.isNotEmpty) {
+ sampleList = sampleList
+ .where((element) => (element.atSign ?? '').contains(searchKeyword))
+ .map((e) => e)
+ .toList();
+ }
+ return sampleList;
+ }
+
+ Widget buildSearchWidget() {
+ return Row(
+ children: [
+ Expanded(
+ child: TextField(
+ maxLines: 1,
+ onChanged: (value) {
+ groupsProvider.changeSearchKeyword(value);
+ },
+ style: CustomTextStyles.blackW50014,
+ decoration: InputDecoration(
+ contentPadding: EdgeInsets.only(
+ left: 20,
+ top: 20,
+ bottom: 20,
+ right: 24,
+ ),
+ hintText: 'Search',
+ hintStyle: CustomTextStyles.greyW50014,
+ border: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(10),
+ borderSide: BorderSide.none,
+ ),
+ fillColor: Colors.white,
+ filled: true,
+ isDense: true,
+ suffixIcon: Icon(
+ Icons.search,
+ size: 20,
+ color: ColorConstants.grey,
+ ),
+ ),
+ controller: searchController,
+ ),
+ ),
+ SizedBox(width: 12),
+ InkWell(
+ onTap: () {
+ groupsProvider.setShowTrustedMembersStatus();
+ },
+ child: Container(
+ width: 40,
+ height: 40,
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ color: ColorConstants.iconButtonColor,
+ shape: BoxShape.circle,
+ ),
+ child: Selector(
+ builder: (context, value, child) {
+ return SvgPicture.asset(
+ AppVectors.icTrust,
+ width: 24,
+ height: 20,
+ color: value ? ColorConstants.orange : Colors.black,
+ fit: BoxFit.cover,
+ );
+ },
+ selector: (_, p) => p.showTrustedMembers,
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+
+ List buildContactList(List list) {
+ final List result = [];
+ if (list.isNotEmpty) {
+ final List sortedList = sortGroupAlphabetical(list);
+ bool isSameCharWithPrev(int i) =>
+ (((sortedList[i].atSign ?? '').isNotEmpty
+ ? sortedList[i].atSign![1]
+ : ' ') !=
+ ((sortedList[i - 1].atSign ?? '').isNotEmpty
+ ? sortedList[i - 1].atSign![1]
+ : ' '));
+
+ bool isPrevCharacter(int i) => RegExp(r'^[a-z]+$').hasMatch(
+ (((sortedList[i - 1].atSign ?? '').isNotEmpty
+ ? sortedList[i - 1].atSign![1]
+ : ' '))[0]
+ .toLowerCase());
+
+ for (int i = 0; i < sortedList.length; i++) {
+ if (i == 0 || (isSameCharWithPrev(i) && isPrevCharacter(i))) {
+ result.add(buildAlphabeticalTitle(
+ (sortedList[i].atSign ?? '').isNotEmpty
+ ? sortedList[i].atSign![1]
+ : ''));
+ }
+ result.add(
+ Selector(
+ builder: (context, value, child) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 4),
+ child: GroupsMemberItemWidget(
+ member: sortedList[i],
+ isTrusted: trustedContactProvider.trustedContacts.any(
+ (element) => element.atSign == sortedList[i].atSign,
+ ),
+ isSelected: value,
+ onTap: () {
+ groupsProvider.addOrRemoveMember(sortedList[i]);
+ },
+ ),
+ );
+ },
+ selector: (_, p) => p.members.any(
+ (element) => element.atSign == sortedList[i].atSign,
+ ),
+ ),
+ );
+ }
+ }
+ return result;
+ }
+
+ Widget buildAlphabeticalTitle(String char) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(vertical: 4),
+ child: Row(
+ children: [
+ char.isNotEmpty && RegExp(r'^[a-z]+$').hasMatch(char.toLowerCase())
+ ? Text(
+ char.toUpperCase(),
+ style: CustomTextStyles.darkSliverBold20,
+ )
+ : Text(
+ 'Others',
+ style: CustomTextStyles.darkSliverBold20,
+ ),
+ SizedBox(width: 20),
+ Divider(
+ height: 1.toHeight,
+ color: ColorConstants.dividerColor,
+ )
+ ],
+ ),
+ );
+ }
+
+ List sortGroupAlphabetical(List list) {
+ final List emptyTitleContact = list
+ .where((e) =>
+ (e.atSign ?? '').isEmpty ||
+ !RegExp(r'^[a-z]+$').hasMatch(
+ (e.atSign ?? '')[1].toLowerCase(),
+ ))
+ .toList();
+ final List nonEmptyTitleContact = list
+ .where((e) =>
+ (e.atSign ?? '').isNotEmpty &&
+ RegExp(r'^[a-z]+$').hasMatch(
+ (e.atSign ?? '')[1].toLowerCase(),
+ ))
+ .toList();
+ nonEmptyTitleContact.sort(
+ (a, b) => (a.atSign?[1] ?? '').compareTo(
+ (b.atSign?[1] ?? ''),
+ ),
+ );
+ return [...nonEmptyTitleContact, ...emptyTitleContact];
+ }
+
+ Widget buildSelectContactButton() {
+ return InkWell(
+ onTap: () async {
+ await groupsProvider.updateMembers(context);
+ },
+ child: Container(
+ padding: EdgeInsets.symmetric(vertical: 16),
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ color: Colors.black,
+ borderRadius: BorderRadius.circular(7),
+ ),
+ child: Selector>(
+ builder: (context, value, child) {
+ return value.item2
+ ? SizedBox(
+ width: 20,
+ height: 20,
+ child: CircularProgressIndicator(
+ color: Colors.white,
+ ),
+ )
+ : RichText(
+ text: TextSpan(
+ text: 'Selects Contact ',
+ style: CustomTextStyles.whiteBold16,
+ children: [
+ TextSpan(
+ text: '(${value.item1})',
+ style: CustomTextStyles.whiteW40016,
+ ),
+ ],
+ ),
+ );
+ },
+ selector: (_, p) => Tuple2(
+ p.members.length,
+ p.isLoading,
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/screens/group_contacts/widgets/groups_member_item_widget.dart b/lib/screens/group_contacts/widgets/groups_member_item_widget.dart
new file mode 100644
index 00000000..ee03da7f
--- /dev/null
+++ b/lib/screens/group_contacts/widgets/groups_member_item_widget.dart
@@ -0,0 +1,96 @@
+import 'dart:typed_data';
+
+import 'package:at_contact/at_contact.dart';
+import 'package:atsign_atmosphere_pro/screens/common_widgets/contact_initial.dart';
+import 'package:atsign_atmosphere_pro/utils/colors.dart';
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:atsign_atmosphere_pro/utils/vectors.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+class GroupsMemberItemWidget extends StatelessWidget {
+ final AtContact member;
+ final bool isTrusted;
+ final bool isSelected;
+ final Function()? onTap;
+
+ const GroupsMemberItemWidget({
+ required this.member,
+ required this.isTrusted,
+ this.isSelected = false,
+ this.onTap,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return InkWell(
+ onTap: onTap,
+ child: Container(
+ padding: EdgeInsets.fromLTRB(16, 16, 24, 16),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(10),
+ border: isSelected
+ ? Border.all(
+ color: ColorConstants.orange,
+ width: 2,
+ )
+ : null,
+ ),
+ child: Row(
+ children: [
+ member.tags?['image'] != null
+ ? ClipRRect(
+ borderRadius: BorderRadius.circular(18),
+ child: Image.memory(
+ Uint8List.fromList(member.tags?['image'].cast()),
+ width: 40,
+ height: 40,
+ fit: BoxFit.cover,
+ ),
+ )
+ : ContactInitial(
+ initials: member.atSign?.substring(1),
+ borderRadius: 18,
+ ),
+ SizedBox(width: 16),
+ Expanded(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ member.atSign ?? '',
+ style: CustomTextStyles.blackW60013,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ if (member.tags?['nickname'] != null)
+ Text(
+ member.tags?['nickname'],
+ style: CustomTextStyles.blackW60013,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ ],
+ )),
+ SizedBox(width: 16),
+ SizedBox(
+ width: 28,
+ height: 28,
+ child: SvgPicture.asset(
+ isTrusted
+ ? AppVectors.icTrustActivated
+ : AppVectors.icTrustDeactivated,
+ width: 24,
+ height: 20,
+ fit: BoxFit.cover,
+ alignment: Alignment.center,
+ ),
+ )
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/screens/group_contacts/widgets/groups_member_list_view.dart b/lib/screens/group_contacts/widgets/groups_member_list_view.dart
new file mode 100644
index 00000000..0aac6fe0
--- /dev/null
+++ b/lib/screens/group_contacts/widgets/groups_member_list_view.dart
@@ -0,0 +1,43 @@
+import 'package:at_contact/at_contact.dart';
+import 'package:atsign_atmosphere_pro/screens/group_contacts/widgets/groups_member_item_widget.dart';
+import 'package:atsign_atmosphere_pro/view_models/trusted_sender_view_model.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+class GroupsMemberListView extends StatefulWidget {
+ final Set members;
+
+ const GroupsMemberListView({
+ required this.members,
+ });
+
+ @override
+ State createState() => _GroupsMemberListViewState();
+}
+
+class _GroupsMemberListViewState extends State {
+ late TrustedContactProvider trustedContactProvider =
+ context.read();
+
+ @override
+ Widget build(BuildContext context) {
+ return ListView.separated(
+ padding: EdgeInsets.symmetric(horizontal: 36, vertical: 28),
+ shrinkWrap: true,
+ physics: NeverScrollableScrollPhysics(),
+ itemBuilder: (context, index) {
+ return GroupsMemberItemWidget(
+ member: widget.members.elementAt(index),
+ isTrusted: trustedContactProvider.trustedContacts.any(
+ (element) =>
+ element.atSign == widget.members.elementAt(index).atSign,
+ ),
+ );
+ },
+ separatorBuilder: (context, index) {
+ return SizedBox(height: 16);
+ },
+ itemCount: widget.members.length,
+ );
+ }
+}
diff --git a/lib/screens/trusted_contacts/trusted_contacts.dart b/lib/screens/trusted_contacts/trusted_contacts.dart
index 5717d159..855cae04 100644
--- a/lib/screens/trusted_contacts/trusted_contacts.dart
+++ b/lib/screens/trusted_contacts/trusted_contacts.dart
@@ -9,7 +9,7 @@ import 'package:atsign_atmosphere_pro/screens/common_widgets/contact_initial.dar
import 'package:atsign_atmosphere_pro/screens/common_widgets/custom_button.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/custom_circle_avatar.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/provider_handler.dart';
-import 'package:atsign_atmosphere_pro/screens/group_contacts_screen/widgets/group_contact_list_tile.dart';
+import 'package:atsign_atmosphere_pro/screens/common_widgets/contact_list_tile.dart';
import 'package:atsign_atmosphere_pro/screens/trusted_contacts/widgets/remove_trusted_contact_dialog.dart';
import 'package:atsign_atmosphere_pro/utils/images.dart';
import 'package:atsign_atmosphere_pro/utils/text_strings.dart';
diff --git a/lib/screens/welcome_screen/widgets/overlapping_contacts.dart b/lib/screens/welcome_screen/widgets/overlapping_contacts.dart
index 17b6294e..7f87541b 100644
--- a/lib/screens/welcome_screen/widgets/overlapping_contacts.dart
+++ b/lib/screens/welcome_screen/widgets/overlapping_contacts.dart
@@ -7,7 +7,7 @@ import 'package:at_contact/at_contact.dart';
import 'package:at_contacts_group_flutter/models/group_contacts_model.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/contact_initial.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/custom_circle_avatar.dart';
-import 'package:atsign_atmosphere_pro/screens/group_contacts_screen/widgets/group_contact_list_tile.dart';
+import 'package:atsign_atmosphere_pro/screens/common_widgets/contact_list_tile.dart';
import 'package:atsign_atmosphere_pro/screens/welcome_screen/widgets/contact_card.dart';
import 'package:atsign_atmosphere_pro/services/common_utility_functions.dart';
import 'package:atsign_atmosphere_pro/view_models/trusted_sender_view_model.dart';
diff --git a/lib/services/picker_service.dart b/lib/services/picker_service.dart
index 292b8e96..e37bb13a 100644
--- a/lib/services/picker_service.dart
+++ b/lib/services/picker_service.dart
@@ -15,8 +15,10 @@ class PickerService {
source: ImageSource.gallery,
);
if (image != null) {
- final result = await File(image.path).readAsBytes();
- onPickedImage.call(result);
+ final result = await File(image.path).readAsBytes();
+ onPickedImage.call(result);
+ } else {
+ onPickedImage.call(Uint8List(0));
}
}
}
diff --git a/lib/utils/colors.dart b/lib/utils/colors.dart
index 3a3031ce..948f0d8e 100644
--- a/lib/utils/colors.dart
+++ b/lib/utils/colors.dart
@@ -61,6 +61,7 @@ class ColorConstants {
static const Color gray2 = Color(0xFFB9B9B9);
static const Color lightGray2 = Color(0xFFE3E3E3);
static const Color darkGray2 = Color(0xFF474747);
+ static const Color dimGray = Color(0xFF696969);
static const Color sidebarTextUnselected = Color(0xFFA4A4A5);
static const Color sidebarTextSelected = Color(0xFF000000);
@@ -106,6 +107,7 @@ class ColorConstants {
static const Color disableTooltipColor = Color(0xFFC4C4C4);
static const Color inactiveIconColor = Color(0xFFC5C5C5);
static const Color closeButtonColor = Color(0xFFE6E6E6);
+ static const Color editMembersButtonColor = Color(0xFFF0F0F0);
static const Color listFileShadowColor = Color(0xFFD7D7D7);
static const Color spanishGray = Color(0xFF969393);
}
diff --git a/lib/utils/text_styles.dart b/lib/utils/text_styles.dart
index 753f9e36..506d454b 100644
--- a/lib/utils/text_styles.dart
+++ b/lib/utils/text_styles.dart
@@ -379,6 +379,12 @@ class CustomTextStyles {
letterSpacing: 0.1,
fontWeight: FontWeight.normal);
+ static TextStyle desktopPrimaryW50018 = TextStyle(
+ color: Colors.black,
+ fontSize: 18,
+ fontWeight: FontWeight.w500,
+ );
+
static TextStyle desktopPrimaryRegular16 = TextStyle(
color: Colors.black,
fontSize: 16,
@@ -411,6 +417,12 @@ class CustomTextStyles {
fontWeight: FontWeight.w500,
);
+ static TextStyle whiteW40016 = TextStyle(
+ color: Colors.white,
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ );
+
static TextStyle blackW50020 = const TextStyle(
color: Colors.black,
fontSize: 20,
@@ -418,7 +430,10 @@ class CustomTextStyles {
);
static TextStyle orangeW50014 = TextStyle(
- color: ColorConstants.orange, fontSize: 14, fontWeight: FontWeight.w500);
+ color: ColorConstants.orange,
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ );
static TextStyle whiteBold12 = const TextStyle(
color: Colors.white,
@@ -438,6 +453,26 @@ class CustomTextStyles {
fontWeight: FontWeight.w400,
);
+ static TextStyle blackUnderlineW40012 = const TextStyle(
+ color: Colors.black,
+ fontSize: 12,
+ fontWeight: FontWeight.w400,
+ decoration: TextDecoration.underline,
+ );
+
+ static TextStyle blackW40013 = const TextStyle(
+ color: Colors.black,
+ fontSize: 13,
+ fontWeight: FontWeight.w400,
+ );
+
+ static TextStyle blackItalicW40013 = const TextStyle(
+ color: Colors.black,
+ fontSize: 13,
+ fontWeight: FontWeight.w400,
+ fontStyle: FontStyle.italic,
+ );
+
static TextStyle darkSliverBold20 = TextStyle(
color: ColorConstants.darkSliver,
fontSize: 20,
@@ -498,10 +533,16 @@ class CustomTextStyles {
fontSize: 12,
);
- static TextStyle blackW40013 = TextStyle(
+ static TextStyle blackW40015 = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
- fontSize: 13,
+ fontSize: 15,
+ );
+
+ static TextStyle blackW40017 = TextStyle(
+ color: Colors.black,
+ fontWeight: FontWeight.w400,
+ fontSize: 17,
);
static TextStyle raisinBlackW40010 = TextStyle(
@@ -516,6 +557,12 @@ class CustomTextStyles {
fontWeight: FontWeight.w400,
);
+ static TextStyle raisinBlackW50015 = TextStyle(
+ color: ColorConstants.raisinBlack,
+ fontSize: 15,
+ fontWeight: FontWeight.w500,
+ );
+
static TextStyle blackW60010 = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
@@ -528,6 +575,12 @@ class CustomTextStyles {
fontSize: 11,
);
+ static TextStyle blackW5008 = TextStyle(
+ color: Colors.black,
+ fontWeight: FontWeight.w500,
+ fontSize: 8,
+ );
+
static TextStyle blackW50014 = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w500,
@@ -581,4 +634,16 @@ class CustomTextStyles {
fontWeight: FontWeight.w500,
color: ColorConstants.portlandOrange,
);
+
+ static TextStyle greyW50014 = TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ color: ColorConstants.grey,
+ );
+
+ static TextStyle dimGrayW50015 = TextStyle(
+ fontSize: 15,
+ fontWeight: FontWeight.w500,
+ color: ColorConstants.dimGray,
+ );
}
diff --git a/lib/utils/vectors.dart b/lib/utils/vectors.dart
index cb88e5b2..b35eba3b 100644
--- a/lib/utils/vectors.dart
+++ b/lib/utils/vectors.dart
@@ -98,9 +98,13 @@ class AppVectors {
static String icDownloadDisable = '$_basePath/ic_download_disable.svg';
static String icSendDisable = '$_basePath/ic_send_disable.svg';
static String icDeleteDisable = '$_basePath/ic_delete_disable.svg';
+ static String icPencil = '$_basePath/ic_pencil.svg';
+ static String icMobileImage = '$_basePath/ic_mobile_image.svg';
+ static String icTrustDeactivated = '$_basePath/ic_trust_deactivated.svg';
static String icDownloadOutline = '$_basePath/ic_download_outline.svg';
static String icDeliveredCheck = '$_basePath/ic_delivered_check.svg';
static String icDownloadedCheck = '$_basePath/ic_downloaded_check.svg';
static String icDone = '$_basePath/ic_done.svg';
static String icUndone = '$_basePath/ic_undone.svg';
+ static String icDeleteGroup = '$_basePath/ic_delete_group.svg';
}
diff --git a/lib/view_models/desktop_groups_screen_provider.dart b/lib/view_models/desktop_groups_screen_provider.dart
index 5d7923d0..7a929123 100644
--- a/lib/view_models/desktop_groups_screen_provider.dart
+++ b/lib/view_models/desktop_groups_screen_provider.dart
@@ -15,6 +15,7 @@ class DesktopGroupsScreenProvider extends ChangeNotifier {
bool isAddingContacts = false;
Uint8List? selectedGroupImage;
String? selectedGroupName;
+ bool showEditOptions = false;
void reset() {
searchGroupText = '';
@@ -23,6 +24,7 @@ class DesktopGroupsScreenProvider extends ChangeNotifier {
isEditing = false;
isAddingContacts = false;
groupCardState = GroupCardState.disable;
+ showEditOptions = false;
notifyListeners();
}
@@ -36,15 +38,15 @@ class DesktopGroupsScreenProvider extends ChangeNotifier {
notifyListeners();
}
- void setSelectedAtGroup(AtGroup? atGroup) {
+ Future setSelectedAtGroup(AtGroup? atGroup) async {
selectedAtGroup = atGroup;
setSelectedGroupName(selectedAtGroup?.groupName ?? '');
if (selectedAtGroup?.groupPicture != null) {
- setSelectedGroupImage(
+ await setSelectedGroupImage(
Uint8List.fromList(selectedAtGroup?.groupPicture.cast()),
);
} else {
- setSelectedGroupImage(Uint8List(0));
+ await setSelectedGroupImage(Uint8List(0));
}
notifyListeners();
}
@@ -64,13 +66,13 @@ class DesktopGroupsScreenProvider extends ChangeNotifier {
notifyListeners();
}
- void setIsEditing(bool status) {
+ Future setIsEditing(bool status) async {
isEditing = status;
if (selectedAtGroup?.groupPicture != null) {
- setSelectedGroupImage(
+ await setSelectedGroupImage(
Uint8List.fromList(selectedAtGroup?.groupPicture.cast()));
} else {
- setSelectedGroupImage(Uint8List(0));
+ await setSelectedGroupImage(Uint8List(0));
}
notifyListeners();
}
@@ -80,14 +82,18 @@ class DesktopGroupsScreenProvider extends ChangeNotifier {
notifyListeners();
}
- void setSelectedGroupImage(Uint8List data) {
- AppUtils.checkGroupImageSize(
- image: data,
- onSatisfy: (value) {
- selectedGroupImage = value;
- notifyListeners();
- },
- );
+ Future setSelectedGroupImage(Uint8List data) async {
+ if (data.isNotEmpty) {
+ await AppUtils.checkGroupImageSize(
+ image: data,
+ onSatisfy: (value) {
+ selectedGroupImage = value;
+ },
+ );
+ } else {
+ selectedGroupImage = data;
+ }
+ notifyListeners();
}
void setSelectedGroupName(String name) {
@@ -96,4 +102,9 @@ class DesktopGroupsScreenProvider extends ChangeNotifier {
notifyListeners();
}
}
+
+ void setShowEditOptionsStatus() {
+ showEditOptions = !showEditOptions;
+ notifyListeners();
+ }
}
diff --git a/lib/view_models/groups_provider.dart b/lib/view_models/groups_provider.dart
new file mode 100644
index 00000000..51ab3345
--- /dev/null
+++ b/lib/view_models/groups_provider.dart
@@ -0,0 +1,326 @@
+import 'package:at_commons/at_commons.dart';
+import 'package:at_contact/at_contact.dart';
+import 'package:at_contacts_group_flutter/services/group_service.dart';
+import 'package:at_contacts_group_flutter/utils/text_constants.dart';
+import 'package:atsign_atmosphere_pro/routes/route_names.dart';
+import 'package:atsign_atmosphere_pro/screens/common_widgets/custom_toast.dart';
+import 'package:atsign_atmosphere_pro/utils/app_utils.dart';
+import 'package:atsign_atmosphere_pro/utils/colors.dart';
+import 'package:atsign_atmosphere_pro/utils/text_styles.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+class GroupsProvider extends ChangeNotifier {
+ AtGroup? atGroup;
+ bool showEditOptions = false;
+ bool isEditingName = false;
+ bool isEditingImage = false;
+ dynamic groupPicture;
+ Set members = {};
+ bool isLoading = false;
+ bool needRefresh = false;
+ bool showTrustedMembers = false;
+ String searchKeyword = '';
+
+ Future init(AtGroup data) async {
+ atGroup = data;
+ members = atGroup?.members ?? {};
+ if (atGroup?.groupPicture != null) {
+ await setGroupPicture(
+ Uint8List.fromList(atGroup?.groupPicture.cast()),
+ );
+ }
+ notifyListeners();
+ }
+
+ void reset() {
+ showEditOptions = false;
+ isEditingName = false;
+ isEditingImage = false;
+ groupPicture = null;
+ members.clear();
+ atGroup = null;
+ isLoading = false;
+ needRefresh = false;
+ notifyListeners();
+ }
+
+ void resetManageMembers() {
+ showTrustedMembers = false;
+ searchKeyword = '';
+ notifyListeners();
+ }
+
+ void setShowEditOptions() {
+ showEditOptions = !showEditOptions;
+ notifyListeners();
+ }
+
+ void setIsEditingName() {
+ isEditingName = !isEditingName;
+ notifyListeners();
+ }
+
+ void setIsEditingImage() {
+ isEditingImage = !isEditingImage;
+ notifyListeners();
+ }
+
+ Future setGroupPicture(Uint8List? data) async {
+ if (data != null) {
+ await AppUtils.checkGroupImageSize(
+ image: data,
+ onSatisfy: (value) {
+ groupPicture = value;
+ },
+ );
+ } else {
+ groupPicture = null;
+ }
+ notifyListeners();
+ }
+
+ Future updateGroupName(
+ BuildContext context, {
+ required String name,
+ }) async {
+ if (isLoading) return;
+ isLoading = true;
+ notifyListeners();
+
+ if (name.trim().isEmpty) {
+ ScaffoldMessenger.of(context)
+ .showSnackBar(SnackBar(content: Text(TextConstants().EMPTY_NAME)));
+ } else {
+ AtGroup group = atGroup!;
+ group.groupName = name.trim();
+ group.displayName = name.trim();
+ final result = await GroupService().updateGroup(group);
+ if (result is AtGroup) {
+ atGroup = result;
+
+ isEditingName = false;
+ } else if (result != null) {
+ if (result.runtimeType == AlreadyExistsException) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(TextConstants().GROUP_ALREADY_EXISTS)));
+ } else if (result.runtimeType == InvalidAtSignException) {
+ ScaffoldMessenger.of(context)
+ .showSnackBar(SnackBar(content: Text(result.content)));
+ } else {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(TextConstants().SERVICE_ERROR)));
+ }
+ } else {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(TextConstants().SERVICE_ERROR)));
+ }
+ }
+ isLoading = false;
+ notifyListeners();
+ }
+
+ Future updateGroupPicture(BuildContext context) async {
+ if (isLoading) return;
+ isLoading = true;
+ notifyListeners();
+
+ AtGroup group = atGroup!;
+ group.groupPicture = groupPicture;
+
+ final result = await GroupService().updateGroup(group);
+ if (result is AtGroup) {
+ atGroup = result;
+ isEditingImage = false;
+ if (!needRefresh) {
+ needRefresh = true;
+ }
+ } else if (result != null) {
+ if (result.runtimeType == AlreadyExistsException) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(TextConstants().GROUP_ALREADY_EXISTS)));
+ } else if (result.runtimeType == InvalidAtSignException) {
+ ScaffoldMessenger.of(context)
+ .showSnackBar(SnackBar(content: Text(result.content)));
+ } else {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(TextConstants().SERVICE_ERROR)));
+ }
+ } else {
+ ScaffoldMessenger.of(context)
+ .showSnackBar(SnackBar(content: Text(TextConstants().SERVICE_ERROR)));
+ }
+ isLoading = false;
+ notifyListeners();
+ }
+
+ Future updateMembers(BuildContext context) async {
+ if (isLoading) return;
+ isLoading = true;
+ notifyListeners();
+
+ if (members.isEmpty) {
+ await showDeleteConfirmDialog(context);
+ } else {
+ AtGroup group = atGroup!;
+ group.members = members;
+ final result = await GroupService().updateGroup(group);
+ if (result is AtGroup) {
+ atGroup = result;
+ resetManageMembers();
+ Navigator.pop(context);
+ } else if (result != null) {
+ if (result.runtimeType == AlreadyExistsException) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(TextConstants().GROUP_ALREADY_EXISTS)));
+ } else if (result.runtimeType == InvalidAtSignException) {
+ ScaffoldMessenger.of(context)
+ .showSnackBar(SnackBar(content: Text(result.content)));
+ } else {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(TextConstants().SERVICE_ERROR)));
+ }
+ } else {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(TextConstants().SERVICE_ERROR)));
+ }
+ }
+ isLoading = false;
+ notifyListeners();
+ }
+
+ void addOrRemoveMember(AtContact member) {
+ final membersList = members;
+ if (membersList.any((element) => element.atSign == member.atSign)) {
+ print('remove');
+ membersList.removeWhere(
+ (element) => element.atSign == member.atSign,
+ );
+ print(membersList);
+ } else {
+ print('add');
+ membersList.add(member);
+ }
+ setMembers(membersList);
+ }
+
+ void setMembers(Set data) {
+ members = data;
+ notifyListeners();
+ }
+
+ void setShowTrustedMembersStatus() {
+ showTrustedMembers = !showTrustedMembers;
+ notifyListeners();
+ }
+
+ void changeSearchKeyword(String text) {
+ searchKeyword = text;
+ notifyListeners();
+ }
+
+ Future showDeleteConfirmDialog(BuildContext context) async {
+ await showDialog(
+ context: context,
+ barrierDismissible: false,
+ builder: (context) {
+ return AlertDialog(
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(7),
+ ),
+ contentPadding:
+ const EdgeInsets.symmetric(horizontal: 28, vertical: 24),
+ content: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ 'Delete',
+ style: CustomTextStyles.blackBold(size: 14),
+ ),
+ SizedBox(height: 8),
+ Text(
+ 'Are you sure you want to delete ${atGroup?.groupName ?? ''}?',
+ style: CustomTextStyles.blackW40013,
+ textAlign: TextAlign.center,
+ ),
+ RichText(
+ text: TextSpan(
+ children: [
+ TextSpan(
+ text: 'Note',
+ style: CustomTextStyles.blackItalicW40013,
+ ),
+ TextSpan(
+ text: ': this action cannot be undone.',
+ style: CustomTextStyles.blackW40013,
+ )
+ ],
+ ),
+ ),
+ SizedBox(height: 8),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 12),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ InkWell(
+ onTap: () {
+ Navigator.pop(context);
+ },
+ child: Container(
+ height: 36,
+ width: 84,
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(4),
+ border: Border.all(color: Colors.black),
+ ),
+ child: Text(
+ 'Cancel',
+ style: CustomTextStyles.blackUnderlineW40012,
+ ),
+ ),
+ ),
+ InkWell(
+ onTap: () async {
+ final result =
+ await GroupService().deleteGroup(atGroup!);
+ if (result != null && result) {
+ reset();
+ resetManageMembers();
+ await GroupService().fetchGroupsAndContacts();
+ Navigator.popUntil(
+ context,
+ ModalRoute.withName(Routes.WELCOME_SCREEN),
+ );
+ } else {
+ CustomToast().show(
+ TextConstants().SERVICE_ERROR,
+ context,
+ );
+ }
+ },
+ child: Container(
+ height: 36,
+ width: 84,
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(4),
+ color: ColorConstants.bottomBlack,
+ ),
+ child: Text(
+ 'Move',
+ style: CustomTextStyles.whiteBold12,
+ ),
+ ),
+ )
+ ],
+ ),
+ )
+ ],
+ ),
+ );
+ },
+ );
+ }
+}
diff --git a/pubspec.lock b/pubspec.lock
index dfa79fe0..6c745edd 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1381,6 +1381,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.9.2"
+ tuple:
+ dependency: "direct main"
+ description:
+ name: tuple
+ sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.2"
tutorial_coach_mark:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 4e5f7e18..c344e593 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -87,6 +87,7 @@ dependencies:
open_store: ^0.5.0
flutter_image_compress: ^2.2.0
smooth_page_indicator: ^1.1.0
+ tuple: ^2.0.2
dev_dependencies:
flutter_test:
diff --git a/test/widget_tests/group_contact_screen_test/group_contact_list_test.dart b/test/widget_tests/group_contact_screen_test/group_contact_list_test.dart
index 3a9a6b08..28a364c3 100644
--- a/test/widget_tests/group_contact_screen_test/group_contact_list_test.dart
+++ b/test/widget_tests/group_contact_screen_test/group_contact_list_test.dart
@@ -1,6 +1,6 @@
import 'package:at_common_flutter/at_common_flutter.dart';
import 'package:atsign_atmosphere_pro/screens/common_widgets/contact_initial.dart';
-import 'package:atsign_atmosphere_pro/screens/group_contacts_screen/widgets/group_contact_list_tile.dart';
+import 'package:atsign_atmosphere_pro/screens/common_widgets/contact_list_tile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';