70 lines
2.8 KiB
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);
|
|
}
|