mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2026-02-16 06:19: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;
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
|
|
|
|||
72
lib/generated/intl/messages_all.dart
Normal file
72
lib/generated/intl/messages_all.dart
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
|
||||
// This is a library that looks up messages for specific locales by
|
||||
// delegating to the appropriate library.
|
||||
|
||||
// Ignore issues from commonly used lints in this file.
|
||||
// ignore_for_file:implementation_imports, file_names, unnecessary_new
|
||||
// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering
|
||||
// ignore_for_file:argument_type_not_assignable, invalid_assignment
|
||||
// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases
|
||||
// ignore_for_file:comment_references
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:intl/message_lookup_by_library.dart';
|
||||
import 'package:intl/src/intl_helpers.dart';
|
||||
|
||||
import 'messages_en.dart' as messages_en;
|
||||
import 'messages_zh.dart' as messages_zh;
|
||||
|
||||
typedef Future<dynamic> LibraryLoader();
|
||||
Map<String, LibraryLoader> _deferredLibraries = {
|
||||
'en': () => new SynchronousFuture(null),
|
||||
'zh': () => new SynchronousFuture(null),
|
||||
};
|
||||
|
||||
MessageLookupByLibrary? _findExact(String localeName) {
|
||||
switch (localeName) {
|
||||
case 'en':
|
||||
return messages_en.messages;
|
||||
case 'zh':
|
||||
return messages_zh.messages;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// User programs should call this before using [localeName] for messages.
|
||||
Future<bool> initializeMessages(String localeName) {
|
||||
var availableLocale = Intl.verifiedLocale(
|
||||
localeName,
|
||||
(locale) => _deferredLibraries[locale] != null,
|
||||
onFailure: (_) => null,
|
||||
);
|
||||
if (availableLocale == null) {
|
||||
return new SynchronousFuture(false);
|
||||
}
|
||||
var lib = _deferredLibraries[availableLocale];
|
||||
lib == null ? new SynchronousFuture(false) : lib();
|
||||
initializeInternalMessageLookup(() => new CompositeMessageLookup());
|
||||
messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
|
||||
return new SynchronousFuture(true);
|
||||
}
|
||||
|
||||
bool _messagesExistFor(String locale) {
|
||||
try {
|
||||
return _findExact(locale) != null;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
|
||||
var actualLocale = Intl.verifiedLocale(
|
||||
locale,
|
||||
_messagesExistFor,
|
||||
onFailure: (_) => null,
|
||||
);
|
||||
if (actualLocale == null) return null;
|
||||
return _findExact(actualLocale);
|
||||
}
|
||||
150
lib/generated/intl/messages_en.dart
Normal file
150
lib/generated/intl/messages_en.dart
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
|
||||
// This is a library that provides messages for a en locale. All the
|
||||
// messages from the main program should be duplicated here with the same
|
||||
// function name.
|
||||
|
||||
// Ignore issues from commonly used lints in this file.
|
||||
// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
|
||||
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
|
||||
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
|
||||
// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
|
||||
// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
|
||||
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:intl/message_lookup_by_library.dart';
|
||||
|
||||
final messages = new MessageLookup();
|
||||
|
||||
typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||
|
||||
class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'en';
|
||||
|
||||
static String m0(item) => "Are you sure you want to delete ${item}?";
|
||||
|
||||
static String m1(item) => "Deleted ${item}";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"appSettings": MessageLookupByLibrary.simpleMessage("App Settings"),
|
||||
"appearance": MessageLookupByLibrary.simpleMessage("Appearance"),
|
||||
"autoTurnOnSleepTimer": MessageLookupByLibrary.simpleMessage(
|
||||
"Auto Turn On Sleep Timer",
|
||||
),
|
||||
"automaticallyDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Automatically turn on the sleep timer based on the time of day",
|
||||
),
|
||||
"backup": MessageLookupByLibrary.simpleMessage("Backup"),
|
||||
"backupAndRestore": MessageLookupByLibrary.simpleMessage(
|
||||
"Backup and Restore",
|
||||
),
|
||||
"bookAbout": MessageLookupByLibrary.simpleMessage("About the Book"),
|
||||
"bookAboutDefault": MessageLookupByLibrary.simpleMessage(
|
||||
"Sorry, no description found",
|
||||
),
|
||||
"bookMetadataAbridged": MessageLookupByLibrary.simpleMessage("Abridged"),
|
||||
"bookMetadataLength": MessageLookupByLibrary.simpleMessage("Length"),
|
||||
"bookMetadataPublished": MessageLookupByLibrary.simpleMessage("Published"),
|
||||
"bookMetadataUnabridged": MessageLookupByLibrary.simpleMessage(
|
||||
"Unabridged",
|
||||
),
|
||||
"cancel": MessageLookupByLibrary.simpleMessage("Cancel"),
|
||||
"copyToClipboard": MessageLookupByLibrary.simpleMessage(
|
||||
"Copy to Clipboard",
|
||||
),
|
||||
"copyToClipboardDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Copy the app settings to the clipboard",
|
||||
),
|
||||
"copyToClipboardToast": MessageLookupByLibrary.simpleMessage(
|
||||
"Settings copied to clipboard",
|
||||
),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("Delete"),
|
||||
"deleteDialog": m0,
|
||||
"deleted": m1,
|
||||
"explore": MessageLookupByLibrary.simpleMessage("explore"),
|
||||
"exploreTooltip": MessageLookupByLibrary.simpleMessage(
|
||||
"Search and Explore",
|
||||
),
|
||||
"general": MessageLookupByLibrary.simpleMessage("General"),
|
||||
"home": MessageLookupByLibrary.simpleMessage("Home"),
|
||||
"homeContinueListening": MessageLookupByLibrary.simpleMessage(
|
||||
"Continue Listening",
|
||||
),
|
||||
"homeListenAgain": MessageLookupByLibrary.simpleMessage("Listen Again"),
|
||||
"homePageSettings": MessageLookupByLibrary.simpleMessage(
|
||||
"Home Page Settings",
|
||||
),
|
||||
"homePageSettingsDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Customize the home page",
|
||||
),
|
||||
"homeStartListening": MessageLookupByLibrary.simpleMessage(
|
||||
"Start Listening",
|
||||
),
|
||||
"language": MessageLookupByLibrary.simpleMessage("Language"),
|
||||
"languageDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Language switch",
|
||||
),
|
||||
"library": MessageLookupByLibrary.simpleMessage("Library"),
|
||||
"libraryTooltip": MessageLookupByLibrary.simpleMessage(
|
||||
"Browse your library",
|
||||
),
|
||||
"no": MessageLookupByLibrary.simpleMessage("No"),
|
||||
"notificationMediaPlayer": MessageLookupByLibrary.simpleMessage(
|
||||
"Notification Media Player",
|
||||
),
|
||||
"notificationMediaPlayerDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Customize the media player in notifications",
|
||||
),
|
||||
"ok": MessageLookupByLibrary.simpleMessage("OK"),
|
||||
"pause": MessageLookupByLibrary.simpleMessage("Pause"),
|
||||
"play": MessageLookupByLibrary.simpleMessage("Play"),
|
||||
"playerSettings": MessageLookupByLibrary.simpleMessage("Player Settings"),
|
||||
"playerSettingsDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Customize the player settings",
|
||||
),
|
||||
"readLess": MessageLookupByLibrary.simpleMessage("Read Less"),
|
||||
"readMore": MessageLookupByLibrary.simpleMessage("Read More"),
|
||||
"reset": MessageLookupByLibrary.simpleMessage("Reset"),
|
||||
"resetAppSettings": MessageLookupByLibrary.simpleMessage(
|
||||
"Reset App Settings",
|
||||
),
|
||||
"resetAppSettingsDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Reset the app settings to the default values",
|
||||
),
|
||||
"resetAppSettingsDialog": MessageLookupByLibrary.simpleMessage(
|
||||
"Are you sure you want to reset the app settings?",
|
||||
),
|
||||
"restore": MessageLookupByLibrary.simpleMessage("Restore"),
|
||||
"restoreBackup": MessageLookupByLibrary.simpleMessage("Restore Backup"),
|
||||
"restoreBackupHint": MessageLookupByLibrary.simpleMessage(
|
||||
"Paste the backup here",
|
||||
),
|
||||
"restoreBackupInvalid": MessageLookupByLibrary.simpleMessage(
|
||||
"Invalid backup",
|
||||
),
|
||||
"restoreBackupSuccess": MessageLookupByLibrary.simpleMessage(
|
||||
"Settings restored",
|
||||
),
|
||||
"restoreBackupValidator": MessageLookupByLibrary.simpleMessage(
|
||||
"Please paste the backup here",
|
||||
),
|
||||
"restoreDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Restore the app settings from the backup",
|
||||
),
|
||||
"resume": MessageLookupByLibrary.simpleMessage("Resume"),
|
||||
"shakeDetector": MessageLookupByLibrary.simpleMessage("Shake Detector"),
|
||||
"shakeDetectorDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Customize the shake detector settings",
|
||||
),
|
||||
"themeSettings": MessageLookupByLibrary.simpleMessage("Theme Settings"),
|
||||
"themeSettingsDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"Customize the app theme",
|
||||
),
|
||||
"unknown": MessageLookupByLibrary.simpleMessage("Unknown"),
|
||||
"yes": MessageLookupByLibrary.simpleMessage("Yes"),
|
||||
"you": MessageLookupByLibrary.simpleMessage("You"),
|
||||
"youTooltip": MessageLookupByLibrary.simpleMessage(
|
||||
"Your Profile and Settings",
|
||||
),
|
||||
};
|
||||
}
|
||||
104
lib/generated/intl/messages_zh.dart
Normal file
104
lib/generated/intl/messages_zh.dart
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
|
||||
// This is a library that provides messages for a zh locale. All the
|
||||
// messages from the main program should be duplicated here with the same
|
||||
// function name.
|
||||
|
||||
// Ignore issues from commonly used lints in this file.
|
||||
// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
|
||||
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
|
||||
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
|
||||
// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
|
||||
// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
|
||||
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:intl/message_lookup_by_library.dart';
|
||||
|
||||
final messages = new MessageLookup();
|
||||
|
||||
typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||
|
||||
class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'zh';
|
||||
|
||||
static String m0(item) => "确定要删除 ${item} 吗?";
|
||||
|
||||
static String m1(item) => "已删除 ${item}";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"appSettings": MessageLookupByLibrary.simpleMessage("应用设置"),
|
||||
"appearance": MessageLookupByLibrary.simpleMessage("外观"),
|
||||
"autoTurnOnSleepTimer": MessageLookupByLibrary.simpleMessage("自动开启睡眠定时器"),
|
||||
"automaticallyDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"根据一天中的时间自动打开睡眠定时器",
|
||||
),
|
||||
"backup": MessageLookupByLibrary.simpleMessage("备份"),
|
||||
"backupAndRestore": MessageLookupByLibrary.simpleMessage("备份与恢复"),
|
||||
"bookAbout": MessageLookupByLibrary.simpleMessage("关于本书"),
|
||||
"bookAboutDefault": MessageLookupByLibrary.simpleMessage("抱歉,找不到描述"),
|
||||
"cancel": MessageLookupByLibrary.simpleMessage("取消"),
|
||||
"copyToClipboard": MessageLookupByLibrary.simpleMessage("复制到剪贴板"),
|
||||
"copyToClipboardDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"将应用程序设置复制到剪贴板",
|
||||
),
|
||||
"copyToClipboardToast": MessageLookupByLibrary.simpleMessage("设置已复制到剪贴板"),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("Delete"),
|
||||
"deleteDialog": m0,
|
||||
"deleted": m1,
|
||||
"explore": MessageLookupByLibrary.simpleMessage("探索"),
|
||||
"exploreTooltip": MessageLookupByLibrary.simpleMessage("搜索和探索"),
|
||||
"general": MessageLookupByLibrary.simpleMessage("通用"),
|
||||
"home": MessageLookupByLibrary.simpleMessage("首页"),
|
||||
"homeContinueListening": MessageLookupByLibrary.simpleMessage("继续收听"),
|
||||
"homeListenAgain": MessageLookupByLibrary.simpleMessage("再听一遍"),
|
||||
"homePageSettings": MessageLookupByLibrary.simpleMessage("主页设置"),
|
||||
"homePageSettingsDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"自定义主页",
|
||||
),
|
||||
"homeStartListening": MessageLookupByLibrary.simpleMessage("开始收听"),
|
||||
"language": MessageLookupByLibrary.simpleMessage("语言"),
|
||||
"languageDescription": MessageLookupByLibrary.simpleMessage("语言切换"),
|
||||
"library": MessageLookupByLibrary.simpleMessage("媒体库"),
|
||||
"libraryTooltip": MessageLookupByLibrary.simpleMessage("浏览您的媒体库"),
|
||||
"no": MessageLookupByLibrary.simpleMessage("否"),
|
||||
"notificationMediaPlayer": MessageLookupByLibrary.simpleMessage("通知媒体播放器"),
|
||||
"notificationMediaPlayerDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"在通知中自定义媒体播放器",
|
||||
),
|
||||
"ok": MessageLookupByLibrary.simpleMessage("确定"),
|
||||
"pause": MessageLookupByLibrary.simpleMessage("暂停"),
|
||||
"play": MessageLookupByLibrary.simpleMessage("播放"),
|
||||
"playerSettings": MessageLookupByLibrary.simpleMessage("播放器设置"),
|
||||
"playerSettingsDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"自定义播放器设置",
|
||||
),
|
||||
"readLess": MessageLookupByLibrary.simpleMessage("折叠"),
|
||||
"readMore": MessageLookupByLibrary.simpleMessage("展开"),
|
||||
"reset": MessageLookupByLibrary.simpleMessage("重置"),
|
||||
"resetAppSettings": MessageLookupByLibrary.simpleMessage("重置应用程序设置"),
|
||||
"resetAppSettingsDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"将应用程序设置重置为默认值",
|
||||
),
|
||||
"resetAppSettingsDialog": MessageLookupByLibrary.simpleMessage(
|
||||
"您确定要重置应用程序设置吗?",
|
||||
),
|
||||
"restore": MessageLookupByLibrary.simpleMessage("恢复"),
|
||||
"restoreBackup": MessageLookupByLibrary.simpleMessage("恢复备份"),
|
||||
"restoreBackupHint": MessageLookupByLibrary.simpleMessage("将备份粘贴到此处"),
|
||||
"restoreBackupInvalid": MessageLookupByLibrary.simpleMessage("无效备份"),
|
||||
"restoreBackupSuccess": MessageLookupByLibrary.simpleMessage("设置已恢复"),
|
||||
"restoreBackupValidator": MessageLookupByLibrary.simpleMessage("请将备份粘贴到此处"),
|
||||
"restoreDescription": MessageLookupByLibrary.simpleMessage("从备份中还原应用程序设置"),
|
||||
"resume": MessageLookupByLibrary.simpleMessage("继续"),
|
||||
"shakeDetector": MessageLookupByLibrary.simpleMessage("抖动检测器"),
|
||||
"shakeDetectorDescription": MessageLookupByLibrary.simpleMessage(
|
||||
"自定义抖动检测器设置",
|
||||
),
|
||||
"themeSettings": MessageLookupByLibrary.simpleMessage("主题设置"),
|
||||
"themeSettingsDescription": MessageLookupByLibrary.simpleMessage("自定义应用主题"),
|
||||
"unknown": MessageLookupByLibrary.simpleMessage("未知"),
|
||||
"yes": MessageLookupByLibrary.simpleMessage("是"),
|
||||
"you": MessageLookupByLibrary.simpleMessage("我的"),
|
||||
"youTooltip": MessageLookupByLibrary.simpleMessage("您的个人资料和设置"),
|
||||
};
|
||||
}
|
||||
598
lib/generated/l10n.dart
Normal file
598
lib/generated/l10n.dart
Normal file
|
|
@ -0,0 +1,598 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'intl/messages_all.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// Generator: Flutter Intl IDE plugin
|
||||
// Made by Localizely
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars
|
||||
// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each
|
||||
// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes
|
||||
|
||||
class S {
|
||||
S();
|
||||
|
||||
static S? _current;
|
||||
|
||||
static S get current {
|
||||
assert(
|
||||
_current != null,
|
||||
'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.',
|
||||
);
|
||||
return _current!;
|
||||
}
|
||||
|
||||
static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
|
||||
|
||||
static Future<S> load(Locale locale) {
|
||||
final name = (locale.countryCode?.isEmpty ?? false)
|
||||
? locale.languageCode
|
||||
: locale.toString();
|
||||
final localeName = Intl.canonicalizedLocale(name);
|
||||
return initializeMessages(localeName).then((_) {
|
||||
Intl.defaultLocale = localeName;
|
||||
final instance = S();
|
||||
S._current = instance;
|
||||
|
||||
return instance;
|
||||
});
|
||||
}
|
||||
|
||||
static S of(BuildContext context) {
|
||||
final instance = S.maybeOf(context);
|
||||
assert(
|
||||
instance != null,
|
||||
'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?',
|
||||
);
|
||||
return instance!;
|
||||
}
|
||||
|
||||
static S? maybeOf(BuildContext context) {
|
||||
return Localizations.of<S>(context, S);
|
||||
}
|
||||
|
||||
/// `Yes`
|
||||
String get yes {
|
||||
return Intl.message('Yes', name: 'yes', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `No`
|
||||
String get no {
|
||||
return Intl.message('No', name: 'no', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `OK`
|
||||
String get ok {
|
||||
return Intl.message('OK', name: 'ok', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Cancel`
|
||||
String get cancel {
|
||||
return Intl.message('Cancel', name: 'cancel', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Reset`
|
||||
String get reset {
|
||||
return Intl.message('Reset', name: 'reset', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Delete`
|
||||
String get delete {
|
||||
return Intl.message('Delete', name: 'delete', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Are you sure you want to delete {item}?`
|
||||
String deleteDialog(String item) {
|
||||
return Intl.message(
|
||||
'Are you sure you want to delete $item?',
|
||||
name: 'deleteDialog',
|
||||
desc: '',
|
||||
args: [item],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Deleted {item}`
|
||||
String deleted(String item) {
|
||||
return Intl.message(
|
||||
'Deleted $item',
|
||||
name: 'deleted',
|
||||
desc: '已删除 {}',
|
||||
args: [item],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Pause`
|
||||
String get pause {
|
||||
return Intl.message('Pause', name: 'pause', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Play`
|
||||
String get play {
|
||||
return Intl.message('Play', name: 'play', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Resume`
|
||||
String get resume {
|
||||
return Intl.message('Resume', name: 'resume', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Unknown`
|
||||
String get unknown {
|
||||
return Intl.message('Unknown', name: 'unknown', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Read More`
|
||||
String get readMore {
|
||||
return Intl.message('Read More', name: 'readMore', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Read Less`
|
||||
String get readLess {
|
||||
return Intl.message('Read Less', name: 'readLess', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Home`
|
||||
String get home {
|
||||
return Intl.message('Home', name: 'home', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Listen Again`
|
||||
String get homeListenAgain {
|
||||
return Intl.message(
|
||||
'Listen Again',
|
||||
name: 'homeListenAgain',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Continue Listening`
|
||||
String get homeContinueListening {
|
||||
return Intl.message(
|
||||
'Continue Listening',
|
||||
name: 'homeContinueListening',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Start Listening`
|
||||
String get homeStartListening {
|
||||
return Intl.message(
|
||||
'Start Listening',
|
||||
name: 'homeStartListening',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `About the Book`
|
||||
String get bookAbout {
|
||||
return Intl.message(
|
||||
'About the Book',
|
||||
name: 'bookAbout',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Sorry, no description found`
|
||||
String get bookAboutDefault {
|
||||
return Intl.message(
|
||||
'Sorry, no description found',
|
||||
name: 'bookAboutDefault',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Abridged`
|
||||
String get bookMetadataAbridged {
|
||||
return Intl.message(
|
||||
'Abridged',
|
||||
name: 'bookMetadataAbridged',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Unabridged`
|
||||
String get bookMetadataUnabridged {
|
||||
return Intl.message(
|
||||
'Unabridged',
|
||||
name: 'bookMetadataUnabridged',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Length`
|
||||
String get bookMetadataLength {
|
||||
return Intl.message(
|
||||
'Length',
|
||||
name: 'bookMetadataLength',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Published`
|
||||
String get bookMetadataPublished {
|
||||
return Intl.message(
|
||||
'Published',
|
||||
name: 'bookMetadataPublished',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Library`
|
||||
String get library {
|
||||
return Intl.message('Library', name: 'library', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Browse your library`
|
||||
String get libraryTooltip {
|
||||
return Intl.message(
|
||||
'Browse your library',
|
||||
name: 'libraryTooltip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `explore`
|
||||
String get explore {
|
||||
return Intl.message('explore', name: 'explore', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Search and Explore`
|
||||
String get exploreTooltip {
|
||||
return Intl.message(
|
||||
'Search and Explore',
|
||||
name: 'exploreTooltip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `You`
|
||||
String get you {
|
||||
return Intl.message('You', name: 'you', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Your Profile and Settings`
|
||||
String get youTooltip {
|
||||
return Intl.message(
|
||||
'Your Profile and Settings',
|
||||
name: 'youTooltip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `App Settings`
|
||||
String get appSettings {
|
||||
return Intl.message(
|
||||
'App Settings',
|
||||
name: 'appSettings',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `General`
|
||||
String get general {
|
||||
return Intl.message('General', name: 'general', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Language`
|
||||
String get language {
|
||||
return Intl.message('Language', name: 'language', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Language switch`
|
||||
String get languageDescription {
|
||||
return Intl.message(
|
||||
'Language switch',
|
||||
name: 'languageDescription',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Player Settings`
|
||||
String get playerSettings {
|
||||
return Intl.message(
|
||||
'Player Settings',
|
||||
name: 'playerSettings',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Customize the player settings`
|
||||
String get playerSettingsDescription {
|
||||
return Intl.message(
|
||||
'Customize the player settings',
|
||||
name: 'playerSettingsDescription',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Auto Turn On Sleep Timer`
|
||||
String get autoTurnOnSleepTimer {
|
||||
return Intl.message(
|
||||
'Auto Turn On Sleep Timer',
|
||||
name: 'autoTurnOnSleepTimer',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Automatically turn on the sleep timer based on the time of day`
|
||||
String get automaticallyDescription {
|
||||
return Intl.message(
|
||||
'Automatically turn on the sleep timer based on the time of day',
|
||||
name: 'automaticallyDescription',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Shake Detector`
|
||||
String get shakeDetector {
|
||||
return Intl.message(
|
||||
'Shake Detector',
|
||||
name: 'shakeDetector',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Customize the shake detector settings`
|
||||
String get shakeDetectorDescription {
|
||||
return Intl.message(
|
||||
'Customize the shake detector settings',
|
||||
name: 'shakeDetectorDescription',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Appearance`
|
||||
String get appearance {
|
||||
return Intl.message('Appearance', name: 'appearance', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Theme Settings`
|
||||
String get themeSettings {
|
||||
return Intl.message(
|
||||
'Theme Settings',
|
||||
name: 'themeSettings',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Customize the app theme`
|
||||
String get themeSettingsDescription {
|
||||
return Intl.message(
|
||||
'Customize the app theme',
|
||||
name: 'themeSettingsDescription',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Notification Media Player`
|
||||
String get notificationMediaPlayer {
|
||||
return Intl.message(
|
||||
'Notification Media Player',
|
||||
name: 'notificationMediaPlayer',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Customize the media player in notifications`
|
||||
String get notificationMediaPlayerDescription {
|
||||
return Intl.message(
|
||||
'Customize the media player in notifications',
|
||||
name: 'notificationMediaPlayerDescription',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Home Page Settings`
|
||||
String get homePageSettings {
|
||||
return Intl.message(
|
||||
'Home Page Settings',
|
||||
name: 'homePageSettings',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Customize the home page`
|
||||
String get homePageSettingsDescription {
|
||||
return Intl.message(
|
||||
'Customize the home page',
|
||||
name: 'homePageSettingsDescription',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Backup and Restore`
|
||||
String get backupAndRestore {
|
||||
return Intl.message(
|
||||
'Backup and Restore',
|
||||
name: 'backupAndRestore',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Copy to Clipboard`
|
||||
String get copyToClipboard {
|
||||
return Intl.message(
|
||||
'Copy to Clipboard',
|
||||
name: 'copyToClipboard',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Copy the app settings to the clipboard`
|
||||
String get copyToClipboardDescription {
|
||||
return Intl.message(
|
||||
'Copy the app settings to the clipboard',
|
||||
name: 'copyToClipboardDescription',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Settings copied to clipboard`
|
||||
String get copyToClipboardToast {
|
||||
return Intl.message(
|
||||
'Settings copied to clipboard',
|
||||
name: 'copyToClipboardToast',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Restore`
|
||||
String get restore {
|
||||
return Intl.message('Restore', name: 'restore', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Restore the app settings from the backup`
|
||||
String get restoreDescription {
|
||||
return Intl.message(
|
||||
'Restore the app settings from the backup',
|
||||
name: 'restoreDescription',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Restore Backup`
|
||||
String get restoreBackup {
|
||||
return Intl.message(
|
||||
'Restore Backup',
|
||||
name: 'restoreBackup',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Backup`
|
||||
String get backup {
|
||||
return Intl.message('Backup', name: 'backup', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Paste the backup here`
|
||||
String get restoreBackupHint {
|
||||
return Intl.message(
|
||||
'Paste the backup here',
|
||||
name: 'restoreBackupHint',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Please paste the backup here`
|
||||
String get restoreBackupValidator {
|
||||
return Intl.message(
|
||||
'Please paste the backup here',
|
||||
name: 'restoreBackupValidator',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Invalid backup`
|
||||
String get restoreBackupInvalid {
|
||||
return Intl.message(
|
||||
'Invalid backup',
|
||||
name: 'restoreBackupInvalid',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Settings restored`
|
||||
String get restoreBackupSuccess {
|
||||
return Intl.message(
|
||||
'Settings restored',
|
||||
name: 'restoreBackupSuccess',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Reset App Settings`
|
||||
String get resetAppSettings {
|
||||
return Intl.message(
|
||||
'Reset App Settings',
|
||||
name: 'resetAppSettings',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Reset the app settings to the default values`
|
||||
String get resetAppSettingsDescription {
|
||||
return Intl.message(
|
||||
'Reset the app settings to the default values',
|
||||
name: 'resetAppSettingsDescription',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Are you sure you want to reset the app settings?`
|
||||
String get resetAppSettingsDialog {
|
||||
return Intl.message(
|
||||
'Are you sure you want to reset the app settings?',
|
||||
name: 'resetAppSettingsDialog',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||
const AppLocalizationDelegate();
|
||||
|
||||
List<Locale> get supportedLocales {
|
||||
return const <Locale>[
|
||||
Locale.fromSubtags(languageCode: 'en'),
|
||||
Locale.fromSubtags(languageCode: 'zh'),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
bool isSupported(Locale locale) => _isSupported(locale);
|
||||
@override
|
||||
Future<S> load(Locale locale) => S.load(locale);
|
||||
@override
|
||||
bool shouldReload(AppLocalizationDelegate old) => false;
|
||||
|
||||
bool _isSupported(Locale locale) {
|
||||
for (var supportedLocale in supportedLocales) {
|
||||
if (supportedLocale.languageCode == locale.languageCode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
87
lib/l10n/intl_en.arb
Normal file
87
lib/l10n/intl_en.arb
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
{
|
||||
"@@locale": "en",
|
||||
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"ok": "OK",
|
||||
"cancel": "Cancel",
|
||||
"reset": "Reset",
|
||||
"delete": "Delete",
|
||||
"deleteDialog": "Are you sure you want to delete {item}?",
|
||||
"@deleteDialog": {
|
||||
"placeholders": {
|
||||
"item": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deleted": "Deleted {item}",
|
||||
"@deleted": {
|
||||
"description": "已删除 {}",
|
||||
"placeholders": {
|
||||
"item": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"pause": "Pause",
|
||||
"play": "Play",
|
||||
"resume": "Resume",
|
||||
"unknown": "Unknown",
|
||||
"readMore": "Read More",
|
||||
"readLess": "Read Less",
|
||||
|
||||
"home": "Home",
|
||||
"homeListenAgain": "Listen Again",
|
||||
"homeContinueListening": "Continue Listening",
|
||||
"homeStartListening": "Start Listening",
|
||||
"bookAbout": "About the Book",
|
||||
"bookAboutDefault": "Sorry, no description found",
|
||||
"bookMetadataAbridged": "Abridged",
|
||||
"bookMetadataUnabridged": "Unabridged",
|
||||
"bookMetadataLength": "Length",
|
||||
"bookMetadataPublished": "Published",
|
||||
|
||||
"library": "Library",
|
||||
"libraryTooltip": "Browse your library",
|
||||
"explore": "explore",
|
||||
"exploreTooltip": "Search and Explore",
|
||||
"you": "You",
|
||||
"youTooltip": "Your Profile and Settings",
|
||||
|
||||
"appSettings": "App Settings",
|
||||
"general": "General",
|
||||
"language": "Language",
|
||||
"languageDescription": "Language switch",
|
||||
"playerSettings": "Player Settings",
|
||||
"playerSettingsDescription": "Customize the player settings",
|
||||
"autoTurnOnSleepTimer": "Auto Turn On Sleep Timer",
|
||||
"automaticallyDescription": "Automatically turn on the sleep timer based on the time of day",
|
||||
"shakeDetector": "Shake Detector",
|
||||
"shakeDetectorDescription": "Customize the shake detector settings",
|
||||
"appearance": "Appearance",
|
||||
"themeSettings": "Theme Settings",
|
||||
"themeSettingsDescription": "Customize the app theme",
|
||||
"notificationMediaPlayer": "Notification Media Player",
|
||||
"notificationMediaPlayerDescription": "Customize the media player in notifications",
|
||||
"homePageSettings": "Home Page Settings",
|
||||
"homePageSettingsDescription": "Customize the home page",
|
||||
"backupAndRestore": "Backup and Restore",
|
||||
"copyToClipboard": "Copy to Clipboard",
|
||||
"copyToClipboardDescription": "Copy the app settings to the clipboard",
|
||||
"copyToClipboardToast": "Settings copied to clipboard",
|
||||
"restore": "Restore",
|
||||
"restoreDescription": "Restore the app settings from the backup",
|
||||
"restoreBackup": "Restore Backup",
|
||||
"backup": "Backup",
|
||||
"restoreBackupHint": "Paste the backup here",
|
||||
"restoreBackupValidator": "Please paste the backup here",
|
||||
"restoreBackupInvalid": "Invalid backup",
|
||||
"restoreBackupSuccess": "Settings restored",
|
||||
|
||||
"resetAppSettings": "Reset App Settings",
|
||||
"resetAppSettingsDescription": "Reset the app settings to the default values",
|
||||
"resetAppSettingsDialog": "Are you sure you want to reset the app settings?"
|
||||
|
||||
}
|
||||
84
lib/l10n/intl_zh.arb
Normal file
84
lib/l10n/intl_zh.arb
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
{
|
||||
"@@locale": "zh",
|
||||
|
||||
"yes": "是",
|
||||
"no": "否",
|
||||
"ok": "确定",
|
||||
"cancel": "取消",
|
||||
"reset": "重置",
|
||||
"delete": "Delete",
|
||||
"deleteDialog": "确定要删除 {item} 吗?",
|
||||
"@deleteDialog": {
|
||||
"placeholders": {
|
||||
"item": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deleted": "已删除 {item}",
|
||||
"@deleted": {
|
||||
"description": "已删除 {}",
|
||||
"placeholders": {
|
||||
"item": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"pause": "暂停",
|
||||
"play": "播放",
|
||||
"resume": "继续",
|
||||
"unknown": "未知",
|
||||
"readMore": "展开",
|
||||
"readLess": "折叠",
|
||||
|
||||
"home": "首页",
|
||||
"homeListenAgain": "再听一遍",
|
||||
"homeContinueListening": "继续收听",
|
||||
"homeStartListening": "开始收听",
|
||||
"bookAbout": "关于本书",
|
||||
"bookAboutDefault": "抱歉,找不到描述",
|
||||
|
||||
"library": "媒体库",
|
||||
"libraryTooltip": "浏览您的媒体库",
|
||||
"explore": "探索",
|
||||
"exploreTooltip": "搜索和探索",
|
||||
"you": "我的",
|
||||
"youTooltip": "您的个人资料和设置",
|
||||
|
||||
"appSettings": "应用设置",
|
||||
"general": "通用",
|
||||
"language": "语言",
|
||||
"languageDescription": "语言切换",
|
||||
"playerSettings": "播放器设置",
|
||||
"playerSettingsDescription": "自定义播放器设置",
|
||||
"autoTurnOnSleepTimer": "自动开启睡眠定时器",
|
||||
"automaticallyDescription": "根据一天中的时间自动打开睡眠定时器",
|
||||
|
||||
"shakeDetector": "抖动检测器",
|
||||
"shakeDetectorDescription": "自定义抖动检测器设置",
|
||||
"appearance": "外观",
|
||||
"themeSettings": "主题设置",
|
||||
"themeSettingsDescription": "自定义应用主题",
|
||||
"notificationMediaPlayer": "通知媒体播放器",
|
||||
"notificationMediaPlayerDescription": "在通知中自定义媒体播放器",
|
||||
"homePageSettings": "主页设置",
|
||||
"homePageSettingsDescription": "自定义主页",
|
||||
"backupAndRestore": "备份与恢复",
|
||||
"copyToClipboard": "复制到剪贴板",
|
||||
"copyToClipboardDescription": "将应用程序设置复制到剪贴板",
|
||||
"copyToClipboardToast": "设置已复制到剪贴板",
|
||||
"restore": "恢复",
|
||||
"restoreDescription": "从备份中还原应用程序设置",
|
||||
"restoreBackup": "恢复备份",
|
||||
"backup": "备份",
|
||||
"restoreBackupHint": "将备份粘贴到此处",
|
||||
"restoreBackupValidator": "请将备份粘贴到此处",
|
||||
"restoreBackupInvalid": "无效备份",
|
||||
"restoreBackupSuccess": "设置已恢复",
|
||||
|
||||
"resetAppSettings": "重置应用程序设置",
|
||||
"resetAppSettingsDescription": "将应用程序设置重置为默认值",
|
||||
"resetAppSettingsDialog": "您确定要重置应用程序设置吗?"
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:vaani/api/server_provider.dart';
|
||||
|
|
@ -12,6 +13,7 @@ import 'package:vaani/features/player/providers/audiobook_player.dart'
|
|||
show audiobookPlayerProvider, simpleAudiobookPlayerProvider;
|
||||
import 'package:vaani/features/shake_detection/providers/shake_detector.dart';
|
||||
import 'package:vaani/features/sleep_timer/providers/sleep_timer_provider.dart';
|
||||
import 'package:vaani/generated/l10n.dart';
|
||||
import 'package:vaani/router/router.dart';
|
||||
import 'package:vaani/settings/api_settings_provider.dart';
|
||||
import 'package:vaani/settings/app_settings_provider.dart';
|
||||
|
|
@ -126,6 +128,15 @@ class MyApp extends ConsumerWidget {
|
|||
try {
|
||||
return MaterialApp.router(
|
||||
// debugShowCheckedModeBanner: false,
|
||||
locale: Locale(appSettings.language),
|
||||
localizationsDelegates: [
|
||||
// 以下是其他代理
|
||||
S.delegate, //String 资源的 本地化
|
||||
GlobalMaterialLocalizations.delegate, //Material Widgets 的本地化
|
||||
GlobalWidgetsLocalizations.delegate, //基础 Widgets 的本地化
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: S.delegate.supportedLocales,
|
||||
theme: appThemeLight,
|
||||
darkTheme: appThemeDark,
|
||||
themeMode: themeSettings.themeMode,
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@ import 'transitions/slide.dart';
|
|||
|
||||
part 'constants.dart';
|
||||
|
||||
final GlobalKey<NavigatorState> rootNavigatorKey =
|
||||
GlobalKey<NavigatorState>(debugLabel: 'root');
|
||||
final GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'root');
|
||||
final GlobalKey<NavigatorState> sectionHomeNavigatorKey =
|
||||
GlobalKey<NavigatorState>(debugLabel: 'HomeNavigator');
|
||||
|
||||
|
|
@ -94,10 +93,8 @@ class MyAppRouter {
|
|||
// itemId: itemId, extra: state.extra);
|
||||
// },
|
||||
pageBuilder: (context, state) {
|
||||
final itemId = state
|
||||
.pathParameters[Routes.libraryItem.pathParamName]!;
|
||||
final child =
|
||||
LibraryItemPage(itemId: itemId, extra: state.extra);
|
||||
final itemId = state.pathParameters[Routes.libraryItem.pathParamName]!;
|
||||
final child = LibraryItemPage(itemId: itemId, extra: state.extra);
|
||||
return buildPageWithDefaultTransition(
|
||||
context: context,
|
||||
state: state,
|
||||
|
|
@ -204,8 +201,7 @@ class MyAppRouter {
|
|||
GoRoute(
|
||||
path: Routes.playerSettings.pathName,
|
||||
name: Routes.playerSettings.name,
|
||||
pageBuilder:
|
||||
defaultPageBuilder(const PlayerSettingsPage()),
|
||||
pageBuilder: defaultPageBuilder(const PlayerSettingsPage()),
|
||||
),
|
||||
GoRoute(
|
||||
path: Routes.shakeDetectorSettings.pathName,
|
||||
|
|
@ -253,8 +249,7 @@ class MyAppRouter {
|
|||
final stateParam = state.uri.queryParameters['state'];
|
||||
appLogger.fine('deep linking callback: code: $code, state: $stateParam');
|
||||
|
||||
var callbackPage =
|
||||
CallbackPage(code: code, state: stateParam, key: ValueKey(stateParam));
|
||||
var callbackPage = CallbackPage(code: code, state: stateParam, key: ValueKey(stateParam));
|
||||
return buildPageWithDefaultTransition(
|
||||
context: context,
|
||||
state: state,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import 'package:vaani/features/player/providers/player_form.dart';
|
|||
import 'package:vaani/features/player/view/audiobook_player.dart';
|
||||
import 'package:vaani/features/player/view/player_when_expanded.dart';
|
||||
import 'package:vaani/features/you/view/widgets/library_switch_chip.dart';
|
||||
import 'package:vaani/generated/l10n.dart';
|
||||
import 'package:vaani/main.dart';
|
||||
import 'package:vaani/router/router.dart';
|
||||
import 'package:vaani/shared/icons/abs_icons.dart' show AbsIcons;
|
||||
|
|
@ -36,7 +37,7 @@ class ScaffoldWithNavBar extends HookConsumerWidget {
|
|||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final playerProgress = ref.watch(playerHeightProvider);
|
||||
final isMobile = Platform.isAndroid || Platform.isIOS;
|
||||
final isMobile = Platform.isFuchsia || Platform.isAndroid || Platform.isIOS;
|
||||
onBackButtonPressed() async {
|
||||
final isPlayerExpanded = playerProgress != playerMinHeight;
|
||||
|
||||
|
|
@ -90,75 +91,72 @@ class ScaffoldWithNavBar extends HookConsumerWidget {
|
|||
return BackButtonListener(
|
||||
onBackButtonPressed: onBackButtonPressed,
|
||||
child: Scaffold(
|
||||
body: isMobile
|
||||
? Stack(
|
||||
children: [
|
||||
navigationShell,
|
||||
const AudiobookPlayer(),
|
||||
],
|
||||
)
|
||||
: buildNavLeft(context, ref),
|
||||
body: Stack(
|
||||
children: [
|
||||
isMobile ? navigationShell : buildNavLeft(context, ref),
|
||||
const AudiobookPlayer(),
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: isMobile ? buildNavBottom(context, ref) : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildNavLeft(BuildContext context, WidgetRef ref) {
|
||||
return Row(
|
||||
children: [
|
||||
SafeArea(
|
||||
child: NavigationRail(
|
||||
minWidth: 60,
|
||||
minExtendedWidth: 120,
|
||||
extended: MediaQuery.of(context).size.width > 640,
|
||||
// extended: false,
|
||||
destinations: _navigationItems.map((item) {
|
||||
final isDestinationLibrary = item.name == 'Library';
|
||||
var currentLibrary = ref.watch(currentLibraryProvider).valueOrNull;
|
||||
final libraryIcon = AbsIcons.getIconByName(
|
||||
currentLibrary?.icon,
|
||||
);
|
||||
final destinationWidget = NavigationRailDestination(
|
||||
icon: Icon(
|
||||
isDestinationLibrary ? libraryIcon ?? item.icon : item.icon,
|
||||
),
|
||||
selectedIcon: Icon(
|
||||
isDestinationLibrary ? libraryIcon ?? item.activeIcon : item.activeIcon,
|
||||
),
|
||||
label: Text(isDestinationLibrary ? currentLibrary?.name ?? item.name : item.name),
|
||||
// tooltip: item.tooltip,
|
||||
);
|
||||
// if (isDestinationLibrary) {
|
||||
// return GestureDetector(
|
||||
// onSecondaryTap: () => showLibrarySwitcher(context, ref),
|
||||
// onDoubleTap: () => showLibrarySwitcher(context, ref),
|
||||
// child:
|
||||
// destinationWidget, // Wrap the actual NavigationDestination
|
||||
// );
|
||||
// } else {
|
||||
// // Return the unwrapped destination for other items
|
||||
// return destinationWidget;
|
||||
// }
|
||||
return destinationWidget;
|
||||
// return NavigationRailDestination(icon: Icon(nav.icon), label: Text(nav.name));
|
||||
}).toList(),
|
||||
selectedIndex: navigationShell.currentIndex,
|
||||
onDestinationSelected: (int index) {
|
||||
print(index);
|
||||
_onTap(context, index, ref);
|
||||
},
|
||||
final isPlayerActive = ref.watch(isPlayerActiveProvider);
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: isPlayerActive ? playerMinHeight : 0),
|
||||
child: Row(
|
||||
children: [
|
||||
SafeArea(
|
||||
child: NavigationRail(
|
||||
minWidth: 60,
|
||||
minExtendedWidth: 120,
|
||||
extended: MediaQuery.of(context).size.width > 640,
|
||||
// extended: false,
|
||||
destinations: _navigationItems(context).map((item) {
|
||||
final isDestinationLibrary = item.name == S.of(context).library;
|
||||
var currentLibrary = ref.watch(currentLibraryProvider).valueOrNull;
|
||||
final libraryIcon = AbsIcons.getIconByName(
|
||||
currentLibrary?.icon,
|
||||
);
|
||||
final destinationWidget = NavigationRailDestination(
|
||||
icon: Icon(
|
||||
isDestinationLibrary ? libraryIcon ?? item.icon : item.icon,
|
||||
),
|
||||
selectedIcon: Icon(
|
||||
isDestinationLibrary ? libraryIcon ?? item.activeIcon : item.activeIcon,
|
||||
),
|
||||
label: Text(isDestinationLibrary ? currentLibrary?.name ?? item.name : item.name),
|
||||
// tooltip: item.tooltip,
|
||||
);
|
||||
// if (isDestinationLibrary) {
|
||||
// return GestureDetector(
|
||||
// onSecondaryTap: () => showLibrarySwitcher(context, ref),
|
||||
// onDoubleTap: () => showLibrarySwitcher(context, ref),
|
||||
// child:
|
||||
// destinationWidget, // Wrap the actual NavigationDestination
|
||||
// );
|
||||
// } else {
|
||||
// // Return the unwrapped destination for other items
|
||||
// return destinationWidget;
|
||||
// }
|
||||
return destinationWidget;
|
||||
// return NavigationRailDestination(icon: Icon(nav.icon), label: Text(nav.name));
|
||||
}).toList(),
|
||||
selectedIndex: navigationShell.currentIndex,
|
||||
onDestinationSelected: (int index) {
|
||||
print(index);
|
||||
_onTap(context, index, ref);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
VerticalDivider(width: 0.5, thickness: 0.5),
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
navigationShell,
|
||||
const AudiobookPlayer(),
|
||||
],
|
||||
VerticalDivider(width: 0.5, thickness: 0.5),
|
||||
Expanded(
|
||||
child: navigationShell,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -184,8 +182,8 @@ class ScaffoldWithNavBar extends HookConsumerWidget {
|
|||
// world scenario, the items would most likely be generated from the
|
||||
// branches of the shell route, which can be fetched using
|
||||
// `navigationShell.route.branches`.
|
||||
destinations: _navigationItems.map((item) {
|
||||
final isDestinationLibrary = item.name == 'Library';
|
||||
destinations: _navigationItems(context).map((item) {
|
||||
final isDestinationLibrary = item.name == S.of(context).library;
|
||||
var currentLibrary = ref.watch(currentLibraryProvider).valueOrNull;
|
||||
final libraryIcon = AbsIcons.getIconByName(
|
||||
currentLibrary?.icon,
|
||||
|
|
@ -217,6 +215,42 @@ class ScaffoldWithNavBar extends HookConsumerWidget {
|
|||
);
|
||||
}
|
||||
|
||||
List<_NavigationItem> _navigationItems(BuildContext context) {
|
||||
return [
|
||||
_NavigationItem(
|
||||
// name: 'Home',
|
||||
name: S.of(context).home,
|
||||
icon: Icons.home_outlined,
|
||||
activeIcon: Icons.home,
|
||||
),
|
||||
// Library
|
||||
_NavigationItem(
|
||||
// name: 'Library',
|
||||
name: S.of(context).library,
|
||||
icon: Icons.book_outlined,
|
||||
activeIcon: Icons.book,
|
||||
// tooltip: 'Browse your library',
|
||||
tooltip: S.of(context).exploreTooltip,
|
||||
),
|
||||
_NavigationItem(
|
||||
// name: 'Explore',
|
||||
name: S.of(context).explore,
|
||||
icon: Icons.search_outlined,
|
||||
activeIcon: Icons.search,
|
||||
// tooltip: 'Search and Explore',
|
||||
tooltip: S.of(context).exploreTooltip,
|
||||
),
|
||||
_NavigationItem(
|
||||
// name: 'You',
|
||||
name: S.of(context).you,
|
||||
icon: Icons.account_circle_outlined,
|
||||
activeIcon: Icons.account_circle,
|
||||
// tooltip: 'Your Profile and Settings',
|
||||
tooltip: S.of(context).youTooltip,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/// Navigate to the current location of the branch at the provided index when
|
||||
/// tapping an item in the BottomNavigationBar.
|
||||
void _onTap(BuildContext context, int index, WidgetRef ref) {
|
||||
|
|
@ -267,32 +301,32 @@ class ScaffoldWithNavBar extends HookConsumerWidget {
|
|||
|
||||
// list of constants with names and icons so that they can be used in the bottom navigation bar
|
||||
// and reused for nav rail and other places
|
||||
const _navigationItems = [
|
||||
_NavigationItem(
|
||||
name: 'Home',
|
||||
icon: Icons.home_outlined,
|
||||
activeIcon: Icons.home,
|
||||
),
|
||||
// Library
|
||||
_NavigationItem(
|
||||
name: 'Library',
|
||||
icon: Icons.book_outlined,
|
||||
activeIcon: Icons.book,
|
||||
tooltip: 'Browse your library',
|
||||
),
|
||||
_NavigationItem(
|
||||
name: 'Explore',
|
||||
icon: Icons.search_outlined,
|
||||
activeIcon: Icons.search,
|
||||
tooltip: 'Search and Explore',
|
||||
),
|
||||
_NavigationItem(
|
||||
name: 'You',
|
||||
icon: Icons.account_circle_outlined,
|
||||
activeIcon: Icons.account_circle,
|
||||
tooltip: 'Your Profile and Settings',
|
||||
),
|
||||
];
|
||||
// const _navigationItems = [
|
||||
// _NavigationItem(
|
||||
// name: 'Home',
|
||||
// icon: Icons.home_outlined,
|
||||
// activeIcon: Icons.home,
|
||||
// ),
|
||||
// // Library
|
||||
// _NavigationItem(
|
||||
// name: 'Library',
|
||||
// icon: Icons.book_outlined,
|
||||
// activeIcon: Icons.book,
|
||||
// tooltip: 'Browse your library',
|
||||
// ),
|
||||
// _NavigationItem(
|
||||
// name: 'Explore',
|
||||
// icon: Icons.search_outlined,
|
||||
// activeIcon: Icons.search,
|
||||
// tooltip: 'Search and Explore',
|
||||
// ),
|
||||
// _NavigationItem(
|
||||
// name: 'You',
|
||||
// icon: Icons.account_circle_outlined,
|
||||
// activeIcon: Icons.account_circle,
|
||||
// tooltip: 'Your Profile and Settings',
|
||||
// ),
|
||||
// ];
|
||||
|
||||
class _NavigationItem {
|
||||
const _NavigationItem({
|
||||
|
|
|
|||
|
|
@ -12,18 +12,17 @@ part 'app_settings.g.dart';
|
|||
@freezed
|
||||
class AppSettings with _$AppSettings {
|
||||
const factory AppSettings({
|
||||
@Default('zh') String language,
|
||||
@Default(ThemeSettings()) ThemeSettings themeSettings,
|
||||
@Default(PlayerSettings()) PlayerSettings playerSettings,
|
||||
@Default(SleepTimerSettings()) SleepTimerSettings sleepTimerSettings,
|
||||
@Default(DownloadSettings()) DownloadSettings downloadSettings,
|
||||
@Default(NotificationSettings()) NotificationSettings notificationSettings,
|
||||
@Default(ShakeDetectionSettings())
|
||||
ShakeDetectionSettings shakeDetectionSettings,
|
||||
@Default(ShakeDetectionSettings()) ShakeDetectionSettings shakeDetectionSettings,
|
||||
@Default(HomePageSettings()) HomePageSettings homePageSettings,
|
||||
}) = _AppSettings;
|
||||
|
||||
factory AppSettings.fromJson(Map<String, dynamic> json) =>
|
||||
_$AppSettingsFromJson(json);
|
||||
factory AppSettings.fromJson(Map<String, dynamic> json) => _$AppSettingsFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
|
@ -37,17 +36,14 @@ class ThemeSettings with _$ThemeSettings {
|
|||
@Default(true) bool useCurrentPlayerThemeThroughoutApp,
|
||||
}) = _ThemeSettings;
|
||||
|
||||
factory ThemeSettings.fromJson(Map<String, dynamic> json) =>
|
||||
_$ThemeSettingsFromJson(json);
|
||||
factory ThemeSettings.fromJson(Map<String, dynamic> json) => _$ThemeSettingsFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class PlayerSettings with _$PlayerSettings {
|
||||
const factory PlayerSettings({
|
||||
@Default(MinimizedPlayerSettings())
|
||||
MinimizedPlayerSettings miniPlayerSettings,
|
||||
@Default(ExpandedPlayerSettings())
|
||||
ExpandedPlayerSettings expandedPlayerSettings,
|
||||
@Default(MinimizedPlayerSettings()) MinimizedPlayerSettings miniPlayerSettings,
|
||||
@Default(ExpandedPlayerSettings()) ExpandedPlayerSettings expandedPlayerSettings,
|
||||
@Default(1) double preferredDefaultVolume,
|
||||
@Default(1) double preferredDefaultSpeed,
|
||||
@Default([1, 1.25, 1.5, 1.75, 2]) List<double> speedOptions,
|
||||
|
|
@ -60,8 +56,7 @@ class PlayerSettings with _$PlayerSettings {
|
|||
@Default(true) bool configurePlayerForEveryBook,
|
||||
}) = _PlayerSettings;
|
||||
|
||||
factory PlayerSettings.fromJson(Map<String, dynamic> json) =>
|
||||
_$PlayerSettingsFromJson(json);
|
||||
factory PlayerSettings.fromJson(Map<String, dynamic> json) => _$PlayerSettingsFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
|
@ -144,8 +139,7 @@ class DownloadSettings with _$DownloadSettings {
|
|||
@Default(3) int maxConcurrentByGroup,
|
||||
}) = _DownloadSettings;
|
||||
|
||||
factory DownloadSettings.fromJson(Map<String, dynamic> json) =>
|
||||
_$DownloadSettingsFromJson(json);
|
||||
factory DownloadSettings.fromJson(Map<String, dynamic> json) => _$DownloadSettingsFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
|
@ -202,8 +196,7 @@ class ShakeDetectionSettings with _$ShakeDetectionSettings {
|
|||
@Default(ShakeDirection.horizontal) ShakeDirection direction,
|
||||
@Default(5) double threshold,
|
||||
@Default(ShakeAction.resetSleepTimer) ShakeAction shakeAction,
|
||||
@Default({ShakeDetectedFeedback.vibrate})
|
||||
Set<ShakeDetectedFeedback> feedback,
|
||||
@Default({ShakeDetectedFeedback.vibrate}) Set<ShakeDetectedFeedback> feedback,
|
||||
@Default(0.5) double beepVolume,
|
||||
|
||||
/// the duration to wait before the shake detection is enabled again
|
||||
|
|
@ -241,6 +234,5 @@ class HomePageSettings with _$HomePageSettings {
|
|||
@Default(false) bool showPlayButtonOnListenAgainShelf,
|
||||
}) = _HomePageSettings;
|
||||
|
||||
factory HomePageSettings.fromJson(Map<String, dynamic> json) =>
|
||||
_$HomePageSettingsFromJson(json);
|
||||
factory HomePageSettings.fromJson(Map<String, dynamic> json) => _$HomePageSettingsFromJson(json);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) {
|
|||
|
||||
/// @nodoc
|
||||
mixin _$AppSettings {
|
||||
String get language => throw _privateConstructorUsedError;
|
||||
ThemeSettings get themeSettings => throw _privateConstructorUsedError;
|
||||
PlayerSettings get playerSettings => throw _privateConstructorUsedError;
|
||||
SleepTimerSettings get sleepTimerSettings =>
|
||||
|
|
@ -48,7 +49,8 @@ abstract class $AppSettingsCopyWith<$Res> {
|
|||
_$AppSettingsCopyWithImpl<$Res, AppSettings>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{ThemeSettings themeSettings,
|
||||
{String language,
|
||||
ThemeSettings themeSettings,
|
||||
PlayerSettings playerSettings,
|
||||
SleepTimerSettings sleepTimerSettings,
|
||||
DownloadSettings downloadSettings,
|
||||
|
|
@ -80,6 +82,7 @@ class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings>
|
|||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? language = null,
|
||||
Object? themeSettings = null,
|
||||
Object? playerSettings = null,
|
||||
Object? sleepTimerSettings = null,
|
||||
|
|
@ -89,6 +92,10 @@ class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings>
|
|||
Object? homePageSettings = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
language: null == language
|
||||
? _value.language
|
||||
: language // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
themeSettings: null == themeSettings
|
||||
? _value.themeSettings
|
||||
: themeSettings // ignore: cast_nullable_to_non_nullable
|
||||
|
|
@ -203,7 +210,8 @@ abstract class _$$AppSettingsImplCopyWith<$Res>
|
|||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{ThemeSettings themeSettings,
|
||||
{String language,
|
||||
ThemeSettings themeSettings,
|
||||
PlayerSettings playerSettings,
|
||||
SleepTimerSettings sleepTimerSettings,
|
||||
DownloadSettings downloadSettings,
|
||||
|
|
@ -240,6 +248,7 @@ class __$$AppSettingsImplCopyWithImpl<$Res>
|
|||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? language = null,
|
||||
Object? themeSettings = null,
|
||||
Object? playerSettings = null,
|
||||
Object? sleepTimerSettings = null,
|
||||
|
|
@ -249,6 +258,10 @@ class __$$AppSettingsImplCopyWithImpl<$Res>
|
|||
Object? homePageSettings = null,
|
||||
}) {
|
||||
return _then(_$AppSettingsImpl(
|
||||
language: null == language
|
||||
? _value.language
|
||||
: language // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
themeSettings: null == themeSettings
|
||||
? _value.themeSettings
|
||||
: themeSettings // ignore: cast_nullable_to_non_nullable
|
||||
|
|
@ -285,7 +298,8 @@ class __$$AppSettingsImplCopyWithImpl<$Res>
|
|||
@JsonSerializable()
|
||||
class _$AppSettingsImpl implements _AppSettings {
|
||||
const _$AppSettingsImpl(
|
||||
{this.themeSettings = const ThemeSettings(),
|
||||
{this.language = 'zh',
|
||||
this.themeSettings = const ThemeSettings(),
|
||||
this.playerSettings = const PlayerSettings(),
|
||||
this.sleepTimerSettings = const SleepTimerSettings(),
|
||||
this.downloadSettings = const DownloadSettings(),
|
||||
|
|
@ -296,6 +310,9 @@ class _$AppSettingsImpl implements _AppSettings {
|
|||
factory _$AppSettingsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$AppSettingsImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final String language;
|
||||
@override
|
||||
@JsonKey()
|
||||
final ThemeSettings themeSettings;
|
||||
|
|
@ -320,7 +337,7 @@ class _$AppSettingsImpl implements _AppSettings {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppSettings(themeSettings: $themeSettings, playerSettings: $playerSettings, sleepTimerSettings: $sleepTimerSettings, downloadSettings: $downloadSettings, notificationSettings: $notificationSettings, shakeDetectionSettings: $shakeDetectionSettings, homePageSettings: $homePageSettings)';
|
||||
return 'AppSettings(language: $language, themeSettings: $themeSettings, playerSettings: $playerSettings, sleepTimerSettings: $sleepTimerSettings, downloadSettings: $downloadSettings, notificationSettings: $notificationSettings, shakeDetectionSettings: $shakeDetectionSettings, homePageSettings: $homePageSettings)';
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -328,6 +345,8 @@ class _$AppSettingsImpl implements _AppSettings {
|
|||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$AppSettingsImpl &&
|
||||
(identical(other.language, language) ||
|
||||
other.language == language) &&
|
||||
(identical(other.themeSettings, themeSettings) ||
|
||||
other.themeSettings == themeSettings) &&
|
||||
(identical(other.playerSettings, playerSettings) ||
|
||||
|
|
@ -348,6 +367,7 @@ class _$AppSettingsImpl implements _AppSettings {
|
|||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
language,
|
||||
themeSettings,
|
||||
playerSettings,
|
||||
sleepTimerSettings,
|
||||
|
|
@ -374,7 +394,8 @@ class _$AppSettingsImpl implements _AppSettings {
|
|||
|
||||
abstract class _AppSettings implements AppSettings {
|
||||
const factory _AppSettings(
|
||||
{final ThemeSettings themeSettings,
|
||||
{final String language,
|
||||
final ThemeSettings themeSettings,
|
||||
final PlayerSettings playerSettings,
|
||||
final SleepTimerSettings sleepTimerSettings,
|
||||
final DownloadSettings downloadSettings,
|
||||
|
|
@ -385,6 +406,8 @@ abstract class _AppSettings implements AppSettings {
|
|||
factory _AppSettings.fromJson(Map<String, dynamic> json) =
|
||||
_$AppSettingsImpl.fromJson;
|
||||
|
||||
@override
|
||||
String get language;
|
||||
@override
|
||||
ThemeSettings get themeSettings;
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ part of 'app_settings.dart';
|
|||
|
||||
_$AppSettingsImpl _$$AppSettingsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$AppSettingsImpl(
|
||||
language: json['language'] as String? ?? 'zh',
|
||||
themeSettings: json['themeSettings'] == null
|
||||
? const ThemeSettings()
|
||||
: ThemeSettings.fromJson(
|
||||
|
|
@ -40,6 +41,7 @@ _$AppSettingsImpl _$$AppSettingsImplFromJson(Map<String, dynamic> json) =>
|
|||
|
||||
Map<String, dynamic> _$$AppSettingsImplToJson(_$AppSettingsImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'language': instance.language,
|
||||
'themeSettings': instance.themeSettings,
|
||||
'playerSettings': instance.playerSettings,
|
||||
'sleepTimerSettings': instance.sleepTimerSettings,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import 'package:flutter_settings_ui/flutter_settings_ui.dart';
|
|||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:vaani/generated/l10n.dart';
|
||||
import 'package:vaani/router/router.dart';
|
||||
import 'package:vaani/settings/app_settings_provider.dart';
|
||||
import 'package:vaani/settings/models/app_settings.dart' as model;
|
||||
|
|
@ -18,14 +19,13 @@ class AppSettingsPage extends HookConsumerWidget {
|
|||
const AppSettingsPage({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final appSettings = ref.watch(appSettingsProvider);
|
||||
final sleepTimerSettings = appSettings.sleepTimerSettings;
|
||||
|
||||
final locales = {'en': 'English', 'zh': '中文'};
|
||||
return SimpleSettingsPage(
|
||||
title: const Text('App Settings'),
|
||||
title: Text(S.of(context).appSettings),
|
||||
sections: [
|
||||
// General section
|
||||
SettingsSection(
|
||||
|
|
@ -34,25 +34,42 @@ class AppSettingsPage extends HookConsumerWidget {
|
|||
vertical: 8.0,
|
||||
),
|
||||
title: Text(
|
||||
'General',
|
||||
S.of(context).general,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
tiles: [
|
||||
SettingsTile(
|
||||
title: const Text('Player Settings'),
|
||||
title: Text(S.of(context).language),
|
||||
leading: const Icon(Icons.play_arrow),
|
||||
description: const Text(
|
||||
'Customize the player settings',
|
||||
trailing: DropdownButton(
|
||||
value: appSettings.language,
|
||||
items: S.delegate.supportedLocales.map((locale) {
|
||||
return DropdownMenuItem(
|
||||
value: locale.languageCode,
|
||||
child: Text(locales[locale.languageCode] ?? 'unknown'),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (value) {
|
||||
ref.read(appSettingsProvider.notifier).update(
|
||||
appSettings.copyWith(
|
||||
language: value!,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
description: Text(S.of(context).languageDescription),
|
||||
),
|
||||
SettingsTile(
|
||||
title: Text(S.of(context).playerSettings),
|
||||
leading: const Icon(Icons.play_arrow),
|
||||
description: Text(S.of(context).playerSettingsDescription),
|
||||
onPressed: (context) {
|
||||
context.pushNamed(Routes.playerSettings.name);
|
||||
},
|
||||
),
|
||||
NavigationWithSwitchTile(
|
||||
title: const Text('Auto Turn On Sleep Timer'),
|
||||
description: const Text(
|
||||
'Automatically turn on the sleep timer based on the time of day',
|
||||
),
|
||||
title: Text(S.of(context).autoTurnOnSleepTimer),
|
||||
description: Text(S.of(context).automaticallyDescription),
|
||||
leading: sleepTimerSettings.autoTurnOnTimer
|
||||
? const Icon(Symbols.time_auto, fill: 1)
|
||||
: const Icon(Symbols.timer_off, fill: 1),
|
||||
|
|
@ -69,10 +86,10 @@ class AppSettingsPage extends HookConsumerWidget {
|
|||
},
|
||||
),
|
||||
NavigationWithSwitchTile(
|
||||
title: const Text('Shake Detector'),
|
||||
title: Text(S.of(context).shakeDetector),
|
||||
leading: const Icon(Icons.vibration),
|
||||
description: const Text(
|
||||
'Customize the shake detector settings',
|
||||
description: Text(
|
||||
S.of(context).shakeDetectorDescription,
|
||||
),
|
||||
value: appSettings.shakeDetectionSettings.isEnabled,
|
||||
onPressed: (context) {
|
||||
|
|
@ -96,36 +113,30 @@ class AppSettingsPage extends HookConsumerWidget {
|
|||
vertical: 8.0,
|
||||
),
|
||||
title: Text(
|
||||
'Appearance',
|
||||
S.of(context).appearance,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
tiles: [
|
||||
SettingsTile.navigation(
|
||||
leading: const Icon(Icons.color_lens),
|
||||
title: const Text('Theme Settings'),
|
||||
description: const Text(
|
||||
'Customize the app theme',
|
||||
),
|
||||
title: Text(S.of(context).themeSettings),
|
||||
description: Text(S.of(context).themeSettingsDescription),
|
||||
onPressed: (context) {
|
||||
context.pushNamed(Routes.themeSettings.name);
|
||||
},
|
||||
),
|
||||
SettingsTile(
|
||||
title: const Text('Notification Media Player'),
|
||||
title: Text(S.of(context).notificationMediaPlayer),
|
||||
leading: const Icon(Icons.play_lesson),
|
||||
description: const Text(
|
||||
'Customize the media player in notifications',
|
||||
),
|
||||
description: Text(S.of(context).notificationMediaPlayerDescription),
|
||||
onPressed: (context) {
|
||||
context.pushNamed(Routes.notificationSettings.name);
|
||||
},
|
||||
),
|
||||
SettingsTile.navigation(
|
||||
leading: const Icon(Icons.home_filled),
|
||||
title: const Text('Home Page Settings'),
|
||||
description: const Text(
|
||||
'Customize the home page',
|
||||
),
|
||||
title: Text(S.of(context).homePageSettings),
|
||||
description: Text(S.of(context).homePageSettingsDescription),
|
||||
onPressed: (context) {
|
||||
context.pushNamed(Routes.homePageSettings.name);
|
||||
},
|
||||
|
|
@ -140,15 +151,15 @@ class AppSettingsPage extends HookConsumerWidget {
|
|||
vertical: 8.0,
|
||||
),
|
||||
title: Text(
|
||||
'Backup and Restore',
|
||||
S.of(context).backupAndRestore,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
tiles: [
|
||||
SettingsTile(
|
||||
title: const Text('Copy to Clipboard'),
|
||||
title: Text(S.of(context).copyToClipboard),
|
||||
leading: const Icon(Icons.copy),
|
||||
description: const Text(
|
||||
'Copy the app settings to the clipboard',
|
||||
description: Text(
|
||||
S.of(context).copyToClipboardDescription,
|
||||
),
|
||||
onPressed: (context) async {
|
||||
// copy to clipboard
|
||||
|
|
@ -159,18 +170,16 @@ class AppSettingsPage extends HookConsumerWidget {
|
|||
);
|
||||
// show toast
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Settings copied to clipboard'),
|
||||
SnackBar(
|
||||
content: Text(S.of(context).copyToClipboardToast),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SettingsTile(
|
||||
title: const Text('Restore'),
|
||||
title: Text(S.of(context).restore),
|
||||
leading: const Icon(Icons.restore),
|
||||
description: const Text(
|
||||
'Restore the app settings from the backup',
|
||||
),
|
||||
description: Text(S.of(context).restoreDescription),
|
||||
onPressed: (context) {
|
||||
// show a dialog to get the backup
|
||||
showDialog(
|
||||
|
|
@ -184,33 +193,29 @@ class AppSettingsPage extends HookConsumerWidget {
|
|||
|
||||
// a button to reset the app settings
|
||||
SettingsTile(
|
||||
title: const Text('Reset App Settings'),
|
||||
title: Text(S.of(context).resetAppSettings),
|
||||
leading: const Icon(Icons.settings_backup_restore),
|
||||
description: const Text(
|
||||
'Reset the app settings to the default values',
|
||||
),
|
||||
description: Text(S.of(context).resetAppSettingsDescription),
|
||||
onPressed: (context) async {
|
||||
// confirm the reset
|
||||
final res = await showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Reset App Settings'),
|
||||
content: const Text(
|
||||
'Are you sure you want to reset the app settings?',
|
||||
),
|
||||
title: Text(S.of(context).resetAppSettings),
|
||||
content: Text(S.of(context).resetAppSettingsDialog),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
child: Text(S.of(context).cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
child: const Text('Reset'),
|
||||
child: Text(S.of(context).reset),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
|
@ -242,14 +247,14 @@ class RestoreDialogue extends HookConsumerWidget {
|
|||
|
||||
final settingsInputController = useTextEditingController();
|
||||
return AlertDialog(
|
||||
title: const Text('Restore Backup'),
|
||||
title: Text(S.of(context).restoreBackup),
|
||||
content: Form(
|
||||
key: formKey,
|
||||
child: TextFormField(
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Backup',
|
||||
hintText: 'Paste the backup here',
|
||||
labelText: S.of(context).backup,
|
||||
hintText: S.of(context).restoreBackupHint,
|
||||
// clear button
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(Icons.clear),
|
||||
|
|
@ -260,7 +265,7 @@ class RestoreDialogue extends HookConsumerWidget {
|
|||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Please paste the backup here';
|
||||
return S.of(context).restoreBackupValidator;
|
||||
}
|
||||
try {
|
||||
// try to decode the backup
|
||||
|
|
@ -268,7 +273,7 @@ class RestoreDialogue extends HookConsumerWidget {
|
|||
jsonDecode(value),
|
||||
);
|
||||
} catch (e) {
|
||||
return 'Invalid backup';
|
||||
return S.of(context).restoreBackupInvalid;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
|
@ -281,8 +286,8 @@ class RestoreDialogue extends HookConsumerWidget {
|
|||
if (formKey.currentState!.validate()) {
|
||||
if (settings.value == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Invalid backup'),
|
||||
SnackBar(
|
||||
content: Text(S.of(context).restoreBackupInvalid),
|
||||
),
|
||||
);
|
||||
return;
|
||||
|
|
@ -291,19 +296,19 @@ class RestoreDialogue extends HookConsumerWidget {
|
|||
settingsInputController.clear();
|
||||
Navigator.of(context).pop();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Settings restored'),
|
||||
SnackBar(
|
||||
content: Text(S.of(context).restoreBackupSuccess),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Invalid backup'),
|
||||
SnackBar(
|
||||
content: Text(S.of(context).restoreBackupInvalid),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: const Text('Restore'),
|
||||
child: Text(S.of(context).restore),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:vaani/generated/l10n.dart';
|
||||
|
||||
class OkButton<T> extends StatelessWidget {
|
||||
const OkButton({
|
||||
|
|
@ -12,7 +13,7 @@ class OkButton<T> extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return TextButton(
|
||||
onPressed: onPressed,
|
||||
child: const Text('OK'),
|
||||
child: Text(S.of(context).ok),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -32,7 +33,7 @@ class CancelButton extends StatelessWidget {
|
|||
onPressed?.call();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
child: Text(S.of(context).cancel),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,8 +99,7 @@ class BookOnShelf extends HookConsumerWidget {
|
|||
onTap: handleTapOnBook,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(bottom: 8.0, right: 4.0, left: 4.0),
|
||||
padding: const EdgeInsets.only(bottom: 8.0, right: 4.0, left: 4.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
|
@ -112,9 +111,7 @@ class BookOnShelf extends HookConsumerWidget {
|
|||
alignment: Alignment.bottomRight,
|
||||
children: [
|
||||
Hero(
|
||||
tag: HeroTagPrefixes.bookCover +
|
||||
item.id +
|
||||
heroTagSuffix,
|
||||
tag: HeroTagPrefixes.bookCover + item.id + heroTagSuffix,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: AnimatedSwitcher(
|
||||
|
|
@ -128,17 +125,13 @@ class BookOnShelf extends HookConsumerWidget {
|
|||
var imageWidget = Image.memory(
|
||||
image,
|
||||
fit: BoxFit.fill,
|
||||
cacheWidth: (height *
|
||||
1.2 *
|
||||
MediaQuery.of(context)
|
||||
.devicePixelRatio)
|
||||
.round(),
|
||||
cacheWidth:
|
||||
(height * 1.2 * MediaQuery.of(context).devicePixelRatio)
|
||||
.round(),
|
||||
);
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
),
|
||||
child: imageWidget,
|
||||
);
|
||||
|
|
@ -213,8 +206,7 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final me = ref.watch(meProvider);
|
||||
final player = ref.watch(audiobookPlayerProvider);
|
||||
final isCurrentBookSetInPlayer =
|
||||
player.book?.libraryItemId == libraryItemId;
|
||||
final isCurrentBookSetInPlayer = player.book?.libraryItemId == libraryItemId;
|
||||
final isPlayingThisBook = player.playing && isCurrentBookSetInPlayer;
|
||||
|
||||
final userProgress = me.valueOrNull?.mediaProgress
|
||||
|
|
@ -242,8 +234,7 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
|
|||
return Theme(
|
||||
// if current book is set in player, get theme from the cover image
|
||||
data: ThemeData(
|
||||
colorScheme:
|
||||
coverColorScheme.valueOrNull ?? Theme.of(context).colorScheme,
|
||||
colorScheme: coverColorScheme.valueOrNull ?? Theme.of(context).colorScheme,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(strokeWidth / 2 + 2),
|
||||
|
|
@ -258,10 +249,7 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
|
|||
child: CircularProgressIndicator(
|
||||
value: userProgress.progress,
|
||||
strokeWidth: strokeWidth,
|
||||
backgroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimary
|
||||
.withValues(alpha: 0.8),
|
||||
backgroundColor: Theme.of(context).colorScheme.onPrimary.withValues(alpha: 0.8),
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
|
|
@ -279,15 +267,11 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
|
|||
const Size(size, size),
|
||||
),
|
||||
backgroundColor: WidgetStateProperty.all(
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimary
|
||||
.withValues(alpha: 0.9),
|
||||
Theme.of(context).colorScheme.onPrimary.withValues(alpha: 0.9),
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
final book =
|
||||
await ref.watch(libraryItemProvider(libraryItemId).future);
|
||||
final book = await ref.watch(libraryItemProvider(libraryItemId).future);
|
||||
|
||||
libraryItemPlayButtonOnPressed(
|
||||
ref: ref,
|
||||
|
|
@ -324,10 +308,8 @@ class BookCoverSkeleton extends StatelessWidget {
|
|||
child: SizedBox(
|
||||
width: 150,
|
||||
child: Shimmer.fromColors(
|
||||
baseColor:
|
||||
Theme.of(context).colorScheme.surface.withValues(alpha: 0.3),
|
||||
highlightColor:
|
||||
Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.1),
|
||||
baseColor: Theme.of(context).colorScheme.surface.withValues(alpha: 0.3),
|
||||
highlightColor: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.1),
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue