Merge branch 'main' into novel/font-size
This commit is contained in:
commit
380bea2249
25 changed files with 1227 additions and 858 deletions
|
|
@ -389,5 +389,15 @@
|
|||
"n_minutes_ago": "{minutes} minutes ago",
|
||||
"n_day_ago": "{day} day ago",
|
||||
"now": "now",
|
||||
"library_last_updated": "Library last updated: {lastUpdated}"
|
||||
"library_last_updated": "Library last updated: {lastUpdated}",
|
||||
"data_and_storage": "Data and storage",
|
||||
"download_location_info": "Used for chapter downloads",
|
||||
"storage": "Storage",
|
||||
"clear_chapter_and_episode_cache": "Clear chapter and episode cache",
|
||||
"cache_cleared": "Cache cleared",
|
||||
"clear_chapter_or_episode_cache_on_app_launch": "Clear chapter/episode cache on app launch",
|
||||
"app_settings": "App settings",
|
||||
"sources_settings": "Sources settings",
|
||||
"include_sensitive_settings": "Include sensitive settings (e.g., tracker login tokens)",
|
||||
"create": "Create"
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import 'package:google_fonts/google_fonts.dart';
|
|||
import 'package:intl/date_symbol_data_local.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/providers/storage_usage.dart';
|
||||
import 'package:mangayomi/modules/more/settings/appearance/providers/app_font_family.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/providers/storage_provider.dart';
|
||||
|
|
@ -75,6 +76,13 @@ class _MyAppState extends ConsumerState<MyApp> {
|
|||
@override
|
||||
void initState() {
|
||||
iniDateFormatting();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (ref.read(clearChapterCacheOnAppLaunchStateProvider)) {
|
||||
ref
|
||||
.read(totalChapterCacheSizeStateProvider.notifier)
|
||||
.clearCache(showToast: false);
|
||||
}
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class History {
|
|||
|
||||
History({
|
||||
this.id = Isar.autoIncrement,
|
||||
this.isManga = true,
|
||||
this.isManga,
|
||||
required this.itemType,
|
||||
required this.chapterId,
|
||||
required this.mangaId,
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ class Manga {
|
|||
required this.name,
|
||||
required this.status,
|
||||
required this.description,
|
||||
this.isManga = true,
|
||||
this.isManga,
|
||||
this.itemType = ItemType.manga,
|
||||
this.dateAdded,
|
||||
this.lastUpdate,
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ class Settings {
|
|||
|
||||
int? backupFrequency;
|
||||
|
||||
List<int>? backupFrequencyOptions;
|
||||
List<int>? backupListOptions;
|
||||
|
||||
String? autoBackupLocation;
|
||||
|
||||
|
|
@ -226,6 +226,8 @@ class Settings {
|
|||
|
||||
bool? hideNovel;
|
||||
|
||||
bool? clearChapterCacheOnAppLaunch;
|
||||
|
||||
Settings(
|
||||
{this.id = 227,
|
||||
this.displayType = DisplayType.compactGrid,
|
||||
|
|
@ -282,7 +284,7 @@ class Settings {
|
|||
this.backgroundColor = BackgroundColor.black,
|
||||
this.personalPageModeList,
|
||||
this.backupFrequency,
|
||||
this.backupFrequencyOptions,
|
||||
this.backupListOptions,
|
||||
this.autoBackupLocation,
|
||||
this.startDatebackup,
|
||||
this.usePageTapZones = true,
|
||||
|
|
@ -325,7 +327,8 @@ class Settings {
|
|||
this.novelTextAlign = NovelTextAlign.left,
|
||||
this.hideManga = false,
|
||||
this.hideAnime = false,
|
||||
this.hideNovel = false});
|
||||
this.hideNovel = false,
|
||||
this.clearChapterCacheOnAppLaunch = false});
|
||||
|
||||
Settings.fromJson(Map<String, dynamic> json) {
|
||||
animatePageTransitions = json['animatePageTransitions'];
|
||||
|
|
@ -445,7 +448,7 @@ class Settings {
|
|||
themeIsDark = json['themeIsDark'];
|
||||
userAgent = json['userAgent'];
|
||||
backupFrequency = json['backupFrequency'];
|
||||
backupFrequencyOptions = json['backupFrequencyOptions']?.cast<int>();
|
||||
backupListOptions = json['backupListOptions']?.cast<int>();
|
||||
autoBackupLocation = json['autoBackupLocation'];
|
||||
startDatebackup = json['startDatebackup'];
|
||||
usePageTapZones = json['usePageTapZones'];
|
||||
|
|
@ -503,6 +506,7 @@ class Settings {
|
|||
hideManga = json['hideManga'];
|
||||
hideAnime = json['hideAnime'];
|
||||
hideNovel = json['hideNovel'];
|
||||
clearChapterCacheOnAppLaunch = json['clearChapterCacheOnAppLaunch'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
|
|
@ -574,7 +578,7 @@ class Settings {
|
|||
'themeIsDark': themeIsDark,
|
||||
'userAgent': userAgent,
|
||||
'backupFrequency': backupFrequency,
|
||||
'backupFrequencyOptions': backupFrequencyOptions,
|
||||
'backupListOptions': backupListOptions,
|
||||
'autoBackupLocation': autoBackupLocation,
|
||||
'startDatebackup': startDatebackup,
|
||||
'usePageTapZones': usePageTapZones,
|
||||
|
|
@ -619,7 +623,8 @@ class Settings {
|
|||
'novelTextAlign': novelTextAlign.index,
|
||||
'hideManga': hideManga,
|
||||
'hideAnime': hideAnime,
|
||||
'hideNovel': hideNovel
|
||||
'hideNovel': hideNovel,
|
||||
'clearChapterCacheOnAppLaunch': clearChapterCacheOnAppLaunch
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -86,7 +86,7 @@ class Source {
|
|||
this.versionLast = "0.0.1",
|
||||
this.sourceCode = '',
|
||||
this.headers = '',
|
||||
this.isManga = true,
|
||||
this.isManga,
|
||||
this.itemType = ItemType.manga,
|
||||
this.appMinVerReq = "",
|
||||
this.additionalParams = "",
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import 'package:mangayomi/services/fetch_anime_sources.dart';
|
|||
import 'package:mangayomi/services/fetch_manga_sources.dart';
|
||||
import 'package:mangayomi/modules/main_view/providers/migration.dart';
|
||||
import 'package:mangayomi/modules/more/about/providers/check_for_update.dart';
|
||||
import 'package:mangayomi/modules/more/backup_and_restore/providers/auto_backup.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/providers/auto_backup.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/router/router.dart';
|
||||
import 'package:mangayomi/services/fetch_novel_sources.dart';
|
||||
|
|
|
|||
|
|
@ -1,401 +0,0 @@
|
|||
import 'dart:io';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/eval/model/m_bridge.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/chapter_filter_list_tile_widget.dart';
|
||||
import 'package:mangayomi/modules/more/backup_and_restore/providers/auto_backup.dart';
|
||||
import 'package:mangayomi/modules/more/backup_and_restore/providers/backup.dart';
|
||||
import 'package:mangayomi/modules/more/backup_and_restore/providers/restore.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/providers/storage_provider.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
|
||||
class BackupAndRestore extends ConsumerWidget {
|
||||
const BackupAndRestore({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final backupFrequency = ref.watch(backupFrequencyStateProvider);
|
||||
final backupFrequencyOptions =
|
||||
ref.watch(backupFrequencyOptionsStateProvider);
|
||||
final autoBackupLocation = ref.watch(autoBackupLocationStateProvider);
|
||||
ref.read(autoBackupLocationStateProvider.notifier).refresh();
|
||||
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(l10n.backup_and_restore),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
onTap: () {
|
||||
final list = _getList(context);
|
||||
List<int> indexList = [];
|
||||
indexList.addAll(backupFrequencyOptions);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return AlertDialog(
|
||||
title: Text(l10n.create_backup_dialog_title),
|
||||
content: SizedBox(
|
||||
width: context.width(0.8),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: list.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ListTileChapterFilter(
|
||||
label: list[index],
|
||||
type: indexList.contains(index) ? 1 : 0,
|
||||
onTap: () {
|
||||
if (indexList.contains(index)) {
|
||||
setState(() {
|
||||
indexList.remove(index);
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
indexList.add(index);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
)),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
l10n.cancel,
|
||||
style: TextStyle(
|
||||
color: context.primaryColor),
|
||||
)),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
String? result;
|
||||
if (Platform.isIOS) {
|
||||
result = (await StorageProvider()
|
||||
.getIosBackupDirectory())!
|
||||
.path;
|
||||
} else {
|
||||
result = await FilePicker.platform
|
||||
.getDirectoryPath();
|
||||
}
|
||||
|
||||
if (result != null && context.mounted) {
|
||||
ref.watch(doBackUpProvider(
|
||||
list: indexList,
|
||||
path: result,
|
||||
context: context));
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
l10n.ok,
|
||||
style: TextStyle(
|
||||
color: context.primaryColor),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
title: Text(l10n.create_backup),
|
||||
subtitle: Text(
|
||||
l10n.create_backup_subtitle,
|
||||
style: TextStyle(fontSize: 11, color: context.secondaryColor),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(l10n.restore_backup),
|
||||
content: SizedBox(
|
||||
width: context.width(0.8),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.info_outline_rounded,
|
||||
color: context.secondaryColor),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 5),
|
||||
child:
|
||||
Text(l10n.restore_backup_warning_title),
|
||||
),
|
||||
],
|
||||
)),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
l10n.cancel,
|
||||
style:
|
||||
TextStyle(color: context.primaryColor),
|
||||
)),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
try {
|
||||
FilePickerResult? result =
|
||||
await FilePicker.platform
|
||||
.pickFiles(allowMultiple: false);
|
||||
|
||||
if (result != null && context.mounted) {
|
||||
ref.watch(doRestoreProvider(
|
||||
path: result.files.first.path!,
|
||||
context: context));
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
Navigator.pop(context);
|
||||
} catch (_) {
|
||||
botToast("Error");
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
l10n.ok,
|
||||
style:
|
||||
TextStyle(color: context.primaryColor),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
title: Text(l10n.restore_backup),
|
||||
subtitle: Text(
|
||||
l10n.restore_backup_subtitle,
|
||||
style: TextStyle(fontSize: 11, color: context.secondaryColor),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(l10n.automatic_backups,
|
||||
style:
|
||||
TextStyle(fontSize: 13, color: context.primaryColor)),
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
final list = _getBackupFrequencyList(context);
|
||||
return AlertDialog(
|
||||
title: Text(l10n.backup_frequency),
|
||||
content: SizedBox(
|
||||
width: context.width(0.8),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: list.length,
|
||||
itemBuilder: (context, index) {
|
||||
return RadioListTile(
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
value: index,
|
||||
groupValue: backupFrequency,
|
||||
onChanged: (value) {
|
||||
ref
|
||||
.read(backupFrequencyStateProvider
|
||||
.notifier)
|
||||
.set(value!);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
title: Row(
|
||||
children: [Text(list[index])],
|
||||
),
|
||||
);
|
||||
},
|
||||
)),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
l10n.cancel,
|
||||
style:
|
||||
TextStyle(color: context.primaryColor),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
title: Text(l10n.backup_frequency),
|
||||
subtitle: Text(
|
||||
_getBackupFrequencyList(context)[backupFrequency],
|
||||
style: TextStyle(fontSize: 11, color: context.secondaryColor),
|
||||
),
|
||||
),
|
||||
if (!Platform.isIOS)
|
||||
ListTile(
|
||||
onTap: () async {
|
||||
String? result = await FilePicker.platform.getDirectoryPath();
|
||||
|
||||
if (result != null) {
|
||||
ref
|
||||
.read(autoBackupLocationStateProvider.notifier)
|
||||
.set(result);
|
||||
}
|
||||
},
|
||||
title: Text(l10n.backup_location),
|
||||
subtitle: Text(
|
||||
autoBackupLocation.$2.isEmpty
|
||||
? autoBackupLocation.$1
|
||||
: autoBackupLocation.$2,
|
||||
style: TextStyle(fontSize: 11, color: context.secondaryColor),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {
|
||||
final list = _getList(context);
|
||||
List<int> indexList = [];
|
||||
indexList.addAll(backupFrequencyOptions);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
l10n.backup_options_subtitle,
|
||||
),
|
||||
content: SizedBox(
|
||||
width: context.width(0.8),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: list.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ListTileChapterFilter(
|
||||
label: list[index],
|
||||
type: indexList.contains(index) ? 1 : 0,
|
||||
onTap: () {
|
||||
if (indexList.contains(index)) {
|
||||
setState(() {
|
||||
indexList.remove(index);
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
indexList.add(index);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
)),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
l10n.cancel,
|
||||
style: TextStyle(
|
||||
color: context.primaryColor),
|
||||
)),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
ref
|
||||
.read(
|
||||
backupFrequencyOptionsStateProvider
|
||||
.notifier)
|
||||
.set(indexList);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
l10n.ok,
|
||||
style: TextStyle(
|
||||
color: context.primaryColor),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
title: Text(l10n.backup_options),
|
||||
subtitle: Text(
|
||||
l10n.backup_options_subtitle,
|
||||
style: TextStyle(fontSize: 11, color: context.secondaryColor),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.info_outline_rounded,
|
||||
color: context.secondaryColor),
|
||||
],
|
||||
),
|
||||
),
|
||||
subtitle: Text(l10n.backup_and_restore_warning_info,
|
||||
style:
|
||||
TextStyle(fontSize: 11, color: context.secondaryColor)),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> _getList(BuildContext context) {
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
return [
|
||||
l10n.library_entries,
|
||||
l10n.categories,
|
||||
l10n.chapters_and_episode,
|
||||
l10n.tracking,
|
||||
l10n.history,
|
||||
l10n.settings,
|
||||
l10n.extensions,
|
||||
l10n.updates
|
||||
];
|
||||
}
|
||||
|
||||
List<String> _getBackupFrequencyList(BuildContext context) {
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
return [
|
||||
l10n.off,
|
||||
l10n.every_6_hours,
|
||||
l10n.every_12_hours,
|
||||
l10n.daily,
|
||||
l10n.every_2_days,
|
||||
l10n.weekly
|
||||
];
|
||||
}
|
||||
206
lib/modules/more/data_and_storage/create_backup.dart
Normal file
206
lib/modules/more/data_and_storage/create_backup.dart
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/modules/manga/detail/widgets/chapter_filter_list_tile_widget.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/providers/auto_backup.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/providers/backup.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/providers/storage_provider.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
|
||||
class CreateBackup extends ConsumerStatefulWidget {
|
||||
const CreateBackup({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<CreateBackup> createState() => _CreateBackupState();
|
||||
}
|
||||
|
||||
class _CreateBackupState extends ConsumerState<CreateBackup> {
|
||||
late final List<(String, int)> _libraryList = _getLibraryList(context);
|
||||
late final List<(String, int)> _settingsList = _getSettingsList(context);
|
||||
late final List<(String, int)> _extensionList = _getExtensionsList(context);
|
||||
|
||||
void _set(int index, List<int> indexList) {
|
||||
if (indexList.contains(index)) {
|
||||
ref
|
||||
.read(backupFrequencyOptionsStateProvider.notifier)
|
||||
.set(indexList.where((e) => e != index).toList());
|
||||
} else {
|
||||
ref
|
||||
.read(backupFrequencyOptionsStateProvider.notifier)
|
||||
.set([...indexList, index]);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final indexList = ref.watch(backupFrequencyOptionsStateProvider);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(l10n.create_backup),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(l10n.library,
|
||||
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(25)),
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.symmetric(horizontal: 0, vertical: 15),
|
||||
shrinkWrap: true,
|
||||
primary: false,
|
||||
itemCount: _libraryList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final (label, idx) = _libraryList[index];
|
||||
return ListTileChapterFilter(
|
||||
label: label,
|
||||
type: indexList.contains(idx) ? 1 : 0,
|
||||
onTap: () {
|
||||
_set(idx, indexList);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(l10n.settings,
|
||||
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(25)),
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.symmetric(horizontal: 0, vertical: 15),
|
||||
shrinkWrap: true,
|
||||
primary: false,
|
||||
itemCount: _settingsList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final (label, idx) = _settingsList[index];
|
||||
return ListTileChapterFilter(
|
||||
label: label,
|
||||
type: indexList.contains(idx) ? 1 : 0,
|
||||
onTap: () {
|
||||
_set(idx, indexList);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(l10n.extensions,
|
||||
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(25)),
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.symmetric(horizontal: 0, vertical: 15),
|
||||
shrinkWrap: true,
|
||||
primary: false,
|
||||
itemCount: _extensionList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final (label, idx) = _extensionList[index];
|
||||
return ListTileChapterFilter(
|
||||
label: label,
|
||||
type: indexList.contains(idx) ? 1 : 0,
|
||||
onTap: () {
|
||||
_set(idx, indexList);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Divider(color: context.primaryColor, height: 0.3),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: context.primaryColor),
|
||||
onPressed: () async {
|
||||
String? result;
|
||||
if (Platform.isIOS) {
|
||||
result = (await StorageProvider()
|
||||
.getIosBackupDirectory())!
|
||||
.path;
|
||||
} else {
|
||||
result =
|
||||
await FilePicker.platform.getDirectoryPath();
|
||||
}
|
||||
|
||||
if (result != null && context.mounted) {
|
||||
ref.watch(doBackUpProvider(
|
||||
list: indexList,
|
||||
path: result,
|
||||
context: context));
|
||||
}
|
||||
},
|
||||
child: Text(l10n.create,
|
||||
style: TextStyle(
|
||||
color: context.dynamicBlackWhiteColor))),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
List<(String, int)> _getLibraryList(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return [
|
||||
(l10n.library_entries, 0),
|
||||
(l10n.categories, 1),
|
||||
(l10n.chapters_and_episode, 2),
|
||||
(l10n.tracking, 3),
|
||||
(l10n.history, 4),
|
||||
(l10n.updates, 5)
|
||||
];
|
||||
}
|
||||
|
||||
List<(String, int)> _getSettingsList(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return [
|
||||
(l10n.app_settings, 6),
|
||||
(l10n.sources_settings, 7),
|
||||
(l10n.include_sensitive_settings, 8),
|
||||
];
|
||||
}
|
||||
|
||||
List<(String, int)> _getExtensionsList(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return [(l10n.extensions, 9)];
|
||||
}
|
||||
379
lib/modules/more/data_and_storage/data_and_storage.dart
Normal file
379
lib/modules/more/data_and_storage/data_and_storage.dart
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
import 'dart:io';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mangayomi/eval/model/m_bridge.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/providers/auto_backup.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/providers/restore.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/providers/storage_usage.dart';
|
||||
import 'package:mangayomi/modules/more/settings/downloads/providers/downloads_state_provider.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
|
||||
class DataAndStorage extends ConsumerWidget {
|
||||
const DataAndStorage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final backupFrequency = ref.watch(backupFrequencyStateProvider);
|
||||
final autoBackupLocation = ref.watch(autoBackupLocationStateProvider);
|
||||
final downloadLocationState = ref.watch(downloadLocationStateProvider);
|
||||
final totalChapterCacheSize = ref.watch(totalChapterCacheSizeStateProvider);
|
||||
final clearChapterCacheOnAppLaunch =
|
||||
ref.watch(clearChapterCacheOnAppLaunchStateProvider);
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(l10n.data_and_storage),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(l10n.download_location),
|
||||
content: SizedBox(
|
||||
width: context.width(0.8),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
RadioListTile(
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
value: downloadLocationState.$2.isEmpty
|
||||
? downloadLocationState.$1
|
||||
: downloadLocationState.$2,
|
||||
groupValue: downloadLocationState.$1,
|
||||
onChanged: (value) {
|
||||
ref
|
||||
.read(downloadLocationStateProvider
|
||||
.notifier)
|
||||
.set("");
|
||||
Navigator.pop(context);
|
||||
},
|
||||
title: Text(downloadLocationState.$1)),
|
||||
RadioListTile(
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
value: downloadLocationState.$2.isEmpty
|
||||
? downloadLocationState.$1
|
||||
: downloadLocationState.$2,
|
||||
groupValue: downloadLocationState.$2,
|
||||
onChanged: (value) async {
|
||||
String? result = await FilePicker.platform
|
||||
.getDirectoryPath();
|
||||
|
||||
if (result != null) {
|
||||
ref
|
||||
.read(downloadLocationStateProvider
|
||||
.notifier)
|
||||
.set(result);
|
||||
} else {}
|
||||
if (!context.mounted) return;
|
||||
Navigator.pop(context);
|
||||
},
|
||||
title: Text(l10n.custom_location)),
|
||||
],
|
||||
)),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
l10n.cancel,
|
||||
style:
|
||||
TextStyle(color: context.primaryColor),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
title: Text(l10n.download_location),
|
||||
subtitle: Text(
|
||||
downloadLocationState.$2.isEmpty
|
||||
? downloadLocationState.$1
|
||||
: downloadLocationState.$2,
|
||||
style: TextStyle(fontSize: 11, color: context.secondaryColor),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.info_outline_rounded,
|
||||
color: context.secondaryColor),
|
||||
],
|
||||
),
|
||||
),
|
||||
subtitle: Text(l10n.download_location_info,
|
||||
style:
|
||||
TextStyle(fontSize: 11, color: context.secondaryColor)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(l10n.backup_and_restore,
|
||||
style:
|
||||
TextStyle(fontSize: 13, color: context.primaryColor)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SegmentedButton(
|
||||
emptySelectionAllowed: true,
|
||||
showSelectedIcon: false,
|
||||
style: TextButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(50))),
|
||||
segments: [
|
||||
ButtonSegment(
|
||||
value: 'create',
|
||||
label: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Text(l10n.create_backup),
|
||||
),
|
||||
),
|
||||
ButtonSegment(
|
||||
value: 'restore',
|
||||
label: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Text(l10n.restore_backup),
|
||||
),
|
||||
),
|
||||
],
|
||||
selected: {},
|
||||
onSelectionChanged: (newSelection) {
|
||||
if (newSelection.contains('create')) {
|
||||
context.push('/createBackup');
|
||||
} else if (newSelection.contains('restore')) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(l10n.restore_backup),
|
||||
content: SizedBox(
|
||||
width: context.width(0.8),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.info_outline_rounded,
|
||||
color:
|
||||
context.secondaryColor),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 5),
|
||||
child: Text(l10n
|
||||
.restore_backup_warning_title),
|
||||
),
|
||||
],
|
||||
)),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
l10n.cancel,
|
||||
style: TextStyle(
|
||||
color: context.primaryColor),
|
||||
)),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
try {
|
||||
FilePickerResult? result =
|
||||
await FilePicker.platform
|
||||
.pickFiles(
|
||||
allowMultiple:
|
||||
false);
|
||||
|
||||
if (result != null &&
|
||||
context.mounted) {
|
||||
ref.watch(doRestoreProvider(
|
||||
path: result
|
||||
.files.first.path!,
|
||||
context: context));
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
Navigator.pop(context);
|
||||
} catch (_) {
|
||||
botToast("Error");
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
l10n.ok,
|
||||
style: TextStyle(
|
||||
color: context.primaryColor),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
final list = _getBackupFrequencyList(context);
|
||||
return AlertDialog(
|
||||
title: Text(l10n.backup_frequency),
|
||||
content: SizedBox(
|
||||
width: context.width(0.8),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: list.length,
|
||||
itemBuilder: (context, index) {
|
||||
return RadioListTile(
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
value: index,
|
||||
groupValue: backupFrequency,
|
||||
onChanged: (value) {
|
||||
ref
|
||||
.read(backupFrequencyStateProvider
|
||||
.notifier)
|
||||
.set(value!);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
title: Row(
|
||||
children: [Text(list[index])],
|
||||
),
|
||||
);
|
||||
},
|
||||
)),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
l10n.cancel,
|
||||
style:
|
||||
TextStyle(color: context.primaryColor),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
title: Text(l10n.backup_frequency),
|
||||
subtitle: Text(
|
||||
_getBackupFrequencyList(context)[backupFrequency],
|
||||
style: TextStyle(fontSize: 11, color: context.secondaryColor),
|
||||
),
|
||||
),
|
||||
if (!Platform.isIOS)
|
||||
ListTile(
|
||||
onTap: () async {
|
||||
String? result = await FilePicker.platform.getDirectoryPath();
|
||||
|
||||
if (result != null) {
|
||||
ref
|
||||
.read(autoBackupLocationStateProvider.notifier)
|
||||
.set(result);
|
||||
}
|
||||
},
|
||||
title: Text(l10n.backup_location),
|
||||
subtitle: Text(
|
||||
autoBackupLocation.$2.isEmpty
|
||||
? autoBackupLocation.$1
|
||||
: autoBackupLocation.$2,
|
||||
style: TextStyle(fontSize: 11, color: context.secondaryColor),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.info_outline_rounded,
|
||||
color: context.secondaryColor),
|
||||
],
|
||||
),
|
||||
),
|
||||
subtitle: Text(l10n.backup_and_restore_warning_info,
|
||||
style:
|
||||
TextStyle(fontSize: 11, color: context.secondaryColor)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(l10n.storage,
|
||||
style:
|
||||
TextStyle(fontSize: 13, color: context.primaryColor)),
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(l10n.clear_chapter_and_episode_cache),
|
||||
onTap: () => ref
|
||||
.read(totalChapterCacheSizeStateProvider.notifier)
|
||||
.clearCache(),
|
||||
subtitle: Text(totalChapterCacheSize,
|
||||
style:
|
||||
TextStyle(fontSize: 11, color: context.secondaryColor)),
|
||||
),
|
||||
SwitchListTile(
|
||||
value: clearChapterCacheOnAppLaunch,
|
||||
title: Text(
|
||||
context.l10n.clear_chapter_or_episode_cache_on_app_launch),
|
||||
onChanged: (value) {
|
||||
ref
|
||||
.read(clearChapterCacheOnAppLaunchStateProvider.notifier)
|
||||
.set(value);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> _getBackupFrequencyList(BuildContext context) {
|
||||
final l10n = l10nLocalizations(context)!;
|
||||
return [
|
||||
l10n.off,
|
||||
l10n.every_6_hours,
|
||||
l10n.every_12_hours,
|
||||
l10n.daily,
|
||||
l10n.every_2_days,
|
||||
l10n.weekly
|
||||
];
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:io';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/modules/more/backup_and_restore/providers/backup.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/providers/backup.dart';
|
||||
import 'package:mangayomi/providers/storage_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
|
@ -25,14 +25,15 @@ class BackupFrequencyState extends _$BackupFrequencyState {
|
|||
class BackupFrequencyOptionsState extends _$BackupFrequencyOptionsState {
|
||||
@override
|
||||
List<int> build() {
|
||||
return isar.settings.getSync(227)!.backupFrequencyOptions ?? [0, 1, 2, 3];
|
||||
return isar.settings.getSync(227)!.backupListOptions ??
|
||||
[0, 1, 2, 3, 4, 5, 6, 7];
|
||||
}
|
||||
|
||||
void set(List<int> values) {
|
||||
final settings = isar.settings.getSync(227);
|
||||
state = values;
|
||||
isar.writeTxnSync(() =>
|
||||
isar.settings.putSync(settings!..backupFrequencyOptions = values));
|
||||
isar.writeTxnSync(
|
||||
() => isar.settings.putSync(settings!..backupListOptions = values));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -40,6 +41,7 @@ class BackupFrequencyOptionsState extends _$BackupFrequencyOptionsState {
|
|||
class AutoBackupLocationState extends _$AutoBackupLocationState {
|
||||
@override
|
||||
(String, String) build() {
|
||||
_refresh();
|
||||
return ("", isar.settings.getSync(227)!.autoBackupLocation ?? "");
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +54,7 @@ class AutoBackupLocationState extends _$AutoBackupLocationState {
|
|||
|
||||
Directory? _storageProvider;
|
||||
|
||||
Future refresh() async {
|
||||
Future _refresh() async {
|
||||
_storageProvider = Platform.isIOS
|
||||
? await StorageProvider().getIosBackupDirectory()
|
||||
: await StorageProvider().getDefaultDirectory();
|
||||
|
|
@ -41,7 +41,7 @@ final backupFrequencyStateProvider =
|
|||
|
||||
typedef _$BackupFrequencyState = AutoDisposeNotifier<int>;
|
||||
String _$backupFrequencyOptionsStateHash() =>
|
||||
r'79d93411a02867c8882d2d0f2143f5da6c107075';
|
||||
r'dd5aa850bc250e584973496fee05214b22eed9b1';
|
||||
|
||||
/// See also [BackupFrequencyOptionsState].
|
||||
@ProviderFor(BackupFrequencyOptionsState)
|
||||
|
|
@ -58,7 +58,7 @@ final backupFrequencyOptionsStateProvider = AutoDisposeNotifierProvider<
|
|||
|
||||
typedef _$BackupFrequencyOptionsState = AutoDisposeNotifier<List<int>>;
|
||||
String _$autoBackupLocationStateHash() =>
|
||||
r'80c41bb34c4ed4d07dce626dd03bb20f01672aec';
|
||||
r'be78346bb300287ae9b8581e27ee6814483400a7';
|
||||
|
||||
/// See also [AutoBackupLocationState].
|
||||
@ProviderFor(AutoBackupLocationState)
|
||||
|
|
@ -74,13 +74,6 @@ Future<void> doBackUp(Ref ref,
|
|||
.map((e) => e.toJson())
|
||||
.toList();
|
||||
datas.addAll({"tracks": res});
|
||||
final res_ = isar.trackPreferences
|
||||
.filter()
|
||||
.syncIdIsNotNull()
|
||||
.findAllSync()
|
||||
.map((e) => e.toJson())
|
||||
.toList();
|
||||
datas.addAll({"trackPreferences": res_});
|
||||
}
|
||||
if (list.contains(4)) {
|
||||
final res = isar.historys
|
||||
|
|
@ -92,6 +85,15 @@ Future<void> doBackUp(Ref ref,
|
|||
datas.addAll({"history": res});
|
||||
}
|
||||
if (list.contains(5)) {
|
||||
final res = isar.updates
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.findAllSync()
|
||||
.map((e) => e.toJson())
|
||||
.toList();
|
||||
datas.addAll({"updates": res});
|
||||
}
|
||||
if (list.contains(6)) {
|
||||
final res = isar.settings
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
|
|
@ -100,7 +102,25 @@ Future<void> doBackUp(Ref ref,
|
|||
.toList();
|
||||
datas.addAll({"settings": res});
|
||||
}
|
||||
if (list.contains(6)) {
|
||||
if (list.contains(7)) {
|
||||
final res = isar.sourcePreferences
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.findAllSync()
|
||||
.map((e) => e.toJson())
|
||||
.toList();
|
||||
datas.addAll({"extensions_preferences": res});
|
||||
}
|
||||
if (list.contains(8)) {
|
||||
final res_ = isar.trackPreferences
|
||||
.filter()
|
||||
.syncIdIsNotNull()
|
||||
.findAllSync()
|
||||
.map((e) => e.toJson())
|
||||
.toList();
|
||||
datas.addAll({"trackPreferences": res_});
|
||||
}
|
||||
if (list.contains(9)) {
|
||||
final res = isar.sources
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
|
|
@ -108,23 +128,6 @@ Future<void> doBackUp(Ref ref,
|
|||
.map((e) => e.toJson())
|
||||
.toList();
|
||||
datas.addAll({"extensions": res});
|
||||
final resSourePref = isar.sourcePreferences
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.keyIsNotNull()
|
||||
.findAllSync()
|
||||
.map((e) => e.toJson())
|
||||
.toList();
|
||||
datas.addAll({"extensions_preferences": resSourePref});
|
||||
}
|
||||
if (list.contains(7)) {
|
||||
final res = isar.updates
|
||||
.filter()
|
||||
.idIsNotNull()
|
||||
.findAllSync()
|
||||
.map((e) => e.toJson())
|
||||
.toList();
|
||||
datas.addAll({"updates": res});
|
||||
}
|
||||
final regExp = RegExp(r'[^a-zA-Z0-9 .()\-\s]');
|
||||
final name =
|
||||
|
|
@ -6,7 +6,7 @@ part of 'backup.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$doBackUpHash() => r'50189cb247f37cadd4d64ddc0377774987859681';
|
||||
String _$doBackUpHash() => r'ad907e7ff4cd9f05bb3fa2da0fd1a1f1d2c23258';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
@ -84,7 +84,7 @@ void restoreBackup(Ref ref, Map<String, dynamic> backup) {
|
|||
final extensions = (backup["extensions"] as List?)
|
||||
?.map((e) => Source.fromJson(e)..itemType = _convertToItemType(e))
|
||||
.toList();
|
||||
final extensionsPref = (backup["extensions_preferences"] as List?)
|
||||
final sourcesPrefs = (backup["extensions_preferences"] as List?)
|
||||
?.map((e) => SourcePreference.fromJson(e))
|
||||
.toList();
|
||||
final updates =
|
||||
|
|
@ -166,8 +166,8 @@ void restoreBackup(Ref ref, Map<String, dynamic> backup) {
|
|||
}
|
||||
|
||||
isar.sourcePreferences.clearSync();
|
||||
if (extensionsPref != null) {
|
||||
isar.sourcePreferences.putAllSync(extensionsPref);
|
||||
if (sourcesPrefs != null) {
|
||||
isar.sourcePreferences.putAllSync(sourcesPrefs);
|
||||
}
|
||||
isar.settings.clearSync();
|
||||
if (settings != null) {
|
||||
|
|
@ -173,7 +173,7 @@ class _DoRestoreProviderElement extends AutoDisposeProviderElement<void>
|
|||
BuildContext get context => (origin as DoRestoreProvider).context;
|
||||
}
|
||||
|
||||
String _$restoreBackupHash() => r'834ba688f122cabe7969752ce8be06552c2e70c5';
|
||||
String _$restoreBackupHash() => r'726b88cc165ac6cae83a2bbbb5d8b5533c3a1f46';
|
||||
|
||||
/// See also [restoreBackup].
|
||||
@ProviderFor(restoreBackup)
|
||||
101
lib/modules/more/data_and_storage/providers/storage_usage.dart
Normal file
101
lib/modules/more/data_and_storage/providers/storage_usage.dart
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import 'dart:io';
|
||||
import 'package:mangayomi/eval/model/m_bridge.dart';
|
||||
import 'package:mangayomi/main.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/router/router.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
part 'storage_usage.g.dart';
|
||||
|
||||
@riverpod
|
||||
class TotalChapterCacheSizeState extends _$TotalChapterCacheSizeState {
|
||||
@override
|
||||
String build() {
|
||||
_getTotalDiskSpace().then((value) => state = _formatBytes(value));
|
||||
return "0.00 B";
|
||||
}
|
||||
|
||||
final String _cacheImageMangaPath = join('Mangayomi', 'cacheimagemanga');
|
||||
final String _cacheDownloadPath = join('Mangayomi', 'downloads');
|
||||
|
||||
Future<void> clearCache({bool showToast = true}) async {
|
||||
final tempPath = (await getTemporaryDirectory()).path;
|
||||
String? msg;
|
||||
try {
|
||||
final dir = Directory(join(tempPath, _cacheImageMangaPath));
|
||||
if (dir.existsSync()) {
|
||||
await dir.delete(recursive: true);
|
||||
}
|
||||
msg = "0.00 B";
|
||||
} catch (_) {}
|
||||
try {
|
||||
final dir = Directory(join(tempPath, _cacheDownloadPath));
|
||||
if (dir.existsSync()) {
|
||||
await dir.delete(recursive: true);
|
||||
}
|
||||
msg = "0.00 B";
|
||||
} catch (_) {}
|
||||
if (msg != null && showToast) {
|
||||
state = msg;
|
||||
botToast(
|
||||
navigatorKey.currentContext?.l10n.cache_cleared ?? "Cache cleared");
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> _getTotalDiskSpace() async {
|
||||
final tempPath = (await getTemporaryDirectory()).path;
|
||||
try {
|
||||
return await _getdirectorySize(
|
||||
Directory(join(tempPath, _cacheImageMangaPath))) +
|
||||
await _getdirectorySize(
|
||||
Directory(join(tempPath, _cacheDownloadPath)));
|
||||
} catch (_) {}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Future<int> _getdirectorySize(Directory directory) async {
|
||||
try {
|
||||
if (await directory.exists()) {
|
||||
return directory
|
||||
.list(recursive: true, followLinks: false)
|
||||
.where((entity) => entity is File)
|
||||
.cast<File>()
|
||||
.fold(0, (total, file) {
|
||||
return total + file.lengthSync();
|
||||
});
|
||||
}
|
||||
} catch (_) {}
|
||||
return 0;
|
||||
}
|
||||
|
||||
String _formatBytes(int bytes) {
|
||||
const units = ['B', 'KB', 'MB', 'GB'];
|
||||
int unitIndex = 0;
|
||||
double size = bytes.toDouble();
|
||||
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
return '${size.toStringAsFixed(2)} ${units[unitIndex]}';
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class ClearChapterCacheOnAppLaunchState
|
||||
extends _$ClearChapterCacheOnAppLaunchState {
|
||||
@override
|
||||
bool build() {
|
||||
return isar.settings.getSync(227)!.clearChapterCacheOnAppLaunch ?? false;
|
||||
}
|
||||
|
||||
void set(bool value) {
|
||||
final settings = isar.settings.getSync(227);
|
||||
state = value;
|
||||
isar.writeTxnSync(() =>
|
||||
isar.settings.putSync(settings!..clearChapterCacheOnAppLaunch = value));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'storage_usage.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$totalChapterCacheSizeStateHash() =>
|
||||
r'83bf78de5ef674d61d3a9311061ef0933e59109b';
|
||||
|
||||
/// See also [TotalChapterCacheSizeState].
|
||||
@ProviderFor(TotalChapterCacheSizeState)
|
||||
final totalChapterCacheSizeStateProvider =
|
||||
AutoDisposeNotifierProvider<TotalChapterCacheSizeState, String>.internal(
|
||||
TotalChapterCacheSizeState.new,
|
||||
name: r'totalChapterCacheSizeStateProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$totalChapterCacheSizeStateHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$TotalChapterCacheSizeState = AutoDisposeNotifier<String>;
|
||||
String _$clearChapterCacheOnAppLaunchStateHash() =>
|
||||
r'6fdefd7a3bdc4309dd596dad43891c654ec2ba70';
|
||||
|
||||
/// See also [ClearChapterCacheOnAppLaunchState].
|
||||
@ProviderFor(ClearChapterCacheOnAppLaunchState)
|
||||
final clearChapterCacheOnAppLaunchStateProvider = AutoDisposeNotifierProvider<
|
||||
ClearChapterCacheOnAppLaunchState, bool>.internal(
|
||||
ClearChapterCacheOnAppLaunchState.new,
|
||||
name: r'clearChapterCacheOnAppLaunchStateProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$clearChapterCacheOnAppLaunchStateHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$ClearChapterCacheOnAppLaunchState = AutoDisposeNotifier<bool>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
|
|
@ -57,10 +57,10 @@ class MoreScreen extends StatelessWidget {
|
|||
),
|
||||
ListTileWidget(
|
||||
onTap: () {
|
||||
context.push('/backupAndRestore');
|
||||
context.push('/dataAndStorage');
|
||||
},
|
||||
icon: Icons.settings_backup_restore_sharp,
|
||||
title: l10n.backup_and_restore,
|
||||
icon: Icons.storage,
|
||||
title: l10n.data_and_storage,
|
||||
),
|
||||
const Divider(),
|
||||
ListTileWidget(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mangayomi/modules/more/settings/downloads/providers/downloads_state_provider.dart';
|
||||
import 'package:mangayomi/providers/l10n_providers.dart';
|
||||
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
|
||||
|
||||
class DownloadsScreen extends ConsumerStatefulWidget {
|
||||
const DownloadsScreen({super.key});
|
||||
|
|
@ -19,8 +17,6 @@ class _DownloadsScreenState extends ConsumerState<DownloadsScreen> {
|
|||
) {
|
||||
final saveAsCBZArchiveState = ref.watch(saveAsCBZArchiveStateProvider);
|
||||
final onlyOnWifiState = ref.watch(onlyOnWifiStateProvider);
|
||||
final downloadLocationState = ref.watch(downloadLocationStateProvider);
|
||||
ref.read(downloadLocationStateProvider.notifier).refresh();
|
||||
final l10n = l10nLocalizations(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
|
|
@ -29,83 +25,6 @@ class _DownloadsScreenState extends ConsumerState<DownloadsScreen> {
|
|||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(l10n.download_location),
|
||||
content: SizedBox(
|
||||
width: context.width(0.8),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
RadioListTile(
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
value: downloadLocationState.$2.isEmpty
|
||||
? downloadLocationState.$1
|
||||
: downloadLocationState.$2,
|
||||
groupValue: downloadLocationState.$1,
|
||||
onChanged: (value) {
|
||||
ref
|
||||
.read(downloadLocationStateProvider
|
||||
.notifier)
|
||||
.set("");
|
||||
Navigator.pop(context);
|
||||
},
|
||||
title: Text(downloadLocationState.$1)),
|
||||
RadioListTile(
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
value: downloadLocationState.$2.isEmpty
|
||||
? downloadLocationState.$1
|
||||
: downloadLocationState.$2,
|
||||
groupValue: downloadLocationState.$2,
|
||||
onChanged: (value) async {
|
||||
String? result = await FilePicker.platform
|
||||
.getDirectoryPath();
|
||||
|
||||
if (result != null) {
|
||||
ref
|
||||
.read(downloadLocationStateProvider
|
||||
.notifier)
|
||||
.set(result);
|
||||
} else {}
|
||||
if (!context.mounted) return;
|
||||
Navigator.pop(context);
|
||||
},
|
||||
title: Text(l10n.custom_location)),
|
||||
],
|
||||
)),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
l10n.cancel,
|
||||
style:
|
||||
TextStyle(color: context.primaryColor),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
title: Text(l10n.download_location),
|
||||
subtitle: Text(
|
||||
downloadLocationState.$2.isEmpty
|
||||
? downloadLocationState.$1
|
||||
: downloadLocationState.$2,
|
||||
style: TextStyle(fontSize: 11, color: context.secondaryColor),
|
||||
),
|
||||
),
|
||||
SwitchListTile(
|
||||
value: onlyOnWifiState,
|
||||
title: Text(l10n.only_on_wifi),
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ class SaveAsCBZArchiveState extends _$SaveAsCBZArchiveState {
|
|||
class DownloadLocationState extends _$DownloadLocationState {
|
||||
@override
|
||||
(String, String) build() {
|
||||
_refresh();
|
||||
return ("", isar.settings.getSync(227)!.downloadLocation ?? "");
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +53,7 @@ class DownloadLocationState extends _$DownloadLocationState {
|
|||
|
||||
Directory? _storageProvider;
|
||||
|
||||
Future refresh() async {
|
||||
Future _refresh() async {
|
||||
_storageProvider = await StorageProvider().getDefaultDirectory();
|
||||
final settings = isar.settings.getSync(227);
|
||||
state = (
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ final saveAsCBZArchiveStateProvider =
|
|||
|
||||
typedef _$SaveAsCBZArchiveState = AutoDisposeNotifier<bool>;
|
||||
String _$downloadLocationStateHash() =>
|
||||
r'e4c2215366c10f850b7d52c96c03545a9413b752';
|
||||
r'dba216dd8de1206c1c272d0f76684e016e10417f';
|
||||
|
||||
/// See also [DownloadLocationState].
|
||||
@ProviderFor(DownloadLocationState)
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@ import 'package:mangayomi/modules/browse/extension/edit_code.dart';
|
|||
import 'package:mangayomi/modules/browse/extension/extension_detail.dart';
|
||||
import 'package:mangayomi/modules/browse/extension/widgets/create_extension.dart';
|
||||
import 'package:mangayomi/modules/browse/sources/sources_filter_screen.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/create_backup.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/data_and_storage.dart';
|
||||
import 'package:mangayomi/modules/novel/novel_reader_view.dart';
|
||||
import 'package:mangayomi/modules/updates/updates_screen.dart';
|
||||
import 'package:mangayomi/modules/more/backup_and_restore/backup_and_restore.dart';
|
||||
import 'package:mangayomi/modules/more/categories/categories_screen.dart';
|
||||
import 'package:mangayomi/modules/more/settings/downloads/downloads_screen.dart';
|
||||
import 'package:mangayomi/modules/more/settings/player/player_screen.dart';
|
||||
|
|
@ -500,15 +501,15 @@ class RouterNotifier extends ChangeNotifier {
|
|||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "/backupAndRestore",
|
||||
name: "backupAndRestore",
|
||||
path: "/dataAndStorage",
|
||||
name: "dataAndStorage",
|
||||
builder: (context, state) {
|
||||
return const BackupAndRestore();
|
||||
return const DataAndStorage();
|
||||
},
|
||||
pageBuilder: (context, state) {
|
||||
return transitionPage(
|
||||
key: state.pageKey,
|
||||
child: const BackupAndRestore(),
|
||||
child: const DataAndStorage(),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -581,6 +582,19 @@ class RouterNotifier extends ChangeNotifier {
|
|||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: "/createBackup",
|
||||
name: "createBackup",
|
||||
builder: (context, state) {
|
||||
return const CreateBackup();
|
||||
},
|
||||
pageBuilder: (context, state) {
|
||||
return transitionPage(
|
||||
key: state.pageKey,
|
||||
child: const CreateBackup(),
|
||||
);
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import 'package:mangayomi/models/chapter.dart';
|
|||
import 'package:mangayomi/models/history.dart';
|
||||
import 'package:mangayomi/models/settings.dart';
|
||||
import 'package:mangayomi/models/source.dart';
|
||||
import 'package:mangayomi/modules/more/backup_and_restore/providers/restore.dart';
|
||||
import 'package:mangayomi/modules/more/data_and_storage/providers/restore.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/models/jwt.dart';
|
||||
import 'package:mangayomi/modules/more/settings/sync/providers/sync_providers.dart';
|
||||
import 'dart:convert';
|
||||
|
|
|
|||
Loading…
Reference in a new issue