mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2026-02-16 14:29:35 +00:00
添加语言切换
This commit is contained in:
parent
e0deb84123
commit
e06c834d0e
21 changed files with 1416 additions and 281 deletions
|
|
@ -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,
|
||||
),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue