refactor(mobile): introduce Album & User classes (#1561)
replace usages of AlbumResponseDto with Album replace usages of UserResponseDto with User
This commit is contained in:
parent
527aa61a87
commit
2139853dd9
25 changed files with 475 additions and 255 deletions
|
@ -2,24 +2,26 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:immich_mobile/modules/album/services/album.service.dart';
|
||||
import 'package:immich_mobile/modules/album/services/album_cache.service.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
|
||||
class AlbumNotifier extends StateNotifier<List<AlbumResponseDto>> {
|
||||
class AlbumNotifier extends StateNotifier<List<Album>> {
|
||||
AlbumNotifier(this._albumService, this._albumCacheService) : super([]);
|
||||
final AlbumService _albumService;
|
||||
final AlbumCacheService _albumCacheService;
|
||||
|
||||
_cacheState() {
|
||||
void _cacheState() {
|
||||
_albumCacheService.put(state);
|
||||
}
|
||||
|
||||
getAllAlbums() async {
|
||||
Future<void> getAllAlbums() async {
|
||||
if (await _albumCacheService.isValid() && state.isEmpty) {
|
||||
state = await _albumCacheService.get();
|
||||
final albums = await _albumCacheService.get();
|
||||
if (albums != null) {
|
||||
state = albums;
|
||||
}
|
||||
}
|
||||
|
||||
List<AlbumResponseDto>? albums =
|
||||
await _albumService.getAlbums(isShared: false);
|
||||
final albums = await _albumService.getAlbums(isShared: false);
|
||||
|
||||
if (albums != null) {
|
||||
state = albums;
|
||||
|
@ -27,17 +29,16 @@ class AlbumNotifier extends StateNotifier<List<AlbumResponseDto>> {
|
|||
}
|
||||
}
|
||||
|
||||
deleteAlbum(String albumId) {
|
||||
state = state.where((album) => album.id != albumId).toList();
|
||||
void deleteAlbum(Album album) {
|
||||
state = state.where((a) => a.id != album.id).toList();
|
||||
_cacheState();
|
||||
}
|
||||
|
||||
Future<AlbumResponseDto?> createAlbum(
|
||||
Future<Album?> createAlbum(
|
||||
String albumTitle,
|
||||
Set<Asset> assets,
|
||||
) async {
|
||||
AlbumResponseDto? album =
|
||||
await _albumService.createAlbum(albumTitle, assets, []);
|
||||
Album? album = await _albumService.createAlbum(albumTitle, assets, []);
|
||||
|
||||
if (album != null) {
|
||||
state = [...state, album];
|
||||
|
@ -49,8 +50,7 @@ class AlbumNotifier extends StateNotifier<List<AlbumResponseDto>> {
|
|||
}
|
||||
}
|
||||
|
||||
final albumProvider =
|
||||
StateNotifierProvider<AlbumNotifier, List<AlbumResponseDto>>((ref) {
|
||||
final albumProvider = StateNotifierProvider<AlbumNotifier, List<Album>>((ref) {
|
||||
return AlbumNotifier(
|
||||
ref.watch(albumServiceProvider),
|
||||
ref.watch(albumCacheServiceProvider),
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:immich_mobile/modules/album/models/album_viewer_page_state.model.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/services/album.service.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
|
||||
class AlbumViewerNotifier extends StateNotifier<AlbumViewerPageState> {
|
||||
AlbumViewerNotifier(this.ref)
|
||||
|
@ -30,14 +31,12 @@ class AlbumViewerNotifier extends StateNotifier<AlbumViewerPageState> {
|
|||
}
|
||||
|
||||
Future<bool> changeAlbumTitle(
|
||||
String albumId,
|
||||
String ownerId,
|
||||
Album album,
|
||||
String newAlbumTitle,
|
||||
) async {
|
||||
AlbumService service = ref.watch(albumServiceProvider);
|
||||
|
||||
bool isSuccess =
|
||||
await service.changeTitleAlbum(albumId, ownerId, newAlbumTitle);
|
||||
bool isSuccess = await service.changeTitleAlbum(album, newAlbumTitle);
|
||||
|
||||
if (isSuccess) {
|
||||
state = state.copyWith(editTitleText: "", isEditAlbum: false);
|
||||
|
|
|
@ -2,30 +2,31 @@ import 'package:flutter/material.dart';
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/album/services/album.service.dart';
|
||||
import 'package:immich_mobile/modules/album/services/album_cache.service.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
|
||||
class SharedAlbumNotifier extends StateNotifier<List<AlbumResponseDto>> {
|
||||
class SharedAlbumNotifier extends StateNotifier<List<Album>> {
|
||||
SharedAlbumNotifier(this._albumService, this._sharedAlbumCacheService)
|
||||
: super([]);
|
||||
|
||||
final AlbumService _albumService;
|
||||
final SharedAlbumCacheService _sharedAlbumCacheService;
|
||||
|
||||
_cacheState() {
|
||||
void _cacheState() {
|
||||
_sharedAlbumCacheService.put(state);
|
||||
}
|
||||
|
||||
Future<AlbumResponseDto?> createSharedAlbum(
|
||||
Future<Album?> createSharedAlbum(
|
||||
String albumName,
|
||||
Set<Asset> assets,
|
||||
List<String> sharedUserIds,
|
||||
Iterable<Asset> assets,
|
||||
Iterable<User> sharedUsers,
|
||||
) async {
|
||||
try {
|
||||
var newAlbum = await _albumService.createAlbum(
|
||||
albumName,
|
||||
assets,
|
||||
sharedUserIds,
|
||||
sharedUsers,
|
||||
);
|
||||
|
||||
if (newAlbum != null) {
|
||||
|
@ -41,13 +42,15 @@ class SharedAlbumNotifier extends StateNotifier<List<AlbumResponseDto>> {
|
|||
}
|
||||
}
|
||||
|
||||
getAllSharedAlbums() async {
|
||||
Future<void> getAllSharedAlbums() async {
|
||||
if (await _sharedAlbumCacheService.isValid() && state.isEmpty) {
|
||||
state = await _sharedAlbumCacheService.get();
|
||||
final albums = await _sharedAlbumCacheService.get();
|
||||
if (albums != null) {
|
||||
state = albums;
|
||||
}
|
||||
}
|
||||
|
||||
List<AlbumResponseDto>? sharedAlbums =
|
||||
await _albumService.getAlbums(isShared: true);
|
||||
List<Album>? sharedAlbums = await _albumService.getAlbums(isShared: true);
|
||||
|
||||
if (sharedAlbums != null) {
|
||||
state = sharedAlbums;
|
||||
|
@ -55,16 +58,16 @@ class SharedAlbumNotifier extends StateNotifier<List<AlbumResponseDto>> {
|
|||
}
|
||||
}
|
||||
|
||||
deleteAlbum(String albumId) async {
|
||||
state = state.where((album) => album.id != albumId).toList();
|
||||
void deleteAlbum(Album album) {
|
||||
state = state.where((a) => a.id != album.id).toList();
|
||||
_cacheState();
|
||||
}
|
||||
|
||||
Future<bool> leaveAlbum(String albumId) async {
|
||||
var res = await _albumService.leaveAlbum(albumId);
|
||||
Future<bool> leaveAlbum(Album album) async {
|
||||
var res = await _albumService.leaveAlbum(album);
|
||||
|
||||
if (res) {
|
||||
state = state.where((album) => album.id != albumId).toList();
|
||||
state = state.where((a) => a.id != album.id).toList();
|
||||
_cacheState();
|
||||
return true;
|
||||
} else {
|
||||
|
@ -73,10 +76,10 @@ class SharedAlbumNotifier extends StateNotifier<List<AlbumResponseDto>> {
|
|||
}
|
||||
|
||||
Future<bool> removeAssetFromAlbum(
|
||||
String albumId,
|
||||
List<String> assetIds,
|
||||
Album album,
|
||||
Iterable<Asset> assets,
|
||||
) async {
|
||||
var res = await _albumService.removeAssetFromAlbum(albumId, assetIds);
|
||||
var res = await _albumService.removeAssetFromAlbum(album, assets);
|
||||
|
||||
if (res) {
|
||||
return true;
|
||||
|
@ -87,15 +90,15 @@ class SharedAlbumNotifier extends StateNotifier<List<AlbumResponseDto>> {
|
|||
}
|
||||
|
||||
final sharedAlbumProvider =
|
||||
StateNotifierProvider<SharedAlbumNotifier, List<AlbumResponseDto>>((ref) {
|
||||
StateNotifierProvider<SharedAlbumNotifier, List<Album>>((ref) {
|
||||
return SharedAlbumNotifier(
|
||||
ref.watch(albumServiceProvider),
|
||||
ref.watch(sharedAlbumCacheServiceProvider),
|
||||
);
|
||||
});
|
||||
|
||||
final sharedAlbumDetailProvider = FutureProvider.autoDispose
|
||||
.family<AlbumResponseDto?, String>((ref, albumId) async {
|
||||
final sharedAlbumDetailProvider =
|
||||
FutureProvider.autoDispose.family<Album?, String>((ref, albumId) async {
|
||||
final AlbumService sharedAlbumService = ref.watch(albumServiceProvider);
|
||||
|
||||
return await sharedAlbumService.getAlbumDetail(albumId);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/services/user.service.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final suggestedSharedUsersProvider =
|
||||
FutureProvider.autoDispose<List<UserResponseDto>>((ref) async {
|
||||
FutureProvider.autoDispose<List<User>>((ref) async {
|
||||
UserService userService = ref.watch(userServiceProvider);
|
||||
|
||||
return await userService.getAllUsersInfo(isAll: false) ?? [];
|
||||
return await userService.getAllUsers(isAll: false) ?? [];
|
||||
});
|
||||
|
|
|
@ -2,7 +2,9 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
@ -18,29 +20,31 @@ class AlbumService {
|
|||
|
||||
AlbumService(this._apiService);
|
||||
|
||||
Future<List<AlbumResponseDto>?> getAlbums({required bool isShared}) async {
|
||||
Future<List<Album>?> getAlbums({required bool isShared}) async {
|
||||
try {
|
||||
return await _apiService.albumApi
|
||||
final dto = await _apiService.albumApi
|
||||
.getAllAlbums(shared: isShared ? isShared : null);
|
||||
return dto?.map(Album.remote).toList();
|
||||
} catch (e) {
|
||||
debugPrint("Error getAllSharedAlbum ${e.toString()}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<AlbumResponseDto?> createAlbum(
|
||||
Future<Album?> createAlbum(
|
||||
String albumName,
|
||||
Iterable<Asset> assets,
|
||||
List<String> sharedUserIds,
|
||||
) async {
|
||||
Iterable<Asset> assets, [
|
||||
Iterable<User> sharedUsers = const [],
|
||||
]) async {
|
||||
try {
|
||||
return await _apiService.albumApi.createAlbum(
|
||||
final dto = await _apiService.albumApi.createAlbum(
|
||||
CreateAlbumDto(
|
||||
albumName: albumName,
|
||||
assetIds: assets.map((asset) => asset.id).toList(),
|
||||
sharedWithUserIds: sharedUserIds,
|
||||
assetIds: assets.map((asset) => asset.remoteId!).toList(),
|
||||
sharedWithUserIds: sharedUsers.map((e) => e.id).toList(),
|
||||
),
|
||||
);
|
||||
return dto != null ? Album.remote(dto) : null;
|
||||
} catch (e) {
|
||||
debugPrint("Error createSharedAlbum ${e.toString()}");
|
||||
return null;
|
||||
|
@ -50,14 +54,14 @@ class AlbumService {
|
|||
/*
|
||||
* Creates names like Untitled, Untitled (1), Untitled (2), ...
|
||||
*/
|
||||
String _getNextAlbumName(List<AlbumResponseDto>? albums) {
|
||||
String _getNextAlbumName(List<Album>? albums) {
|
||||
const baseName = "Untitled";
|
||||
|
||||
if (albums != null) {
|
||||
for (int round = 0; round < albums.length; round++) {
|
||||
final proposedName = "$baseName${round == 0 ? "" : " ($round)"}";
|
||||
|
||||
if (albums.where((a) => a.albumName == proposedName).isEmpty) {
|
||||
if (albums.where((a) => a.name == proposedName).isEmpty) {
|
||||
return proposedName;
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +69,7 @@ class AlbumService {
|
|||
return baseName;
|
||||
}
|
||||
|
||||
Future<AlbumResponseDto?> createAlbumWithGeneratedName(
|
||||
Future<Album?> createAlbumWithGeneratedName(
|
||||
Iterable<Asset> assets,
|
||||
) async {
|
||||
return createAlbum(
|
||||
|
@ -75,9 +79,10 @@ class AlbumService {
|
|||
);
|
||||
}
|
||||
|
||||
Future<AlbumResponseDto?> getAlbumDetail(String albumId) async {
|
||||
Future<Album?> getAlbumDetail(String albumId) async {
|
||||
try {
|
||||
return await _apiService.albumApi.getAlbumInfo(albumId);
|
||||
final dto = await _apiService.albumApi.getAlbumInfo(albumId);
|
||||
return dto != null ? Album.remote(dto) : null;
|
||||
} catch (e) {
|
||||
debugPrint('Error [getAlbumDetail] ${e.toString()}');
|
||||
return null;
|
||||
|
@ -86,12 +91,12 @@ class AlbumService {
|
|||
|
||||
Future<AddAssetsResponseDto?> addAdditionalAssetToAlbum(
|
||||
Iterable<Asset> assets,
|
||||
String albumId,
|
||||
Album album,
|
||||
) async {
|
||||
try {
|
||||
var result = await _apiService.albumApi.addAssetsToAlbum(
|
||||
albumId,
|
||||
AddAssetsDto(assetIds: assets.map((asset) => asset.id).toList()),
|
||||
album.remoteId!,
|
||||
AddAssetsDto(assetIds: assets.map((asset) => asset.remoteId!).toList()),
|
||||
);
|
||||
return result;
|
||||
} catch (e) {
|
||||
|
@ -102,11 +107,11 @@ class AlbumService {
|
|||
|
||||
Future<bool> addAdditionalUserToAlbum(
|
||||
List<String> sharedUserIds,
|
||||
String albumId,
|
||||
Album album,
|
||||
) async {
|
||||
try {
|
||||
var result = await _apiService.albumApi.addUsersToAlbum(
|
||||
albumId,
|
||||
album.remoteId!,
|
||||
AddUsersDto(sharedUserIds: sharedUserIds),
|
||||
);
|
||||
|
||||
|
@ -117,9 +122,9 @@ class AlbumService {
|
|||
}
|
||||
}
|
||||
|
||||
Future<bool> deleteAlbum(String albumId) async {
|
||||
Future<bool> deleteAlbum(Album album) async {
|
||||
try {
|
||||
await _apiService.albumApi.deleteAlbum(albumId);
|
||||
await _apiService.albumApi.deleteAlbum(album.remoteId!);
|
||||
return true;
|
||||
} catch (e) {
|
||||
debugPrint("Error deleteAlbum ${e.toString()}");
|
||||
|
@ -127,10 +132,9 @@ class AlbumService {
|
|||
}
|
||||
}
|
||||
|
||||
Future<bool> leaveAlbum(String albumId) async {
|
||||
Future<bool> leaveAlbum(Album album) async {
|
||||
try {
|
||||
await _apiService.albumApi.removeUserFromAlbum(albumId, "me");
|
||||
|
||||
await _apiService.albumApi.removeUserFromAlbum(album.remoteId!, "me");
|
||||
return true;
|
||||
} catch (e) {
|
||||
debugPrint("Error deleteAlbum ${e.toString()}");
|
||||
|
@ -139,13 +143,15 @@ class AlbumService {
|
|||
}
|
||||
|
||||
Future<bool> removeAssetFromAlbum(
|
||||
String albumId,
|
||||
List<String> assetIds,
|
||||
Album album,
|
||||
Iterable<Asset> assets,
|
||||
) async {
|
||||
try {
|
||||
await _apiService.albumApi.removeAssetFromAlbum(
|
||||
albumId,
|
||||
RemoveAssetsDto(assetIds: assetIds),
|
||||
album.remoteId!,
|
||||
RemoveAssetsDto(
|
||||
assetIds: assets.map((e) => e.remoteId!).toList(growable: false),
|
||||
),
|
||||
);
|
||||
|
||||
return true;
|
||||
|
@ -156,17 +162,17 @@ class AlbumService {
|
|||
}
|
||||
|
||||
Future<bool> changeTitleAlbum(
|
||||
String albumId,
|
||||
String ownerId,
|
||||
Album album,
|
||||
String newAlbumTitle,
|
||||
) async {
|
||||
try {
|
||||
await _apiService.albumApi.updateAlbumInfo(
|
||||
albumId,
|
||||
album.remoteId!,
|
||||
UpdateAlbumDto(
|
||||
albumName: newAlbumTitle,
|
||||
),
|
||||
);
|
||||
album.name = newAlbumTitle;
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,32 +1,30 @@
|
|||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/services/json_cache.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class BaseAlbumCacheService extends JsonCache<List<AlbumResponseDto>> {
|
||||
class BaseAlbumCacheService extends JsonCache<List<Album>> {
|
||||
BaseAlbumCacheService(super.cacheFileName);
|
||||
|
||||
@override
|
||||
void put(List<AlbumResponseDto> data) {
|
||||
void put(List<Album> data) {
|
||||
putRawData(data.map((e) => e.toJson()).toList());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<AlbumResponseDto>> get() async {
|
||||
Future<List<Album>?> get() async {
|
||||
try {
|
||||
final mapList = await readRawData() as List<dynamic>;
|
||||
|
||||
final responseData = mapList
|
||||
.map((e) => AlbumResponseDto.fromJson(e))
|
||||
.whereNotNull()
|
||||
.toList();
|
||||
final responseData =
|
||||
mapList.map((e) => Album.fromJson(e)).whereNotNull().toList();
|
||||
|
||||
return responseData;
|
||||
} catch (e) {
|
||||
await invalidate();
|
||||
debugPrint(e.toString());
|
||||
return [];
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,10 +38,9 @@ class SharedAlbumCacheService extends BaseAlbumCacheService {
|
|||
}
|
||||
|
||||
final albumCacheServiceProvider = Provider(
|
||||
(ref) => AlbumCacheService(),
|
||||
(ref) => AlbumCacheService(),
|
||||
);
|
||||
|
||||
final sharedAlbumCacheServiceProvider = Provider(
|
||||
(ref) => SharedAlbumCacheService(),
|
||||
(ref) => SharedAlbumCacheService(),
|
||||
);
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@ import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart
|
|||
import 'package:immich_mobile/modules/album/services/album.service.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/add_to_album_sliverlist.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/ui/drag_sheet.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class AddToAlbumBottomSheet extends HookConsumerWidget {
|
||||
/// The asset to add to an album
|
||||
|
@ -39,22 +39,22 @@ class AddToAlbumBottomSheet extends HookConsumerWidget {
|
|||
[],
|
||||
);
|
||||
|
||||
void addToAlbum(AlbumResponseDto album) async {
|
||||
void addToAlbum(Album album) async {
|
||||
final result = await albumService.addAdditionalAssetToAlbum(
|
||||
assets,
|
||||
album.id,
|
||||
album,
|
||||
);
|
||||
|
||||
if (result != null) {
|
||||
if (result.alreadyInAlbum.isNotEmpty) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: 'Already in ${album.albumName}',
|
||||
msg: 'Already in ${album.name}',
|
||||
);
|
||||
} else {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: 'Added to ${album.albumName}',
|
||||
msg: 'Added to ${album.name}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/album_thumbnail_listtile.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
|
||||
class AddToAlbumSliverList extends HookConsumerWidget {
|
||||
|
||||
/// The asset to add to an album
|
||||
final List<AlbumResponseDto> albums;
|
||||
final List<AlbumResponseDto> sharedAlbums;
|
||||
final void Function(AlbumResponseDto) onAddToAlbum;
|
||||
final List<Album> albums;
|
||||
final List<Album> sharedAlbums;
|
||||
final void Function(Album) onAddToAlbum;
|
||||
|
||||
const AddToAlbumSliverList({
|
||||
Key? key,
|
||||
|
@ -21,36 +20,36 @@ class AddToAlbumSliverList extends HookConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
childCount: albums.length + (sharedAlbums.isEmpty ? 0 : 1),
|
||||
(context, index) {
|
||||
// Build shared expander
|
||||
if (index == 0 && sharedAlbums.isNotEmpty) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: ExpansionTile(
|
||||
title: const Text('Shared'),
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||
leading: const Icon(Icons.group),
|
||||
children: sharedAlbums.map((album) =>
|
||||
AlbumThumbnailListTile(
|
||||
childCount: albums.length + (sharedAlbums.isEmpty ? 0 : 1),
|
||||
(context, index) {
|
||||
// Build shared expander
|
||||
if (index == 0 && sharedAlbums.isNotEmpty) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: ExpansionTile(
|
||||
title: const Text('Shared'),
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||
leading: const Icon(Icons.group),
|
||||
children: sharedAlbums
|
||||
.map(
|
||||
(album) => AlbumThumbnailListTile(
|
||||
album: album,
|
||||
onTap: () => onAddToAlbum(album),
|
||||
),
|
||||
).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Build albums list
|
||||
final offset = index - (sharedAlbums.isNotEmpty ? 1 : 0);
|
||||
final album = albums[offset];
|
||||
return AlbumThumbnailListTile(
|
||||
album: album,
|
||||
onTap: () => onAddToAlbum(album),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
),
|
||||
|
||||
// Build albums list
|
||||
final offset = index - (sharedAlbums.isNotEmpty ? 1 : 0);
|
||||
final album = albums[offset];
|
||||
return AlbumThumbnailListTile(
|
||||
album: album,
|
||||
onTap: () => onAddToAlbum(album),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:hive/hive.dart';
|
||||
import 'package:immich_mobile/constants/hive_box.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
|
@ -14,7 +15,7 @@ class AlbumThumbnailCard extends StatelessWidget {
|
|||
required this.album,
|
||||
}) : super(key: key);
|
||||
|
||||
final AlbumResponseDto album;
|
||||
final Album album;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -72,7 +73,7 @@ class AlbumThumbnailCard extends StatelessWidget {
|
|||
child: SizedBox(
|
||||
width: cardSize,
|
||||
child: Text(
|
||||
album.albumName,
|
||||
album.name,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:hive/hive.dart';
|
||||
import 'package:immich_mobile/constants/hive_box.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
|
@ -15,7 +16,7 @@ class AlbumThumbnailListTile extends StatelessWidget {
|
|||
this.onTap,
|
||||
}) : super(key: key);
|
||||
|
||||
final AlbumResponseDto album;
|
||||
final Album album;
|
||||
final void Function()? onTap;
|
||||
|
||||
@override
|
||||
|
@ -80,7 +81,7 @@ class AlbumThumbnailListTile extends StatelessWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
album.albumName,
|
||||
album.name,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
|
|
|
@ -9,21 +9,19 @@ import 'package:immich_mobile/modules/album/providers/asset_selection.provider.d
|
|||
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/services/album.service.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
||||
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
||||
const AlbumViewerAppbar({
|
||||
Key? key,
|
||||
required this.albumInfo,
|
||||
required this.album,
|
||||
required this.userId,
|
||||
required this.albumId,
|
||||
}) : super(key: key);
|
||||
|
||||
final AlbumResponseDto albumInfo;
|
||||
final Album album;
|
||||
final String userId;
|
||||
final String albumId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
@ -34,19 +32,18 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||
final newAlbumTitle = ref.watch(albumViewerProvider).editTitleText;
|
||||
final isEditAlbum = ref.watch(albumViewerProvider).isEditAlbum;
|
||||
|
||||
void onDeleteAlbumPressed(String albumId) async {
|
||||
void onDeleteAlbumPressed() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
|
||||
bool isSuccess =
|
||||
await ref.watch(albumServiceProvider).deleteAlbum(albumId);
|
||||
bool isSuccess = await ref.watch(albumServiceProvider).deleteAlbum(album);
|
||||
|
||||
if (isSuccess) {
|
||||
if (albumInfo.shared) {
|
||||
ref.watch(sharedAlbumProvider.notifier).deleteAlbum(albumId);
|
||||
if (album.shared) {
|
||||
ref.watch(sharedAlbumProvider.notifier).deleteAlbum(album);
|
||||
AutoRouter.of(context)
|
||||
.navigate(const TabControllerRoute(children: [SharingRoute()]));
|
||||
} else {
|
||||
ref.watch(albumProvider.notifier).deleteAlbum(albumId);
|
||||
ref.watch(albumProvider.notifier).deleteAlbum(album);
|
||||
AutoRouter.of(context)
|
||||
.navigate(const TabControllerRoute(children: [LibraryRoute()]));
|
||||
}
|
||||
|
@ -62,11 +59,11 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
}
|
||||
|
||||
void onLeaveAlbumPressed(String albumId) async {
|
||||
void onLeaveAlbumPressed() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
|
||||
bool isSuccess =
|
||||
await ref.watch(sharedAlbumProvider.notifier).leaveAlbum(albumId);
|
||||
await ref.watch(sharedAlbumProvider.notifier).leaveAlbum(album);
|
||||
|
||||
if (isSuccess) {
|
||||
AutoRouter.of(context)
|
||||
|
@ -84,20 +81,20 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||
ImmichLoadingOverlayController.appLoader.hide();
|
||||
}
|
||||
|
||||
void onRemoveFromAlbumPressed(String albumId) async {
|
||||
void onRemoveFromAlbumPressed() async {
|
||||
ImmichLoadingOverlayController.appLoader.show();
|
||||
|
||||
bool isSuccess =
|
||||
await ref.watch(sharedAlbumProvider.notifier).removeAssetFromAlbum(
|
||||
albumId,
|
||||
selectedAssetsInAlbum.map((a) => a.id).toList(),
|
||||
album,
|
||||
selectedAssetsInAlbum,
|
||||
);
|
||||
|
||||
if (isSuccess) {
|
||||
Navigator.pop(context);
|
||||
ref.watch(assetSelectionProvider.notifier).disableMultiselection();
|
||||
ref.watch(albumProvider.notifier).getAllAlbums();
|
||||
ref.invalidate(sharedAlbumDetailProvider(albumId));
|
||||
ref.invalidate(sharedAlbumDetailProvider(album.id));
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
ImmichToast.show(
|
||||
|
@ -113,27 +110,27 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||
|
||||
buildBottomSheetActionButton() {
|
||||
if (isMultiSelectionEnable) {
|
||||
if (albumInfo.ownerId == userId) {
|
||||
if (album.ownerId == userId) {
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.delete_sweep_rounded),
|
||||
title: const Text(
|
||||
'album_viewer_appbar_share_remove',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
onTap: () => onRemoveFromAlbumPressed(albumId),
|
||||
onTap: () => onRemoveFromAlbumPressed(),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
} else {
|
||||
if (albumInfo.ownerId == userId) {
|
||||
if (album.ownerId == userId) {
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.delete_forever_rounded),
|
||||
title: const Text(
|
||||
'album_viewer_appbar_share_delete',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
onTap: () => onDeleteAlbumPressed(albumId),
|
||||
onTap: () => onDeleteAlbumPressed(),
|
||||
);
|
||||
} else {
|
||||
return ListTile(
|
||||
|
@ -142,7 +139,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||
'album_viewer_appbar_share_leave',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
onTap: () => onLeaveAlbumPressed(albumId),
|
||||
onTap: () => onLeaveAlbumPressed(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -180,7 +177,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
|
|||
onPressed: () async {
|
||||
bool isSuccess = await ref
|
||||
.watch(albumViewerProvider.notifier)
|
||||
.changeAlbumTitle(albumId, userId, newAlbumTitle);
|
||||
.changeAlbumTitle(album, newAlbumTitle);
|
||||
|
||||
if (!isSuccess) {
|
||||
ImmichToast.show(
|
||||
|
|
|
@ -3,21 +3,20 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album_viewer.provider.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
|
||||
class AlbumViewerEditableTitle extends HookConsumerWidget {
|
||||
final AlbumResponseDto albumInfo;
|
||||
final Album album;
|
||||
final FocusNode titleFocusNode;
|
||||
const AlbumViewerEditableTitle({
|
||||
Key? key,
|
||||
required this.albumInfo,
|
||||
required this.album,
|
||||
required this.titleFocusNode,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final titleTextEditController =
|
||||
useTextEditingController(text: albumInfo.albumName);
|
||||
final titleTextEditController = useTextEditingController(text: album.name);
|
||||
final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
void onFocusModeChange() {
|
||||
|
@ -50,9 +49,7 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
|
|||
onTap: () {
|
||||
FocusScope.of(context).requestFocus(titleFocusNode);
|
||||
|
||||
ref
|
||||
.watch(albumViewerProvider.notifier)
|
||||
.setEditTitleText(albumInfo.albumName);
|
||||
ref.watch(albumViewerProvider.notifier).setEditTitleText(album.name);
|
||||
ref.watch(albumViewerProvider.notifier).enableEditAlbum();
|
||||
|
||||
if (titleTextEditController.text == 'Untitled') {
|
||||
|
|
|
@ -19,11 +19,10 @@ import 'package:immich_mobile/modules/album/ui/album_viewer_thumbnail.dart';
|
|||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_sliver_persistent_app_bar_delegate.dart';
|
||||
import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class AlbumViewerPage extends HookConsumerWidget {
|
||||
final String albumId;
|
||||
|
@ -34,16 +33,16 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
FocusNode titleFocusNode = useFocusNode();
|
||||
ScrollController scrollController = useScrollController();
|
||||
var albumInfo = ref.watch(sharedAlbumDetailProvider(albumId));
|
||||
final album = ref.watch(sharedAlbumDetailProvider(albumId));
|
||||
|
||||
final userId = ref.watch(authenticationProvider).userId;
|
||||
|
||||
/// Find out if the assets in album exist on the device
|
||||
/// If they exist, add to selected asset state to show they are already selected.
|
||||
void onAddPhotosPressed(AlbumResponseDto albumInfo) async {
|
||||
void onAddPhotosPressed(Album albumInfo) async {
|
||||
if (albumInfo.assets.isNotEmpty == true) {
|
||||
ref.watch(assetSelectionProvider.notifier).addNewAssets(
|
||||
albumInfo.assets.map((e) => Asset.remote(e)).toList(),
|
||||
albumInfo.assets,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -60,7 +59,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
var addAssetsResult =
|
||||
await ref.watch(albumServiceProvider).addAdditionalAssetToAlbum(
|
||||
returnPayload.selectedAdditionalAsset,
|
||||
albumId,
|
||||
albumInfo,
|
||||
);
|
||||
|
||||
if (addAssetsResult != null &&
|
||||
|
@ -78,10 +77,10 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
}
|
||||
}
|
||||
|
||||
void onAddUsersPressed(AlbumResponseDto albumInfo) async {
|
||||
void onAddUsersPressed(Album album) async {
|
||||
List<String>? sharedUserIds =
|
||||
await AutoRouter.of(context).push<List<String>?>(
|
||||
SelectAdditionalUserForSharingRoute(albumInfo: albumInfo),
|
||||
SelectAdditionalUserForSharingRoute(album: album),
|
||||
);
|
||||
|
||||
if (sharedUserIds != null) {
|
||||
|
@ -89,7 +88,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
|
||||
var isSuccess = await ref
|
||||
.watch(albumServiceProvider)
|
||||
.addAdditionalUserToAlbum(sharedUserIds, albumId);
|
||||
.addAdditionalUserToAlbum(sharedUserIds, album);
|
||||
|
||||
if (isSuccess) {
|
||||
ref.invalidate(sharedAlbumDetailProvider(albumId));
|
||||
|
@ -99,18 +98,18 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
}
|
||||
}
|
||||
|
||||
Widget buildTitle(AlbumResponseDto albumInfo) {
|
||||
Widget buildTitle(Album album) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8, top: 16),
|
||||
child: userId == albumInfo.ownerId
|
||||
child: userId == album.ownerId
|
||||
? AlbumViewerEditableTitle(
|
||||
albumInfo: albumInfo,
|
||||
album: album,
|
||||
titleFocusNode: titleFocusNode,
|
||||
)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: Text(
|
||||
albumInfo.albumName,
|
||||
album.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
@ -120,30 +119,22 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
);
|
||||
}
|
||||
|
||||
Widget buildAlbumDateRange(AlbumResponseDto albumInfo) {
|
||||
String startDate = "";
|
||||
DateTime parsedStartDate =
|
||||
DateTime.parse(albumInfo.assets.first.createdAt);
|
||||
DateTime parsedEndDate = DateTime.parse(
|
||||
albumInfo.assets.last.createdAt,
|
||||
); //Need default.
|
||||
|
||||
if (parsedStartDate.year == parsedEndDate.year) {
|
||||
startDate = DateFormat('LLL d').format(parsedStartDate);
|
||||
} else {
|
||||
startDate = DateFormat('LLL d, y').format(parsedStartDate);
|
||||
}
|
||||
|
||||
String endDate = DateFormat('LLL d, y').format(parsedEndDate);
|
||||
Widget buildAlbumDateRange(Album album) {
|
||||
final DateTime startDate = album.assets.first.createdAt;
|
||||
final DateTime endDate = album.assets.last.createdAt; //Need default.
|
||||
final String startDateText =
|
||||
DateFormat(startDate.year == endDate.year ? 'LLL d' : 'LLL d, y')
|
||||
.format(startDate);
|
||||
final String endDateText = DateFormat('LLL d, y').format(endDate);
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 16.0,
|
||||
top: 8.0,
|
||||
bottom: albumInfo.shared ? 0.0 : 8.0,
|
||||
bottom: album.shared ? 0.0 : 8.0,
|
||||
),
|
||||
child: Text(
|
||||
"$startDate-$endDate",
|
||||
"$startDateText-$endDateText",
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
@ -153,15 +144,14 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
);
|
||||
}
|
||||
|
||||
Widget buildHeader(AlbumResponseDto albumInfo) {
|
||||
Widget buildHeader(Album album) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildTitle(albumInfo),
|
||||
if (albumInfo.assets.isNotEmpty == true)
|
||||
buildAlbumDateRange(albumInfo),
|
||||
if (albumInfo.shared)
|
||||
buildTitle(album),
|
||||
if (album.assets.isNotEmpty == true) buildAlbumDateRange(album),
|
||||
if (album.shared)
|
||||
SizedBox(
|
||||
height: 60,
|
||||
child: ListView.builder(
|
||||
|
@ -185,7 +175,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
),
|
||||
);
|
||||
}),
|
||||
itemCount: albumInfo.sharedUsers.length,
|
||||
itemCount: album.sharedUsers.length,
|
||||
),
|
||||
)
|
||||
],
|
||||
|
@ -193,12 +183,12 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
);
|
||||
}
|
||||
|
||||
Widget buildImageGrid(AlbumResponseDto albumInfo) {
|
||||
Widget buildImageGrid(Album album) {
|
||||
final appSettingService = ref.watch(appSettingsServiceProvider);
|
||||
final bool showStorageIndicator =
|
||||
appSettingService.getSetting(AppSettingsEnum.storageIndicator);
|
||||
|
||||
if (albumInfo.assets.isNotEmpty) {
|
||||
if (album.assets.isNotEmpty) {
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.only(top: 10.0),
|
||||
sliver: SliverGrid(
|
||||
|
@ -211,13 +201,12 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
return AlbumViewerThumbnail(
|
||||
asset: Asset.remote(albumInfo.assets[index]),
|
||||
assetList:
|
||||
albumInfo.assets.map((e) => Asset.remote(e)).toList(),
|
||||
asset: album.assets[index],
|
||||
assetList: album.assets,
|
||||
showStorageIndicator: showStorageIndicator,
|
||||
);
|
||||
},
|
||||
childCount: albumInfo.assetCount,
|
||||
childCount: album.assetCount,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -225,7 +214,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
return const SliverToBoxAdapter();
|
||||
}
|
||||
|
||||
Widget buildControlButton(AlbumResponseDto albumInfo) {
|
||||
Widget buildControlButton(Album album) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, top: 8, bottom: 8),
|
||||
child: SizedBox(
|
||||
|
@ -235,13 +224,13 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
children: [
|
||||
AlbumActionOutlinedButton(
|
||||
iconData: Icons.add_photo_alternate_outlined,
|
||||
onPressed: () => onAddPhotosPressed(albumInfo),
|
||||
onPressed: () => onAddPhotosPressed(album),
|
||||
labelText: "share_add_photos".tr(),
|
||||
),
|
||||
if (userId == albumInfo.ownerId)
|
||||
if (userId == album.ownerId)
|
||||
AlbumActionOutlinedButton(
|
||||
iconData: Icons.person_add_alt_rounded,
|
||||
onPressed: () => onAddUsersPressed(albumInfo),
|
||||
onPressed: () => onAddUsersPressed(album),
|
||||
labelText: "album_viewer_page_share_add_users".tr(),
|
||||
),
|
||||
],
|
||||
|
@ -251,7 +240,10 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
Future<bool> onWillPop() async {
|
||||
final isMultiselectEnable = ref.read(assetSelectionProvider).selectedAssetsInAlbumViewer.isNotEmpty;
|
||||
final isMultiselectEnable = ref
|
||||
.read(assetSelectionProvider)
|
||||
.selectedAssetsInAlbumViewer
|
||||
.isNotEmpty;
|
||||
if (isMultiselectEnable) {
|
||||
ref.watch(assetSelectionProvider.notifier).removeAll();
|
||||
return false;
|
||||
|
@ -260,7 +252,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
return true;
|
||||
}
|
||||
|
||||
Widget buildBody(AlbumResponseDto albumInfo) {
|
||||
Widget buildBody(Album album) {
|
||||
return WillPopScope(
|
||||
onWillPop: onWillPop,
|
||||
child: GestureDetector(
|
||||
|
@ -274,7 +266,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
child: CustomScrollView(
|
||||
controller: scrollController,
|
||||
slivers: [
|
||||
buildHeader(albumInfo),
|
||||
buildHeader(album),
|
||||
SliverPersistentHeader(
|
||||
pinned: true,
|
||||
delegate: ImmichSliverPersistentAppBarDelegate(
|
||||
|
@ -282,11 +274,11 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
maxHeight: 50,
|
||||
child: Container(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
child: buildControlButton(albumInfo),
|
||||
child: buildControlButton(album),
|
||||
),
|
||||
),
|
||||
),
|
||||
buildImageGrid(albumInfo)
|
||||
buildImageGrid(album)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -295,13 +287,12 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: albumInfo.when(
|
||||
data: (AlbumResponseDto? data) {
|
||||
appBar: album.when(
|
||||
data: (Album? data) {
|
||||
if (data != null) {
|
||||
return AlbumViewerAppbar(
|
||||
albumInfo: data,
|
||||
album: data,
|
||||
userId: userId,
|
||||
albumId: albumId,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
@ -309,7 +300,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
error: (e, _) => null,
|
||||
loading: () => null,
|
||||
),
|
||||
body: albumInfo.when(
|
||||
body: album.when(
|
||||
data: (albumInfo) => albumInfo != null
|
||||
? buildBody(albumInfo)
|
||||
: const Center(
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:immich_mobile/modules/album/providers/album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/album_thumbnail_card.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
|
||||
class LibraryPage extends HookConsumerWidget {
|
||||
const LibraryPage({Key? key}) : super(key: key);
|
||||
|
@ -41,11 +41,11 @@ class LibraryPage extends HookConsumerWidget {
|
|||
|
||||
final selectedAlbumSortOrder = useState(0);
|
||||
|
||||
List<AlbumResponseDto> sortedAlbums() {
|
||||
List<Album> sortedAlbums() {
|
||||
if (selectedAlbumSortOrder.value == 0) {
|
||||
return albums.sortedBy((album) => album.createdAt).reversed.toList();
|
||||
}
|
||||
return albums.sortedBy((album) => album.albumName);
|
||||
return albums.sortedBy((album) => album.name);
|
||||
}
|
||||
|
||||
Widget buildSortButton() {
|
||||
|
|
|
@ -4,27 +4,28 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/suggested_shared_users.provider.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
||||
final AlbumResponseDto albumInfo;
|
||||
final Album album;
|
||||
|
||||
const SelectAdditionalUserForSharingPage({Key? key, required this.albumInfo})
|
||||
const SelectAdditionalUserForSharingPage({Key? key, required this.album})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
AsyncValue<List<UserResponseDto>> suggestedShareUsers =
|
||||
final AsyncValue<List<User>> suggestedShareUsers =
|
||||
ref.watch(suggestedSharedUsersProvider);
|
||||
final sharedUsersList = useState<Set<UserResponseDto>>({});
|
||||
final sharedUsersList = useState<Set<User>>({});
|
||||
|
||||
addNewUsersHandler() {
|
||||
AutoRouter.of(context)
|
||||
.pop(sharedUsersList.value.map((e) => e.id).toList());
|
||||
}
|
||||
|
||||
buildTileIcon(UserResponseDto user) {
|
||||
buildTileIcon(User user) {
|
||||
if (sharedUsersList.value.contains(user)) {
|
||||
return CircleAvatar(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
|
@ -42,7 +43,7 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
}
|
||||
}
|
||||
|
||||
buildUserList(List<UserResponseDto> users) {
|
||||
buildUserList(List<User> users) {
|
||||
List<Widget> usersChip = [];
|
||||
|
||||
for (var user in sharedUsersList.value) {
|
||||
|
@ -140,9 +141,9 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
),
|
||||
body: suggestedShareUsers.when(
|
||||
data: (users) {
|
||||
for (var sharedUsers in albumInfo.sharedUsers) {
|
||||
for (var sharedUsers in album.sharedUsers) {
|
||||
users.removeWhere(
|
||||
(u) => u.id == sharedUsers.id || u.id == albumInfo.ownerId,
|
||||
(u) => u.id == sharedUsers.id || u.id == album.ownerId,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,16 +8,16 @@ import 'package:immich_mobile/modules/album/providers/asset_selection.provider.d
|
|||
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/suggested_shared_users.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class SelectUserForSharingPage extends HookConsumerWidget {
|
||||
const SelectUserForSharingPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final sharedUsersList = useState<Set<UserResponseDto>>({});
|
||||
AsyncValue<List<UserResponseDto>> suggestedShareUsers =
|
||||
final sharedUsersList = useState<Set<User>>({});
|
||||
AsyncValue<List<User>> suggestedShareUsers =
|
||||
ref.watch(suggestedSharedUsersProvider);
|
||||
|
||||
createSharedAlbum() async {
|
||||
|
@ -25,7 +25,7 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||
await ref.watch(sharedAlbumProvider.notifier).createSharedAlbum(
|
||||
ref.watch(albumTitleProvider),
|
||||
ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum,
|
||||
sharedUsersList.value.map((userInfo) => userInfo.id).toList(),
|
||||
sharedUsersList.value,
|
||||
);
|
||||
|
||||
if (newAlbum != null) {
|
||||
|
@ -44,7 +44,7 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||
);
|
||||
}
|
||||
|
||||
buildTileIcon(UserResponseDto user) {
|
||||
buildTileIcon(User user) {
|
||||
if (sharedUsersList.value.contains(user)) {
|
||||
return CircleAvatar(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
|
@ -62,7 +62,7 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||
}
|
||||
}
|
||||
|
||||
buildUserList(List<UserResponseDto> users) {
|
||||
buildUserList(List<User> users) {
|
||||
List<Widget> usersChip = [];
|
||||
|
||||
for (var user in sharedUsersList.value) {
|
||||
|
|
|
@ -9,8 +9,8 @@ import 'package:immich_mobile/constants/hive_box.dart';
|
|||
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/sharing_sliver_appbar.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class SharingPage extends HookConsumerWidget {
|
||||
const SharingPage({Key? key}) : super(key: key);
|
||||
|
@ -18,7 +18,7 @@ class SharingPage extends HookConsumerWidget {
|
|||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
var box = Hive.box(userInfoBox);
|
||||
final List<AlbumResponseDto> sharedAlbums = ref.watch(sharedAlbumProvider);
|
||||
final List<Album> sharedAlbums = ref.watch(sharedAlbumProvider);
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
|
@ -52,7 +52,7 @@ class SharingPage extends HookConsumerWidget {
|
|||
),
|
||||
),
|
||||
title: Text(
|
||||
sharedAlbums[index].albumName,
|
||||
sharedAlbums[index].name,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
|
|
|
@ -4,16 +4,16 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:immich_mobile/modules/album/ui/add_to_album_sliverlist.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/delete_diaglog.dart';
|
||||
import 'package:immich_mobile/shared/ui/drag_sheet.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
|
||||
class ControlBottomAppBar extends ConsumerWidget {
|
||||
final Function onShare;
|
||||
final Function onDelete;
|
||||
final Function(AlbumResponseDto album) onAddToAlbum;
|
||||
final Function(Album album) onAddToAlbum;
|
||||
final void Function() onCreateNewAlbum;
|
||||
|
||||
final List<AlbumResponseDto> albums;
|
||||
final List<AlbumResponseDto> sharedAlbums;
|
||||
final List<Album> albums;
|
||||
final List<Album> sharedAlbums;
|
||||
|
||||
const ControlBottomAppBar({
|
||||
Key? key,
|
||||
|
|
|
@ -18,6 +18,7 @@ import 'package:immich_mobile/modules/home/ui/profile_drawer/profile_drawer.dart
|
|||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
|
||||
|
@ -25,7 +26,6 @@ import 'package:immich_mobile/shared/providers/websocket.provider.dart';
|
|||
import 'package:immich_mobile/shared/services/share.service.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class HomePage extends HookConsumerWidget {
|
||||
const HomePage({Key? key}) : super(key: key);
|
||||
|
@ -102,14 +102,14 @@ class HomePage extends HookConsumerWidget {
|
|||
return assets;
|
||||
}
|
||||
|
||||
void onAddToAlbum(AlbumResponseDto album) async {
|
||||
void onAddToAlbum(Album album) async {
|
||||
final Iterable<Asset> assets = remoteOnlySelection();
|
||||
if (assets.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final result = await albumService.addAdditionalAssetToAlbum(
|
||||
assets,
|
||||
album.id,
|
||||
album,
|
||||
);
|
||||
|
||||
if (result != null) {
|
||||
|
@ -118,7 +118,7 @@ class HomePage extends HookConsumerWidget {
|
|||
context: context,
|
||||
msg: "home_page_add_to_album_conflicts".tr(
|
||||
namedArgs: {
|
||||
"album": album.albumName,
|
||||
"album": album.name,
|
||||
"added": result.successfullyAdded.toString(),
|
||||
"failed": result.alreadyInAlbum.length.toString()
|
||||
},
|
||||
|
@ -129,7 +129,7 @@ class HomePage extends HookConsumerWidget {
|
|||
context: context,
|
||||
msg: "home_page_add_to_album_success".tr(
|
||||
namedArgs: {
|
||||
"album": album.albumName,
|
||||
"album": album.name,
|
||||
"added": result.successfullyAdded.toString(),
|
||||
},
|
||||
),
|
||||
|
|
|
@ -24,12 +24,12 @@ import 'package:immich_mobile/modules/search/views/search_result_page.dart';
|
|||
import 'package:immich_mobile/modules/settings/views/settings_page.dart';
|
||||
import 'package:immich_mobile/routing/auth_guard.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||
import 'package:immich_mobile/shared/views/app_log_page.dart';
|
||||
import 'package:immich_mobile/shared/views/splash_screen.dart';
|
||||
import 'package:immich_mobile/shared/views/tab_controller_page.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
|
||||
part 'router.gr.dart';
|
||||
|
|
|
@ -108,7 +108,7 @@ class _$AppRouter extends RootStackRouter {
|
|||
return CustomPage<List<String>?>(
|
||||
routeData: routeData,
|
||||
child: SelectAdditionalUserForSharingPage(
|
||||
key: args.key, albumInfo: args.albumInfo),
|
||||
key: args.key, album: args.album),
|
||||
transitionsBuilder: TransitionsBuilders.slideBottom,
|
||||
opaque: true,
|
||||
barrierDismissible: false);
|
||||
|
@ -447,27 +447,26 @@ class AlbumViewerRouteArgs {
|
|||
/// [SelectAdditionalUserForSharingPage]
|
||||
class SelectAdditionalUserForSharingRoute
|
||||
extends PageRouteInfo<SelectAdditionalUserForSharingRouteArgs> {
|
||||
SelectAdditionalUserForSharingRoute(
|
||||
{Key? key, required AlbumResponseDto albumInfo})
|
||||
SelectAdditionalUserForSharingRoute({Key? key, required Album album})
|
||||
: super(SelectAdditionalUserForSharingRoute.name,
|
||||
path: '/select-additional-user-for-sharing-page',
|
||||
args: SelectAdditionalUserForSharingRouteArgs(
|
||||
key: key, albumInfo: albumInfo));
|
||||
key: key, album: album));
|
||||
|
||||
static const String name = 'SelectAdditionalUserForSharingRoute';
|
||||
}
|
||||
|
||||
class SelectAdditionalUserForSharingRouteArgs {
|
||||
const SelectAdditionalUserForSharingRouteArgs(
|
||||
{this.key, required this.albumInfo});
|
||||
{this.key, required this.album});
|
||||
|
||||
final Key? key;
|
||||
|
||||
final AlbumResponseDto albumInfo;
|
||||
final Album album;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SelectAdditionalUserForSharingRouteArgs{key: $key, albumInfo: $albumInfo}';
|
||||
return 'SelectAdditionalUserForSharingRouteArgs{key: $key, album: $album}';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
132
mobile/lib/shared/models/album.dart
Normal file
132
mobile/lib/shared/models/album.dart
Normal file
|
@ -0,0 +1,132 @@
|
|||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
class Album {
|
||||
Album.remote(AlbumResponseDto dto)
|
||||
: remoteId = dto.id,
|
||||
name = dto.albumName,
|
||||
createdAt = DateTime.parse(dto.createdAt),
|
||||
// TODO add modifiedAt to server
|
||||
modifiedAt = DateTime.parse(dto.createdAt),
|
||||
shared = dto.shared,
|
||||
ownerId = dto.ownerId,
|
||||
albumThumbnailAssetId = dto.albumThumbnailAssetId,
|
||||
assetCount = dto.assetCount,
|
||||
sharedUsers = dto.sharedUsers.map((e) => User.fromDto(e)).toList(),
|
||||
assets = dto.assets.map(Asset.remote).toList();
|
||||
|
||||
Album({
|
||||
this.remoteId,
|
||||
this.localId,
|
||||
required this.name,
|
||||
required this.ownerId,
|
||||
required this.createdAt,
|
||||
required this.modifiedAt,
|
||||
required this.shared,
|
||||
required this.assetCount,
|
||||
this.albumThumbnailAssetId,
|
||||
this.sharedUsers = const [],
|
||||
this.assets = const [],
|
||||
});
|
||||
|
||||
String? remoteId;
|
||||
String? localId;
|
||||
String name;
|
||||
String ownerId;
|
||||
DateTime createdAt;
|
||||
DateTime modifiedAt;
|
||||
bool shared;
|
||||
String? albumThumbnailAssetId;
|
||||
int assetCount;
|
||||
List<User> sharedUsers = const [];
|
||||
List<Asset> assets = const [];
|
||||
|
||||
bool get isRemote => remoteId != null;
|
||||
|
||||
bool get isLocal => localId != null;
|
||||
|
||||
String get id => isRemote ? remoteId! : localId!;
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
if (other is! Album) return false;
|
||||
return remoteId == other.remoteId &&
|
||||
localId == other.localId &&
|
||||
name == other.name &&
|
||||
createdAt == other.createdAt &&
|
||||
modifiedAt == other.modifiedAt &&
|
||||
shared == other.shared &&
|
||||
ownerId == other.ownerId &&
|
||||
albumThumbnailAssetId == other.albumThumbnailAssetId;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
remoteId.hashCode ^
|
||||
localId.hashCode ^
|
||||
name.hashCode ^
|
||||
createdAt.hashCode ^
|
||||
modifiedAt.hashCode ^
|
||||
shared.hashCode ^
|
||||
ownerId.hashCode ^
|
||||
albumThumbnailAssetId.hashCode;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json["remoteId"] = remoteId;
|
||||
json["localId"] = localId;
|
||||
json["name"] = name;
|
||||
json["ownerId"] = ownerId;
|
||||
json["createdAt"] = createdAt.millisecondsSinceEpoch;
|
||||
json["modifiedAt"] = modifiedAt.millisecondsSinceEpoch;
|
||||
json["shared"] = shared;
|
||||
json["albumThumbnailAssetId"] = albumThumbnailAssetId;
|
||||
json["assetCount"] = assetCount;
|
||||
json["sharedUsers"] = sharedUsers;
|
||||
json["assets"] = assets;
|
||||
return json;
|
||||
}
|
||||
|
||||
static Album? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
return Album(
|
||||
remoteId: json["remoteId"],
|
||||
localId: json["localId"],
|
||||
name: json["name"],
|
||||
ownerId: json["ownerId"],
|
||||
createdAt: DateTime.fromMillisecondsSinceEpoch(
|
||||
json["createdAt"],
|
||||
isUtc: true,
|
||||
),
|
||||
modifiedAt: DateTime.fromMillisecondsSinceEpoch(
|
||||
json["modifiedAt"],
|
||||
isUtc: true,
|
||||
),
|
||||
shared: json["shared"],
|
||||
albumThumbnailAssetId: json["albumThumbnailAssetId"],
|
||||
assetCount: json["assetCount"],
|
||||
sharedUsers: _listFromJson<User>(json["sharedUsers"], User.fromJson),
|
||||
assets: _listFromJson<Asset>(json["assets"], Asset.fromJson),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
List<T> _listFromJson<T>(
|
||||
dynamic json,
|
||||
T? Function(dynamic) fromJson,
|
||||
) {
|
||||
final result = <T>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final entry in json) {
|
||||
final value = fromJson(entry);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
94
mobile/lib/shared/models/user.dart
Normal file
94
mobile/lib/shared/models/user.dart
Normal file
|
@ -0,0 +1,94 @@
|
|||
import 'package:openapi/api.dart';
|
||||
|
||||
class User {
|
||||
User({
|
||||
required this.id,
|
||||
required this.email,
|
||||
required this.firstName,
|
||||
required this.lastName,
|
||||
required this.profileImagePath,
|
||||
required this.isAdmin,
|
||||
required this.oauthId,
|
||||
});
|
||||
|
||||
User.fromDto(UserResponseDto dto)
|
||||
: id = dto.id,
|
||||
email = dto.email,
|
||||
firstName = dto.firstName,
|
||||
lastName = dto.lastName,
|
||||
profileImagePath = dto.profileImagePath,
|
||||
isAdmin = dto.isAdmin,
|
||||
oauthId = dto.oauthId;
|
||||
|
||||
String id;
|
||||
String email;
|
||||
String firstName;
|
||||
String lastName;
|
||||
String profileImagePath;
|
||||
bool isAdmin;
|
||||
String oauthId;
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
if (other is! User) return false;
|
||||
return id == other.id &&
|
||||
email == other.email &&
|
||||
firstName == other.firstName &&
|
||||
lastName == other.lastName &&
|
||||
profileImagePath == other.profileImagePath &&
|
||||
isAdmin == other.isAdmin &&
|
||||
oauthId == other.oauthId;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
id.hashCode ^
|
||||
email.hashCode ^
|
||||
firstName.hashCode ^
|
||||
lastName.hashCode ^
|
||||
profileImagePath.hashCode ^
|
||||
isAdmin.hashCode ^
|
||||
oauthId.hashCode;
|
||||
|
||||
UserResponseDto toDto() {
|
||||
return UserResponseDto(
|
||||
id: id,
|
||||
email: email,
|
||||
firstName: firstName,
|
||||
lastName: lastName,
|
||||
profileImagePath: profileImagePath,
|
||||
createdAt: '',
|
||||
isAdmin: isAdmin,
|
||||
shouldChangePassword: false,
|
||||
oauthId: oauthId,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json["id"] = id;
|
||||
json["email"] = email;
|
||||
json["firstName"] = firstName;
|
||||
json["lastName"] = lastName;
|
||||
json["profileImagePath"] = profileImagePath;
|
||||
json["isAdmin"] = isAdmin;
|
||||
json["oauthId"] = oauthId;
|
||||
return json;
|
||||
}
|
||||
|
||||
static User? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
return User(
|
||||
id: json["id"],
|
||||
email: json["email"],
|
||||
firstName: json["firstName"],
|
||||
lastName: json["lastName"],
|
||||
profileImagePath: json["profileImagePath"],
|
||||
isAdmin: json["isAdmin"],
|
||||
oauthId: json["oauthId"],
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:http/http.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
||||
import 'package:immich_mobile/shared/services/api.service.dart';
|
||||
import 'package:immich_mobile/utils/files_helper.dart';
|
||||
|
@ -19,9 +20,10 @@ class UserService {
|
|||
|
||||
UserService(this._apiService);
|
||||
|
||||
Future<List<UserResponseDto>?> getAllUsersInfo({required bool isAll}) async {
|
||||
Future<List<User>?> getAllUsers({required bool isAll}) async {
|
||||
try {
|
||||
return await _apiService.userApi.getAllUsers(isAll);
|
||||
final dto = await _apiService.userApi.getAllUsers(isAll);
|
||||
return dto?.map(User.fromDto).toList();
|
||||
} catch (e) {
|
||||
debugPrint("Error [getAllUsersInfo] ${e.toString()}");
|
||||
return null;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:hive/hive.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
|
@ -15,7 +16,7 @@ String getThumbnailCacheKey(
|
|||
final Asset asset, {
|
||||
ThumbnailFormat type = ThumbnailFormat.WEBP,
|
||||
}) {
|
||||
return _getThumbnailCacheKey(asset.id, type);
|
||||
return _getThumbnailCacheKey(asset.remoteId!, type);
|
||||
}
|
||||
|
||||
String _getThumbnailCacheKey(final String id, final ThumbnailFormat type) {
|
||||
|
@ -27,7 +28,7 @@ String _getThumbnailCacheKey(final String id, final ThumbnailFormat type) {
|
|||
}
|
||||
|
||||
String getAlbumThumbnailUrl(
|
||||
final AlbumResponseDto album, {
|
||||
final Album album, {
|
||||
ThumbnailFormat type = ThumbnailFormat.WEBP,
|
||||
}) {
|
||||
if (album.albumThumbnailAssetId == null) {
|
||||
|
@ -37,7 +38,7 @@ String getAlbumThumbnailUrl(
|
|||
}
|
||||
|
||||
String getAlbumThumbNailCacheKey(
|
||||
final AlbumResponseDto album, {
|
||||
final Album album, {
|
||||
ThumbnailFormat type = ThumbnailFormat.WEBP,
|
||||
}) {
|
||||
if (album.albumThumbnailAssetId == null) {
|
||||
|
|
Loading…
Reference in a new issue