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