2024-06-30 20:25:36 +02:00
|
|
|
// SPDX-FileCopyrightText: (C) 2024 Matyáš Caras
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
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 {
|
2024-06-30 11:30:07 +02:00
|
|
|
/// Currently selected wallet
|
|
|
|
static Wallet? selectedWallet;
|
|
|
|
|
|
|
|
/// Path to the directory with wallet files
|
|
|
|
///
|
|
|
|
/// Saved beforehand so we don't have to use async everywhere
|
|
|
|
static late String walletPath;
|
|
|
|
|
2023-12-29 21:39:54 +01:00
|
|
|
/// Returns a list of all [Wallet]s
|
2024-06-30 11:30:07 +02:00
|
|
|
static List<Wallet> listWallets() {
|
|
|
|
final path = Directory(walletPath);
|
2023-09-08 11:50:21 +02:00
|
|
|
if (!path.existsSync()) {
|
|
|
|
path.createSync();
|
|
|
|
}
|
2023-12-29 21:39:54 +01:00
|
|
|
final wallets = <Wallet>[];
|
2024-06-30 11:30:33 +02:00
|
|
|
for (final w in path
|
|
|
|
.listSync()
|
|
|
|
.whereType<File>()
|
|
|
|
.where((e) => e.path.endsWith(".json"))
|
|
|
|
.map((e) => e.path.split("/").last)
|
|
|
|
.toList()) {
|
2023-11-21 20:23:14 +01:00
|
|
|
try {
|
2024-06-30 11:30:07 +02:00
|
|
|
wallets.add(loadWallet(w));
|
2023-11-21 20:23:14 +01:00
|
|
|
} 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
|
2024-06-30 11:30:07 +02:00
|
|
|
static void deleteAllData() {
|
|
|
|
final path = Directory(walletPath);
|
2024-01-22 14:41:16 +01:00
|
|
|
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>);
|
2024-06-30 11:30:07 +02:00
|
|
|
if (WalletManager.exists(w.name)) {
|
2024-01-22 23:32:21 +01:00
|
|
|
throw Exception("Wallet already exists!");
|
|
|
|
}
|
2024-06-30 11:30:07 +02:00
|
|
|
WalletManager.saveWallet(
|
2024-01-22 23:32:21 +01:00
|
|
|
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
|
2024-06-30 11:30:07 +02:00
|
|
|
static Wallet loadWallet(String name) {
|
|
|
|
final path = Directory(walletPath);
|
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()) {
|
2024-06-30 11:30:07 +02:00
|
|
|
throw Exception("Wallet does not exist");
|
2023-09-08 11:50:21 +02:00
|
|
|
}
|
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
|
2024-06-30 11:30:07 +02:00
|
|
|
static void saveWallet(Wallet w) {
|
|
|
|
final path = Directory(walletPath);
|
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-09-14 17:11:40 +02:00
|
|
|
|
2023-12-29 21:39:54 +01:00
|
|
|
/// Deletes the corresponding [Wallet] file
|
2024-06-30 11:30:07 +02:00
|
|
|
static void deleteWallet(Wallet w) {
|
|
|
|
final path = Directory(walletPath);
|
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
|
2024-06-30 11:30:07 +02:00
|
|
|
static bool exists(String name) {
|
2024-01-22 23:32:21 +01:00
|
|
|
return File(
|
2024-06-30 11:30:07 +02:00
|
|
|
"$walletPath/$name",
|
2024-01-22 23:32:21 +01:00
|
|
|
).existsSync();
|
|
|
|
}
|
2023-09-08 11:50:21 +02:00
|
|
|
}
|