diff --git a/mobile/lib/features/channels/add_members_sheet.dart b/mobile/lib/features/channels/add_members_sheet.dart index a710c21ac7..7e2e38f3d1 100644 --- a/mobile/lib/features/channels/add_members_sheet.dart +++ b/mobile/lib/features/channels/add_members_sheet.dart @@ -158,37 +158,44 @@ class AddChannelMembersSheet extends HookConsumerWidget { final selected = selectedPubkeys.contains( user.pubkey.toLowerCase(), ); - return ListTile( + return Semantics( key: ValueKey( 'add-channel-member-${user.pubkey}', ), - contentPadding: EdgeInsets.zero, - leading: AvatarImage( - imageUrl: user.avatarUrl, - radius: 20, - backgroundColor: - context.colors.primaryContainer, - fallback: Text(user.initial), + button: true, + selected: selected, + label: selected + ? '${user.label}, selected' + : user.label, + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: AvatarImage( + imageUrl: user.avatarUrl, + radius: 20, + backgroundColor: + context.colors.primaryContainer, + fallback: Text(user.initial), + ), + title: Text( + user.label, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + user.secondaryLabel, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + trailing: Icon( + selected + ? LucideIcons.circleCheck + : LucideIcons.plus, + color: selected + ? context.colors.primary + : context.colors.onSurfaceVariant, + ), + onTap: () => toggleUser(user), ), - title: Text( - user.label, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - subtitle: Text( - user.secondaryLabel, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - trailing: Icon( - selected - ? LucideIcons.circleCheck - : LucideIcons.plus, - color: selected - ? context.colors.primary - : context.colors.onSurfaceVariant, - ), - onTap: () => toggleUser(user), ); }, ), diff --git a/mobile/lib/features/channels/channel_actions_sheet.dart b/mobile/lib/features/channels/channel_actions_sheet.dart index c1073bcb02..b312b54d37 100644 --- a/mobile/lib/features/channels/channel_actions_sheet.dart +++ b/mobile/lib/features/channels/channel_actions_sheet.dart @@ -220,7 +220,11 @@ class ChannelActionsSheet extends ConsumerWidget { maxWidth: 640, maxHeight: MediaQuery.sizeOf(context).height * 0.9, ), - builder: (_) => ManageChannelSheet(channel: channel), + builder: (_) => ManageChannelSheet( + channel: channel, + canEditDetails: + canManageLifecycle && !channel.isArchived, + ), ); if (shouldClose == true && context.mounted) { Navigator.of(context).pop(true); diff --git a/mobile/lib/features/channels/channel_details_page.dart b/mobile/lib/features/channels/channel_details_page.dart index 54d038ea2f..ef52042845 100644 --- a/mobile/lib/features/channels/channel_details_page.dart +++ b/mobile/lib/features/channels/channel_details_page.dart @@ -197,6 +197,7 @@ class ChannelDetailsPage extends HookConsumerWidget { ), builder: (_) => ManageChannelSheet( channel: resolvedChannel, + canEditDetails: canEdit, onChannelUpdated: (updated) => displayedChannel.value = updated, ), ); diff --git a/mobile/lib/features/channels/manage_channel_sheet.dart b/mobile/lib/features/channels/manage_channel_sheet.dart index c330e7cd01..588ca49653 100644 --- a/mobile/lib/features/channels/manage_channel_sheet.dart +++ b/mobile/lib/features/channels/manage_channel_sheet.dart @@ -11,10 +11,12 @@ class ManageChannelSheet extends HookConsumerWidget { const ManageChannelSheet({ super.key, required this.channel, + this.canEditDetails = true, this.onChannelUpdated, }); final Channel channel; + final bool canEditDetails; final ValueChanged? onChannelUpdated; @override @@ -48,6 +50,7 @@ class ManageChannelSheet extends HookConsumerWidget { final nameDirty = canonicalName != channel.name.trim(); final descriptionDirty = description != channel.description.trim(); final canSaveDetails = + canEditDetails && canonicalName.isNotEmpty && (nameDirty || descriptionDirty) && !isSavingDetails.value; @@ -113,7 +116,7 @@ class ManageChannelSheet extends HookConsumerWidget { fieldKey: const ValueKey('manage-channel-name'), outlineKey: const ValueKey('manage-channel-name-outline'), controller: nameController, - enabled: !isSavingDetails.value, + enabled: canEditDetails && !isSavingDetails.value, hintText: 'Channel name', textInputAction: TextInputAction.next, ), @@ -122,7 +125,7 @@ class ManageChannelSheet extends HookConsumerWidget { fieldKey: const ValueKey('manage-channel-description'), outlineKey: const ValueKey('manage-channel-description-outline'), controller: descriptionController, - enabled: !isSavingDetails.value, + enabled: canEditDetails && !isSavingDetails.value, hintText: 'Description', minLines: 2, maxLines: 4, @@ -284,28 +287,32 @@ class _ManageChannelTextField extends StatelessWidget { ), borderRadius: BorderRadius.circular(Radii.card), ), - child: TextField( - key: fieldKey, - controller: controller, - enabled: enabled, - minLines: minLines, - maxLines: maxLines, - style: context.textTheme.bodyLarge, - decoration: InputDecoration( - hintText: hintText, - hintStyle: context.textTheme.bodyLarge?.copyWith( - color: context.colors.onSurfaceVariant, - ), - border: InputBorder.none, - enabledBorder: InputBorder.none, - focusedBorder: InputBorder.none, - isDense: true, - contentPadding: const EdgeInsets.symmetric( - horizontal: Grid.xs, - vertical: Grid.twelve, + child: Semantics( + label: hintText, + textField: true, + child: TextField( + key: fieldKey, + controller: controller, + enabled: enabled, + minLines: minLines, + maxLines: maxLines, + style: context.textTheme.bodyLarge, + decoration: InputDecoration( + hintText: hintText, + hintStyle: context.textTheme.bodyLarge?.copyWith( + color: context.colors.onSurfaceVariant, + ), + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + isDense: true, + contentPadding: const EdgeInsets.symmetric( + horizontal: Grid.xs, + vertical: Grid.twelve, + ), ), + textInputAction: textInputAction, ), - textInputAction: textInputAction, ), ); } diff --git a/mobile/test/features/channels/channel_detail_page_test.dart b/mobile/test/features/channels/channel_detail_page_test.dart index 07cb3214d7..abd1a82d0c 100644 --- a/mobile/test/features/channels/channel_detail_page_test.dart +++ b/mobile/test/features/channels/channel_detail_page_test.dart @@ -1107,8 +1107,27 @@ void main() { ); await tester.pumpAndSettle(); + await tester.pump(); + final unselectedSemantics = tester.getSemantics( + find.byKey(const ValueKey('add-channel-member-alice')), + ); + expect(unselectedSemantics.flagsCollection.isButton, isTrue); + expect( + unselectedSemantics.flagsCollection.isSelected.toString(), + 'Tristate.isFalse', + ); + await tester.tap(find.byKey(const ValueKey('add-channel-member-alice'))); await tester.pump(); + final selectedSemantics = tester.getSemantics( + find.byKey(const ValueKey('add-channel-member-alice')), + ); + expect(selectedSemantics.flagsCollection.isButton, isTrue); + expect( + selectedSemantics.flagsCollection.isSelected.toString(), + 'Tristate.isTrue', + ); + expect(selectedSemantics.label, 'Alice, selected'); await tester.tap( find.byKey(const ValueKey('add-channel-members-submit')), );