implemented comprehensive accessibility (A11y) support across the app

This commit is contained in:
2026-07-13 23:41:47 +02:00
parent 8274dd46cd
commit 478f0ff20b
46 changed files with 590 additions and 226 deletions
@@ -6,6 +6,7 @@ import '../../../../api/marianumcloud/talk/delete_react_message/delete_react_mes
import '../../../../api/marianumcloud/talk/react_message/react_message.dart';
import '../../../../api/marianumcloud/talk/react_message/react_message_params.dart';
import '../../../../api/marianumcloud/talk/room/get_room_response.dart';
import '../../../../widget/a11y/a11y_labels.dart';
import '../../../../widget/async_action_button.dart';
import '../../../../widget/demo_restricted.dart';
import '../../../../widget/emoji_text.dart';
@@ -41,44 +42,58 @@ class ChatBubbleReactions extends StatelessWidget {
children: reactions.entries.map<Widget>((e) {
final hasSelfReacted =
bubbleData.reactionsSelf?.contains(e.key) ?? false;
void toggle() {
if (guardDemoAction(context)) return;
runWithErrorDialog(context, () async {
if (hasSelfReacted) {
await DeleteReactMessage(
chatToken: chatData.token,
messageId: bubbleData.id,
params: DeleteReactMessageParams(e.key),
).run();
} else {
await ReactMessage(
chatToken: chatData.token,
messageId: bubbleData.id,
params: ReactMessageParams(e.key),
).run();
}
onChanged(renew: true);
});
}
// Ownership (eigene Reaktion) wird visuell nur über die
// Hintergrundfarbe transportiert für den Screenreader ins Label.
final label = hasSelfReacted
? '${e.key} ${e.value}, ${A11yLabels.yourReaction}'
: '${e.key} ${e.value}';
return Container(
margin: const EdgeInsets.only(right: 2.5, left: 2.5),
child: ActionChip(
label: Row(
mainAxisSize: MainAxisSize.min,
children: [
EmojiText(e.key, size: EmojiText.sizeInline),
const SizedBox(width: 4),
Text('${e.value}'),
],
child: Semantics(
button: true,
label: label,
onTap: toggle,
child: ExcludeSemantics(
child: ActionChip(
label: Row(
mainAxisSize: MainAxisSize.min,
children: [
EmojiText(e.key, size: EmojiText.sizeInline),
const SizedBox(width: 4),
Text('${e.value}'),
],
),
visualDensity: const VisualDensity(
vertical: VisualDensity.minimumDensity,
horizontal: VisualDensity.minimumDensity,
),
padding: EdgeInsets.zero,
backgroundColor: hasSelfReacted
? Theme.of(context).primaryColor
: null,
onPressed: toggle,
),
),
visualDensity: const VisualDensity(
vertical: VisualDensity.minimumDensity,
horizontal: VisualDensity.minimumDensity,
),
padding: EdgeInsets.zero,
backgroundColor: hasSelfReacted
? Theme.of(context).primaryColor
: null,
onPressed: () {
if (guardDemoAction(context)) return;
runWithErrorDialog(context, () async {
if (hasSelfReacted) {
await DeleteReactMessage(
chatToken: chatData.token,
messageId: bubbleData.id,
params: DeleteReactMessageParams(e.key),
).run();
} else {
await ReactMessage(
chatToken: chatData.token,
messageId: bubbleData.id,
params: ReactMessageParams(e.key),
).run();
}
onChanged(renew: true);
});
},
),
);
}).toList(),