mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2026-07-15 05:41:36 +00:00
fix(accessibility): label icon controls and semantic tap targets
This commit is contained in:
parent
e30e84ded1
commit
b552e9843c
15 changed files with 118 additions and 66 deletions
|
|
@ -257,6 +257,7 @@ class BookSearchResultMini extends HookConsumerWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
|
tooltip: 'More options',
|
||||||
icon: const Icon(Icons.more_vert),
|
icon: const Icon(Icons.more_vert),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// TODO: show a menu with options for the book
|
// TODO: show a menu with options for the book
|
||||||
|
|
@ -311,6 +312,7 @@ class SearchResultMiniSection extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: 'View more ${category.toString().split('.').last}',
|
||||||
icon: const Icon(Icons.arrow_forward_ios),
|
icon: const Icon(Icons.arrow_forward_ios),
|
||||||
onPressed: onTap ?? openSearch,
|
onPressed: onTap ?? openSearch,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import 'package:vaani/settings/api_settings_provider.dart';
|
||||||
import 'package:vaani/settings/app_settings_provider.dart';
|
import 'package:vaani/settings/app_settings_provider.dart';
|
||||||
import 'package:vaani/shared/extensions/model_conversions.dart';
|
import 'package:vaani/shared/extensions/model_conversions.dart';
|
||||||
import 'package:vaani/shared/utils.dart';
|
import 'package:vaani/shared/utils.dart';
|
||||||
|
import 'package:vaani/shared/widgets/not_implemented.dart';
|
||||||
|
|
||||||
class LibraryItemActions extends HookConsumerWidget {
|
class LibraryItemActions extends HookConsumerWidget {
|
||||||
const LibraryItemActions({super.key, required this.id});
|
const LibraryItemActions({super.key, required this.id});
|
||||||
|
|
@ -64,11 +65,15 @@ class LibraryItemActions extends HookConsumerWidget {
|
||||||
children: [
|
children: [
|
||||||
// read list button
|
// read list button
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {},
|
tooltip: 'Add to playlist',
|
||||||
|
onPressed: () {
|
||||||
|
showNotImplementedToast(context);
|
||||||
|
},
|
||||||
icon: const Icon(Icons.playlist_add_rounded),
|
icon: const Icon(Icons.playlist_add_rounded),
|
||||||
),
|
),
|
||||||
// share button
|
// share button
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: 'Share item',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
appLogger.fine('Sharing');
|
appLogger.fine('Sharing');
|
||||||
var currentServerUrl =
|
var currentServerUrl =
|
||||||
|
|
@ -97,6 +102,7 @@ class LibraryItemActions extends HookConsumerWidget {
|
||||||
|
|
||||||
// more button
|
// more button
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: 'More options',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// show the bottom sheet with download history
|
// show the bottom sheet with download history
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
|
|
@ -215,6 +221,7 @@ class LibItemDownloadButton extends HookConsumerWidget {
|
||||||
return isItemDownloading
|
return isItemDownloading
|
||||||
? ItemCurrentlyInDownloadQueue(item: item)
|
? ItemCurrentlyInDownloadQueue(item: item)
|
||||||
: IconButton(
|
: IconButton(
|
||||||
|
tooltip: 'Download item',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
appLogger.fine('Pressed download button');
|
appLogger.fine('Pressed download button');
|
||||||
|
|
||||||
|
|
@ -280,6 +287,7 @@ class AlreadyItemDownloadedButton extends HookConsumerWidget {
|
||||||
final isBookPlaying = ref.watch(audiobookPlayerProvider).book != null;
|
final isBookPlaying = ref.watch(audiobookPlayerProvider).book != null;
|
||||||
|
|
||||||
return IconButton(
|
return IconButton(
|
||||||
|
tooltip: 'Downloaded item options',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
appLogger.fine('Pressed already downloaded button');
|
appLogger.fine('Pressed already downloaded button');
|
||||||
// manager.openDownloadedFile(item);
|
// manager.openDownloadedFile(item);
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,11 @@ class UserLoginWithPassword extends HookConsumerWidget {
|
||||||
).colorScheme.primary.withValues(alpha: 0.8),
|
).colorScheme.primary.withValues(alpha: 0.8),
|
||||||
BlendMode.srcIn,
|
BlendMode.srcIn,
|
||||||
),
|
),
|
||||||
|
child: Semantics(
|
||||||
|
button: true,
|
||||||
|
label: isPasswordVisible.value
|
||||||
|
? 'Hide password'
|
||||||
|
: 'Show password',
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: BorderRadius.circular(50),
|
borderRadius: BorderRadius.circular(50),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
|
@ -151,6 +156,7 @@ class UserLoginWithPassword extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
suffixIconConstraints: const BoxConstraints(maxHeight: 45),
|
suffixIconConstraints: const BoxConstraints(maxHeight: 45),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,7 @@ class AudiobookPlayerPlayPauseButton extends HookConsumerWidget {
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
ProcessingState.completed => IconButton(
|
ProcessingState.completed => IconButton(
|
||||||
|
tooltip: 'Replay',
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await player.seek(const Duration(seconds: 0));
|
await player.seek(const Duration(seconds: 0));
|
||||||
await player.play();
|
await player.play();
|
||||||
|
|
@ -170,6 +171,7 @@ class AudiobookPlayerPlayPauseButton extends HookConsumerWidget {
|
||||||
icon: const Icon(Icons.replay),
|
icon: const Icon(Icons.replay),
|
||||||
),
|
),
|
||||||
ProcessingState.ready => IconButton(
|
ProcessingState.ready => IconButton(
|
||||||
|
tooltip: player.playing ? 'Pause' : 'Play',
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await player.togglePlayPause();
|
await player.togglePlayPause();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ class PlayerWhenExpanded extends HookConsumerWidget {
|
||||||
IconButton(
|
IconButton(
|
||||||
iconSize: 30,
|
iconSize: 30,
|
||||||
icon: const Icon(Icons.keyboard_arrow_down),
|
icon: const Icon(Icons.keyboard_arrow_down),
|
||||||
|
tooltip: 'Minimize player',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// minimize the player
|
// minimize the player
|
||||||
audioBookMiniplayerController.animateToHeight(
|
audioBookMiniplayerController.animateToHeight(
|
||||||
|
|
@ -75,6 +76,7 @@ class PlayerWhenExpanded extends HookConsumerWidget {
|
||||||
// the cast button
|
// the cast button
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.cast),
|
icon: const Icon(Icons.cast),
|
||||||
|
tooltip: 'Cast',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showNotImplementedToast(context);
|
showNotImplementedToast(context);
|
||||||
},
|
},
|
||||||
|
|
@ -108,8 +110,6 @@ class PlayerWhenExpanded extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: imageSize,
|
height: imageSize,
|
||||||
child: InkWell(
|
|
||||||
onTap: () {},
|
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
AppElementSizes.borderRadiusRegular * earlyPercentage,
|
AppElementSizes.borderRadiusRegular * earlyPercentage,
|
||||||
|
|
@ -120,7 +120,6 @@ class PlayerWhenExpanded extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
|
|
||||||
// the chapter title
|
// the chapter title
|
||||||
Opacity(
|
Opacity(
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,9 @@ class PlayerWhenMinimized extends HookConsumerWidget {
|
||||||
horizontal:
|
horizontal:
|
||||||
((availWidth - maxImgSize) / 2) * percentageMiniplayer,
|
((availWidth - maxImgSize) / 2) * percentageMiniplayer,
|
||||||
),
|
),
|
||||||
|
child: Semantics(
|
||||||
|
button: true,
|
||||||
|
label: 'Open current book details',
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// navigate to item page
|
// navigate to item page
|
||||||
|
|
@ -68,6 +71,7 @@ class PlayerWhenMinimized extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
// author and title of the book
|
// author and title of the book
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
@ -112,11 +116,16 @@ class PlayerWhenMinimized extends HookConsumerWidget {
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 8),
|
padding: const EdgeInsets.only(left: 8),
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
tooltip: 'Rewind 30 seconds',
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.replay_30,
|
Icons.replay_30,
|
||||||
size: AppElementSizes.iconSizeSmall,
|
size: AppElementSizes.iconSizeSmall,
|
||||||
),
|
),
|
||||||
onPressed: () {},
|
onPressed: () {
|
||||||
|
player.seek(
|
||||||
|
player.positionInBook - const Duration(seconds: 30),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ class AudiobookPlayerSeekButton extends HookConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final player = ref.watch(audiobookPlayerProvider);
|
final player = ref.watch(audiobookPlayerProvider);
|
||||||
return IconButton(
|
return IconButton(
|
||||||
|
tooltip: isForward
|
||||||
|
? 'Seek forward 30 seconds'
|
||||||
|
: 'Seek backward 30 seconds',
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
isForward ? Icons.forward_30 : Icons.replay_30,
|
isForward ? Icons.forward_30 : Icons.replay_30,
|
||||||
size: AppElementSizes.iconSizeSmall,
|
size: AppElementSizes.iconSizeSmall,
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ class AudiobookPlayerSeekChapterButton extends HookConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
return IconButton(
|
return IconButton(
|
||||||
|
tooltip: isForward ? 'Next chapter' : 'Previous chapter',
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
isForward ? Icons.skip_next : Icons.skip_previous,
|
isForward ? Icons.skip_next : Icons.skip_previous,
|
||||||
size: AppElementSizes.iconSizeSmall,
|
size: AppElementSizes.iconSizeSmall,
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,7 @@ class SpeedWheel extends StatelessWidget {
|
||||||
// a minus button to decrease the speed
|
// a minus button to decrease the speed
|
||||||
if (showIncrementButtons)
|
if (showIncrementButtons)
|
||||||
IconButton.filledTonal(
|
IconButton.filledTonal(
|
||||||
|
tooltip: 'Decrease speed',
|
||||||
icon: const Icon(Icons.remove),
|
icon: const Icon(Icons.remove),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// animate to index - 1
|
// animate to index - 1
|
||||||
|
|
@ -198,6 +199,7 @@ class SpeedWheel extends StatelessWidget {
|
||||||
if (showIncrementButtons)
|
if (showIncrementButtons)
|
||||||
// a plus button to increase the speed
|
// a plus button to increase the speed
|
||||||
IconButton.filledTonal(
|
IconButton.filledTonal(
|
||||||
|
tooltip: 'Increase speed',
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// animate to index + 1
|
// animate to index + 1
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@ class SleepTimerButton extends HookConsumerWidget {
|
||||||
// if the sleep timer is active, show the remaining time in a pill shaped container
|
// if the sleep timer is active, show the remaining time in a pill shaped container
|
||||||
return Tooltip(
|
return Tooltip(
|
||||||
message: 'Sleep Timer',
|
message: 'Sleep Timer',
|
||||||
|
child: Semantics(
|
||||||
|
button: true,
|
||||||
|
label: 'Sleep timer',
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
appLogger.fine('Sleep Timer button pressed');
|
appLogger.fine('Sleep Timer button pressed');
|
||||||
|
|
@ -59,6 +62,7 @@ class SleepTimerButton extends HookConsumerWidget {
|
||||||
: RemainingSleepTimeDisplay(timer: sleepTimer),
|
: RemainingSleepTimeDisplay(timer: sleepTimer),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -256,6 +260,7 @@ class SleepTimerWheel extends StatelessWidget {
|
||||||
// a minus button to decrease the speed
|
// a minus button to decrease the speed
|
||||||
if (showIncrementButtons)
|
if (showIncrementButtons)
|
||||||
IconButton.filledTonal(
|
IconButton.filledTonal(
|
||||||
|
tooltip: 'Decrease sleep timer',
|
||||||
icon: const Icon(Icons.remove),
|
icon: const Icon(Icons.remove),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// animate to index - 1
|
// animate to index - 1
|
||||||
|
|
@ -294,6 +299,7 @@ class SleepTimerWheel extends StatelessWidget {
|
||||||
if (showIncrementButtons)
|
if (showIncrementButtons)
|
||||||
// a plus button to increase the speed
|
// a plus button to increase the speed
|
||||||
IconButton.filledTonal(
|
IconButton.filledTonal(
|
||||||
|
tooltip: 'Increase sleep timer',
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// animate to index + 1
|
// animate to index + 1
|
||||||
|
|
|
||||||
|
|
@ -283,6 +283,7 @@ class AvailableUserTile extends HookConsumerWidget {
|
||||||
context.goNamed(Routes.home.name);
|
context.goNamed(Routes.home.name);
|
||||||
},
|
},
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
|
tooltip: 'Remove user',
|
||||||
icon: const Icon(Icons.delete),
|
icon: const Icon(Icons.delete),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
|
|
|
||||||
|
|
@ -235,6 +235,7 @@ class RestoreDialogue extends HookConsumerWidget {
|
||||||
hintText: 'Paste the backup here',
|
hintText: 'Paste the backup here',
|
||||||
// clear button
|
// clear button
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
|
tooltip: 'Clear backup',
|
||||||
icon: Icon(Icons.clear),
|
icon: Icon(Icons.clear),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
settingsInputController.clear();
|
settingsInputController.clear();
|
||||||
|
|
|
||||||
|
|
@ -350,6 +350,7 @@ class NotificationTitlePicker extends HookConsumerWidget {
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
helper: const Text('Select a field below to insert it'),
|
helper: const Text('Select a field below to insert it'),
|
||||||
suffix: IconButton(
|
suffix: IconButton(
|
||||||
|
tooltip: 'Clear title',
|
||||||
icon: const Icon(Icons.clear),
|
icon: const Icon(Icons.clear),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
controller.clear();
|
controller.clear();
|
||||||
|
|
|
||||||
|
|
@ -283,6 +283,7 @@ class ShakeForceSelector extends HookConsumerWidget {
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
// clear button
|
// clear button
|
||||||
suffix: IconButton(
|
suffix: IconButton(
|
||||||
|
tooltip: 'Clear threshold',
|
||||||
icon: const Icon(Icons.clear),
|
icon: const Icon(Icons.clear),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
controller.clear();
|
controller.clear();
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,15 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
|
||||||
(element) => element.libraryItemId == libraryItemId,
|
(element) => element.libraryItemId == libraryItemId,
|
||||||
);
|
);
|
||||||
final isBookCompleted = userProgress?.isFinished ?? false;
|
final isBookCompleted = userProgress?.isFinished ?? false;
|
||||||
|
final playTooltip = isCurrentBookSetInPlayer
|
||||||
|
? isPlayingThisBook
|
||||||
|
? 'Pause'
|
||||||
|
: 'Resume'
|
||||||
|
: isBookCompleted
|
||||||
|
? 'Listen again'
|
||||||
|
: userProgress?.progress != null
|
||||||
|
? 'Continue listening'
|
||||||
|
: 'Start listening';
|
||||||
|
|
||||||
const size = 40.0;
|
const size = 40.0;
|
||||||
|
|
||||||
|
|
@ -268,6 +277,7 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
|
||||||
|
|
||||||
// the play button
|
// the play button
|
||||||
IconButton(
|
IconButton(
|
||||||
|
tooltip: playTooltip,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue