refactor(mobile): consolidate image requests (#25743)

remote url image provider

remove cached_network_image

formatting

linting

remove thumb provider

formatting
This commit is contained in:
Mert
2026-02-05 12:16:42 -05:00
committed by GitHub
parent 237ea3aedd
commit ba2dfa7df6
32 changed files with 95 additions and 501 deletions

View File

@@ -10,8 +10,8 @@ import 'package:immich_mobile/presentation/widgets/people/person_edit_name_modal
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/people.provider.dart';
import 'package:immich_mobile/providers/routes.provider.dart';
import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/utils/image_url_builder.dart';
import 'package:immich_mobile/utils/people.utils.dart';
@@ -108,8 +108,6 @@ class _PeopleAvatar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final headers = ApiService.getRequestHeaders();
return ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 96),
child: Padding(
@@ -127,7 +125,7 @@ class _PeopleAvatar extends StatelessWidget {
elevation: 3,
child: CircleAvatar(
maxRadius: imageSize / 2,
backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers),
backgroundImage: RemoteImageProvider(url: getFaceThumbnailUrl(person.id)),
),
),
),

View File

@@ -134,7 +134,7 @@ ImageProvider? getThumbnailImageProvider(BaseAsset asset, {Size size = kThumbnai
final assetId = asset is RemoteAsset ? asset.id : (asset as LocalAsset).remoteId;
final thumbhash = asset is RemoteAsset ? asset.thumbHash ?? "" : "";
return assetId != null ? RemoteThumbProvider(assetId: assetId, thumbhash: thumbhash) : null;
return assetId != null ? RemoteImageProvider.thumbnail(assetId: assetId, thumbhash: thumbhash) : null;
}
bool _shouldUseLocalAsset(BaseAsset asset) =>

View File

@@ -10,50 +10,48 @@ import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/utils/image_url_builder.dart';
import 'package:openapi/api.dart';
class RemoteThumbProvider extends CancellableImageProvider<RemoteThumbProvider>
with CancellableImageProviderMixin<RemoteThumbProvider> {
final String assetId;
final String thumbhash;
class RemoteImageProvider extends CancellableImageProvider<RemoteImageProvider>
with CancellableImageProviderMixin<RemoteImageProvider> {
final String url;
RemoteThumbProvider({required this.assetId, required this.thumbhash});
RemoteImageProvider({required this.url});
RemoteImageProvider.thumbnail({required String assetId, required String thumbhash})
: url = getThumbnailUrlForRemoteId(assetId, thumbhash: thumbhash);
@override
Future<RemoteThumbProvider> obtainKey(ImageConfiguration configuration) {
Future<RemoteImageProvider> obtainKey(ImageConfiguration configuration) {
return SynchronousFuture(this);
}
@override
ImageStreamCompleter loadImage(RemoteThumbProvider key, ImageDecoderCallback decode) {
ImageStreamCompleter loadImage(RemoteImageProvider key, ImageDecoderCallback decode) {
return OneFramePlaceholderImageStreamCompleter(
_codec(key, decode),
informationCollector: () => <DiagnosticsNode>[
DiagnosticsProperty<ImageProvider>('Image provider', this),
DiagnosticsProperty<String>('Asset Id', key.assetId),
DiagnosticsProperty<String>('URL', key.url),
],
onDispose: cancel,
);
}
Stream<ImageInfo> _codec(RemoteThumbProvider key, ImageDecoderCallback decode) {
final request = this.request = RemoteImageRequest(
uri: getThumbnailUrlForRemoteId(key.assetId, thumbhash: key.thumbhash),
headers: ApiService.getRequestHeaders(),
);
Stream<ImageInfo> _codec(RemoteImageProvider key, ImageDecoderCallback decode) {
final request = this.request = RemoteImageRequest(uri: key.url, headers: ApiService.getRequestHeaders());
return loadRequest(request, decode);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (other is RemoteThumbProvider) {
return assetId == other.assetId && thumbhash == other.thumbhash;
if (other is RemoteImageProvider) {
return url == other.url;
}
return false;
}
@override
int get hashCode => assetId.hashCode ^ thumbhash.hashCode;
int get hashCode => url.hashCode;
}
class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImageProvider>
@@ -73,7 +71,7 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
ImageStreamCompleter loadImage(RemoteFullImageProvider key, ImageDecoderCallback decode) {
return OneFramePlaceholderImageStreamCompleter(
_codec(key, decode),
initialImage: getInitialImage(RemoteThumbProvider(assetId: key.assetId, thumbhash: key.thumbhash)),
initialImage: getInitialImage(RemoteImageProvider.thumbnail(assetId: key.assetId, thumbhash: key.thumbhash)),
informationCollector: () => <DiagnosticsNode>[
DiagnosticsProperty<ImageProvider>('Image provider', this),
DiagnosticsProperty<String>('Asset Id', key.assetId),

View File

@@ -27,7 +27,7 @@ class Thumbnail extends StatefulWidget {
this.fit = BoxFit.cover,
Size size = kThumbnailResolution,
super.key,
}) : imageProvider = RemoteThumbProvider(assetId: remoteId, thumbhash: thumbhash),
}) : imageProvider = RemoteImageProvider.thumbnail(assetId: remoteId, thumbhash: thumbhash),
thumbhashProvider = null;
Thumbnail.fromAsset({

View File

@@ -1,10 +1,9 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart';
class PartnerUserAvatar extends StatelessWidget {
const PartnerUserAvatar({super.key, required this.partner});
@@ -18,11 +17,7 @@ class PartnerUserAvatar extends StatelessWidget {
return CircleAvatar(
radius: 16,
backgroundColor: context.primaryColor.withAlpha(50),
foregroundImage: CachedNetworkImageProvider(
url,
headers: ApiService.getRequestHeaders(),
cacheKey: "user-${partner.id}-profile",
),
foregroundImage: RemoteImageProvider(url: url),
// silence errors if user has no profile image, use initials as fallback
onForegroundImageError: (exception, stackTrace) {},
child: Text(nameFirstLetter.toUpperCase()),