mirror of
https://github.com/immich-app/immich.git
synced 2025-08-11 09:16:31 -04:00
feat: view shared links inside of mobile app
This commit is contained in:
parent
f972b8d514
commit
c8c6f86518
21
mobile/lib/domain/models/album/shared_album.model.dart
Normal file
21
mobile/lib/domain/models/album/shared_album.model.dart
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
|
|
||||||
|
class SharedRemoteAlbum extends RemoteAlbum {
|
||||||
|
final List<RemoteAsset> assets;
|
||||||
|
|
||||||
|
const SharedRemoteAlbum({
|
||||||
|
required super.id,
|
||||||
|
required this.assets,
|
||||||
|
required super.name,
|
||||||
|
required super.ownerId,
|
||||||
|
required super.description,
|
||||||
|
required super.createdAt,
|
||||||
|
required super.updatedAt,
|
||||||
|
super.thumbnailAssetId,
|
||||||
|
required super.isActivityEnabled,
|
||||||
|
required super.order,
|
||||||
|
required super.assetCount,
|
||||||
|
required super.ownerName,
|
||||||
|
});
|
||||||
|
}
|
14
mobile/lib/domain/services/remote_shared_album.service.dart
Normal file
14
mobile/lib/domain/services/remote_shared_album.service.dart
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:immich_mobile/domain/models/album/shared_album.model.dart';
|
||||||
|
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
|
||||||
|
|
||||||
|
class RemoteSharedAlbumService {
|
||||||
|
final DriftAlbumApiRepository _albumApiRepository;
|
||||||
|
|
||||||
|
const RemoteSharedAlbumService(this._albumApiRepository);
|
||||||
|
|
||||||
|
Future<SharedRemoteAlbum?> getSharedAlbum(String albumId) {
|
||||||
|
return _albumApiRepository.getShared(albumId);
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,9 @@
|
|||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/search_result.model.dart';
|
import 'package:immich_mobile/domain/models/search_result.model.dart';
|
||||||
import 'package:immich_mobile/extensions/string_extensions.dart';
|
import 'package:immich_mobile/extensions/string_extensions.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/search_api.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/search_api.repository.dart';
|
||||||
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
||||||
|
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:openapi/api.dart' as api show AssetVisibility;
|
|
||||||
import 'package:openapi/api.dart' hide AssetVisibility;
|
import 'package:openapi/api.dart' hide AssetVisibility;
|
||||||
|
|
||||||
class SearchService {
|
class SearchService {
|
||||||
@ -52,41 +51,3 @@ class SearchService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on AssetResponseDto {
|
|
||||||
RemoteAsset toDto() {
|
|
||||||
return RemoteAsset(
|
|
||||||
id: id,
|
|
||||||
name: originalFileName,
|
|
||||||
checksum: checksum,
|
|
||||||
createdAt: fileCreatedAt,
|
|
||||||
updatedAt: updatedAt,
|
|
||||||
ownerId: ownerId,
|
|
||||||
visibility: switch (visibility) {
|
|
||||||
api.AssetVisibility.timeline => AssetVisibility.timeline,
|
|
||||||
api.AssetVisibility.hidden => AssetVisibility.hidden,
|
|
||||||
api.AssetVisibility.archive => AssetVisibility.archive,
|
|
||||||
api.AssetVisibility.locked => AssetVisibility.locked,
|
|
||||||
_ => AssetVisibility.timeline,
|
|
||||||
},
|
|
||||||
durationInSeconds: duration.toDuration()?.inSeconds ?? 0,
|
|
||||||
height: exifInfo?.exifImageHeight?.toInt(),
|
|
||||||
width: exifInfo?.exifImageWidth?.toInt(),
|
|
||||||
isFavorite: isFavorite,
|
|
||||||
livePhotoVideoId: livePhotoVideoId,
|
|
||||||
thumbHash: thumbhash,
|
|
||||||
localId: null,
|
|
||||||
type: type.toAssetType(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension on AssetTypeEnum {
|
|
||||||
AssetType toAssetType() => switch (this) {
|
|
||||||
AssetTypeEnum.IMAGE => AssetType.image,
|
|
||||||
AssetTypeEnum.VIDEO => AssetType.video,
|
|
||||||
AssetTypeEnum.AUDIO => AssetType.audio,
|
|
||||||
AssetTypeEnum.OTHER => AssetType.other,
|
|
||||||
_ => throw Exception('Unknown AssetType value: $this'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
@ -158,6 +158,7 @@ class TimelineService {
|
|||||||
BaseAsset getRandomAsset() => _buffer.elementAt(math.Random().nextInt(_buffer.length));
|
BaseAsset getRandomAsset() => _buffer.elementAt(math.Random().nextInt(_buffer.length));
|
||||||
|
|
||||||
BaseAsset getAsset(int index) {
|
BaseAsset getAsset(int index) {
|
||||||
|
print("buffer len: " + _buffer.length.toString());
|
||||||
if (!hasRange(index, 1)) {
|
if (!hasRange(index, 1)) {
|
||||||
throw RangeError(
|
throw RangeError(
|
||||||
'TimelineService::getAsset Index $index not in buffer range [$_bufferOffset, ${_bufferOffset + _buffer.length})',
|
'TimelineService::getAsset Index $index not in buffer range [$_bufferOffset, ${_bufferOffset + _buffer.length})',
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
|
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
enum SharedLinkSource { album, individual }
|
enum SharedLinkSource { album, individual }
|
||||||
@ -14,6 +16,8 @@ class SharedLink {
|
|||||||
final String key;
|
final String key;
|
||||||
final bool showMetadata;
|
final bool showMetadata;
|
||||||
final SharedLinkSource type;
|
final SharedLinkSource type;
|
||||||
|
final List<RemoteAsset> assets;
|
||||||
|
final String? albumId;
|
||||||
|
|
||||||
const SharedLink({
|
const SharedLink({
|
||||||
required this.id,
|
required this.id,
|
||||||
@ -27,6 +31,8 @@ class SharedLink {
|
|||||||
required this.key,
|
required this.key,
|
||||||
required this.showMetadata,
|
required this.showMetadata,
|
||||||
required this.type,
|
required this.type,
|
||||||
|
this.assets = const [],
|
||||||
|
this.albumId,
|
||||||
});
|
});
|
||||||
|
|
||||||
SharedLink copyWith({
|
SharedLink copyWith({
|
||||||
@ -74,7 +80,9 @@ class SharedLink {
|
|||||||
? dto.album?.albumThumbnailAssetId
|
? dto.album?.albumThumbnailAssetId
|
||||||
: dto.assets.isNotEmpty
|
: dto.assets.isNotEmpty
|
||||||
? dto.assets[0].id
|
? dto.assets[0].id
|
||||||
: null;
|
: null,
|
||||||
|
assets = dto.assets.map((asset) => asset.toDto()).toList(),
|
||||||
|
albumId = dto.album?.id;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() =>
|
String toString() =>
|
||||||
|
176
mobile/lib/presentation/pages/shared_remote_link.page.dart
Normal file
176
mobile/lib/presentation/pages/shared_remote_link.page.dart
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/services/remote_shared_album.service.dart';
|
||||||
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
|
import 'package:immich_mobile/models/shared_link/shared_link.model.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
|
||||||
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
|
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
|
||||||
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
|
import 'package:immich_mobile/services/shared_link.service.dart';
|
||||||
|
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||||
|
// ignore: import_rule_openapi
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class RemoteSharedLinkPage extends ConsumerStatefulWidget {
|
||||||
|
final String shareKey;
|
||||||
|
final String endpoint;
|
||||||
|
|
||||||
|
const RemoteSharedLinkPage({super.key, required this.shareKey, required this.endpoint});
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<RemoteSharedLinkPage> createState() => _RemoteSharedLinkPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RemoteSharedLinkPageState extends ConsumerState<RemoteSharedLinkPage> {
|
||||||
|
late final ApiService _apiService;
|
||||||
|
|
||||||
|
SharedLink? sharedLink;
|
||||||
|
List<RemoteAsset>? assets;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
String endpoint = widget.endpoint;
|
||||||
|
if (!endpoint.endsWith('/api')) {
|
||||||
|
endpoint += '/api';
|
||||||
|
}
|
||||||
|
ImageUrlBuilder.setHost(endpoint);
|
||||||
|
ImageUrlBuilder.setParameter('key', widget.shareKey);
|
||||||
|
_apiService = ApiService.shared(endpoint, widget.shareKey);
|
||||||
|
|
||||||
|
retrieveSharedLink();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
ImageUrlBuilder.clear();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> retrieveSharedLink() async {
|
||||||
|
try {
|
||||||
|
sharedLink = await SharedLinkService(_apiService).getMySharedLink();
|
||||||
|
} on ApiException catch (error, _) {
|
||||||
|
if (error.code == 401) {
|
||||||
|
// We need a password from user.
|
||||||
|
// TODO: make password input and try to auth again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sharedLink == null) {
|
||||||
|
ImmichToast.show(
|
||||||
|
context: context,
|
||||||
|
msg: "shared_link_not_found".t(context: context),
|
||||||
|
toastType: ToastType.error,
|
||||||
|
);
|
||||||
|
context.pop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve assets from the shared link
|
||||||
|
switch (sharedLink!.type) {
|
||||||
|
case SharedLinkSource.album:
|
||||||
|
assets = await retrieveSharedAlbumAssets();
|
||||||
|
break;
|
||||||
|
case SharedLinkSource.individual:
|
||||||
|
assets = sharedLink!.assets;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (assets!.isEmpty) {
|
||||||
|
// context.pop();
|
||||||
|
// }
|
||||||
|
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<RemoteAsset>> retrieveSharedAlbumAssets() async {
|
||||||
|
try {
|
||||||
|
final driftApiRepository = DriftAlbumApiRepository(_apiService.albumsApi);
|
||||||
|
final albumService = RemoteSharedAlbumService(driftApiRepository);
|
||||||
|
final sharedAlbum = await albumService.getSharedAlbum(sharedLink!.albumId!);
|
||||||
|
|
||||||
|
return sharedAlbum?.assets ?? [];
|
||||||
|
} catch (e) {
|
||||||
|
ImmichToast.show(
|
||||||
|
context: context,
|
||||||
|
msg: "failed_to_retrieve_assets".t(context: context),
|
||||||
|
toastType: ToastType.error,
|
||||||
|
);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addAssets(BuildContext context) async {
|
||||||
|
// TODO add upload assets to shared album
|
||||||
|
}
|
||||||
|
|
||||||
|
void showOptionSheet(BuildContext context) {
|
||||||
|
final user = ref.watch(currentUserProvider);
|
||||||
|
|
||||||
|
// showModalBottomSheet(
|
||||||
|
// context: context,
|
||||||
|
// backgroundColor: context.colorScheme.surface,
|
||||||
|
// isScrollControlled: false,
|
||||||
|
// builder: (context) {
|
||||||
|
// return DriftRemoteAlbumOption(
|
||||||
|
// onDeleteAlbum: isOwner
|
||||||
|
// ? () async {
|
||||||
|
// await deleteAlbum(context);
|
||||||
|
// if (context.mounted) {
|
||||||
|
// context.pop();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// : null,
|
||||||
|
// onAddUsers: isOwner
|
||||||
|
// ? () async {
|
||||||
|
// await addUsers(context);
|
||||||
|
// context.pop();
|
||||||
|
// }
|
||||||
|
// : null,
|
||||||
|
// onAddPhotos: () async {
|
||||||
|
// await addAssets(context);
|
||||||
|
// context.pop();
|
||||||
|
// },
|
||||||
|
// onToggleAlbumOrder: () async {
|
||||||
|
// await toggleAlbumOrder();
|
||||||
|
// context.pop();
|
||||||
|
// },
|
||||||
|
// onEditAlbum: () async {
|
||||||
|
// context.pop();
|
||||||
|
// await showEditTitleAndDescription(context);
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (assets == null || assets!.isEmpty) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
apiServiceProvider.overrideWith((ref) => _apiService),
|
||||||
|
timelineServiceProvider.overrideWith((ref) {
|
||||||
|
print(assets!.length);
|
||||||
|
final timelineService = ref.watch(timelineFactoryProvider).fromAssets(assets!);
|
||||||
|
ref.onDispose(timelineService.dispose);
|
||||||
|
return timelineService;
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
child: Timeline(appBar: SliverToBoxAdapter(child: AppBar())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -4,5 +4,5 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|||||||
|
|
||||||
part 'api.provider.g.dart';
|
part 'api.provider.g.dart';
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true, dependencies: [])
|
||||||
ApiService apiService(Ref _) => ApiService();
|
ApiService apiService(Ref _) => ApiService();
|
||||||
|
6
mobile/lib/providers/api.provider.g.dart
generated
6
mobile/lib/providers/api.provider.g.dart
generated
@ -6,7 +6,7 @@ part of 'api.provider.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$apiServiceHash() => r'187a7de59b064fab1104c23717f18ce0ae3e426c';
|
String _$apiServiceHash() => r'46d8a043f41b85f36f56d81e5261c842cb5c0c06';
|
||||||
|
|
||||||
/// See also [apiService].
|
/// See also [apiService].
|
||||||
@ProviderFor(apiService)
|
@ProviderFor(apiService)
|
||||||
@ -16,8 +16,8 @@ final apiServiceProvider = Provider<ApiService>.internal(
|
|||||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
? null
|
? null
|
||||||
: _$apiServiceHash,
|
: _$apiServiceHash,
|
||||||
dependencies: null,
|
dependencies: const <ProviderOrFamily>[],
|
||||||
allTransitiveDependencies: null,
|
allTransitiveDependencies: const <ProviderOrFamily>{},
|
||||||
);
|
);
|
||||||
|
|
||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
|
@ -28,7 +28,7 @@ final authProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
|
|||||||
ref.watch(secureStorageServiceProvider),
|
ref.watch(secureStorageServiceProvider),
|
||||||
ref.watch(widgetServiceProvider),
|
ref.watch(widgetServiceProvider),
|
||||||
);
|
);
|
||||||
});
|
}, dependencies: [apiServiceProvider]);
|
||||||
|
|
||||||
class AuthNotifier extends StateNotifier<AuthState> {
|
class AuthNotifier extends StateNotifier<AuthState> {
|
||||||
final AuthService _authService;
|
final AuthService _authService;
|
||||||
|
@ -2,10 +2,12 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart' as old_asset_entity;
|
import 'package:immich_mobile/entities/asset.entity.dart' as old_asset_entity;
|
||||||
import 'package:immich_mobile/models/cast/cast_manager_state.dart';
|
import 'package:immich_mobile/models/cast/cast_manager_state.dart';
|
||||||
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/services/gcast.service.dart';
|
import 'package:immich_mobile/services/gcast.service.dart';
|
||||||
|
|
||||||
final castProvider = StateNotifierProvider<CastNotifier, CastManagerState>(
|
final castProvider = StateNotifierProvider<CastNotifier, CastManagerState>(
|
||||||
(ref) => CastNotifier(ref.watch(gCastServiceProvider)),
|
(ref) => CastNotifier(ref.watch(gCastServiceProvider)),
|
||||||
|
dependencies: [apiServiceProvider],
|
||||||
);
|
);
|
||||||
|
|
||||||
class CastNotifier extends StateNotifier<CastManagerState> {
|
class CastNotifier extends StateNotifier<CastManagerState> {
|
||||||
|
@ -5,6 +5,7 @@ import 'package:immich_mobile/domain/services/local_album.service.dart';
|
|||||||
import 'package:immich_mobile/domain/services/remote_album.service.dart';
|
import 'package:immich_mobile/domain/services/remote_album.service.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
|
||||||
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
|
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
|
||||||
@ -31,7 +32,7 @@ final remoteAlbumRepository = Provider<DriftRemoteAlbumRepository>(
|
|||||||
|
|
||||||
final remoteAlbumServiceProvider = Provider<RemoteAlbumService>(
|
final remoteAlbumServiceProvider = Provider<RemoteAlbumService>(
|
||||||
(ref) => RemoteAlbumService(ref.watch(remoteAlbumRepository), ref.watch(driftAlbumApiRepositoryProvider)),
|
(ref) => RemoteAlbumService(ref.watch(remoteAlbumRepository), ref.watch(driftAlbumApiRepositoryProvider)),
|
||||||
dependencies: [remoteAlbumRepository],
|
dependencies: [remoteAlbumRepository, apiServiceProvider],
|
||||||
);
|
);
|
||||||
|
|
||||||
final remoteAlbumProvider = NotifierProvider<RemoteAlbumNotifier, RemoteAlbumState>(
|
final remoteAlbumProvider = NotifierProvider<RemoteAlbumNotifier, RemoteAlbumState>(
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:immich_mobile/constants/enums.dart';
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/stack.model.dart';
|
import 'package:immich_mobile/domain/models/stack.model.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart' hide AssetType;
|
||||||
|
import 'package:immich_mobile/extensions/string_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/api.provider.dart';
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/api.repository.dart';
|
import 'package:immich_mobile/repositories/api.repository.dart';
|
||||||
import 'package:maplibre_gl/maplibre_gl.dart';
|
import 'package:maplibre_gl/maplibre_gl.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart' as api show AssetVisibility;
|
||||||
|
import 'package:openapi/api.dart' hide AssetVisibility;
|
||||||
|
|
||||||
final assetApiRepositoryProvider = Provider(
|
final assetApiRepositoryProvider = Provider(
|
||||||
(ref) => AssetApiRepository(
|
(ref) => AssetApiRepository(
|
||||||
@ -108,3 +111,41 @@ extension on StackResponseDto {
|
|||||||
return StackResponse(id: id, primaryAssetId: primaryAssetId, assetIds: assets.map((asset) => asset.id).toList());
|
return StackResponse(id: id, primaryAssetId: primaryAssetId, assetIds: assets.map((asset) => asset.id).toList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension RemoteAssetDtoExt on AssetResponseDto {
|
||||||
|
RemoteAsset toDto() {
|
||||||
|
return RemoteAsset(
|
||||||
|
id: id,
|
||||||
|
name: originalFileName,
|
||||||
|
checksum: checksum,
|
||||||
|
createdAt: fileCreatedAt,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
ownerId: ownerId,
|
||||||
|
visibility: switch (visibility) {
|
||||||
|
api.AssetVisibility.timeline => AssetVisibility.timeline,
|
||||||
|
api.AssetVisibility.hidden => AssetVisibility.hidden,
|
||||||
|
api.AssetVisibility.archive => AssetVisibility.archive,
|
||||||
|
api.AssetVisibility.locked => AssetVisibility.locked,
|
||||||
|
_ => AssetVisibility.timeline,
|
||||||
|
},
|
||||||
|
durationInSeconds: duration.toDuration()?.inSeconds ?? 0,
|
||||||
|
height: exifInfo?.exifImageHeight?.toInt(),
|
||||||
|
width: exifInfo?.exifImageWidth?.toInt(),
|
||||||
|
isFavorite: isFavorite,
|
||||||
|
livePhotoVideoId: livePhotoVideoId,
|
||||||
|
thumbHash: thumbhash,
|
||||||
|
localId: null,
|
||||||
|
type: type.toType(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on AssetTypeEnum {
|
||||||
|
AssetType toType() => switch (this) {
|
||||||
|
AssetTypeEnum.IMAGE => AssetType.image,
|
||||||
|
AssetTypeEnum.VIDEO => AssetType.video,
|
||||||
|
AssetTypeEnum.AUDIO => AssetType.audio,
|
||||||
|
AssetTypeEnum.OTHER => AssetType.other,
|
||||||
|
_ => throw Exception('Unknown AssetType value: $this'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/album/shared_album.model.dart';
|
||||||
import 'package:immich_mobile/providers/api.provider.dart';
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/api.repository.dart';
|
import 'package:immich_mobile/repositories/api.repository.dart';
|
||||||
|
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
||||||
// ignore: import_rule_openapi
|
// ignore: import_rule_openapi
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
@ -87,6 +89,11 @@ class DriftAlbumApiRepository extends ApiRepository {
|
|||||||
final response = await checkNull(_api.addUsersToAlbum(albumId, AddUsersDto(albumUsers: albumUsers)));
|
final response = await checkNull(_api.addUsersToAlbum(albumId, AddUsersDto(albumUsers: albumUsers)));
|
||||||
return response.toRemoteAlbum();
|
return response.toRemoteAlbum();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<SharedRemoteAlbum?> getShared(String albumId) async {
|
||||||
|
final responseDto = await checkNull(_api.getAlbumInfo(albumId));
|
||||||
|
return responseDto.toSharedRemoteAlbum();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on AlbumResponseDto {
|
extension on AlbumResponseDto {
|
||||||
@ -105,4 +112,21 @@ extension on AlbumResponseDto {
|
|||||||
ownerName: owner.name,
|
ownerName: owner.name,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SharedRemoteAlbum toSharedRemoteAlbum() {
|
||||||
|
return SharedRemoteAlbum(
|
||||||
|
id: id,
|
||||||
|
name: albumName,
|
||||||
|
ownerId: owner.id,
|
||||||
|
description: description,
|
||||||
|
createdAt: createdAt,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
thumbnailAssetId: albumThumbnailAssetId,
|
||||||
|
isActivityEnabled: isActivityEnabled,
|
||||||
|
order: order == AssetOrder.asc ? AlbumAssetOrder.asc : AlbumAssetOrder.desc,
|
||||||
|
assetCount: assetCount,
|
||||||
|
ownerName: owner.name,
|
||||||
|
assets: assets.map((e) => e.toDto()).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import 'package:immich_mobile/domain/services/store.service.dart';
|
|||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
|
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
@ -17,6 +18,11 @@ class AuthGuard extends AutoRouteGuard {
|
|||||||
void onNavigation(NavigationResolver resolver, StackRouter router) async {
|
void onNavigation(NavigationResolver resolver, StackRouter router) async {
|
||||||
resolver.next(true);
|
resolver.next(true);
|
||||||
|
|
||||||
|
if (ImageUrlBuilder.isSharedLink()) {
|
||||||
|
// If the URL is a shared link, we don't need to validate the access token
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Look in the store for an access token
|
// Look in the store for an access token
|
||||||
Store.get(StoreKey.accessToken);
|
Store.get(StoreKey.accessToken);
|
||||||
|
@ -23,11 +23,11 @@ import 'package:immich_mobile/pages/album/album_shared_user_selection.page.dart'
|
|||||||
import 'package:immich_mobile/pages/album/album_viewer.page.dart';
|
import 'package:immich_mobile/pages/album/album_viewer.page.dart';
|
||||||
import 'package:immich_mobile/pages/albums/albums.page.dart';
|
import 'package:immich_mobile/pages/albums/albums.page.dart';
|
||||||
import 'package:immich_mobile/pages/backup/album_preview.page.dart';
|
import 'package:immich_mobile/pages/backup/album_preview.page.dart';
|
||||||
import 'package:immich_mobile/pages/backup/drift_backup_album_selection.page.dart';
|
|
||||||
import 'package:immich_mobile/pages/backup/drift_backup.page.dart';
|
|
||||||
import 'package:immich_mobile/pages/backup/backup_album_selection.page.dart';
|
import 'package:immich_mobile/pages/backup/backup_album_selection.page.dart';
|
||||||
import 'package:immich_mobile/pages/backup/backup_controller.page.dart';
|
import 'package:immich_mobile/pages/backup/backup_controller.page.dart';
|
||||||
import 'package:immich_mobile/pages/backup/backup_options.page.dart';
|
import 'package:immich_mobile/pages/backup/backup_options.page.dart';
|
||||||
|
import 'package:immich_mobile/pages/backup/drift_backup.page.dart';
|
||||||
|
import 'package:immich_mobile/pages/backup/drift_backup_album_selection.page.dart';
|
||||||
import 'package:immich_mobile/pages/backup/drift_backup_options.page.dart';
|
import 'package:immich_mobile/pages/backup/drift_backup_options.page.dart';
|
||||||
import 'package:immich_mobile/pages/backup/drift_upload_detail.page.dart';
|
import 'package:immich_mobile/pages/backup/drift_upload_detail.page.dart';
|
||||||
import 'package:immich_mobile/pages/backup/failed_backup_status.page.dart';
|
import 'package:immich_mobile/pages/backup/failed_backup_status.page.dart';
|
||||||
@ -95,12 +95,13 @@ import 'package:immich_mobile/presentation/pages/drift_person.page.dart';
|
|||||||
import 'package:immich_mobile/presentation/pages/drift_place.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_place.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_place_detail.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_place_detail.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_recently_taken.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_recently_taken.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/drift_remote_album.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_remote_album.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_trash.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_trash.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_video.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_video.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/local_timeline.page.dart';
|
import 'package:immich_mobile/presentation/pages/local_timeline.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/search/drift_search.page.dart';
|
import 'package:immich_mobile/presentation/pages/search/drift_search.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/shared_remote_link.page.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart';
|
||||||
import 'package:immich_mobile/providers/api.provider.dart';
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
||||||
@ -329,6 +330,7 @@ class AppRouter extends RootStackRouter {
|
|||||||
AutoRoute(page: DriftPeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: DriftPeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
AutoRoute(page: DriftPersonRoute.page, guards: [_authGuard]),
|
AutoRoute(page: DriftPersonRoute.page, guards: [_authGuard]),
|
||||||
AutoRoute(page: DriftBackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]),
|
AutoRoute(page: DriftBackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
|
AutoRoute(page: RemoteSharedLinkRoute.page, guards: [_authGuard, _duplicateGuard]),
|
||||||
// required to handle all deeplinks in deep_link.service.dart
|
// required to handle all deeplinks in deep_link.service.dart
|
||||||
// auto_route_library#1722
|
// auto_route_library#1722
|
||||||
RedirectRoute(path: '*', redirectTo: '/'),
|
RedirectRoute(path: '*', redirectTo: '/'),
|
||||||
|
@ -2167,6 +2167,58 @@ class RemoteMediaSummaryRoute extends PageRouteInfo<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [RemoteSharedLinkPage]
|
||||||
|
class RemoteSharedLinkRoute extends PageRouteInfo<RemoteSharedLinkRouteArgs> {
|
||||||
|
RemoteSharedLinkRoute({
|
||||||
|
Key? key,
|
||||||
|
required String shareKey,
|
||||||
|
required String endpoint,
|
||||||
|
List<PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
|
RemoteSharedLinkRoute.name,
|
||||||
|
args: RemoteSharedLinkRouteArgs(
|
||||||
|
key: key,
|
||||||
|
shareKey: shareKey,
|
||||||
|
endpoint: endpoint,
|
||||||
|
),
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'RemoteSharedLinkRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
final args = data.argsAs<RemoteSharedLinkRouteArgs>();
|
||||||
|
return RemoteSharedLinkPage(
|
||||||
|
key: args.key,
|
||||||
|
shareKey: args.shareKey,
|
||||||
|
endpoint: args.endpoint,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class RemoteSharedLinkRouteArgs {
|
||||||
|
const RemoteSharedLinkRouteArgs({
|
||||||
|
this.key,
|
||||||
|
required this.shareKey,
|
||||||
|
required this.endpoint,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Key? key;
|
||||||
|
|
||||||
|
final String shareKey;
|
||||||
|
|
||||||
|
final String endpoint;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'RemoteSharedLinkRouteArgs{key: $key, shareKey: $shareKey, endpoint: $endpoint}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [SearchPage]
|
/// [SearchPage]
|
||||||
class SearchRoute extends PageRouteInfo<SearchRouteArgs> {
|
class SearchRoute extends PageRouteInfo<SearchRouteArgs> {
|
||||||
|
@ -8,9 +8,9 @@ import 'package:http/http.dart';
|
|||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/utils/url_helper.dart';
|
import 'package:immich_mobile/utils/url_helper.dart';
|
||||||
|
import 'package:immich_mobile/utils/user_agent.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
import 'package:immich_mobile/utils/user_agent.dart';
|
|
||||||
|
|
||||||
class ApiService implements Authentication {
|
class ApiService implements Authentication {
|
||||||
late ApiClient _apiClient;
|
late ApiClient _apiClient;
|
||||||
@ -45,7 +45,14 @@ class ApiService implements Authentication {
|
|||||||
setEndpoint(endpoint);
|
setEndpoint(endpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApiService.shared(String endpoint, String sharedKey) {
|
||||||
|
setEndpoint(endpoint);
|
||||||
|
_queryParams = {'key': sharedKey};
|
||||||
|
}
|
||||||
|
|
||||||
String? _accessToken;
|
String? _accessToken;
|
||||||
|
Map<String, String>? _queryParams;
|
||||||
final _log = Logger("ApiService");
|
final _log = Logger("ApiService");
|
||||||
|
|
||||||
setEndpoint(String endpoint) {
|
setEndpoint(String endpoint) {
|
||||||
@ -208,6 +215,8 @@ class ApiService implements Authentication {
|
|||||||
return Future<void>(() {
|
return Future<void>(() {
|
||||||
var headers = ApiService.getRequestHeaders();
|
var headers = ApiService.getRequestHeaders();
|
||||||
headerParams.addAll(headers);
|
headerParams.addAll(headers);
|
||||||
|
|
||||||
|
queryParams.addAll(_queryParams?.entries.map((e) => QueryParam(e.key, e.value)) ?? []);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ import 'package:immich_mobile/domain/services/timeline.service.dart';
|
|||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
import 'package:immich_mobile/providers/album/current_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart' as beta_asset_provider;
|
|
||||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart' as beta_asset_provider;
|
||||||
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/memory.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/memory.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
@ -80,6 +80,7 @@ class DeepLinkService {
|
|||||||
"memory" => await _buildMemoryDeepLink(queryParams['id'] ?? ''),
|
"memory" => await _buildMemoryDeepLink(queryParams['id'] ?? ''),
|
||||||
"asset" => await _buildAssetDeepLink(queryParams['id'] ?? ''),
|
"asset" => await _buildAssetDeepLink(queryParams['id'] ?? ''),
|
||||||
"album" => await _buildAlbumDeepLink(queryParams['id'] ?? ''),
|
"album" => await _buildAlbumDeepLink(queryParams['id'] ?? ''),
|
||||||
|
"sharedlink" => await _buildSharedLinkDeepLink(queryParams['key'] ?? '', queryParams['instanceUrl'] ?? ''),
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -99,18 +100,24 @@ class DeepLinkService {
|
|||||||
final path = link.uri.path;
|
final path = link.uri.path;
|
||||||
|
|
||||||
const uuidRegex = r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
|
const uuidRegex = r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
|
||||||
|
const b64Regex = r'^[A-Za-z0-9_-]+$';
|
||||||
final assetRegex = RegExp('/photos/($uuidRegex)');
|
final assetRegex = RegExp('/photos/($uuidRegex)');
|
||||||
final albumRegex = RegExp('/albums/($uuidRegex)');
|
final albumRegex = RegExp('/albums/($uuidRegex)');
|
||||||
|
final sharedLinkRegex = RegExp('/share/($b64Regex)');
|
||||||
|
|
||||||
PageRouteInfo<dynamic>? deepLinkRoute;
|
PageRouteInfo<dynamic>? deepLinkRoute;
|
||||||
|
|
||||||
if (assetRegex.hasMatch(path)) {
|
if (assetRegex.hasMatch(path)) {
|
||||||
final assetId = assetRegex.firstMatch(path)?.group(1) ?? '';
|
final assetId = assetRegex.firstMatch(path)?.group(1) ?? '';
|
||||||
deepLinkRoute = await _buildAssetDeepLink(assetId);
|
deepLinkRoute = await _buildAssetDeepLink(assetId);
|
||||||
} else if (albumRegex.hasMatch(path)) {
|
} else if (albumRegex.hasMatch(path)) {
|
||||||
final albumId = albumRegex.firstMatch(path)?.group(1) ?? '';
|
final albumId = albumRegex.firstMatch(path)?.group(1) ?? '';
|
||||||
deepLinkRoute = await _buildAlbumDeepLink(albumId);
|
deepLinkRoute = await _buildAlbumDeepLink(albumId);
|
||||||
|
} else if (sharedLinkRegex.hasMatch(path)) {
|
||||||
|
final shareKey = sharedLinkRegex.firstMatch(path)?.group(1) ?? '';
|
||||||
|
final instanceUrl = link.uri.queryParameters['instanceUrl'] ?? '';
|
||||||
|
deepLinkRoute = await _buildSharedLinkDeepLink(shareKey, instanceUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deep link resolution failed, safely handle it based on the app state
|
// Deep link resolution failed, safely handle it based on the app state
|
||||||
if (deepLinkRoute == null) {
|
if (deepLinkRoute == null) {
|
||||||
if (isColdStart) return DeepLink.defaultPath;
|
if (isColdStart) return DeepLink.defaultPath;
|
||||||
@ -185,4 +192,8 @@ class DeepLinkService {
|
|||||||
return AlbumViewerRoute(albumId: album.id);
|
return AlbumViewerRoute(albumId: album.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<PageRouteInfo?> _buildSharedLinkDeepLink(String shareKey, String instanceUrl) async {
|
||||||
|
return RemoteSharedLinkRoute(shareKey: shareKey, endpoint: instanceUrl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,4 +111,13 @@ class SharedLinkService {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<SharedLink?> getMySharedLink() async {
|
||||||
|
final responseDto = await _apiService.sharedLinksApi.getMySharedLink();
|
||||||
|
if (responseDto != null) {
|
||||||
|
return SharedLink.fromDto(responseDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ String getAlbumThumbNailCacheKey(final Album album, {AssetMediaSize type = Asset
|
|||||||
}
|
}
|
||||||
|
|
||||||
String getOriginalUrlForRemoteId(final String id) {
|
String getOriginalUrlForRemoteId(final String id) {
|
||||||
return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/original';
|
return ImageUrlBuilder.build('/assets/$id/original');
|
||||||
}
|
}
|
||||||
|
|
||||||
String getImageCacheKey(final Asset asset) {
|
String getImageCacheKey(final Asset asset) {
|
||||||
@ -46,16 +46,51 @@ String getImageCacheKey(final Asset asset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String getThumbnailUrlForRemoteId(final String id, {AssetMediaSize type = AssetMediaSize.thumbnail}) {
|
String getThumbnailUrlForRemoteId(final String id, {AssetMediaSize type = AssetMediaSize.thumbnail}) {
|
||||||
return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail?size=${type.value}';
|
return ImageUrlBuilder.build('/assets/$id/thumbnail?size=${type.value}');
|
||||||
}
|
}
|
||||||
|
|
||||||
String getPreviewUrlForRemoteId(final String id) =>
|
String getPreviewUrlForRemoteId(final String id) {
|
||||||
'${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail?size=${AssetMediaSize.preview}';
|
return ImageUrlBuilder.build('/assets/$id/thumbnail?size=${AssetMediaSize.preview}');
|
||||||
|
}
|
||||||
|
|
||||||
String getPlaybackUrlForRemoteId(final String id) {
|
String getPlaybackUrlForRemoteId(final String id) {
|
||||||
return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/video/playback?';
|
return ImageUrlBuilder.build('/assets/$id/video/playback?');
|
||||||
}
|
}
|
||||||
|
|
||||||
String getFaceThumbnailUrl(final String personId) {
|
String getFaceThumbnailUrl(final String personId) {
|
||||||
return '${Store.get(StoreKey.serverEndpoint)}/people/$personId/thumbnail';
|
return ImageUrlBuilder.build('/people/$personId/thumbnail');
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImageUrlBuilder {
|
||||||
|
static String? host;
|
||||||
|
static Map<String, String>? queryParams;
|
||||||
|
|
||||||
|
static void setHost(String? host) {
|
||||||
|
ImageUrlBuilder.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isSharedLink() {
|
||||||
|
return ImageUrlBuilder.host != null && ImageUrlBuilder.queryParams!.containsKey('key');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setParameter(String key, String value) {
|
||||||
|
ImageUrlBuilder.queryParams ??= {};
|
||||||
|
ImageUrlBuilder.queryParams![key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String build(String path) {
|
||||||
|
final endpoint = host ?? Store.get(StoreKey.serverEndpoint);
|
||||||
|
|
||||||
|
final uri = Uri.parse('$endpoint$path');
|
||||||
|
if (queryParams == null || queryParams!.isEmpty) {
|
||||||
|
return uri.toString();
|
||||||
|
}
|
||||||
|
final updatedUri = uri.replace(queryParameters: {...uri.queryParameters, ...queryParams!});
|
||||||
|
return updatedUri.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clear() {
|
||||||
|
ImageUrlBuilder.host = null;
|
||||||
|
ImageUrlBuilder.queryParams = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user