添加语言切换

This commit is contained in:
rang 2025-10-22 17:58:29 +08:00
parent e0deb84123
commit e06c834d0e
21 changed files with 1416 additions and 281 deletions

View file

@ -18,6 +18,7 @@ import 'package:vaani/features/item_viewer/view/library_item_page.dart';
import 'package:vaani/features/per_book_settings/providers/book_settings_provider.dart';
import 'package:vaani/features/player/providers/audiobook_player.dart';
import 'package:vaani/features/player/providers/player_form.dart';
import 'package:vaani/generated/l10n.dart';
import 'package:vaani/main.dart';
import 'package:vaani/router/router.dart';
import 'package:vaani/settings/api_settings_provider.dart';
@ -76,11 +77,9 @@ class LibraryItemActions extends HookConsumerWidget {
IconButton(
onPressed: () {
appLogger.fine('Sharing');
var currentServerUrl =
apiSettings.activeServer!.serverUrl;
var currentServerUrl = apiSettings.activeServer!.serverUrl;
if (!currentServerUrl.hasScheme) {
currentServerUrl =
Uri.https(currentServerUrl.toString());
currentServerUrl = Uri.https(currentServerUrl.toString());
}
handleLaunchUrl(
Uri.parse(
@ -139,8 +138,7 @@ class LibraryItemActions extends HookConsumerWidget {
FileDownloader()
.database
.deleteRecordWithId(
record
.task.taskId,
record.task.taskId,
);
Navigator.pop(context);
},
@ -159,8 +157,7 @@ class LibraryItemActions extends HookConsumerWidget {
},
onTap: () async {
// open the file location
final didOpen =
await FileDownloader().openFile(
final didOpen = await FileDownloader().openFile(
task: record.task,
);
@ -229,9 +226,7 @@ class LibItemDownloadButton extends HookConsumerWidget {
onPressed: () {
appLogger.fine('Pressed download button');
ref
.read(downloadManagerProvider.notifier)
.queueAudioBookDownload(item);
ref.read(downloadManagerProvider.notifier).queueAudioBookDownload(item);
},
icon: const Icon(
Icons.download_rounded,
@ -250,10 +245,7 @@ class ItemCurrentlyInDownloadQueue extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final progress = ref
.watch(itemDownloadProgressProvider(item.id))
.valueOrNull
?.clamp(0.05, 1.0);
final progress = ref.watch(itemDownloadProgressProvider(item.id)).valueOrNull?.clamp(0.05, 1.0);
if (progress == 1) {
return AlreadyItemDownloadedButton(item: item);
@ -366,7 +358,7 @@ class DownloadSheet extends HookConsumerWidget {
// },
// ),
ListTile(
title: const Text('Delete'),
title: Text(S.of(context).delete),
leading: const Icon(
Icons.delete_rounded,
),
@ -377,28 +369,26 @@ class DownloadSheet extends HookConsumerWidget {
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Delete'),
title: Text(S.of(context).delete),
content: Text(
'Are you sure you want to delete ${item.media.metadata.title}?',
S.of(context).deleteDialog(item.media.metadata.title ?? ''),
),
actions: [
TextButton(
onPressed: () {
// delete the file
ref
.read(downloadManagerProvider.notifier)
.deleteDownloadedItem(
ref.read(downloadManagerProvider.notifier).deleteDownloadedItem(
item,
);
GoRouter.of(context).pop(true);
},
child: const Text('Yes'),
child: Text(S.of(context).yes),
),
TextButton(
onPressed: () {
GoRouter.of(context).pop(false);
},
child: const Text('No'),
child: Text(S.of(context).no),
),
],
);
@ -406,12 +396,12 @@ class DownloadSheet extends HookConsumerWidget {
);
if (wasDeleted ?? false) {
appLogger.fine('Deleted ${item.media.metadata.title}');
appLogger.fine(S.of(context).deleted(item.media.metadata.title ?? ''));
GoRouter.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Deleted ${item.media.metadata.title}',
S.of(context).deleted(item.media.metadata.title ?? ''),
),
),
);
@ -445,19 +435,19 @@ class _LibraryItemPlayButton extends HookConsumerWidget {
if (!isCurrentBookSetInPlayer) {
// either play or resume or listen again based on the progress
if (isBookCompleted) {
return 'Listen Again';
return S.of(context).homeListenAgain;
}
// if some progress is made, then 'continue listening'
if (userMediaProgress?.progress != null) {
return 'Continue Listening';
return S.of(context).homeContinueListening;
}
return 'Start Listening';
return S.of(context).homeStartListening;
} else {
// if book is set to player
if (isPlayingThisBook) {
return 'Pause';
return S.of(context).pause;
}
return 'Resume';
return S.of(context).resume;
}
}
@ -529,8 +519,7 @@ Future<void> libraryItemPlayButtonOnPressed({
appLogger.info('Setting the book ${book.libraryItemId}');
appLogger.info('Initial position: ${userMediaProgress?.currentTime}');
final downloadManager = ref.watch(simpleDownloadManagerProvider);
final libItem =
await ref.read(libraryItemProvider(book.libraryItemId).future);
final libItem = await ref.read(libraryItemProvider(book.libraryItemId).future);
final downloadedUris = await downloadManager.getDownloadedFilesUri(libItem);
setSourceFuture = player.setSourceAudiobook(
book,
@ -546,27 +535,23 @@ Future<void> libraryItemPlayButtonOnPressed({
}
}
// set the volume as this is the first time playing and dismissing causes the volume to go to 0
var bookPlayerSettings =
ref.read(bookSettingsProvider(book.libraryItemId)).playerSettings;
var bookPlayerSettings = ref.read(bookSettingsProvider(book.libraryItemId)).playerSettings;
var appPlayerSettings = ref.read(appSettingsProvider).playerSettings;
var configurePlayerForEveryBook =
appPlayerSettings.configurePlayerForEveryBook;
var configurePlayerForEveryBook = appPlayerSettings.configurePlayerForEveryBook;
await Future.wait([
setSourceFuture ?? Future.value(),
// set the volume
player.setVolume(
configurePlayerForEveryBook
? bookPlayerSettings.preferredDefaultVolume ??
appPlayerSettings.preferredDefaultVolume
? bookPlayerSettings.preferredDefaultVolume ?? appPlayerSettings.preferredDefaultVolume
: appPlayerSettings.preferredDefaultVolume,
),
// set the speed
player.setSpeed(
configurePlayerForEveryBook
? bookPlayerSettings.preferredDefaultSpeed ??
appPlayerSettings.preferredDefaultSpeed
? bookPlayerSettings.preferredDefaultSpeed ?? appPlayerSettings.preferredDefaultSpeed
: appPlayerSettings.preferredDefaultSpeed,
),
]);

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vaani/api/library_item_provider.dart';
import 'package:vaani/generated/l10n.dart';
import 'package:vaani/shared/extensions/model_conversions.dart';
class LibraryItemMetadata extends HookConsumerWidget {
@ -24,9 +25,8 @@ class LibraryItemMetadata extends HookConsumerWidget {
if (book == null) {
return null;
}
final duration = book.audioFiles
.map((e) => e.duration)
.reduce((value, element) => value + element);
final duration =
book.audioFiles.map((e) => e.duration).reduce((value, element) => value + element);
final hours = duration.inHours;
final minutes = duration.inMinutes.remainder(60);
return '${hours}h ${minutes}m';
@ -41,10 +41,12 @@ class LibraryItemMetadata extends HookConsumerWidget {
if (book == null) {
return null;
}
final size = book.audioFiles
.map((e) => e.metadata.size)
.reduce((value, element) => value + element);
return '${size / 1024 ~/ 1024} MB';
final size =
book.audioFiles.map((e) => e.metadata.size).reduce((value, element) => value + element);
if (size / 1024 / 1024 < 1024) {
return '${(size / 1024 / 1024).toStringAsFixed(2)} MB';
}
return '${(size / 1024 / 1024 / 1024).toStringAsFixed(2)} GB';
}
/// will return the codec and bitrate of the book
@ -64,21 +66,21 @@ class LibraryItemMetadata extends HookConsumerWidget {
// duration of the book
_MetadataItem(
title: switch (itemBookMetadata?.abridged) {
true => 'Abridged',
false => 'Unabridged',
_ => 'Length',
true => S.of(context).bookMetadataAbridged,
false => S.of(context).bookMetadataUnabridged,
_ => S.of(context).bookMetadataLength,
},
value: getDurationFormatted() ?? 'time is just a concept',
),
_MetadataItem(
title: 'Published',
title: S.of(context).bookMetadataPublished,
value: itemBookMetadata?.publishedDate ??
itemBookMetadata?.publishedYear ??
'Unknown',
S.of(context).unknown,
),
_MetadataItem(
title: getCodecAndBitrate() ?? 'Codec & Bitrate',
value: getSizeFormatted() ?? 'Unknown',
value: getSizeFormatted() ?? S.of(context).unknown,
),
];
return Padding(
@ -96,10 +98,7 @@ class LibraryItemMetadata extends HookConsumerWidget {
return VerticalDivider(
indent: 6,
endIndent: 6,
color: Theme.of(context)
.colorScheme
.onSurface
.withValues(alpha: 0.6),
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
);
},
),

View file

@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vaani/api/library_item_provider.dart';
import 'package:vaani/features/item_viewer/view/library_item_sliver_app_bar.dart';
import 'package:vaani/features/player/view/mini_player_bottom_padding.dart';
import 'package:vaani/generated/l10n.dart';
import 'package:vaani/router/models/library_item_extras.dart';
import 'package:vaani/shared/widgets/expandable_description.dart';
@ -27,8 +28,7 @@ class LibraryItemPage extends HookConsumerWidget {
static const double _showFabThreshold = 300.0;
@override
Widget build(BuildContext context, WidgetRef ref) {
final additionalItemData =
extra is LibraryItemExtras ? extra as LibraryItemExtras : null;
final additionalItemData = extra is LibraryItemExtras ? extra as LibraryItemExtras : null;
final scrollController = useScrollController();
final showFab = useState(false);
@ -150,8 +150,10 @@ class LibraryItemDescription extends HookConsumerWidget {
return const SizedBox();
}
return ExpandableDescription(
title: 'About the Book',
content: item.media.metadata.description ?? 'Sorry, no description found',
title: S.of(context).bookAbout,
content: item.media.metadata.description ?? S.of(context).bookAboutDefault,
readMoreText: S.of(context).readMore,
readLessText: S.of(context).readLess,
);
}
}
@ -166,10 +168,8 @@ double calculateWidth(
/// height ratio of the cover image to the available height
double maxHeightToUse = 0.25,
}) {
final availHeight =
min(constraints.maxHeight, MediaQuery.of(context).size.height);
final availWidth =
min(constraints.maxWidth, MediaQuery.of(context).size.width);
final availHeight = min(constraints.maxHeight, MediaQuery.of(context).size.height);
final availWidth = min(constraints.maxWidth, MediaQuery.of(context).size.width);
// make the width widthRatio of the available width
var width = availWidth * widthRatio;

View file

@ -31,6 +31,8 @@ class PlayerWhenMinimized extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final player = ref.watch(audiobookPlayerProvider);
final currentChapter = ref.watch(currentPlayingChapterProvider);
final vanishingPercentage = 1 - percentageMiniplayer;
final progress = useStream(player.slowPositionStream, initialData: Duration.zero);
@ -77,7 +79,7 @@ class PlayerWhenMinimized extends HookConsumerWidget {
children: [
// AutoScrollText(
Text(
bookMetaExpanded?.title ?? '',
'${bookMetaExpanded?.title ?? ''} - ${currentChapter?.title ?? ''}',
maxLines: 1, overflow: TextOverflow.ellipsis,
// velocity:
// const Velocity(pixelsPerSecond: Offset(16, 0)),