mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-04-21 16:01:58 +00:00
426 lines
15 KiB
Dart
426 lines
15 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:isar/isar.dart';
|
|
import 'package:mangayomi/eval/model/m_bridge.dart';
|
|
import 'package:mangayomi/main.dart';
|
|
import 'package:mangayomi/models/changed.dart';
|
|
import 'package:mangayomi/models/chapter.dart';
|
|
import 'package:mangayomi/models/history.dart';
|
|
import 'package:mangayomi/models/manga.dart';
|
|
import 'package:mangayomi/models/source.dart';
|
|
import 'package:mangayomi/models/update.dart';
|
|
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
|
import 'package:mangayomi/providers/l10n_providers.dart';
|
|
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
|
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
|
|
|
|
class BrowseSScreen extends ConsumerWidget {
|
|
const BrowseSScreen({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final androidProxyServer = ref.watch(androidProxyServerStateProvider);
|
|
final onlyIncludePinnedSource = ref.watch(
|
|
onlyIncludePinnedSourceStateProvider,
|
|
);
|
|
final checkForExtensionUpdates = ref.watch(
|
|
checkForExtensionsUpdateStateProvider,
|
|
);
|
|
final autoUpdateExtensions = ref.watch(autoUpdateExtensionsStateProvider);
|
|
final l10n = l10nLocalizations(context);
|
|
return Scaffold(
|
|
appBar: AppBar(title: Text(l10n!.browse)),
|
|
body: SingleChildScrollView(
|
|
child: Column(
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.only(bottom: 30, top: 20),
|
|
child: Column(
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
child: Row(
|
|
children: [
|
|
Text(
|
|
l10n.extensions,
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
color: context.primaryColor,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
ListTile(
|
|
onTap: () => _showAndroidProxyServerDialog(
|
|
context,
|
|
ref,
|
|
androidProxyServer,
|
|
),
|
|
title: Text(l10n.android_proxy_server),
|
|
subtitle: Text(
|
|
androidProxyServer,
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: context.secondaryColor,
|
|
),
|
|
),
|
|
),
|
|
ListTile(
|
|
onTap: () {
|
|
context.push(
|
|
"/SourceRepositories",
|
|
extra: ItemType.manga,
|
|
);
|
|
},
|
|
title: Text(l10n.manga_extensions_repo),
|
|
subtitle: Text(
|
|
l10n.manage_manga_repo_urls,
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: context.secondaryColor,
|
|
),
|
|
),
|
|
),
|
|
ListTile(
|
|
onTap: () {
|
|
context.push(
|
|
"/SourceRepositories",
|
|
extra: ItemType.anime,
|
|
);
|
|
},
|
|
title: Text(l10n.anime_extensions_repo),
|
|
subtitle: Text(
|
|
l10n.manage_anime_repo_urls,
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: context.secondaryColor,
|
|
),
|
|
),
|
|
),
|
|
ListTile(
|
|
onTap: () {
|
|
context.push(
|
|
"/SourceRepositories",
|
|
extra: ItemType.novel,
|
|
);
|
|
},
|
|
title: Text(l10n.novel_extensions_repo),
|
|
subtitle: Text(
|
|
l10n.manage_novel_repo_urls,
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: context.secondaryColor,
|
|
),
|
|
),
|
|
),
|
|
SwitchListTile(
|
|
value: checkForExtensionUpdates,
|
|
title: Text(l10n.check_for_extension_updates),
|
|
onChanged: (value) {
|
|
ref
|
|
.read(checkForExtensionsUpdateStateProvider.notifier)
|
|
.set(value);
|
|
},
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 15,
|
|
vertical: 5,
|
|
),
|
|
child: SizedBox(
|
|
width: context.width(1),
|
|
child: ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
),
|
|
onPressed: () =>
|
|
_showClearAllSourcesDialog(context, l10n),
|
|
child: Text(
|
|
l10n.clear_all_sources,
|
|
style: TextStyle(
|
|
fontSize: 20,
|
|
color: Colors.redAccent.withValues(alpha: 0.8),
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
ListTile(
|
|
onTap: () => _showCleanNonLibraryDialog(context, l10n),
|
|
title: Text(l10n.clean_database),
|
|
subtitle: Text(
|
|
l10n.clean_database_desc,
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: context.secondaryColor,
|
|
),
|
|
),
|
|
),
|
|
if (checkForExtensionUpdates)
|
|
SwitchListTile(
|
|
value: autoUpdateExtensions,
|
|
title: Text(l10n.auto_extensions_updates),
|
|
subtitle: Text(
|
|
l10n.auto_extensions_updates_subtitle,
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: context.secondaryColor,
|
|
),
|
|
),
|
|
onChanged: (value) {
|
|
ref
|
|
.read(autoUpdateExtensionsStateProvider.notifier)
|
|
.set(value);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.only(bottom: 30),
|
|
child: Column(
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
child: Row(
|
|
children: [
|
|
Text(
|
|
l10n.global_search,
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
color: context.primaryColor,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
SwitchListTile(
|
|
value: onlyIncludePinnedSource,
|
|
title: Text(l10n.only_include_pinned_sources),
|
|
onChanged: (value) {
|
|
ref
|
|
.read(onlyIncludePinnedSourceStateProvider.notifier)
|
|
.set(value);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
void _showClearAllSourcesDialog(BuildContext context, dynamic l10n) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (ctx) {
|
|
return AlertDialog(
|
|
title: Text(l10n.clear_all_sources),
|
|
content: Text(l10n.clear_all_sources_msg),
|
|
actions: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.pop(ctx);
|
|
},
|
|
child: Text(l10n.cancel),
|
|
),
|
|
const SizedBox(width: 15),
|
|
Consumer(
|
|
builder: (context, ref, child) => TextButton(
|
|
onPressed: () {
|
|
isar.writeTxnSync(() {
|
|
isar.sources.clearSync();
|
|
});
|
|
|
|
Navigator.pop(ctx);
|
|
botToast(l10n.sources_cleared);
|
|
},
|
|
child: Text(l10n.ok),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
void _showAndroidProxyServerDialog(
|
|
BuildContext context,
|
|
WidgetRef ref,
|
|
String proxyServer,
|
|
) {
|
|
final serverController = TextEditingController(text: proxyServer);
|
|
String server = proxyServer;
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => StatefulBuilder(
|
|
builder: (context, setState) {
|
|
return AlertDialog(
|
|
title: Text(
|
|
context.l10n.android_proxy_server,
|
|
style: const TextStyle(fontSize: 30),
|
|
),
|
|
content: SizedBox(
|
|
width: context.width(0.8),
|
|
height: context.height(0.3),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const SizedBox(height: 10),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
|
child: TextFormField(
|
|
controller: serverController,
|
|
autofocus: true,
|
|
onChanged: (value) => setState(() {
|
|
server = value;
|
|
}),
|
|
decoration: InputDecoration(
|
|
hintText:
|
|
"Server IP (e.g., 10.0.0.5 or https://example.com)",
|
|
filled: false,
|
|
contentPadding: const EdgeInsets.all(12),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: const BorderSide(width: 0.4),
|
|
borderRadius: BorderRadius.circular(5),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: const BorderSide(),
|
|
borderRadius: BorderRadius.circular(5),
|
|
),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(5),
|
|
borderSide: const BorderSide(),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 20),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
|
child: SizedBox(
|
|
width: context.width(1),
|
|
child: ElevatedButton(
|
|
onPressed: () {
|
|
ref
|
|
.read(androidProxyServerStateProvider.notifier)
|
|
.set(server);
|
|
Navigator.pop(context);
|
|
},
|
|
child: Text(context.l10n.dialog_confirm),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
void _showCleanNonLibraryDialog(BuildContext context, dynamic l10n) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (ctx) {
|
|
return AlertDialog(
|
|
title: Text(l10n.clean_database),
|
|
content: Text(l10n.clean_database_desc),
|
|
actions: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.pop(ctx);
|
|
},
|
|
child: Text(l10n.cancel),
|
|
),
|
|
const SizedBox(width: 15),
|
|
Consumer(
|
|
builder: (context, ref, child) => TextButton(
|
|
onPressed: () {
|
|
final mangasList = isar.mangas
|
|
.filter()
|
|
.favoriteEqualTo(false)
|
|
.findAllSync();
|
|
final provider = ref.read(
|
|
synchingProvider(syncId: 1).notifier,
|
|
);
|
|
isar.writeTxnSync(() {
|
|
for (var manga in mangasList) {
|
|
final histories = isar.historys
|
|
.filter()
|
|
.mangaIdEqualTo(manga.id)
|
|
.findAllSync();
|
|
for (var history in histories) {
|
|
isar.historys.deleteSync(history.id!);
|
|
provider.addChangedPart(
|
|
ActionType.removeHistory,
|
|
history.id,
|
|
"{}",
|
|
false,
|
|
);
|
|
}
|
|
|
|
for (var chapter in manga.chapters) {
|
|
final updates = isar.updates
|
|
.filter()
|
|
.mangaIdEqualTo(chapter.mangaId)
|
|
.chapterNameEqualTo(chapter.name)
|
|
.findAllSync();
|
|
for (var update in updates) {
|
|
isar.updates.deleteSync(update.id!);
|
|
provider.addChangedPart(
|
|
ActionType.removeUpdate,
|
|
update.id,
|
|
"{}",
|
|
false,
|
|
);
|
|
}
|
|
isar.chapters.deleteSync(chapter.id!);
|
|
provider.addChangedPart(
|
|
ActionType.removeChapter,
|
|
chapter.id,
|
|
"{}",
|
|
false,
|
|
);
|
|
}
|
|
isar.mangas.deleteSync(manga.id!);
|
|
provider.addChangedPart(
|
|
ActionType.removeItem,
|
|
manga.id,
|
|
"{}",
|
|
false,
|
|
);
|
|
}
|
|
});
|
|
|
|
Navigator.pop(ctx);
|
|
botToast(l10n.cleaned_database(mangasList.length));
|
|
},
|
|
child: Text(l10n.ok),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|