uploadNewImagesOrFiles<DataT extends Data> method
upload / add a bunch of images
Implementation
Future<String?> uploadNewImagesOrFiles<DataT extends Data>(
DataT data,
List<XFile> files, {
Data? caller,
bool forceUpdate = false,
}) async {
final preparedFiles =
await _normalizeUploadFilesForStorage(data, files, caller: caller);
// Wenn forceOffline aktiv ist: nur lokal speichern und Request zum Retry vormerken
bool prefersOffline =
_dataPrefersCache(caller, type: Helper.SimulatedRequestType.PUT) ??
false;
// fallback: nutze Daten-Flag oder globale Option
try {
prefersOffline = prefersOffline || (caller as WithOffline).forceOffline;
} catch (_) {}
try {
prefersOffline = prefersOffline || (data as WithOffline).forceOffline;
} catch (_) {}
prefersOffline = prefersOffline || Options().forceOffline;
// Always persist images locally first (even when online), so cache and
// on-device storage are immediately available with canonical filenames.
await local.uploadNewImagesOrFiles(
data,
preparedFiles,
caller: caller,
forceUpdate: forceUpdate,
);
if (prefersOffline) {
final rap = remote.uploadNewImagesOrFiles<DataT>(data, preparedFiles);
await local.logFailedReq(rap.rd);
return 'added files offline (queued)';
}
await _touchPrueferIfNeeded(
requestType: Helper.SimulatedRequestType.PUT,
data: data,
caller: caller,
);
final requestType = Helper.SimulatedRequestType.PUT;
return _run(
itPrefersCache: _dataPrefersCache(data, type: requestType),
offline: () async => 'added files offline',
online: () => remote.uploadNewImagesOrFiles(
data,
preparedFiles,
),
onlineSuccessCB: (body) async {
// If the backend returns hashes for uploaded images, replace any local placeholders.
try {
final decoded = jsonDecode(body ?? '');
final uploaded = (decoded is Map) ? decoded['uploaded_images'] : null;
if (uploaded is List) {
final map = <String, String>{};
for (final e in uploaded) {
if (e is Map) {
final client = e['client_filename']?.toString();
final hash = e['hash']?.toString();
if (client != null &&
client.isNotEmpty &&
hash != null &&
hash.isNotEmpty) {
map[client] = hash;
final scope = local.scopeFor(data, caller: caller);
final base =
client.contains('/') ? client.split('/').last : client;
final baseNoLocalPrefix =
base.startsWith(LOCALLY_ADDED_PREFIX)
? base.substring(LOCALLY_ADDED_PREFIX.length)
: base;
final localPrefixedBase =
'$LOCALLY_ADDED_PREFIX$baseNoLocalPrefix';
map[base] = hash;
map[baseNoLocalPrefix] = hash;
map[localPrefixedBase] = hash;
String scoped(String b) => scope.isNotEmpty ? '$scope/$b' : b;
final candidateStoredNames = <String>[
scoped(baseNoLocalPrefix),
scoped(base),
scoped(localPrefixedBase),
];
String storedName = candidateStoredNames.first;
for (final candidate in candidateStoredNames) {
final f = await localFile(candidate);
if (f.existsSync()) {
storedName = candidate;
break;
}
}
await indexImageHash(
hash: hash,
storedName: storedName,
scope: scope,
);
final fallbackStoredName =
scope.isNotEmpty ? '$scope/$hash' : hash;
if (fallbackStoredName != storedName) {
try {
final fallbackFile = await localFile(fallbackStoredName);
if (fallbackFile.existsSync()) {
await fallbackFile.delete();
}
} catch (_) {}
}
}
}
}
if (map.isNotEmpty) {
String? rewrite(String? v) {
if (v == null) return null;
// match scoped values like "<scope>/<client_filename>"
final base = v.contains('/') ? v.split('/').last : v;
final repl = map[base] ?? map[v];
return repl ?? v;
}
data.mainhash = rewrite(data.mainhash);
if (data.imagehashes != null) {
data.imagehashes =
data.imagehashes!.map((h) => rewrite(h) ?? h).toList();
}
// Persist updated hashes locally so subsequent requests use backend hashes.
try {
await local.storeData(
data,
forId: caller?.id ?? await rootID,
);
} catch (_) {}
}
}
} catch (_) {}
},
// onlineSuccessCB: (response) async {},
onlineFailedCB: (onlineRes, rap) {
debugPrint('failed to upload images, ' +
rap.rd.json.toString() +
': ' +
onlineRes.toString());
var rd = rap.rd;
//biscchen ugly
// rd.multipartFiles = rd.multipartFiles.map((_e) async {
// var e = await _e;
// // e.name = newName;
// var newName = LOCALLY_ADDED_PREFIX + e.name;
// var newPath =
// e.path.substring(0, e.path.length - e.name.length) + newName;
// e.saveTo(newPath);
// e = XFile(newPath);
// return e;
// }).toList();
local.logFailedReq(rd);
},
requestType: requestType,
).last;
}