✨ Realm member manage
This commit is contained in:
		| @@ -240,6 +240,8 @@ | ||||
|   "fileUploadingProgress": "Uploading file #{}: {}%", | ||||
|   "removeChatMember": "Remove Chat Room Member", | ||||
|   "removeChatMemberHint": "Are you sure to remove this member from the room?", | ||||
|   "removeRealmMember": "Remove Realm Member", | ||||
|   "removeRealmMemberHint": "Are you sure to remove this member from the realm?", | ||||
|   "memberRole": "Member Role", | ||||
|   "memberRoleHint": "Greater number has higher permission.", | ||||
|   "memberRoleEdit": "Edit role for @{}" | ||||
|   | ||||
| @@ -228,6 +228,8 @@ class _RealmMemberListSheet extends HookConsumerWidget { | ||||
|       realmMemberStateProvider(realmSlug).notifier, | ||||
|     ); | ||||
|  | ||||
|     final realmIdentity = ref.watch(realmIdentityProvider(realmSlug)); | ||||
|  | ||||
|     useEffect(() { | ||||
|       Future(() { | ||||
|         memberNotifier.loadMore(); | ||||
| @@ -320,6 +322,7 @@ class _RealmMemberListSheet extends HookConsumerWidget { | ||||
|  | ||||
|                         final member = memberState.members[index]; | ||||
|                         return ListTile( | ||||
|                           contentPadding: EdgeInsets.only(left: 16, right: 12), | ||||
|                           leading: ProfilePictureWidget( | ||||
|                             fileId: member.account!.profile.pictureId, | ||||
|                           ), | ||||
| @@ -344,6 +347,55 @@ class _RealmMemberListSheet extends HookConsumerWidget { | ||||
|                               Expanded(child: Text("@${member.account!.name}")), | ||||
|                             ], | ||||
|                           ), | ||||
|                           trailing: Row( | ||||
|                             mainAxisSize: MainAxisSize.min, | ||||
|                             children: [ | ||||
|                               if ((realmIdentity.value?.role ?? 0) >= 50) | ||||
|                                 IconButton( | ||||
|                                   icon: const Icon(Symbols.edit), | ||||
|                                   onPressed: () { | ||||
|                                     showModalBottomSheet( | ||||
|                                       isScrollControlled: true, | ||||
|                                       context: context, | ||||
|                                       builder: | ||||
|                                           (context) => _RealmMemberRoleSheet( | ||||
|                                             realmSlug: realmSlug, | ||||
|                                             member: member, | ||||
|                                           ), | ||||
|                                     ).then((value) { | ||||
|                                       if (value != null) { | ||||
|                                         memberNotifier.reset(); | ||||
|                                         memberNotifier.loadMore(); | ||||
|                                       } | ||||
|                                     }); | ||||
|                                   }, | ||||
|                                 ), | ||||
|                               if ((realmIdentity.value?.role ?? 0) >= 50) | ||||
|                                 IconButton( | ||||
|                                   icon: const Icon(Symbols.delete), | ||||
|                                   onPressed: () { | ||||
|                                     showConfirmAlert( | ||||
|                                       'removeRealmMemberHint'.tr(), | ||||
|                                       'removeRealmMember'.tr(), | ||||
|                                     ).then((confirm) async { | ||||
|                                       if (confirm != true) return; | ||||
|                                       try { | ||||
|                                         final apiClient = ref.watch( | ||||
|                                           apiClientProvider, | ||||
|                                         ); | ||||
|                                         await apiClient.delete( | ||||
|                                           '/realms/$realmSlug/members/${member.accountId}', | ||||
|                                         ); | ||||
|                                         memberNotifier.reset(); | ||||
|                                         memberNotifier.loadMore(); | ||||
|                                       } catch (err) { | ||||
|                                         showErrorAlert(err); | ||||
|                                       } | ||||
|                                     }); | ||||
|                                   }, | ||||
|                                 ), | ||||
|                             ], | ||||
|                           ), | ||||
|                         ); | ||||
|                       }, | ||||
|                     ), | ||||
| @@ -381,3 +433,120 @@ class RealmMemberState { | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class _RealmMemberRoleSheet extends HookConsumerWidget { | ||||
|   final String realmSlug; | ||||
|   final SnRealmMember member; | ||||
|  | ||||
|   const _RealmMemberRoleSheet({required this.realmSlug, required this.member}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final roleController = useTextEditingController( | ||||
|       text: member.role.toString(), | ||||
|     ); | ||||
|  | ||||
|     return Container( | ||||
|       padding: EdgeInsets.only( | ||||
|         bottom: MediaQuery.of(context).viewInsets.bottom, | ||||
|       ), | ||||
|       child: SafeArea( | ||||
|         child: Column( | ||||
|           mainAxisSize: MainAxisSize.min, | ||||
|           crossAxisAlignment: CrossAxisAlignment.stretch, | ||||
|           children: [ | ||||
|             Padding( | ||||
|               padding: EdgeInsets.only( | ||||
|                 top: 16, | ||||
|                 left: 20, | ||||
|                 right: 16, | ||||
|                 bottom: 12, | ||||
|               ), | ||||
|               child: Row( | ||||
|                 children: [ | ||||
|                   Text( | ||||
|                     'memberRoleEdit'.tr(args: [member.account!.name]), | ||||
|                     style: Theme.of(context).textTheme.headlineSmall?.copyWith( | ||||
|                       fontWeight: FontWeight.w600, | ||||
|                       letterSpacing: -0.5, | ||||
|                     ), | ||||
|                   ), | ||||
|                   const Spacer(), | ||||
|                   IconButton( | ||||
|                     icon: const Icon(Symbols.close), | ||||
|                     onPressed: () => Navigator.pop(context), | ||||
|                     style: IconButton.styleFrom( | ||||
|                       minimumSize: const Size(36, 36), | ||||
|                     ), | ||||
|                   ), | ||||
|                 ], | ||||
|               ), | ||||
|             ), | ||||
|             const Divider(height: 1), | ||||
|             Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.stretch, | ||||
|               children: [ | ||||
|                 Autocomplete<int>( | ||||
|                   optionsBuilder: (TextEditingValue textEditingValue) { | ||||
|                     if (textEditingValue.text.isEmpty) { | ||||
|                       return const [100, 50, 0]; | ||||
|                     } | ||||
|                     final int? value = int.tryParse(textEditingValue.text); | ||||
|                     if (value == null) return const [100, 50, 0]; | ||||
|                     return [100, 50, 0].where( | ||||
|                       (option) => | ||||
|                           option.toString().contains(textEditingValue.text), | ||||
|                     ); | ||||
|                   }, | ||||
|                   onSelected: (int selection) { | ||||
|                     roleController.text = selection.toString(); | ||||
|                   }, | ||||
|                   fieldViewBuilder: ( | ||||
|                     context, | ||||
|                     controller, | ||||
|                     focusNode, | ||||
|                     onFieldSubmitted, | ||||
|                   ) { | ||||
|                     return TextField( | ||||
|                       controller: controller, | ||||
|                       focusNode: focusNode, | ||||
|                       keyboardType: TextInputType.number, | ||||
|                       decoration: InputDecoration( | ||||
|                         labelText: 'memberRole'.tr(), | ||||
|                         helperText: 'memberRoleHint'.tr(), | ||||
|                       ), | ||||
|                       onTapOutside: (event) => focusNode.unfocus(), | ||||
|                     ); | ||||
|                   }, | ||||
|                 ), | ||||
|                 const Gap(16), | ||||
|                 FilledButton.icon( | ||||
|                   onPressed: () async { | ||||
|                     try { | ||||
|                       final newRole = int.parse(roleController.text); | ||||
|                       if (newRole < 0 || newRole > 100) { | ||||
|                         throw 'Role must be between 0 and 100'; | ||||
|                       } | ||||
|  | ||||
|                       final apiClient = ref.read(apiClientProvider); | ||||
|                       await apiClient.patch( | ||||
|                         '/realms/$realmSlug/members/${member.accountId}/role', | ||||
|                         data: newRole, | ||||
|                       ); | ||||
|  | ||||
|                       if (context.mounted) Navigator.pop(context, true); | ||||
|                     } catch (err) { | ||||
|                       showErrorAlert(err); | ||||
|                     } | ||||
|                   }, | ||||
|                   icon: const Icon(Symbols.save), | ||||
|                   label: const Text('saveChanges').tr(), | ||||
|                 ), | ||||
|               ], | ||||
|             ).padding(vertical: 16, horizontal: 24), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user