getImageByHash method

Future<ImageData<Object>?> getImageByHash(
  1. String hash, {
  2. Data? owner,
})

gets image specified by its hash

Implementation

Future<ImageData?> getImageByHash(String hash, {Data? owner}) async {
  // Scoped/local hashes (with folders or local prefix) must not trigger remote fetches
  final isLocalScoped = hash.contains('/') ||
      hash.startsWith(LOCALLY_ADDED_PREFIX) ||
      isTimestampImageFilename(hash);

  // Always prefer local cache first; if present, never hit network.
  try {
    return await local.getImageByHash(hash, owner: owner);
  } catch (_) {
    // not cached locally (or unreadable) -> continue
  }

  // For local-scoped names we never download remotely.
  if (isLocalScoped) return null;

  final activeDownload = DownloadProgress.instance.active;
  if (activeDownload != null &&
      activeDownload.notifier.value.stepIndex <
          InspectionDownloadSteps.photos) {
    return null;
  }

  // Deduplicate in-flight downloads for the same hash to prevent repeated
  // downloads on rebuild/opening views.
  final key = hash;
  final existing = _inflightImageFetches[key];
  if (existing != null) return await existing;

  Future<ImageData?> fetchRemote() async {
    final progressSession = DownloadProgress.instance.active;
    final progressToken = progressSession?.beginTask(
      '',
      key: '/image/get|$hash',
      step: InspectionDownloadSteps.photos,
    );

    void completeProgress(bool success) {
      if (progressToken != null) {
        progressSession?.endTask(progressToken, success: success);
      }
    }

    try {
      await tryNetwork(requestType: Helper.SimulatedRequestType.GET);
      final rap = remote.getImageByHash(hash, owner: owner);
      final res = await remote.postJSON(rap.rd);
      if (res == null) {
        completeProgress(false);
        return null;
      }
      final parsed = await rap.parser(res);
      completeProgress(parsed != null);
      if (parsed != null) return parsed;
    } catch (_) {}
    // last-chance local read (in case another concurrent fetch stored it)
    try {
      final cached = await local.getImageByHash(hash, owner: owner);
      completeProgress(cached != null);
      return cached;
    } catch (_) {
      completeProgress(false);
      return null;
    }
  }

  Future<ImageData?> fetch() async {
    final session = DownloadProgress.instance.active;
    if (session == null) return fetchRemote();
    return session.enqueueDownload(fetchRemote);
  }

  final future = fetch();
  _inflightImageFetches[key] = future;
  try {
    return await future;
  } finally {
    _inflightImageFetches.remove(key);
  }
}