added categories, import export, settings
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
/// JSON backup document: products, trips, settings, and embedded photos.
|
||||
library;
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
const int kBackupVersion = 1;
|
||||
|
||||
class BackupDocument {
|
||||
const BackupDocument({
|
||||
required this.version,
|
||||
required this.exportedAt,
|
||||
required this.darkMode,
|
||||
required this.supermarkets,
|
||||
required this.products,
|
||||
required this.shops,
|
||||
required this.shopItems,
|
||||
required this.images,
|
||||
});
|
||||
|
||||
final int version;
|
||||
final String exportedAt;
|
||||
final bool darkMode;
|
||||
final List<Map<String, Object?>> supermarkets;
|
||||
final List<Map<String, Object?>> products;
|
||||
final List<Map<String, Object?>> shops;
|
||||
final List<Map<String, Object?>> shopItems;
|
||||
final Map<String, String> images;
|
||||
|
||||
Map<String, Object?> toJson() => {
|
||||
'format': 'receipity-backup',
|
||||
'version': version,
|
||||
'exportedAt': exportedAt,
|
||||
'darkMode': darkMode,
|
||||
'supermarkets': supermarkets,
|
||||
'products': products,
|
||||
'shops': shops,
|
||||
'shopItems': shopItems,
|
||||
'images': images,
|
||||
};
|
||||
|
||||
String encode() => const JsonEncoder.withIndent(' ').convert(toJson());
|
||||
|
||||
static BackupDocument parse(String json) {
|
||||
final decoded = jsonDecode(json);
|
||||
if (decoded is! Map<String, dynamic>) {
|
||||
throw const FormatException('Backup is not a JSON object.');
|
||||
}
|
||||
if (decoded['format'] != 'receipity-backup') {
|
||||
throw const FormatException('This file is not a Receipity backup.');
|
||||
}
|
||||
final version = decoded['version'];
|
||||
if (version is! int || version < 1 || version > kBackupVersion) {
|
||||
throw const FormatException('Unsupported backup version.');
|
||||
}
|
||||
return BackupDocument(
|
||||
version: version,
|
||||
exportedAt: decoded['exportedAt'] as String? ?? '',
|
||||
darkMode: decoded['darkMode'] == true,
|
||||
supermarkets: _maps(decoded['supermarkets']),
|
||||
products: _maps(decoded['products']),
|
||||
shops: _maps(decoded['shops']),
|
||||
shopItems: _maps(decoded['shopItems']),
|
||||
images: _strings(decoded['images']),
|
||||
);
|
||||
}
|
||||
|
||||
static List<Map<String, Object?>> _maps(Object? value) {
|
||||
if (value == null) return [];
|
||||
if (value is! List) {
|
||||
throw const FormatException('Backup tables must be lists.');
|
||||
}
|
||||
return [
|
||||
for (final item in value)
|
||||
if (item is Map)
|
||||
Map<String, Object?>.from(
|
||||
item.map((key, val) => MapEntry(key.toString(), val)),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
static Map<String, String> _strings(Object? value) {
|
||||
if (value == null) return {};
|
||||
if (value is! Map) return {};
|
||||
return {
|
||||
for (final entry in value.entries)
|
||||
if (entry.value is String) entry.key.toString(): entry.value as String,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
String? backupImageKey(String? absolutePath) {
|
||||
if (absolutePath == null || absolutePath.isEmpty) return null;
|
||||
final normalized = absolutePath.replaceAll('\\', '/');
|
||||
final products = normalized.split('/products/');
|
||||
if (products.length == 2) return 'products/${products.last}';
|
||||
final receipts = normalized.split('/receipts/');
|
||||
if (receipts.length == 2) return 'receipts/${receipts.last}';
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user