MO-Fuel-Tax-Back/lib/services/fuel_report_images.dart

70 lines
2.8 KiB
Dart

import 'dart:io';
import 'dart:typed_data';
import '../models/fuel_entry.dart';
import 'cloud/cloud_storage_provider.dart';
import 'fuel_report.dart';
/// Result of [loadReceiptImageBytes]: the photo bytes that were resolved,
/// plus which entries couldn't be — e.g. because the receipt was uploaded
/// through a *different* cloud provider than the one currently connected
/// (its stored reference is meaningless to the active provider's API), or
/// the entry is offline/the remote file's since been deleted. Surfacing
/// [failedEntries] instead of only ever silently dropping them is what
/// makes that distinguishable from "there was never a receipt at all".
class ReceiptImageLoadResult {
final Map<String, Uint8List> bytesByEntryId;
final List<FuelEntry> failedEntries;
ReceiptImageLoadResult({required this.bytesByEntryId, required this.failedEntries});
}
/// Reads (or downloads) the receipt photo for every entry in [report].
///
/// Prefers the local copy when one's still on-device; falls back to
/// downloading via [activeProvider] for entries that have already been
/// uploaded and cleared locally. An entry that fails to download (offline,
/// revoked cloud access, since-deleted remote file, a reference belonging
/// to a different provider than the one currently connected, ...) is left
/// out of [ReceiptImageLoadResult.bytesByEntryId] and reported in
/// [ReceiptImageLoadResult.failedEntries] instead — one missing receipt
/// photo shouldn't block the rest of the document, but it also shouldn't
/// vanish without a trace.
Future<ReceiptImageLoadResult> loadReceiptImageBytes({
required FuelReport report,
required CloudStorageProvider? activeProvider,
}) async {
final bytesByEntryId = <String, Uint8List>{};
final failedEntries = <FuelEntry>[];
CloudStorageSession? session;
try {
for (final row in report.rows) {
for (final entry in row.entries) {
final localPath = entry.receiptImagePath;
if (localPath != null) {
final file = File(localPath);
if (await file.exists()) {
bytesByEntryId[entry.id] = Uint8List.fromList(await file.readAsBytes());
continue;
}
}
final driveFileId = entry.receiptDriveFileId;
if (driveFileId == null || activeProvider == null || !activeProvider.isSignedIn) {
failedEntries.add(entry);
continue;
}
try {
session ??= activeProvider.beginSession();
final bytes = await session.downloadFileBytes(driveFileId);
bytesByEntryId[entry.id] = Uint8List.fromList(bytes);
} catch (_) {
failedEntries.add(entry);
}
}
}
} finally {
session?.close();
}
return ReceiptImageLoadResult(bytesByEntryId: bytesByEntryId, failedEntries: failedEntries);
}