256 lines
7.8 KiB
Dart
256 lines
7.8 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:warp_api/data_fb_generated.dart';
|
|
import 'package:warp_api/warp_api.dart';
|
|
import 'coin/coins.dart';
|
|
import 'generated/l10n.dart';
|
|
import 'main.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
import 'qrscanner.dart';
|
|
|
|
class ResetPage extends StatefulWidget {
|
|
@override
|
|
State<StatefulWidget> createState() => _ResetState();
|
|
}
|
|
|
|
class _ResetState extends State<ResetPage> {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(title: Text('Emergency Menu')),
|
|
body: Center(
|
|
child: Padding(
|
|
padding: EdgeInsets.all(16),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
ElevatedButton(
|
|
onPressed: _onReset,
|
|
child: Text("TRUNCATE DB (Keeps accounts)")),
|
|
ElevatedButton(
|
|
onPressed: _onBackup,
|
|
child: Text("BACKUP ALL ACCOUNTS")),
|
|
ElevatedButton(
|
|
onPressed: _onRestore,
|
|
child: Text("RESTORE ALL ACCOUNTS")),
|
|
]))));
|
|
}
|
|
|
|
_onReset() async {
|
|
final s = S.of(context);
|
|
final confirmation = await showDialog<bool>(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => AlertDialog(
|
|
title: Text(s.applicationReset),
|
|
content: Text(s.confirmResetApp),
|
|
actions: confirmButtons(context, () {
|
|
Navigator.of(context).pop(true);
|
|
}, okLabel: s.reset, cancelValue: false)),
|
|
) ??
|
|
false;
|
|
if (confirmation) {
|
|
WarpApi.resetApp();
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.clear();
|
|
await showMessageBox(
|
|
context, s.restart, s.pleaseQuitAndRestartTheAppNow, s.ok);
|
|
}
|
|
}
|
|
|
|
_onBackup() {
|
|
Navigator.of(context).pushNamed('/fullBackup');
|
|
}
|
|
|
|
_onRestore() {
|
|
Navigator.of(context).pushNamed('/fullRestore');
|
|
}
|
|
}
|
|
|
|
class FullBackupPage extends StatefulWidget {
|
|
@override
|
|
State<StatefulWidget> createState() => FullBackupState();
|
|
}
|
|
|
|
class FullBackupState extends State<FullBackupPage> {
|
|
final key = TextEditingController();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final s = S.of(context);
|
|
return Observer(builder: (context) {
|
|
key.text = settings.backupEncKey;
|
|
return Scaffold(
|
|
appBar: AppBar(title: Text(s.fullBackup)),
|
|
body: Card(
|
|
child: Column(children: [
|
|
QRInput(s.backupEncryptionKey, key, hint: 'age'),
|
|
Padding(padding: EdgeInsets.symmetric(vertical: 8)),
|
|
ButtonBar(children: [
|
|
ElevatedButton.icon(
|
|
onPressed: _onGenerate,
|
|
icon: Icon(Icons.key),
|
|
label: Text('Generate')),
|
|
ElevatedButton.icon(
|
|
onPressed: () => _onSave(context),
|
|
icon: Icon(Icons.save),
|
|
label: Text(s.saveBackup))
|
|
])
|
|
])));
|
|
});
|
|
}
|
|
|
|
_onSave(BuildContext context) async {
|
|
if (key.text.isNotEmpty) {
|
|
try {
|
|
settings.setBackupEncKey(key.text);
|
|
WarpApi.zipBackup(key.text, settings.tempDir);
|
|
final backupPath = p.join(settings.tempDir, BACKUP_NAME);
|
|
await exportFile(context, backupPath, BACKUP_NAME);
|
|
Navigator.of(context).pop();
|
|
} on String catch (msg) {
|
|
showSnackBar(msg, error: true);
|
|
}
|
|
}
|
|
}
|
|
|
|
_onGenerate() async {
|
|
final keys = WarpApi.generateKey();
|
|
final navigator = Navigator.of(context);
|
|
final state = GlobalKey<AGEKeysState>();
|
|
final assign = await showDialog<bool?>(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
contentPadding: EdgeInsets.all(16),
|
|
title: Text('Backup Keys'),
|
|
content: AGEKeysForm(keys, key: state),
|
|
actions: [
|
|
ElevatedButton.icon(
|
|
icon: Icon(Icons.cancel),
|
|
label: Text(S.current.cancel),
|
|
onPressed: () {
|
|
navigator.pop(null);
|
|
}),
|
|
ElevatedButton.icon(
|
|
icon: Icon(Icons.check),
|
|
label: Text(S.current.set),
|
|
onPressed: () {
|
|
navigator.pop(true);
|
|
})
|
|
])) ??
|
|
false;
|
|
if (assign) settings.setBackupEncKey(state.currentState!.pk.text);
|
|
}
|
|
}
|
|
|
|
class AGEKeysForm extends StatefulWidget {
|
|
final Agekeys keys;
|
|
AGEKeysForm(this.keys, {Key? key}) : super(key: key);
|
|
|
|
@override
|
|
State<StatefulWidget> createState() => AGEKeysState();
|
|
}
|
|
|
|
class AGEKeysState extends State<AGEKeysForm> {
|
|
final pk = TextEditingController();
|
|
final sk = TextEditingController();
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
pk.text = widget.keys.pk!;
|
|
sk.text = widget.keys.sk!;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final s = S.of(context);
|
|
return Form(
|
|
child: SingleChildScrollView(
|
|
child: Column(children: [
|
|
QRDisplay(s.encryptionKey, widget.keys.pk!),
|
|
QRDisplay(s.secretKey, widget.keys.sk!)
|
|
])));
|
|
}
|
|
}
|
|
|
|
class FullRestorePage extends StatefulWidget {
|
|
@override
|
|
State<FullRestorePage> createState() => _FullRestoreState();
|
|
}
|
|
|
|
class _FullRestoreState extends State<FullRestorePage> {
|
|
final controller = TextEditingController();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final s = S.of(context);
|
|
return Scaffold(
|
|
appBar: AppBar(title: Text(s.fullRestore)),
|
|
body: Card(
|
|
child: Column(children: [
|
|
QRInput(s.secretKey, controller, hint: 'AGE-SECRET-KEY-'),
|
|
Padding(padding: EdgeInsets.symmetric(vertical: 8)),
|
|
ElevatedButton.icon(
|
|
onPressed: _onLoad,
|
|
icon: Icon(Icons.open_in_new),
|
|
label: Text(s.loadBackup))
|
|
])));
|
|
}
|
|
|
|
_onLoad() async {
|
|
final s = S.current;
|
|
final result = await pickFile();
|
|
|
|
if (result != null) {
|
|
final filename = result.files.single.path!;
|
|
final key = controller.text;
|
|
try {
|
|
if (key.isNotEmpty) {
|
|
WarpApi.unzipBackup(key, filename, settings.tempDir);
|
|
} else {
|
|
final file = File(filename);
|
|
final backup = await file.readAsString();
|
|
WarpApi.importFromZWL(active.coin, "ZWL Imported Account", backup);
|
|
}
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setBool('recover', true);
|
|
showSnackBar(s.databaseRestored);
|
|
await showRestartMessage(); // This doesn't return
|
|
} on String catch (message) {
|
|
showSnackBar(message, error: true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> showRestartMessage() async {
|
|
final context = navigatorKey.currentContext!;
|
|
await showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => AlertDialog(
|
|
content: Text(S.of(context).pleaseQuitAndRestartTheAppNow)));
|
|
}
|
|
|
|
Future<void> clearApp(BuildContext context) async {
|
|
final reset = await showDialog<bool>(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => AlertDialog(
|
|
title: Text('Reset Database'),
|
|
content: Text('Are you sure you want to DELETE ALL your data?'),
|
|
actions: confirmButtons(
|
|
context, () => Navigator.of(context).pop(true)),
|
|
)) ??
|
|
false;
|
|
if (reset) {
|
|
final c = coins.first;
|
|
File(c.dbDir).deleteSync(recursive: true);
|
|
}
|
|
}
|