Files
venera/lib/pages/settings/app.dart

417 lines
14 KiB
Dart

part of 'settings_page.dart';
class AppSettings extends StatefulWidget {
const AppSettings({super.key});
@override
State<AppSettings> createState() => _AppSettingsState();
}
class _AppSettingsState extends State<AppSettings> {
@override
Widget build(BuildContext context) {
return SmoothCustomScrollView(
slivers: [
SliverAppbar(title: Text("App".tl)),
_SettingPartTitle(
title: "Data".tl,
icon: Icons.storage,
),
ListTile(
title: Text("Storage Path for local comics".tl),
subtitle: Text(LocalManager().path, softWrap: false),
trailing: IconButton(
icon: const Icon(Icons.copy),
onPressed: () {
Clipboard.setData(ClipboardData(text: LocalManager().path));
context.showMessage(message: "Path copied to clipboard".tl);
},
),
).toSliver(),
_CallbackSetting(
title: "Set New Storage Path".tl,
actionTitle: "Set".tl,
callback: () async {
var result;
if (App.isAndroid) {
context.showMessage(message: "Not supported".tl);
return;
}
else if (App.isIOS) {
result = await selectDirectoryIOS();
} else {
result = await selectDirectory();
}
if (result == null) return;
var loadingDialog = showLoadingDialog(
App.rootContext,
barrierDismissible: false,
allowCancel: false,
);
var res = await LocalManager().setNewPath(result);
loadingDialog.close();
if (res != null) {
context.showMessage(message: res);
} else {
context.showMessage(message: "Path set successfully".tl);
setState(() {});
}
},
).toSliver(),
ListTile(
title: Text("Cache Size".tl),
subtitle: Text(bytesToReadableString(CacheManager().currentSize)),
).toSliver(),
_CallbackSetting(
title: "Clear Cache".tl,
actionTitle: "Clear".tl,
callback: () async {
var loadingDialog = showLoadingDialog(
App.rootContext,
barrierDismissible: false,
allowCancel: false,
);
await CacheManager().clear();
loadingDialog.close();
context.showMessage(message: "Cache cleared".tl);
setState(() {});
},
).toSliver(),
_CallbackSetting(
title: "Cache Limit".tl,
subtitle: "${appdata.settings['cacheSize']} MB",
callback: () {
showInputDialog(
context: context,
title: "Set Cache Limit".tl,
hintText: "Size in MB".tl,
inputValidator: RegExp(r"^\d+$"),
onConfirm: (value) {
appdata.settings['cacheSize'] = int.parse(value);
appdata.saveData();
setState(() {});
CacheManager().setLimitSize(appdata.settings['cacheSize']);
return null;
},
);
},
actionTitle: 'Set'.tl,
).toSliver(),
_CallbackSetting(
title: "Export App Data".tl,
callback: () async {
var controller = showLoadingDialog(context);
var file = await exportAppData();
await saveFile(filename: "data.venera", file: file);
controller.close();
},
actionTitle: 'Export'.tl,
).toSliver(),
_CallbackSetting(
title: "Import App Data".tl,
callback: () async {
var controller = showLoadingDialog(context);
var file = await selectFile(ext: ['venera']);
if (file != null) {
var cacheFile = File(FilePath.join(App.cachePath, "temp.venera"));
await file.saveTo(cacheFile.path);
try {
await importAppData(cacheFile);
} catch (e, s) {
Log.error("Import data", e.toString(), s);
context.showMessage(message: "Failed to import data".tl);
}
}
controller.close();
},
actionTitle: 'Import'.tl,
).toSliver(),
_CallbackSetting(
title: "Data Sync".tl,
callback: () async {
showPopUpWidget(context, const _WebdavSetting());
},
actionTitle: 'Set'.tl,
).toSliver(),
_SettingPartTitle(
title: "Log".tl,
icon: Icons.error_outline,
),
_CallbackSetting(
title: "Open Log".tl,
callback: () {
context.to(() => const LogsPage());
},
actionTitle: 'Open'.tl,
).toSliver(),
_SettingPartTitle(
title: "User".tl,
icon: Icons.person_outline,
),
SelectSetting(
title: "Language".tl,
settingKey: "language",
optionTranslation: const {
"system": "System",
"zh-CN": "简体中文",
"zh-TW": "繁體中文",
"en-US": "English",
},
onChanged: () {
App.forceRebuild();
},
).toSliver(),
],
);
}
}
class LogsPage extends StatefulWidget {
const LogsPage({super.key});
@override
State<LogsPage> createState() => _LogsPageState();
}
class _LogsPageState extends State<LogsPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: Appbar(
title: const Text("Logs"),
actions: [
IconButton(
onPressed: () => setState(() {
final RelativeRect position = RelativeRect.fromLTRB(
MediaQuery.of(context).size.width,
MediaQuery.of(context).padding.top + kToolbarHeight,
0.0,
0.0,
);
showMenu(context: context, position: position, items: [
PopupMenuItem(
child: Text("Clear".tl),
onTap: () => setState(() => Log.clear()),
),
PopupMenuItem(
child: Text("Disable Length Limitation".tl),
onTap: () {
Log.ignoreLimitation = true;
context.showMessage(
message: "Only valid for this run");
},
),
PopupMenuItem(
child: Text("Export".tl),
onTap: () => saveLog(Log().toString()),
),
]);
}),
icon: const Icon(Icons.more_horiz))
],
),
body: ListView.builder(
reverse: true,
controller: ScrollController(),
itemCount: Log.logs.length,
itemBuilder: (context, index) {
index = Log.logs.length - index - 1;
return Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
child: SelectionArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.surfaceContainerHighest,
borderRadius:
const BorderRadius.all(Radius.circular(16)),
),
child: Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 5, 1),
child: Text(Log.logs[index].title),
),
),
const SizedBox(
width: 3,
),
Container(
decoration: BoxDecoration(
color: [
Theme.of(context).colorScheme.error,
Theme.of(context).colorScheme.errorContainer,
Theme.of(context).colorScheme.primaryContainer
][Log.logs[index].level.index],
borderRadius:
const BorderRadius.all(Radius.circular(16)),
),
child: Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 5, 1),
child: Text(
Log.logs[index].level.name,
style: TextStyle(
color: Log.logs[index].level.index == 0
? Colors.white
: Colors.black),
),
),
),
],
),
Text(Log.logs[index].content),
Text(Log.logs[index].time
.toString()
.replaceAll(RegExp(r"\.\w+"), "")),
TextButton(
onPressed: () {
Clipboard.setData(
ClipboardData(text: Log.logs[index].content));
},
child: Text("Copy".tl),
),
const Divider(),
],
),
),
);
},
),
);
}
void saveLog(String log) async {
saveFile(data: utf8.encode(log), filename: 'log.txt');
}
}
class _WebdavSetting extends StatefulWidget {
const _WebdavSetting();
@override
State<_WebdavSetting> createState() => _WebdavSettingState();
}
class _WebdavSettingState extends State<_WebdavSetting> {
String url = "";
String user = "";
String pass = "";
bool isTesting = false;
bool upload = true;
@override
void initState() {
super.initState();
if (appdata.settings['webdav'] is! List) {
appdata.settings['webdav'] = [];
}
var configs = appdata.settings['webdav'] as List;
if (configs.whereType<String>().length != 3) {
return;
}
url = configs[0];
user = configs[1];
pass = configs[2];
}
@override
Widget build(BuildContext context) {
return PopUpWidgetScaffold(
title: "Webdav",
body: SingleChildScrollView(
child: Column(
children: [
const SizedBox(height: 12),
TextField(
decoration: const InputDecoration(
labelText: "URL",
border: OutlineInputBorder(),
),
controller: TextEditingController(text: url),
onChanged: (value) => url = value,
),
const SizedBox(height: 12),
TextField(
decoration: InputDecoration(
labelText: "Username".tl,
border: const OutlineInputBorder(),
),
controller: TextEditingController(text: user),
onChanged: (value) => user = value,
),
const SizedBox(height: 12),
TextField(
decoration: InputDecoration(
labelText: "Password".tl,
border: const OutlineInputBorder(),
),
controller: TextEditingController(text: pass),
onChanged: (value) => pass = value,
),
const SizedBox(height: 12),
Row(
children: [
Text("Operation".tl),
Radio<bool>(
groupValue: upload,
value: true,
onChanged: (value) {
setState(() {
upload = value!;
});
},
),
Text("Upload".tl),
Radio<bool>(
groupValue: upload,
value: false,
onChanged: (value) {
setState(() {
upload = value!;
});
},
),
Text("Download".tl),
],
),
const SizedBox(height: 16),
Center(
child: Button.filled(
isLoading: isTesting,
onPressed: () async {
var oldConfig = appdata.settings['webdav'];
appdata.settings['webdav'] = [url, user, pass];
setState(() {
isTesting = true;
});
var testResult = upload
? await DataSync().uploadData()
: await DataSync().downloadData();
if (testResult.error) {
setState(() {
isTesting = false;
});
appdata.settings['webdav'] = oldConfig;
context.showMessage(message: testResult.errorMessage!);
return;
}
appdata.saveData();
context.showMessage(message: "Saved".tl);
App.rootPop();
},
child: Text("Continue".tl),
),
)
],
).paddingHorizontal(16),
),
);
}
}