Refactor mobile to use OpenApi generated SDK (#336)

This commit is contained in:
Alex 2022-07-13 07:23:48 -05:00 committed by GitHub
parent d69470e207
commit ae7e582ec8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
276 changed files with 14513 additions and 3003 deletions

View file

@ -1,12 +1,10 @@
import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
import 'package:openapi/api.dart';
class AssetSelectionPageResult {
final Set<ImmichAsset> selectedNewAsset;
final Set<ImmichAsset> selectedAdditionalAsset;
final Set<AssetResponseDto> selectedNewAsset;
final Set<AssetResponseDto> selectedAdditionalAsset;
final bool isAlbumExist;
AssetSelectionPageResult({
@ -16,8 +14,8 @@ class AssetSelectionPageResult {
});
AssetSelectionPageResult copyWith({
Set<ImmichAsset>? selectedNewAsset,
Set<ImmichAsset>? selectedAdditionalAsset,
Set<AssetResponseDto>? selectedNewAsset,
Set<AssetResponseDto>? selectedAdditionalAsset,
bool? isAlbumExist,
}) {
return AssetSelectionPageResult(
@ -28,35 +26,6 @@ class AssetSelectionPageResult {
);
}
Map<String, dynamic> toMap() {
final result = <String, dynamic>{};
result.addAll(
{'selectedNewAsset': selectedNewAsset.map((x) => x.toMap()).toList()});
result.addAll({
'selectedAdditionalAsset':
selectedAdditionalAsset.map((x) => x.toMap()).toList()
});
result.addAll({'isAlbumExist': isAlbumExist});
return result;
}
factory AssetSelectionPageResult.fromMap(Map<String, dynamic> map) {
return AssetSelectionPageResult(
selectedNewAsset: Set<ImmichAsset>.from(
map['selectedNewAsset']?.map((x) => ImmichAsset.fromMap(x))),
selectedAdditionalAsset: Set<ImmichAsset>.from(
map['selectedAdditionalAsset']?.map((x) => ImmichAsset.fromMap(x))),
isAlbumExist: map['isAlbumExist'] ?? false,
);
}
String toJson() => json.encode(toMap());
factory AssetSelectionPageResult.fromJson(String source) =>
AssetSelectionPageResult.fromMap(json.decode(source));
@override
String toString() =>
'AssetSelectionPageResult(selectedNewAsset: $selectedNewAsset, selectedAdditionalAsset: $selectedAdditionalAsset, isAlbumExist: $isAlbumExist)';

View file

@ -1,14 +1,12 @@
import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
import 'package:openapi/api.dart';
class AssetSelectionState {
final Set<String> selectedMonths;
final Set<ImmichAsset> selectedNewAssetsForAlbum;
final Set<ImmichAsset> selectedAdditionalAssetsForAlbum;
final Set<ImmichAsset> selectedAssetsInAlbumViewer;
final Set<AssetResponseDto> selectedNewAssetsForAlbum;
final Set<AssetResponseDto> selectedAdditionalAssetsForAlbum;
final Set<AssetResponseDto> selectedAssetsInAlbumViewer;
final bool isMultiselectEnable;
/// Indicate the asset selection page is navigated from existing album
@ -24,9 +22,9 @@ class AssetSelectionState {
AssetSelectionState copyWith({
Set<String>? selectedMonths,
Set<ImmichAsset>? selectedNewAssetsForAlbum,
Set<ImmichAsset>? selectedAdditionalAssetsForAlbum,
Set<ImmichAsset>? selectedAssetsInAlbumViewer,
Set<AssetResponseDto>? selectedNewAssetsForAlbum,
Set<AssetResponseDto>? selectedAdditionalAssetsForAlbum,
Set<AssetResponseDto>? selectedAssetsInAlbumViewer,
bool? isMultiselectEnable,
bool? isAlbumExist,
}) {
@ -43,49 +41,6 @@ class AssetSelectionState {
);
}
Map<String, dynamic> toMap() {
final result = <String, dynamic>{};
result.addAll({'selectedMonths': selectedMonths.toList()});
result.addAll({
'selectedNewAssetsForAlbum':
selectedNewAssetsForAlbum.map((x) => x.toMap()).toList()
});
result.addAll({
'selectedAdditionalAssetsForAlbum':
selectedAdditionalAssetsForAlbum.map((x) => x.toMap()).toList()
});
result.addAll({
'selectedAssetsInAlbumViewer':
selectedAssetsInAlbumViewer.map((x) => x.toMap()).toList()
});
result.addAll({'isMultiselectEnable': isMultiselectEnable});
result.addAll({'isAlbumExist': isAlbumExist});
return result;
}
factory AssetSelectionState.fromMap(Map<String, dynamic> map) {
return AssetSelectionState(
selectedMonths: Set<String>.from(map['selectedMonths']),
selectedNewAssetsForAlbum: Set<ImmichAsset>.from(
map['selectedNewAssetsForAlbum']?.map((x) => ImmichAsset.fromMap(x))),
selectedAdditionalAssetsForAlbum: Set<ImmichAsset>.from(
map['selectedAdditionalAssetsForAlbum']
?.map((x) => ImmichAsset.fromMap(x))),
selectedAssetsInAlbumViewer: Set<ImmichAsset>.from(
map['selectedAssetsInAlbumViewer']
?.map((x) => ImmichAsset.fromMap(x))),
isMultiselectEnable: map['isMultiselectEnable'] ?? false,
isAlbumExist: map['isAlbumExist'] ?? false,
);
}
String toJson() => json.encode(toMap());
factory AssetSelectionState.fromJson(String source) =>
AssetSelectionState.fromMap(json.decode(source));
@override
String toString() {
return 'AssetSelectionState(selectedMonths: $selectedMonths, selectedNewAssetsForAlbum: $selectedNewAssetsForAlbum, selectedAdditionalAssetsForAlbum: $selectedAdditionalAssetsForAlbum, selectedAssetsInAlbumViewer: $selectedAssetsInAlbumViewer, isMultiselectEnable: $isMultiselectEnable, isAlbumExist: $isAlbumExist)';
@ -99,10 +54,14 @@ class AssetSelectionState {
return other is AssetSelectionState &&
setEquals(other.selectedMonths, selectedMonths) &&
setEquals(other.selectedNewAssetsForAlbum, selectedNewAssetsForAlbum) &&
setEquals(other.selectedAdditionalAssetsForAlbum,
selectedAdditionalAssetsForAlbum) &&
setEquals(
other.selectedAssetsInAlbumViewer, selectedAssetsInAlbumViewer) &&
other.selectedAdditionalAssetsForAlbum,
selectedAdditionalAssetsForAlbum,
) &&
setEquals(
other.selectedAssetsInAlbumViewer,
selectedAssetsInAlbumViewer,
) &&
other.isMultiselectEnable == isMultiselectEnable &&
other.isAlbumExist == isAlbumExist;
}

View file

@ -1,117 +0,0 @@
import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
import 'package:immich_mobile/shared/models/user.model.dart';
class SharedAlbum {
final String id;
final String ownerId;
final String albumName;
final String createdAt;
final String? albumThumbnailAssetId;
final List<User> sharedUsers;
final List<ImmichAsset>? assets;
SharedAlbum({
required this.id,
required this.ownerId,
required this.albumName,
required this.createdAt,
required this.albumThumbnailAssetId,
required this.sharedUsers,
this.assets,
});
SharedAlbum copyWith({
String? id,
String? ownerId,
String? albumName,
String? createdAt,
String? albumThumbnailAssetId,
List<User>? sharedUsers,
List<ImmichAsset>? assets,
}) {
return SharedAlbum(
id: id ?? this.id,
ownerId: ownerId ?? this.ownerId,
albumName: albumName ?? this.albumName,
createdAt: createdAt ?? this.createdAt,
albumThumbnailAssetId:
albumThumbnailAssetId ?? this.albumThumbnailAssetId,
sharedUsers: sharedUsers ?? this.sharedUsers,
assets: assets ?? this.assets,
);
}
Map<String, dynamic> toMap() {
final result = <String, dynamic>{};
result.addAll({'id': id});
result.addAll({'ownerId': ownerId});
result.addAll({'albumName': albumName});
result.addAll({'createdAt': createdAt});
if (albumThumbnailAssetId != null) {
result.addAll({'albumThumbnailAssetId': albumThumbnailAssetId});
}
result.addAll({'sharedUsers': sharedUsers.map((x) => x.toMap()).toList()});
if (assets != null) {
result.addAll({'assets': assets!.map((x) => x.toMap()).toList()});
}
return result;
}
factory SharedAlbum.fromMap(Map<String, dynamic> map) {
return SharedAlbum(
id: map['id'] ?? '',
ownerId: map['ownerId'] ?? '',
albumName: map['albumName'] ?? '',
createdAt: map['createdAt'] ?? '',
albumThumbnailAssetId: map['albumThumbnailAssetId'],
sharedUsers:
List<User>.from(map['sharedUsers']?.map((x) => User.fromMap(x))),
assets: map['assets'] != null
? List<ImmichAsset>.from(
map['assets']?.map((x) => ImmichAsset.fromMap(x)))
: null,
);
}
String toJson() => json.encode(toMap());
factory SharedAlbum.fromJson(String source) =>
SharedAlbum.fromMap(json.decode(source));
@override
String toString() {
return 'SharedAlbum(id: $id, ownerId: $ownerId, albumName: $albumName, createdAt: $createdAt, albumThumbnailAssetId: $albumThumbnailAssetId, sharedUsers: $sharedUsers, assets: $assets)';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
final listEquals = const DeepCollectionEquality().equals;
return other is SharedAlbum &&
other.id == id &&
other.ownerId == ownerId &&
other.albumName == albumName &&
other.createdAt == createdAt &&
other.albumThumbnailAssetId == albumThumbnailAssetId &&
listEquals(other.sharedUsers, sharedUsers) &&
listEquals(other.assets, assets);
}
@override
int get hashCode {
return id.hashCode ^
ownerId.hashCode ^
albumName.hashCode ^
createdAt.hashCode ^
albumThumbnailAssetId.hashCode ^
sharedUsers.hashCode ^
assets.hashCode;
}
}

View file

@ -13,4 +13,5 @@ class AlbumTitleNotifier extends StateNotifier<String> {
}
final albumTitleProvider = StateNotifierProvider<AlbumTitleNotifier, String>(
(ref) => AlbumTitleNotifier());
(ref) => AlbumTitleNotifier(),
);

View file

@ -30,7 +30,10 @@ class AlbumViewerNotifier extends StateNotifier<AlbumViewerPageState> {
}
Future<bool> changeAlbumTitle(
String albumId, String ownerId, String newAlbumTitle) async {
String albumId,
String ownerId,
String newAlbumTitle,
) async {
SharedAlbumService service = ref.watch(sharedAlbumServiceProvider);
bool isSuccess =

View file

@ -1,41 +1,46 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/sharing/models/asset_selection_state.model.dart';
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
import 'package:openapi/api.dart';
class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
AssetSelectionNotifier()
: super(AssetSelectionState(
selectedNewAssetsForAlbum: {},
selectedMonths: {},
selectedAdditionalAssetsForAlbum: {},
selectedAssetsInAlbumViewer: {},
isAlbumExist: false,
isMultiselectEnable: false,
));
: super(
AssetSelectionState(
selectedNewAssetsForAlbum: {},
selectedMonths: {},
selectedAdditionalAssetsForAlbum: {},
selectedAssetsInAlbumViewer: {},
isAlbumExist: false,
isMultiselectEnable: false,
),
);
void setIsAlbumExist(bool isAlbumExist) {
state = state.copyWith(isAlbumExist: isAlbumExist);
}
void removeAssetsInMonth(
String removedMonth, List<ImmichAsset> assetsInMonth) {
Set<ImmichAsset> currentAssetList = state.selectedNewAssetsForAlbum;
String removedMonth,
List<AssetResponseDto> assetsInMonth,
) {
Set<AssetResponseDto> currentAssetList = state.selectedNewAssetsForAlbum;
Set<String> currentMonthList = state.selectedMonths;
currentMonthList
.removeWhere((selectedMonth) => selectedMonth == removedMonth);
for (ImmichAsset asset in assetsInMonth) {
for (AssetResponseDto asset in assetsInMonth) {
currentAssetList.removeWhere((e) => e.id == asset.id);
}
state = state.copyWith(
selectedNewAssetsForAlbum: currentAssetList,
selectedMonths: currentMonthList);
selectedNewAssetsForAlbum: currentAssetList,
selectedMonths: currentMonthList,
);
}
void addAdditionalAssets(List<ImmichAsset> assets) {
void addAdditionalAssets(List<AssetResponseDto> assets) {
state = state.copyWith(
selectedAdditionalAssetsForAlbum: {
...state.selectedAdditionalAssetsForAlbum,
@ -44,7 +49,7 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
);
}
void addAllAssetsInMonth(String month, List<ImmichAsset> assetsInMonth) {
void addAllAssetsInMonth(String month, List<AssetResponseDto> assetsInMonth) {
state = state.copyWith(
selectedMonths: {...state.selectedMonths, month},
selectedNewAssetsForAlbum: {
@ -54,7 +59,7 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
);
}
void addNewAssets(List<ImmichAsset> assets) {
void addNewAssets(List<AssetResponseDto> assets) {
state = state.copyWith(
selectedNewAssetsForAlbum: {
...state.selectedNewAssetsForAlbum,
@ -63,20 +68,20 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
);
}
void removeSelectedNewAssets(List<ImmichAsset> assets) {
Set<ImmichAsset> currentList = state.selectedNewAssetsForAlbum;
void removeSelectedNewAssets(List<AssetResponseDto> assets) {
Set<AssetResponseDto> currentList = state.selectedNewAssetsForAlbum;
for (ImmichAsset asset in assets) {
for (AssetResponseDto asset in assets) {
currentList.removeWhere((e) => e.id == asset.id);
}
state = state.copyWith(selectedNewAssetsForAlbum: currentList);
}
void removeSelectedAdditionalAssets(List<ImmichAsset> assets) {
Set<ImmichAsset> currentList = state.selectedAdditionalAssetsForAlbum;
void removeSelectedAdditionalAssets(List<AssetResponseDto> assets) {
Set<AssetResponseDto> currentList = state.selectedAdditionalAssetsForAlbum;
for (ImmichAsset asset in assets) {
for (AssetResponseDto asset in assets) {
currentList.removeWhere((e) => e.id == asset.id);
}
@ -104,7 +109,7 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
);
}
void addAssetsInAlbumViewer(List<ImmichAsset> assets) {
void addAssetsInAlbumViewer(List<AssetResponseDto> assets) {
state = state.copyWith(
selectedAssetsInAlbumViewer: {
...state.selectedAssetsInAlbumViewer,
@ -113,10 +118,10 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
);
}
void removeAssetsInAlbumViewer(List<ImmichAsset> assets) {
Set<ImmichAsset> currentList = state.selectedAssetsInAlbumViewer;
void removeAssetsInAlbumViewer(List<AssetResponseDto> assets) {
Set<AssetResponseDto> currentList = state.selectedAssetsInAlbumViewer;
for (ImmichAsset asset in assets) {
for (AssetResponseDto asset in assets) {
currentList.removeWhere((e) => e.id == asset.id);
}

View file

@ -1,17 +1,19 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart';
import 'package:openapi/api.dart';
class SharedAlbumNotifier extends StateNotifier<List<SharedAlbum>> {
class SharedAlbumNotifier extends StateNotifier<List<AlbumResponseDto>> {
SharedAlbumNotifier(this._sharedAlbumService) : super([]);
final SharedAlbumService _sharedAlbumService;
getAllSharedAlbums() async {
List<SharedAlbum> sharedAlbums =
List<AlbumResponseDto>? sharedAlbums =
await _sharedAlbumService.getAllSharedAlbum();
state = sharedAlbums;
if (sharedAlbums != null) {
state = sharedAlbums;
}
}
Future<bool> deleteAlbum(String albumId) async {
@ -37,7 +39,9 @@ class SharedAlbumNotifier extends StateNotifier<List<SharedAlbum>> {
}
Future<bool> removeAssetFromAlbum(
String albumId, List<String> assetIds) async {
String albumId,
List<String> assetIds,
) async {
var res = await _sharedAlbumService.removeAssetFromAlbum(albumId, assetIds);
if (res) {
@ -49,12 +53,12 @@ class SharedAlbumNotifier extends StateNotifier<List<SharedAlbum>> {
}
final sharedAlbumProvider =
StateNotifierProvider<SharedAlbumNotifier, List<SharedAlbum>>((ref) {
StateNotifierProvider<SharedAlbumNotifier, List<AlbumResponseDto>>((ref) {
return SharedAlbumNotifier(ref.watch(sharedAlbumServiceProvider));
});
final sharedAlbumDetailProvider = FutureProvider.autoDispose
.family<SharedAlbum, String>((ref, albumId) async {
.family<AlbumResponseDto?, String>((ref, albumId) async {
final SharedAlbumService sharedAlbumService =
ref.watch(sharedAlbumServiceProvider);

View file

@ -1,10 +1,10 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/shared/models/user.model.dart';
import 'package:immich_mobile/shared/services/user.service.dart';
import 'package:openapi/api.dart';
final suggestedSharedUsersProvider =
FutureProvider.autoDispose<List<User>>((ref) async {
FutureProvider.autoDispose<List<UserResponseDto>>((ref) async {
UserService userService = ref.watch(userServiceProvider);
return await userService.getAllUsersInfo();
return await userService.getAllUsersInfo(isAll: false) ?? [];
});

View file

@ -1,73 +1,69 @@
import 'dart:async';
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
import 'package:immich_mobile/shared/services/network.service.dart';
import 'package:immich_mobile/shared/services/api.service.dart';
import 'package:openapi/api.dart';
final sharedAlbumServiceProvider =
Provider((ref) => SharedAlbumService(ref.watch(networkServiceProvider)));
final sharedAlbumServiceProvider = Provider(
(ref) => SharedAlbumService(
ref.watch(apiServiceProvider),
),
);
class SharedAlbumService {
final NetworkService _networkService;
SharedAlbumService(this._networkService);
final ApiService _apiService;
SharedAlbumService(this._apiService);
Future<List<SharedAlbum>> getAllSharedAlbum() async {
Future<List<AlbumResponseDto>?> getAllSharedAlbum() async {
try {
var res = await _networkService.getRequest(url: 'album?shared=true');
List<dynamic> decodedData = jsonDecode(res.toString());
List<SharedAlbum> result =
List.from(decodedData.map((e) => SharedAlbum.fromMap(e)));
return result;
return await _apiService.albumApi.getAllAlbums(shared: true);
} catch (e) {
debugPrint("Error getAllSharedAlbum ${e.toString()}");
return null;
}
return [];
}
Future<bool> createSharedAlbum(String albumName, Set<ImmichAsset> assets,
List<String> sharedUserIds) async {
Future<bool> createSharedAlbum(
String albumName,
Set<AssetResponseDto> assets,
List<String> sharedUserIds,
) async {
try {
var res = await _networkService.postRequest(url: 'album', data: {
"albumName": albumName,
"sharedWithUserIds": sharedUserIds,
"assetIds": assets.map((asset) => asset.id).toList(),
});
_apiService.albumApi.createAlbum(
CreateAlbumDto(
albumName: albumName,
assetIds: assets.map((asset) => asset.id).toList(),
sharedWithUserIds: sharedUserIds,
),
);
return res != null;
return true;
} catch (e) {
debugPrint("Error createSharedAlbum ${e.toString()}");
return false;
}
}
Future<SharedAlbum> getAlbumDetail(String albumId) async {
Future<AlbumResponseDto?> getAlbumDetail(String albumId) async {
try {
var res = await _networkService.getRequest(url: 'album/$albumId');
dynamic decodedData = jsonDecode(res.toString());
SharedAlbum result = SharedAlbum.fromMap(decodedData);
return result;
return await _apiService.albumApi.getAlbumInfo(albumId);
} catch (e) {
throw Exception('Error getAllSharedAlbum ${e.toString()}');
debugPrint('Error [getAlbumDetail] ${e.toString()}');
return null;
}
}
Future<bool> addAdditionalAssetToAlbum(
Set<ImmichAsset> assets, String albumId) async {
Set<AssetResponseDto> assets,
String albumId,
) async {
try {
var res =
await _networkService.putRequest(url: 'album/$albumId/assets', data: {
"albumId": albumId,
"assetIds": assets.map((asset) => asset.id).toList(),
});
return res != null;
var result = await _apiService.albumApi.addAssetsToAlbum(
albumId,
AddAssetsDto(assetIds: assets.map((asset) => asset.id).toList()),
);
return result != null;
} catch (e) {
debugPrint("Error addAdditionalAssetToAlbum ${e.toString()}");
return false;
@ -75,14 +71,16 @@ class SharedAlbumService {
}
Future<bool> addAdditionalUserToAlbum(
List<String> sharedUserIds, String albumId) async {
List<String> sharedUserIds,
String albumId,
) async {
try {
var res =
await _networkService.putRequest(url: 'album/$albumId/users', data: {
"sharedUserIds": sharedUserIds,
});
var result = await _apiService.albumApi.addUsersToAlbum(
albumId,
AddUsersDto(sharedUserIds: sharedUserIds),
);
return res != null;
return result != null;
} catch (e) {
debugPrint("Error addAdditionalUserToAlbum ${e.toString()}");
return false;
@ -91,12 +89,7 @@ class SharedAlbumService {
Future<bool> deleteAlbum(String albumId) async {
try {
Response res = await _networkService.deleteRequest(url: 'album/$albumId');
if (res.statusCode != 200) {
return false;
}
await _apiService.albumApi.deleteAlbum(albumId);
return true;
} catch (e) {
debugPrint("Error deleteAlbum ${e.toString()}");
@ -106,12 +99,7 @@ class SharedAlbumService {
Future<bool> leaveAlbum(String albumId) async {
try {
Response res =
await _networkService.deleteRequest(url: 'album/$albumId/user/me');
if (res.statusCode != 200) {
return false;
}
await _apiService.albumApi.removeUserFromAlbum(albumId, "me");
return true;
} catch (e) {
@ -121,16 +109,14 @@ class SharedAlbumService {
}
Future<bool> removeAssetFromAlbum(
String albumId, List<String> assetIds) async {
String albumId,
List<String> assetIds,
) async {
try {
Response res = await _networkService
.deleteRequest(url: 'album/$albumId/assets', data: {
"assetIds": assetIds,
});
if (res.statusCode != 200) {
return false;
}
await _apiService.albumApi.removeAssetFromAlbum(
albumId,
RemoveAssetsDto(assetIds: assetIds),
);
return true;
} catch (e) {
@ -140,17 +126,18 @@ class SharedAlbumService {
}
Future<bool> changeTitleAlbum(
String albumId, String ownerId, String newAlbumTitle) async {
String albumId,
String ownerId,
String newAlbumTitle,
) async {
try {
Response res =
await _networkService.patchRequest(url: 'album/$albumId/', data: {
"ownerId": ownerId,
"albumName": newAlbumTitle,
});
if (res.statusCode != 200) {
return false;
}
await _apiService.albumApi.updateAlbumInfo(
albumId,
UpdateAlbumDto(
ownerId: ownerId,
albumName: newAlbumTitle,
),
);
return true;
} catch (e) {

View file

@ -5,12 +5,12 @@ class AlbumActionOutlinedButton extends StatelessWidget {
final String labelText;
final IconData iconData;
const AlbumActionOutlinedButton(
{Key? key,
this.onPressed,
required this.labelText,
required this.iconData})
: super(key: key);
const AlbumActionOutlinedButton({
Key? key,
this.onPressed,
required this.labelText,
required this.iconData,
}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -31,7 +31,10 @@ class AlbumActionOutlinedButton extends StatelessWidget {
label: Text(
labelText,
style: const TextStyle(
fontSize: 12, fontWeight: FontWeight.bold, color: Colors.black87),
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
onPressed: onPressed,
),

View file

@ -31,7 +31,10 @@ class AlbumTitleTextField extends ConsumerWidget {
},
focusNode: albumTitleTextFieldFocusNode,
style: TextStyle(
fontSize: 28, color: Colors.grey[700], fontWeight: FontWeight.bold),
fontSize: 28,
color: Colors.grey[700],
fontWeight: FontWeight.bold,
),
controller: albumTitleController,
onTap: () {
isAlbumTitleTextFieldFocus.value = true;

View file

@ -4,24 +4,24 @@ import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/immich_colors.dart';
import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
import 'package:immich_mobile/modules/sharing/providers/album_viewer.provider.dart';
import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
import 'package:immich_mobile/routing/router.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 AsyncValue<SharedAlbum> albumInfo,
required AsyncValue<AlbumResponseDto?> albumInfo,
required this.userId,
required this.albumId,
}) : _albumInfo = albumInfo,
super(key: key);
final AsyncValue<SharedAlbum> _albumInfo;
final AsyncValue<AlbumResponseDto?> _albumInfo;
final String userId;
final String albumId;
@ -105,7 +105,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
_buildBottomSheetActionButton() {
if (isMultiSelectionEnable) {
if (_albumInfo.asData?.value.ownerId == userId) {
if (_albumInfo.asData?.value?.ownerId == userId) {
return ListTile(
leading: const Icon(Icons.delete_sweep_rounded),
title: const Text(
@ -118,7 +118,7 @@ class AlbumViewerAppbar extends HookConsumerWidget with PreferredSizeWidget {
return const SizedBox();
}
} else {
if (_albumInfo.asData?.value.ownerId == userId) {
if (_albumInfo.asData?.value?.ownerId == userId) {
return ListTile(
leading: const Icon(Icons.delete_forever_rounded),
title: const Text(

View file

@ -2,15 +2,17 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
import 'package:immich_mobile/modules/sharing/providers/album_viewer.provider.dart';
import 'package:openapi/api.dart';
class AlbumViewerEditableTitle extends HookConsumerWidget {
final SharedAlbum albumInfo;
final AlbumResponseDto albumInfo;
final FocusNode titleFocusNode;
const AlbumViewerEditableTitle(
{Key? key, required this.albumInfo, required this.titleFocusNode})
: super(key: key);
const AlbumViewerEditableTitle({
Key? key,
required this.albumInfo,
required this.titleFocusNode,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -24,12 +26,15 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
}
}
useEffect(() {
titleFocusNode.addListener(onFocusModeChange);
return () {
titleFocusNode.removeListener(onFocusModeChange);
};
}, []);
useEffect(
() {
titleFocusNode.addListener(onFocusModeChange);
return () {
titleFocusNode.removeListener(onFocusModeChange);
};
},
[],
);
return TextField(
onChanged: (value) {

View file

@ -7,11 +7,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/hive_box.dart';
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:openapi/api.dart';
class AlbumViewerThumbnail extends HookConsumerWidget {
final ImmichAsset asset;
final AssetResponseDto asset;
const AlbumViewerThumbnail({Key? key, required this.asset}) : super(key: key);
@ -20,7 +20,7 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
final cacheKey = useState(1);
var box = Hive.box(userInfoBox);
var thumbnailRequestUrl =
'${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true';
'${box.get(serverEndpointKey)}/asset/thumbnail/${asset.id}';
var deviceId = ref.watch(authenticationProvider).deviceId;
final selectedAssetsInAlbumViewer =
ref.watch(assetSelectionProvider).selectedAssetsInAlbumViewer;
@ -28,7 +28,7 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
ref.watch(assetSelectionProvider).isMultiselectEnable;
_viewAsset() {
if (asset.type == 'IMAGE') {
if (asset.type == AssetTypeEnum.IMAGE) {
AutoRouter.of(context).push(
ImageViewerRoute(
imageUrl:
@ -41,9 +41,10 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
} else {
AutoRouter.of(context).push(
VideoViewerRoute(
videoUrl:
'${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}',
asset: asset),
videoUrl:
'${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}',
asset: asset,
),
);
}
}
@ -170,16 +171,13 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
return GestureDetector(
onTap: isMultiSelectionEnable ? _handleSelectionGesture : _viewAsset,
onLongPress: _enableMultiSelection,
child: Hero(
tag: asset.id,
child: Stack(
children: [
_buildThumbnailImage(),
_buildAssetStoreLocationIcon(),
if (asset.type != 'IMAGE') _buildVideoLabel(),
if (isMultiSelectionEnable) _buildAssetSelectionIcon(),
],
),
child: Stack(
children: [
_buildThumbnailImage(),
_buildAssetStoreLocationIcon(),
if (asset.type != AssetTypeEnum.IMAGE) _buildVideoLabel(),
if (isMultiSelectionEnable) _buildAssetSelectionIcon(),
],
),
);
}

View file

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/sharing/ui/selection_thumbnail_image.dart';
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
import 'package:openapi/api.dart';
class AssetGridByMonth extends HookConsumerWidget {
final List<ImmichAsset> assetGroup;
final List<AssetResponseDto> assetGroup;
const AssetGridByMonth({Key? key, required this.assetGroup})
: super(key: key);
@override

View file

@ -2,15 +2,17 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
import 'package:openapi/api.dart';
class MonthGroupTitle extends HookConsumerWidget {
final String month;
final List<ImmichAsset> assetGroup;
final List<AssetResponseDto> assetGroup;
const MonthGroupTitle(
{Key? key, required this.month, required this.assetGroup})
: super(key: key);
const MonthGroupTitle({
Key? key,
required this.month,
required this.assetGroup,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -75,7 +77,11 @@ class MonthGroupTitle extends HookConsumerWidget {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(
top: 29.0, bottom: 29.0, left: 14.0, right: 8.0),
top: 29.0,
bottom: 29.0,
left: 14.0,
right: 8.0,
),
child: Row(
children: [
GestureDetector(

View file

@ -5,10 +5,10 @@ import 'package:hive_flutter/hive_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/hive_box.dart';
import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
import 'package:openapi/api.dart';
class SelectionThumbnailImage extends HookConsumerWidget {
final ImmichAsset asset;
final AssetResponseDto asset;
const SelectionThumbnailImage({Key? key, required this.asset})
: super(key: key);
@ -18,14 +18,14 @@ class SelectionThumbnailImage extends HookConsumerWidget {
final cacheKey = useState(1);
var box = Hive.box(userInfoBox);
var thumbnailRequestUrl =
'${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true';
'${box.get(serverEndpointKey)}/asset/thumbnail/${asset.id}';
var selectedAsset =
ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum;
var newAssetsForAlbum =
ref.watch(assetSelectionProvider).selectedAdditionalAssetsForAlbum;
var isAlbumExist = ref.watch(assetSelectionProvider).isAlbumExist;
Widget _buildSelectionIcon(ImmichAsset asset) {
Widget _buildSelectionIcon(AssetResponseDto asset) {
if (selectedAsset.contains(asset) && !isAlbumExist) {
return Icon(
Icons.check_circle,
@ -103,7 +103,7 @@ class SelectionThumbnailImage extends HookConsumerWidget {
cacheKey: "${asset.id}-${cacheKey.value}",
width: 150,
height: 150,
memCacheHeight: asset.type == 'IMAGE' ? 150 : 150,
memCacheHeight: asset.type == AssetTypeEnum.IMAGE ? 150 : 150,
fit: BoxFit.cover,
imageUrl: thumbnailRequestUrl,
httpHeaders: {
@ -131,14 +131,14 @@ class SelectionThumbnailImage extends HookConsumerWidget {
child: _buildSelectionIcon(asset),
),
),
if (asset.type != 'IMAGE')
if (asset.type != AssetTypeEnum.IMAGE)
Positioned(
bottom: 5,
right: 5,
child: Row(
children: [
Text(
'${asset.duration?.substring(0, 7)}',
asset.duration.substring(0, 7),
style: const TextStyle(
color: Colors.white,
fontSize: 10,

View file

@ -4,10 +4,10 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/hive_box.dart';
import 'package:immich_mobile/shared/models/immich_asset.model.dart';
import 'package:openapi/api.dart';
class SharedAlbumThumbnailImage extends HookConsumerWidget {
final ImmichAsset asset;
final AssetResponseDto asset;
const SharedAlbumThumbnailImage({Key? key, required this.asset})
: super(key: key);
@ -18,7 +18,7 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget {
var box = Hive.box(userInfoBox);
var thumbnailRequestUrl =
'${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true';
'${box.get(serverEndpointKey)}/asset/thumbnail/${asset.id}';
return GestureDetector(
onTap: () {
@ -30,7 +30,7 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget {
cacheKey: "${asset.id}-${cacheKey.value}",
width: 500,
height: 500,
memCacheHeight: asset.type == 'IMAGE' ? 500 : 500,
memCacheHeight: 500,
fit: BoxFit.cover,
imageUrl: thumbnailRequestUrl,
httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},

View file

@ -40,7 +40,8 @@ class SharingSliverAppBar extends StatelessWidget {
child: TextButton.icon(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Theme.of(context).primaryColor.withAlpha(20)),
Theme.of(context).primaryColor.withAlpha(20),
),
// foregroundColor: MaterialStateProperty.all(Colors.white),
),
onPressed: () {
@ -65,7 +66,8 @@ class SharingSliverAppBar extends StatelessWidget {
child: TextButton.icon(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Theme.of(context).primaryColor.withAlpha(20)),
Theme.of(context).primaryColor.withAlpha(20),
),
// foregroundColor: MaterialStateProperty.all(Colors.white),
),
onPressed: null,

View file

@ -7,7 +7,6 @@ import 'package:immich_mobile/constants/immich_colors.dart';
import 'package:immich_mobile/modules/home/ui/draggable_scrollbar.dart';
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
import 'package:immich_mobile/modules/sharing/models/asset_selection_page_result.model.dart';
import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
import 'package:immich_mobile/modules/sharing/providers/asset_selection.provider.dart';
import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart';
@ -19,7 +18,7 @@ import 'package:immich_mobile/routing/router.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:intl/intl.dart';
import 'package:openapi/api.dart';
class AlbumViewerPage extends HookConsumerWidget {
final String albumId;
@ -30,18 +29,18 @@ class AlbumViewerPage extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
FocusNode titleFocusNode = useFocusNode();
ScrollController scrollController = useScrollController();
AsyncValue<SharedAlbum> albumInfo =
AsyncValue<AlbumResponseDto?> albumInfo =
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(SharedAlbum albumInfo) async {
if (albumInfo.assets?.isNotEmpty == true) {
void _onAddPhotosPressed(AlbumResponseDto albumInfo) async {
if (albumInfo.assets.isNotEmpty == true) {
ref
.watch(assetSelectionProvider.notifier)
.addNewAssets(albumInfo.assets!.toList());
.addNewAssets(albumInfo.assets.toList());
}
ref.watch(assetSelectionProvider.notifier).setIsAlbumExist(true);
@ -57,7 +56,9 @@ class AlbumViewerPage extends HookConsumerWidget {
var isSuccess = await ref
.watch(sharedAlbumServiceProvider)
.addAdditionalAssetToAlbum(
returnPayload.selectedAdditionalAsset, albumId);
returnPayload.selectedAdditionalAsset,
albumId,
);
if (isSuccess) {
ref.refresh(sharedAlbumDetailProvider(albumId));
@ -72,10 +73,11 @@ class AlbumViewerPage extends HookConsumerWidget {
}
}
void _onAddUsersPressed(SharedAlbum albumInfo) async {
List<String>? sharedUserIds = await AutoRouter.of(context)
.push<List<String>?>(
SelectAdditionalUserForSharingRoute(albumInfo: albumInfo));
void _onAddUsersPressed(AlbumResponseDto albumInfo) async {
List<String>? sharedUserIds =
await AutoRouter.of(context).push<List<String>?>(
SelectAdditionalUserForSharingRoute(albumInfo: albumInfo),
);
if (sharedUserIds != null) {
ImmichLoadingOverlayController.appLoader.show();
@ -92,7 +94,7 @@ class AlbumViewerPage extends HookConsumerWidget {
}
}
Widget _buildTitle(SharedAlbum albumInfo) {
Widget _buildTitle(AlbumResponseDto albumInfo) {
return Padding(
padding: const EdgeInsets.only(left: 8, right: 8, top: 16),
child: userId == albumInfo.ownerId
@ -102,19 +104,24 @@ class AlbumViewerPage extends HookConsumerWidget {
)
: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(albumInfo.albumName,
style: const TextStyle(
fontSize: 24, fontWeight: FontWeight.bold)),
child: Text(
albumInfo.albumName,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
),
);
}
Widget _buildAlbumDateRange(SharedAlbum albumInfo) {
Widget _buildAlbumDateRange(AlbumResponseDto albumInfo) {
String startDate = "";
DateTime parsedStartDate =
DateTime.parse(albumInfo.assets!.first.createdAt);
DateTime.parse(albumInfo.assets.first.createdAt);
DateTime parsedEndDate = DateTime.parse(
albumInfo.assets?.last.createdAt ?? '11111111'); //Need default.
albumInfo.assets.last.createdAt,
); //Need default.
if (parsedStartDate.year == parsedEndDate.year) {
startDate = DateFormat('LLL d').format(parsedStartDate);
@ -129,18 +136,21 @@ class AlbumViewerPage extends HookConsumerWidget {
child: Text(
"$startDate-$endDate",
style: const TextStyle(
fontSize: 14, fontWeight: FontWeight.bold, color: Colors.grey),
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.grey,
),
),
);
}
Widget _buildHeader(SharedAlbum albumInfo) {
Widget _buildHeader(AlbumResponseDto albumInfo) {
return SliverToBoxAdapter(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTitle(albumInfo),
if (albumInfo.assets?.isNotEmpty == true)
if (albumInfo.assets.isNotEmpty == true)
_buildAlbumDateRange(albumInfo),
SizedBox(
height: 60,
@ -172,8 +182,8 @@ class AlbumViewerPage extends HookConsumerWidget {
);
}
Widget _buildImageGrid(SharedAlbum albumInfo) {
if (albumInfo.assets?.isNotEmpty == true) {
Widget _buildImageGrid(AlbumResponseDto albumInfo) {
if (albumInfo.assets.isNotEmpty) {
return SliverPadding(
padding: const EdgeInsets.only(top: 10.0),
sliver: SliverGrid(
@ -184,9 +194,9 @@ class AlbumViewerPage extends HookConsumerWidget {
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return AlbumViewerThumbnail(asset: albumInfo.assets![index]);
return AlbumViewerThumbnail(asset: albumInfo.assets[index]);
},
childCount: albumInfo.assets?.length,
childCount: albumInfo.assets.length,
),
),
);
@ -194,7 +204,7 @@ class AlbumViewerPage extends HookConsumerWidget {
return const SliverToBoxAdapter();
}
Widget _buildControlButton(SharedAlbum albumInfo) {
Widget _buildControlButton(AlbumResponseDto albumInfo) {
return Padding(
padding: const EdgeInsets.only(left: 16.0, top: 8, bottom: 8),
child: SizedBox(
@ -219,7 +229,7 @@ class AlbumViewerPage extends HookConsumerWidget {
);
}
Widget _buildBody(SharedAlbum albumInfo) {
Widget _buildBody(AlbumResponseDto albumInfo) {
return GestureDetector(
onTap: () {
titleFocusNode.unfocus();
@ -252,9 +262,16 @@ class AlbumViewerPage extends HookConsumerWidget {
return Scaffold(
appBar: AlbumViewerAppbar(
albumInfo: albumInfo, userId: userId, albumId: albumId),
albumInfo: albumInfo,
userId: userId,
albumId: albumId,
),
body: albumInfo.when(
data: (albumInfo) => _buildBody(albumInfo),
data: (albumInfo) => albumInfo != null
? _buildBody(albumInfo)
: const Center(
child: CircularProgressIndicator(),
),
error: (e, _) => Center(child: Text("Error loading album info $e")),
loading: () => const Center(
child: ImmichLoadingIndicator(),

View file

@ -56,10 +56,11 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
left: 10,
),
child: AlbumTitleTextField(
isAlbumTitleEmpty: isAlbumTitleEmpty,
albumTitleTextFieldFocusNode: albumTitleTextFieldFocusNode,
albumTitleController: albumTitleController,
isAlbumTitleTextFieldFocus: isAlbumTitleTextFieldFocus),
isAlbumTitleEmpty: isAlbumTitleEmpty,
albumTitleTextFieldFocusNode: albumTitleTextFieldFocusNode,
albumTitleController: albumTitleController,
isAlbumTitleTextFieldFocus: isAlbumTitleTextFieldFocus,
),
);
}
@ -67,8 +68,8 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
if (selectedAssets.isEmpty) {
return SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.only(top: 200, left: 18),
child: Text(
padding: const EdgeInsets.only(top: 200, left: 18),
child: const Text(
'create_shared_album_page_share_add_assets',
style: TextStyle(fontSize: 12),
).tr(),
@ -86,13 +87,16 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
padding: const EdgeInsets.only(top: 16, left: 18, right: 18),
child: OutlinedButton.icon(
style: OutlinedButton.styleFrom(
alignment: Alignment.centerLeft,
padding:
const EdgeInsets.symmetric(vertical: 22, horizontal: 16),
side: const BorderSide(
color: Color.fromARGB(255, 206, 206, 206)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5))),
alignment: Alignment.centerLeft,
padding:
const EdgeInsets.symmetric(vertical: 22, horizontal: 16),
side: const BorderSide(
color: Color.fromARGB(255, 206, 206, 206),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
),
onPressed: _onSelectPhotosButtonPressed,
icon: const Icon(Icons.add_rounded),
label: Padding(
@ -100,9 +104,10 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
child: Text(
'create_shared_album_page_share_select_photos',
style: TextStyle(
fontSize: 16,
color: Colors.grey[700],
fontWeight: FontWeight.bold),
fontSize: 16,
color: Colors.grey[700],
fontWeight: FontWeight.bold,
),
).tr(),
),
),
@ -147,7 +152,8 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
return GestureDetector(
onTap: _onBackgroundTapped,
child: SharedAlbumThumbnailImage(
asset: selectedAssets.toList()[index]),
asset: selectedAssets.toList()[index],
),
);
},
childCount: selectedAssets.length,
@ -160,58 +166,60 @@ class CreateSharedAlbumPage extends HookConsumerWidget {
}
return Scaffold(
appBar: AppBar(
elevation: 0,
centerTitle: false,
leading: IconButton(
onPressed: () {
ref.watch(assetSelectionProvider.notifier).removeAll();
AutoRouter.of(context).pop();
},
icon: const Icon(Icons.close_rounded)),
title: const Text(
'share_create_album',
style: TextStyle(color: Colors.black),
).tr(),
actions: [
TextButton(
onPressed: albumTitleController.text.isNotEmpty
? _showSelectUserPage
: null,
child: Text(
'create_shared_album_page_share'.tr(),
style: TextStyle(
fontWeight: FontWeight.bold,
appBar: AppBar(
elevation: 0,
centerTitle: false,
leading: IconButton(
onPressed: () {
ref.watch(assetSelectionProvider.notifier).removeAll();
AutoRouter.of(context).pop();
},
icon: const Icon(Icons.close_rounded),
),
title: const Text(
'share_create_album',
style: TextStyle(color: Colors.black),
).tr(),
actions: [
TextButton(
onPressed: albumTitleController.text.isNotEmpty
? _showSelectUserPage
: null,
child: Text(
'create_shared_album_page_share'.tr(),
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
],
),
body: GestureDetector(
onTap: _onBackgroundTapped,
child: CustomScrollView(
slivers: [
SliverAppBar(
elevation: 5,
automaticallyImplyLeading: false,
// leading: Container(),
pinned: true,
floating: false,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(66.0),
child: Column(
children: [
_buildTitleInputField(),
if (selectedAssets.isNotEmpty) _buildControlButton(),
],
),
),
),
_buildTitle(),
_buildSelectPhotosButton(),
_buildSelectedImageGrid(),
],
),
body: GestureDetector(
onTap: _onBackgroundTapped,
child: CustomScrollView(
slivers: [
SliverAppBar(
elevation: 5,
automaticallyImplyLeading: false,
// leading: Container(),
pinned: true,
floating: false,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(66.0),
child: Column(
children: [
_buildTitleInputField(),
if (selectedAssets.isNotEmpty) _buildControlButton(),
],
),
),
),
_buildTitle(),
_buildSelectPhotosButton(),
_buildSelectedImageGrid(),
],
),
));
),
);
}
}

View file

@ -3,29 +3,28 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
import 'package:immich_mobile/modules/sharing/providers/suggested_shared_users.provider.dart';
import 'package:immich_mobile/shared/models/user.model.dart';
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
import 'package:openapi/api.dart';
class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
final SharedAlbum albumInfo;
final AlbumResponseDto albumInfo;
const SelectAdditionalUserForSharingPage({Key? key, required this.albumInfo})
: super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
AsyncValue<List<User>> suggestedShareUsers =
AsyncValue<List<UserResponseDto>> suggestedShareUsers =
ref.watch(suggestedSharedUsersProvider);
final sharedUsersList = useState<Set<User>>({});
final sharedUsersList = useState<Set<UserResponseDto>>({});
_addNewUsersHandler() {
AutoRouter.of(context)
.pop(sharedUsersList.value.map((e) => e.id).toList());
}
_buildTileIcon(User user) {
_buildTileIcon(UserResponseDto user) {
if (sharedUsersList.value.contains(user)) {
return CircleAvatar(
backgroundColor: Theme.of(context).primaryColor,
@ -43,7 +42,7 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
}
}
_buildUserList(List<User> users) {
_buildUserList(List<UserResponseDto> users) {
List<Widget> usersChip = [];
for (var user in sharedUsersList.value) {
@ -55,9 +54,10 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
label: Text(
user.email,
style: const TextStyle(
fontSize: 12,
color: Colors.black87,
fontWeight: FontWeight.bold),
fontSize: 12,
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
),
),
@ -70,13 +70,14 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
children: [...usersChip],
),
Padding(
padding: EdgeInsets.all(16.0),
padding: const EdgeInsets.all(16.0),
child: Text(
'select_additional_user_for_sharing_page_suggestions'.tr(),
style: TextStyle(
fontSize: 14,
color: Colors.grey,
fontWeight: FontWeight.bold),
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
fontWeight: FontWeight.bold,
),
),
),
ListView.builder(
@ -87,13 +88,16 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
title: Text(
users[index].email,
style: const TextStyle(
fontSize: 14, fontWeight: FontWeight.bold),
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
onTap: () {
if (sharedUsersList.value.contains(users[index])) {
sharedUsersList.value = sharedUsersList.value
.where((selectedUser) =>
selectedUser.id != users[index].id)
.where(
(selectedUser) => selectedUser.id != users[index].id,
)
.toSet();
} else {
sharedUsersList.value = {
@ -139,7 +143,8 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
data: (users) {
for (var sharedUsers in albumInfo.sharedUsers) {
users.removeWhere(
(u) => u.id == sharedUsers.id || u.id == albumInfo.ownerId);
(u) => u.id == sharedUsers.id || u.id == albumInfo.ownerId,
);
}
return _buildUserList(users);

View file

@ -9,15 +9,16 @@ import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.da
import 'package:immich_mobile/modules/sharing/providers/suggested_shared_users.provider.dart';
import 'package:immich_mobile/modules/sharing/services/shared_album.service.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/models/user.model.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<User>>({});
AsyncValue<List<User>> suggestedShareUsers =
final sharedUsersList = useState<Set<UserResponseDto>>({});
AsyncValue<List<UserResponseDto>> suggestedShareUsers =
ref.watch(suggestedSharedUsersProvider);
_createSharedAlbum() async {
@ -37,10 +38,14 @@ class SelectUserForSharingPage extends HookConsumerWidget {
.navigate(const TabControllerRoute(children: [SharingRoute()]));
}
ScaffoldMessenger(child: SnackBar(content: Text('select_user_for_sharing_page_err_album').tr()));
ScaffoldMessenger(
child: SnackBar(
content: const Text('select_user_for_sharing_page_err_album').tr(),
),
);
}
_buildTileIcon(User user) {
_buildTileIcon(UserResponseDto user) {
if (sharedUsersList.value.contains(user)) {
return CircleAvatar(
backgroundColor: Theme.of(context).primaryColor,
@ -58,7 +63,7 @@ class SelectUserForSharingPage extends HookConsumerWidget {
}
}
_buildUserList(List<User> users) {
_buildUserList(List<UserResponseDto> users) {
List<Widget> usersChip = [];
for (var user in sharedUsersList.value) {
@ -70,9 +75,10 @@ class SelectUserForSharingPage extends HookConsumerWidget {
label: Text(
user.email,
style: const TextStyle(
fontSize: 12,
color: Colors.black87,
fontWeight: FontWeight.bold),
fontSize: 12,
color: Colors.black87,
fontWeight: FontWeight.bold,
),
),
),
),
@ -85,13 +91,14 @@ class SelectUserForSharingPage extends HookConsumerWidget {
children: [...usersChip],
),
Padding(
padding: EdgeInsets.all(16.0),
child: Text(
'share_suggestions',
padding: const EdgeInsets.all(16.0),
child: const Text(
'select_user_for_sharing_page_share_suggestions',
style: TextStyle(
fontSize: 14,
color: Colors.grey,
fontWeight: FontWeight.bold),
fontSize: 14,
color: Colors.grey,
fontWeight: FontWeight.bold,
),
).tr(),
),
ListView.builder(
@ -102,13 +109,16 @@ class SelectUserForSharingPage extends HookConsumerWidget {
title: Text(
users[index].email,
style: const TextStyle(
fontSize: 14, fontWeight: FontWeight.bold),
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
onTap: () {
if (sharedUsersList.value.contains(users[index])) {
sharedUsersList.value = sharedUsersList.value
.where((selectedUser) =>
selectedUser.id != users[index].id)
.where(
(selectedUser) => selectedUser.id != users[index].id,
)
.toSet();
} else {
sharedUsersList.value = {
@ -141,12 +151,13 @@ class SelectUserForSharingPage extends HookConsumerWidget {
),
actions: [
TextButton(
onPressed:
sharedUsersList.value.isEmpty ? null : _createSharedAlbum,
child: const Text(
"share_create_album",
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
).tr())
onPressed:
sharedUsersList.value.isEmpty ? null : _createSharedAlbum,
child: const Text(
"share_create_album",
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
).tr(),
)
],
),
body: suggestedShareUsers.when(

View file

@ -5,10 +5,10 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hive/hive.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/hive_box.dart';
import 'package:immich_mobile/modules/sharing/models/shared_album.model.dart';
import 'package:immich_mobile/modules/sharing/providers/shared_album.provider.dart';
import 'package:immich_mobile/modules/sharing/ui/sharing_sliver_appbar.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:openapi/api.dart';
import 'package:transparent_image/transparent_image.dart';
class SharingPage extends HookConsumerWidget {
@ -18,13 +18,16 @@ class SharingPage extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
var box = Hive.box(userInfoBox);
var thumbnailRequestUrl = '${box.get(serverEndpointKey)}/asset/thumbnail';
final List<SharedAlbum> sharedAlbums = ref.watch(sharedAlbumProvider);
final List<AlbumResponseDto> sharedAlbums = ref.watch(sharedAlbumProvider);
useEffect(() {
ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums();
useEffect(
() {
ref.read(sharedAlbumProvider.notifier).getAllSharedAlbums();
return null;
}, []);
return null;
},
[],
);
_buildAlbumList() {
return SliverList(
@ -60,9 +63,10 @@ class SharingPage extends HookConsumerWidget {
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800),
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
),
onTap: () {
AutoRouter.of(context)
@ -133,9 +137,9 @@ class SharingPage extends HookConsumerWidget {
slivers: [
const SharingSliverAppBar(),
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 12),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
sliver: SliverToBoxAdapter(
child: Text(
child: const Text(
"sharing_page_album",
style: TextStyle(
fontWeight: FontWeight.bold,