added default subtitle lang + statistics screen

This commit is contained in:
Schnitzel5 2025-03-10 00:02:06 +01:00
parent f0aab34f0c
commit 301220a6a4
12 changed files with 618 additions and 257 deletions

View file

@ -66,6 +66,7 @@
"incognito_mode_description": "Pausiert den Leseverlauf",
"download_queue": "Download-Warteschlange",
"categories": "Kategorien",
"statistics": "Statistiken",
"settings": "Einstellungen",
"about": "Über",
"help": "Hilfe",
@ -83,6 +84,7 @@
"general": "Allgemein",
"general_subtitle": "App-Sprache",
"app_language": "App-Sprache",
"default_subtitle_language": "Standard Untertitel-Sprache",
"appearance": "Aussehen",
"appearance_subtitle": "Thema, Datum- & Zeitformat",
"theme": "Thema",

View file

@ -66,6 +66,7 @@
"incognito_mode_description": "Pauses reading history",
"download_queue": "Download Queue",
"categories": "Categories",
"statistics": "Statistics",
"settings": "Settings",
"about": "About",
"help": "Help",
@ -83,6 +84,7 @@
"general": "General",
"general_subtitle": "App language",
"app_language": "App language",
"default_subtitle_language": "Default subtitle language",
"appearance": "Appearance",
"appearance_subtitle": "Theme, date & time format",
"theme": "Theme",

View file

@ -92,6 +92,8 @@ class Settings {
L10nLocale? locale;
L10nLocale? defaultSubtitleLang;
@enumerated
late DisplayType animeDisplayType;
@ -427,6 +429,8 @@ class Settings {
libraryShowNumbersOfItems = json['libraryShowNumbersOfItems'];
locale =
json['locale'] != null ? L10nLocale.fromJson(json['locale']) : null;
defaultSubtitleLang =
json['defaultSubtitleLang'] != null ? L10nLocale.fromJson(json['defaultSubtitleLang']) : null;
onlyIncludePinnedSources = json['onlyIncludePinnedSources'];
pagePreloadAmount = json['pagePreloadAmount'];
if (json['personalPageModeList'] != null) {
@ -616,6 +620,7 @@ class Settings {
'libraryShowLanguage': libraryShowLanguage,
'libraryShowNumbersOfItems': libraryShowNumbersOfItems,
'locale': locale?.toJson(),
'defaultSubtitleLang': defaultSubtitleLang?.toJson(),
'onlyIncludePinnedSources': onlyIncludePinnedSources,
'pagePreloadAmount': pagePreloadAmount,
'personalPageModeList':

File diff suppressed because it is too large Load diff

View file

@ -70,7 +70,7 @@ class _AnimePlayerViewState extends riv.ConsumerState<AnimePlayerView> {
@override
Widget build(BuildContext context) {
final l10nLocale = ref.watch(l10nLocaleStateProvider);
final defaultSubtitleLang = ref.watch(defaultSubtitleLangStateProvider);
final serversData = ref.watch(getVideoListProvider(episode: episode));
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
return serversData.when(
@ -94,7 +94,7 @@ class _AnimePlayerViewState extends riv.ConsumerState<AnimePlayerView> {
return AnimeStreamPage(
defaultSubtitle: completeLanguageNameEnglish(
l10nLocale.toLanguageTag(),
defaultSubtitleLang.toLanguageTag(),
),
episode: episode,
videos: videos,

View file

@ -54,6 +54,13 @@ class MoreScreen extends StatelessWidget {
icon: Icons.label_outline_rounded,
title: l10n.categories,
),
ListTileWidget(
onTap: () {
context.push('/statistics');
},
icon: Icons.query_stats_outlined,
title: l10n.statistics,
),
ListTileWidget(
onTap: () {
context.push('/dataAndStorage');

View file

@ -3,14 +3,17 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/modules/more/settings/player/providers/player_state_provider.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
import 'package:mangayomi/utils/language.dart';
import 'package:numberpicker/numberpicker.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class PlayerScreen extends ConsumerWidget {
const PlayerScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final defaultSubtitleLang = ref.watch(defaultSubtitleLangStateProvider);
final markEpisodeAsSeenType = ref.watch(markEpisodeAsSeenTypeStateProvider);
final defaultSkipIntroLength = ref.watch(
defaultSkipIntroLengthStateProvider,
@ -30,6 +33,67 @@ class PlayerScreen extends ConsumerWidget {
body: SingleChildScrollView(
child: Column(
children: [
ListTile(
onTap: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(context.l10n.default_subtitle_language),
content: SizedBox(
width: context.width(0.8),
child: SuperListView.builder(
shrinkWrap: true,
itemCount: AppLocalizations.supportedLocales.length,
itemBuilder: (context, index) {
final locale =
AppLocalizations.supportedLocales[index];
return RadioListTile(
dense: true,
contentPadding: const EdgeInsets.all(0),
value: locale,
groupValue: defaultSubtitleLang,
onChanged: (value) {
ref
.read(
defaultSubtitleLangStateProvider.notifier,
)
.setLocale(locale);
Navigator.pop(context);
},
title: Text(
completeLanguageName(locale.toLanguageTag()),
),
);
},
),
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () async {
Navigator.pop(context);
},
child: Text(
context.l10n.cancel,
style: TextStyle(color: context.primaryColor),
),
),
],
),
],
);
},
);
},
title: Text(context.l10n.default_subtitle_language),
subtitle: Text(
completeLanguageName(defaultSubtitleLang.toLanguageTag()),
style: TextStyle(fontSize: 11, color: context.secondaryColor),
),
),
ListTile(
onTap: () {
final values = [100, 95, 90, 85, 80, 75, 70];

View file

@ -1,8 +1,40 @@
import 'dart:ui';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/settings.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'player_state_provider.g.dart';
@riverpod
class DefaultSubtitleLangState extends _$DefaultSubtitleLangState {
@override
Locale build() {
return Locale(
_getLocale()!.languageCode ?? "en",
_getLocale()!.countryCode ?? "",
);
}
L10nLocale? _getLocale() {
return isar.settings.getSync(227)!.defaultSubtitleLang ??
L10nLocale(languageCode: "en", countryCode: "");
}
void setLocale(Locale locale) async {
final settings = isar.settings.getSync(227)!;
isar.writeTxnSync(() {
isar.settings.putSync(
settings
..defaultSubtitleLang = L10nLocale(
languageCode: locale.languageCode,
countryCode: locale.countryCode,
),
);
});
state = locale;
}
}
@riverpod
class MarkEpisodeAsSeenTypeState extends _$MarkEpisodeAsSeenTypeState {
@override

View file

@ -6,6 +6,23 @@ part of 'player_state_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$defaultSubtitleLangStateHash() =>
r'bec45a4fbd70922b767dbef7c98a423d908ed9d3';
/// See also [DefaultSubtitleLangState].
@ProviderFor(DefaultSubtitleLangState)
final defaultSubtitleLangStateProvider =
AutoDisposeNotifierProvider<DefaultSubtitleLangState, Locale>.internal(
DefaultSubtitleLangState.new,
name: r'defaultSubtitleLangStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$defaultSubtitleLangStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$DefaultSubtitleLangState = AutoDisposeNotifier<Locale>;
String _$markEpisodeAsSeenTypeStateHash() =>
r'94ae90e6bc51bbd4f88dfc9780cc5e9eb4ed5770';

View file

@ -0,0 +1,157 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isar/isar.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/manga.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
class StatisticsScreen extends ConsumerStatefulWidget {
const StatisticsScreen({super.key});
@override
ConsumerState<StatisticsScreen> createState() => _StatisticsScreenState();
}
class _StatisticsScreenState extends ConsumerState<StatisticsScreen> {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final items = isar.mangas.filter().idIsNotNull().findAllSync();
final chapters = isar.chapters.filter().idIsNotNull().findAllSync();
return Scaffold(
appBar: AppBar(title: Text(l10n.statistics)),
body: SingleChildScrollView(
child: Column(
spacing: 3,
children: [
SizedBox(height: 20,),
Row(
children: [
Expanded(
child: Card(
child: ListTile(
title: Text("Total manga", textAlign: TextAlign.center),
subtitle: Text(
"${items.where((i) => i.itemType == ItemType.manga).length}",
textAlign: TextAlign.center,
),
),
),
),
Expanded(
child: Card(
child: ListTile(
title: Text(
"Total chapters",
textAlign: TextAlign.center,
),
subtitle: Text(
"${chapters.where((i) => i.manga.value!.itemType == ItemType.manga).length}",
textAlign: TextAlign.center,
),
),
),
),
Expanded(
child: Card(
child: ListTile(
title: Text("Read chapters", textAlign: TextAlign.center),
subtitle: Text(
"${chapters.where((i) => i.manga.value!.itemType == ItemType.manga && (i.isRead ?? false)).length}",
textAlign: TextAlign.center,
),
),
),
),
],
),
Row(
children: [
Expanded(
child: Card(
child: ListTile(
title: Text("Total anime", textAlign: TextAlign.center),
subtitle: Text(
"${items.where((i) => i.itemType == ItemType.anime).length}",
textAlign: TextAlign.center,
),
),
),
),
Expanded(
child: Card(
child: ListTile(
title: Text(
"Total episodes",
textAlign: TextAlign.center,
),
subtitle: Text(
"${chapters.where((i) => i.manga.value!.itemType == ItemType.anime).length}",
textAlign: TextAlign.center,
),
),
),
),
Expanded(
child: Card(
child: ListTile(
title: Text(
"Watched episodes",
textAlign: TextAlign.center,
),
subtitle: Text(
"${chapters.where((i) => i.manga.value!.itemType == ItemType.anime && (i.isRead ?? false)).length}",
textAlign: TextAlign.center,
),
),
),
),
],
),
Row(
children: [
Expanded(
child: Card(
child: ListTile(
title: Text("Total novels", textAlign: TextAlign.center),
subtitle: Text(
"${items.where((i) => i.itemType == ItemType.novel).length}",
textAlign: TextAlign.center,
),
),
),
),
Expanded(
child: Card(
child: ListTile(
title: Text(
"Total chapters",
textAlign: TextAlign.center,
),
subtitle: Text(
"${chapters.where((i) => i.manga.value!.itemType == ItemType.novel).length}",
textAlign: TextAlign.center,
),
),
),
),
Expanded(
child: Card(
child: ListTile(
title: Text("Read chapters", textAlign: TextAlign.center),
subtitle: Text(
"${chapters.where((i) => i.manga.value!.itemType == ItemType.novel && (i.isRead ?? false)).length}",
textAlign: TextAlign.center,
),
),
),
),
],
),
],
),
),
);
}
}

View file

@ -16,6 +16,7 @@ import 'package:mangayomi/modules/more/data_and_storage/data_and_storage.dart';
import 'package:mangayomi/modules/more/settings/appearance/custom_navigation_settings.dart';
import 'package:mangayomi/modules/more/settings/browse/source_repositories.dart';
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
import 'package:mangayomi/modules/more/statistics/statistics_screen.dart';
import 'package:mangayomi/modules/novel/novel_reader_view.dart';
import 'package:mangayomi/modules/updates/updates_screen.dart';
import 'package:mangayomi/modules/more/categories/categories_screen.dart';
@ -430,6 +431,16 @@ class RouterNotifier extends ChangeNotifier {
);
},
),
GoRoute(
path: "/statistics",
name: "statistics",
builder: (context, state) {
return StatisticsScreen();
},
pageBuilder: (context, state) {
return transitionPage(key: state.pageKey, child: StatisticsScreen());
},
),
GoRoute(
path: "/general",
name: "general",

View file

@ -121,6 +121,7 @@ final languagesMap = {
"Tagalog": "tl",
};
/// this might not always work depending on how every extension provides its subtitles
completeLanguageNameEnglish(String lang) {
lang = lang.toLowerCase();
for (var element in languagesMapEnglish.entries) {