2023-09-08 11:50:21 +02:00
|
|
|
import 'dart:convert';
|
|
|
|
import 'dart:io';
|
|
|
|
|
2024-01-22 23:32:21 +01:00
|
|
|
import 'package:archive/archive_io.dart';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
|
|
|
|
import 'package:intl/intl.dart';
|
2023-09-08 11:50:21 +02:00
|
|
|
import 'package:path_provider/path_provider.dart';
|
|
|
|
import 'package:prasule/api/wallet.dart';
|
2023-12-31 11:42:27 +01:00
|
|
|
import 'package:prasule/main.dart';
|
2023-09-08 11:50:21 +02:00
|
|
|
|
2023-12-29 21:39:54 +01:00
|
|
|
/// Used for [Wallet]-managing operations
|
2023-09-08 11:50:21 +02:00
|
|
|
class WalletManager {
|
2023-12-29 21:39:54 +01:00
|
|
|
/// Returns a list of all [Wallet]s
|
2023-11-01 18:39:21 +01:00
|
|
|
static Future<List<Wallet>> listWallets() async {
|
2023-12-29 21:39:54 +01:00
|
|
|
final path =
|
2023-09-08 11:50:21 +02:00
|
|
|
Directory("${(await getApplicationDocumentsDirectory()).path}/wallets");
|
|
|
|
if (!path.existsSync()) {
|
|
|
|
path.createSync();
|
|
|
|
}
|
2023-12-29 21:39:54 +01:00
|
|
|
final wallets = <Wallet>[];
|
|
|
|
for (final w
|
|
|
|
in path.listSync().map((e) => e.path.split("/").last).toList()) {
|
2023-11-21 20:23:14 +01:00
|
|
|
try {
|
|
|
|
wallets.add(await loadWallet(w));
|
|
|
|
} catch (e) {
|
2023-12-31 11:42:27 +01:00
|
|
|
logger.e(e);
|
2023-11-21 20:23:14 +01:00
|
|
|
// TODO: do something with unreadable wallets
|
|
|
|
}
|
2023-11-01 18:39:21 +01:00
|
|
|
}
|
|
|
|
return wallets;
|
2023-09-08 11:50:21 +02:00
|
|
|
}
|
|
|
|
|
2024-01-22 14:41:16 +01:00
|
|
|
/// Deletes all [Wallet]s
|
|
|
|
static Future<void> deleteAllData() async {
|
|
|
|
final path =
|
|
|
|
Directory("${(await getApplicationDocumentsDirectory()).path}/wallets");
|
|
|
|
if (!path.existsSync()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (final entry in path.listSync()) {
|
|
|
|
logger.d("Deleting ${entry.path}");
|
|
|
|
entry.deleteSync();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-22 23:32:21 +01:00
|
|
|
/// Creates a ZIP archive from all wallets
|
|
|
|
static Future<void> exportAllWallets() async {
|
|
|
|
if (kIsWeb) {
|
|
|
|
// TODO
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
final archive = Archive();
|
|
|
|
for (final w in Directory(
|
|
|
|
"${(await getApplicationDocumentsDirectory()).path}/wallets",
|
|
|
|
).listSync()) {
|
|
|
|
if (w is! File) continue;
|
|
|
|
logger.i("Zipping ${w.path.split("/").last}");
|
|
|
|
final wf = w;
|
|
|
|
archive.addFile(
|
|
|
|
ArchiveFile.stream(
|
|
|
|
wf.path.split("/").last,
|
|
|
|
wf.lengthSync(),
|
|
|
|
InputFileStream(wf.path),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (!await FlutterFileDialog.isPickDirectorySupported()) {
|
|
|
|
File(
|
|
|
|
"${(await getApplicationDocumentsDirectory()).path}/export_${DateFormat("dd_MM_yyyy").format(DateTime.now())}.zip",
|
|
|
|
).writeAsBytesSync(ZipEncoder().encode(archive) ?? []);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
final dir = await FlutterFileDialog.pickDirectory();
|
|
|
|
if (dir == null) return;
|
|
|
|
await FlutterFileDialog.saveFileToDirectory(
|
|
|
|
directory: dir,
|
|
|
|
data: Uint8List.fromList(ZipEncoder().encode(archive) ?? []),
|
|
|
|
fileName: "export_${DateFormat("dd_MM_yyyy").format(DateTime.now())}.zip",
|
|
|
|
mimeType: "application/zip",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Exports a single [Wallet]
|
|
|
|
static Future<void> exportWallet({Wallet? wallet, String? name}) async {
|
|
|
|
if (wallet == null && name == null) {
|
|
|
|
throw Exception("You need to specify either a wallet or a name");
|
|
|
|
}
|
|
|
|
final n = name ?? wallet!.name;
|
|
|
|
|
|
|
|
if (!await FlutterFileDialog.isPickDirectorySupported()) {
|
|
|
|
File("${(await getApplicationDocumentsDirectory()).path}/wallets/$n")
|
|
|
|
.copySync(
|
2024-01-29 18:02:06 +01:00
|
|
|
"${await getApplicationDocumentsDirectory()}/export_${n.replaceAll(RegExp('[|\\?*<":>+[]/\' ]+'), '_')}_${DateFormat("dd_MM_yyyy").format(DateTime.now())}.json",
|
2024-01-22 23:32:21 +01:00
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
final dir = await FlutterFileDialog.pickDirectory();
|
|
|
|
if (dir == null) return;
|
|
|
|
await FlutterFileDialog.saveFileToDirectory(
|
|
|
|
directory: dir,
|
|
|
|
data:
|
|
|
|
File("${(await getApplicationDocumentsDirectory()).path}/wallets/$n")
|
|
|
|
.readAsBytesSync(),
|
|
|
|
fileName:
|
2024-01-29 18:02:06 +01:00
|
|
|
"export_${n.replaceAll(RegExp('[|\\?*<":>+[]/\' ]+'), '_')}_${DateFormat("dd_MM_yyyy").format(DateTime.now())}.json",
|
2024-01-22 23:32:21 +01:00
|
|
|
mimeType: "application/json",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Import a single wallet
|
|
|
|
static Future<void> importWallet({String? data}) async {
|
|
|
|
var d = data ?? "";
|
|
|
|
if (data == null) {
|
|
|
|
final filePath = await FlutterFileDialog.pickFile(
|
|
|
|
params: const OpenFileDialogParams(
|
|
|
|
mimeTypesFilter: ["application/json"],
|
|
|
|
fileExtensionsFilter: ["json"],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
if (filePath == null) return;
|
|
|
|
d = File(filePath).readAsStringSync();
|
|
|
|
}
|
|
|
|
final w = Wallet.fromJson(jsonDecode(d) as Map<String, dynamic>);
|
|
|
|
if (await WalletManager.exists(w.name)) {
|
|
|
|
throw Exception("Wallet already exists!");
|
|
|
|
}
|
|
|
|
await WalletManager.saveWallet(
|
|
|
|
w,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Imports wallets from a ZIP archive
|
|
|
|
static Future<void> importArchive() async {
|
|
|
|
final filePath = await FlutterFileDialog.pickFile(
|
|
|
|
params: const OpenFileDialogParams(
|
|
|
|
mimeTypesFilter: ["application/zip"],
|
|
|
|
fileExtensionsFilter: ["zip"],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
if (filePath == null) return;
|
|
|
|
if (kIsWeb) {
|
|
|
|
// TODO
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
final temp = Directory("${(await getTemporaryDirectory()).path}/data");
|
|
|
|
if (temp.existsSync()) {
|
|
|
|
temp.deleteSync();
|
|
|
|
}
|
|
|
|
temp.createSync(recursive: true);
|
|
|
|
final archive = ZipDecoder().decodeBuffer(InputFileStream(filePath));
|
|
|
|
for (final file in archive.files) {
|
|
|
|
if (!file.isFile) {
|
|
|
|
logger.d(file.name);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
file.writeContent(OutputFileStream("${temp.path}/${file.name}"));
|
|
|
|
}
|
|
|
|
for (final e in temp.listSync()) {
|
|
|
|
logger.d(e.path);
|
|
|
|
if (e is! File) continue;
|
|
|
|
await importWallet(data: e.readAsStringSync());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-29 21:39:54 +01:00
|
|
|
/// Loads and returns a single [Wallet] by name
|
2023-09-08 11:50:21 +02:00
|
|
|
static Future<Wallet> loadWallet(String name) async {
|
2023-12-29 21:39:54 +01:00
|
|
|
final path =
|
2023-09-08 11:50:21 +02:00
|
|
|
Directory("${(await getApplicationDocumentsDirectory()).path}/wallets");
|
2023-12-29 21:39:54 +01:00
|
|
|
final wallet = File("${path.path}/$name");
|
2023-09-08 11:50:21 +02:00
|
|
|
if (!path.existsSync()) {
|
|
|
|
path.createSync();
|
|
|
|
}
|
|
|
|
if (!wallet.existsSync()) {
|
|
|
|
return Future.error("Wallet does not exist");
|
|
|
|
}
|
2023-12-29 21:39:54 +01:00
|
|
|
return Wallet.fromJson(
|
2023-12-31 11:42:27 +01:00
|
|
|
jsonDecode(wallet.readAsStringSync()) as Map<String, dynamic>,
|
|
|
|
);
|
2023-09-08 11:50:21 +02:00
|
|
|
}
|
|
|
|
|
2023-12-29 21:39:54 +01:00
|
|
|
/// Converts [Wallet] to JSON and saves it to AppData
|
2023-11-01 18:39:21 +01:00
|
|
|
static Future<bool> saveWallet(Wallet w) async {
|
2023-12-29 21:39:54 +01:00
|
|
|
final path =
|
2023-09-08 11:50:21 +02:00
|
|
|
Directory("${(await getApplicationDocumentsDirectory()).path}/wallets");
|
2023-12-29 21:39:54 +01:00
|
|
|
final wallet = File("${path.path}/${w.name}");
|
2023-09-08 11:50:21 +02:00
|
|
|
if (!path.existsSync()) {
|
|
|
|
path.createSync();
|
|
|
|
}
|
2023-11-21 20:23:14 +01:00
|
|
|
// if (!wallet.existsSync()) return false;
|
2023-09-08 11:50:21 +02:00
|
|
|
wallet.writeAsStringSync(jsonEncode(w.toJson()));
|
2023-11-01 18:39:21 +01:00
|
|
|
return true;
|
2023-09-08 11:50:21 +02:00
|
|
|
}
|
2023-09-14 17:11:40 +02:00
|
|
|
|
2023-12-29 21:39:54 +01:00
|
|
|
/// Deletes the corresponding [Wallet] file
|
2023-09-14 17:11:40 +02:00
|
|
|
static Future<void> deleteWallet(Wallet w) async {
|
2023-12-29 21:39:54 +01:00
|
|
|
final path =
|
2023-09-14 17:11:40 +02:00
|
|
|
Directory("${(await getApplicationDocumentsDirectory()).path}/wallets");
|
2023-12-29 21:39:54 +01:00
|
|
|
File("${path.path}/${w.name}").deleteSync();
|
2023-09-14 17:11:40 +02:00
|
|
|
}
|
2024-01-22 23:32:21 +01:00
|
|
|
|
|
|
|
/// Checks if the wallet exists
|
|
|
|
static Future<bool> exists(String name) async {
|
|
|
|
return File(
|
|
|
|
"${(await getApplicationDocumentsDirectory()).path}/wallets/$name",
|
|
|
|
).existsSync();
|
|
|
|
}
|
2023-09-08 11:50:21 +02:00
|
|
|
}
|