mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
Fix backup not resuming after closed and reopen (#266)
* Fixed app not resuming backup after closing and reopening the app * Fixed cosmetic effect of backup button doesn't change state right away after pressing start backup * Fixed grammar * Fixed deep copy problem that cause incorrect asset count when backing up * Format code
This commit is contained in:
parent
d02b97e1c1
commit
40a8115101
63 changed files with 677 additions and 300 deletions
|
|
@ -36,16 +36,20 @@ class AlbumViewerPageState {
|
|||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory AlbumViewerPageState.fromJson(String source) => AlbumViewerPageState.fromMap(json.decode(source));
|
||||
factory AlbumViewerPageState.fromJson(String source) =>
|
||||
AlbumViewerPageState.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
String toString() => 'AlbumViewerPageState(isEditAlbum: $isEditAlbum, editTitleText: $editTitleText)';
|
||||
String toString() =>
|
||||
'AlbumViewerPageState(isEditAlbum: $isEditAlbum, editTitleText: $editTitleText)';
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is AlbumViewerPageState && other.isEditAlbum == isEditAlbum && other.editTitleText == editTitleText;
|
||||
return other is AlbumViewerPageState &&
|
||||
other.isEditAlbum == isEditAlbum &&
|
||||
other.editTitleText == editTitleText;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ class AssetSelectionPageResult {
|
|||
}) {
|
||||
return AssetSelectionPageResult(
|
||||
selectedNewAsset: selectedNewAsset ?? this.selectedNewAsset,
|
||||
selectedAdditionalAsset: selectedAdditionalAsset ?? this.selectedAdditionalAsset,
|
||||
selectedAdditionalAsset:
|
||||
selectedAdditionalAsset ?? this.selectedAdditionalAsset,
|
||||
isAlbumExist: isAlbumExist ?? this.isAlbumExist,
|
||||
);
|
||||
}
|
||||
|
|
@ -30,8 +31,12 @@ 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(
|
||||
{'selectedNewAsset': selectedNewAsset.map((x) => x.toMap()).toList()});
|
||||
result.addAll({
|
||||
'selectedAdditionalAsset':
|
||||
selectedAdditionalAsset.map((x) => x.toMap()).toList()
|
||||
});
|
||||
result.addAll({'isAlbumExist': isAlbumExist});
|
||||
|
||||
return result;
|
||||
|
|
@ -39,16 +44,18 @@ class AssetSelectionPageResult {
|
|||
|
||||
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))),
|
||||
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));
|
||||
factory AssetSelectionPageResult.fromJson(String source) =>
|
||||
AssetSelectionPageResult.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
|
|
@ -66,5 +73,8 @@ class AssetSelectionPageResult {
|
|||
}
|
||||
|
||||
@override
|
||||
int get hashCode => selectedNewAsset.hashCode ^ selectedAdditionalAsset.hashCode ^ isAlbumExist.hashCode;
|
||||
int get hashCode =>
|
||||
selectedNewAsset.hashCode ^
|
||||
selectedAdditionalAsset.hashCode ^
|
||||
isAlbumExist.hashCode;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,9 +32,12 @@ class AssetSelectionState {
|
|||
}) {
|
||||
return AssetSelectionState(
|
||||
selectedMonths: selectedMonths ?? this.selectedMonths,
|
||||
selectedNewAssetsForAlbum: selectedNewAssetsForAlbum ?? this.selectedNewAssetsForAlbum,
|
||||
selectedAdditionalAssetsForAlbum: selectedAdditionalAssetsForAlbum ?? this.selectedAdditionalAssetsForAlbum,
|
||||
selectedAssetsInAlbumViewer: selectedAssetsInAlbumViewer ?? this.selectedAssetsInAlbumViewer,
|
||||
selectedNewAssetsForAlbum:
|
||||
selectedNewAssetsForAlbum ?? this.selectedNewAssetsForAlbum,
|
||||
selectedAdditionalAssetsForAlbum: selectedAdditionalAssetsForAlbum ??
|
||||
this.selectedAdditionalAssetsForAlbum,
|
||||
selectedAssetsInAlbumViewer:
|
||||
selectedAssetsInAlbumViewer ?? this.selectedAssetsInAlbumViewer,
|
||||
isMultiselectEnable: isMultiselectEnable ?? this.isMultiselectEnable,
|
||||
isAlbumExist: isAlbumExist ?? this.isAlbumExist,
|
||||
);
|
||||
|
|
@ -44,10 +47,18 @@ class AssetSelectionState {
|
|||
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({
|
||||
'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});
|
||||
|
||||
|
|
@ -57,12 +68,14 @@ class AssetSelectionState {
|
|||
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))),
|
||||
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,
|
||||
);
|
||||
|
|
@ -70,7 +83,8 @@ class AssetSelectionState {
|
|||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory AssetSelectionState.fromJson(String source) => AssetSelectionState.fromMap(json.decode(source));
|
||||
factory AssetSelectionState.fromJson(String source) =>
|
||||
AssetSelectionState.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
|
|
@ -85,8 +99,10 @@ class AssetSelectionState {
|
|||
return other is AssetSelectionState &&
|
||||
setEquals(other.selectedMonths, selectedMonths) &&
|
||||
setEquals(other.selectedNewAssetsForAlbum, selectedNewAssetsForAlbum) &&
|
||||
setEquals(other.selectedAdditionalAssetsForAlbum, selectedAdditionalAssetsForAlbum) &&
|
||||
setEquals(other.selectedAssetsInAlbumViewer, selectedAssetsInAlbumViewer) &&
|
||||
setEquals(other.selectedAdditionalAssetsForAlbum,
|
||||
selectedAdditionalAssetsForAlbum) &&
|
||||
setEquals(
|
||||
other.selectedAssetsInAlbumViewer, selectedAssetsInAlbumViewer) &&
|
||||
other.isMultiselectEnable == isMultiselectEnable &&
|
||||
other.isAlbumExist == isAlbumExist;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ class SharedAlbum {
|
|||
ownerId: ownerId ?? this.ownerId,
|
||||
albumName: albumName ?? this.albumName,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
albumThumbnailAssetId: albumThumbnailAssetId ?? this.albumThumbnailAssetId,
|
||||
albumThumbnailAssetId:
|
||||
albumThumbnailAssetId ?? this.albumThumbnailAssetId,
|
||||
sharedUsers: sharedUsers ?? this.sharedUsers,
|
||||
assets: assets ?? this.assets,
|
||||
);
|
||||
|
|
@ -69,16 +70,19 @@ class SharedAlbum {
|
|||
albumName: map['albumName'] ?? '',
|
||||
createdAt: map['createdAt'] ?? '',
|
||||
albumThumbnailAssetId: map['albumThumbnailAssetId'],
|
||||
sharedUsers: List<User>.from(map['sharedUsers']?.map((x) => User.fromMap(x))),
|
||||
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)))
|
||||
? 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));
|
||||
factory SharedAlbum.fromJson(String source) =>
|
||||
SharedAlbum.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
|
|
|
|||
|
|
@ -12,4 +12,5 @@ class AlbumTitleNotifier extends StateNotifier<String> {
|
|||
}
|
||||
}
|
||||
|
||||
final albumTitleProvider = StateNotifierProvider<AlbumTitleNotifier, String>((ref) => AlbumTitleNotifier());
|
||||
final albumTitleProvider = StateNotifierProvider<AlbumTitleNotifier, String>(
|
||||
(ref) => AlbumTitleNotifier());
|
||||
|
|
|
|||
|
|
@ -18,35 +18,48 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
|
|||
state = state.copyWith(isAlbumExist: isAlbumExist);
|
||||
}
|
||||
|
||||
void removeAssetsInMonth(String removedMonth, List<ImmichAsset> assetsInMonth) {
|
||||
void removeAssetsInMonth(
|
||||
String removedMonth, List<ImmichAsset> assetsInMonth) {
|
||||
Set<ImmichAsset> currentAssetList = state.selectedNewAssetsForAlbum;
|
||||
Set<String> currentMonthList = state.selectedMonths;
|
||||
|
||||
currentMonthList.removeWhere((selectedMonth) => selectedMonth == removedMonth);
|
||||
currentMonthList
|
||||
.removeWhere((selectedMonth) => selectedMonth == removedMonth);
|
||||
|
||||
for (ImmichAsset asset in assetsInMonth) {
|
||||
currentAssetList.removeWhere((e) => e.id == asset.id);
|
||||
}
|
||||
|
||||
state = state.copyWith(selectedNewAssetsForAlbum: currentAssetList, selectedMonths: currentMonthList);
|
||||
state = state.copyWith(
|
||||
selectedNewAssetsForAlbum: currentAssetList,
|
||||
selectedMonths: currentMonthList);
|
||||
}
|
||||
|
||||
void addAdditionalAssets(List<ImmichAsset> assets) {
|
||||
state = state.copyWith(
|
||||
selectedAdditionalAssetsForAlbum: {...state.selectedAdditionalAssetsForAlbum, ...assets},
|
||||
selectedAdditionalAssetsForAlbum: {
|
||||
...state.selectedAdditionalAssetsForAlbum,
|
||||
...assets
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void addAllAssetsInMonth(String month, List<ImmichAsset> assetsInMonth) {
|
||||
state = state.copyWith(
|
||||
selectedMonths: {...state.selectedMonths, month},
|
||||
selectedNewAssetsForAlbum: {...state.selectedNewAssetsForAlbum, ...assetsInMonth},
|
||||
selectedNewAssetsForAlbum: {
|
||||
...state.selectedNewAssetsForAlbum,
|
||||
...assetsInMonth
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void addNewAssets(List<ImmichAsset> assets) {
|
||||
state = state.copyWith(
|
||||
selectedNewAssetsForAlbum: {...state.selectedNewAssetsForAlbum, ...assets},
|
||||
selectedNewAssetsForAlbum: {
|
||||
...state.selectedNewAssetsForAlbum,
|
||||
...assets
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +106,10 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
|
|||
|
||||
void addAssetsInAlbumViewer(List<ImmichAsset> assets) {
|
||||
state = state.copyWith(
|
||||
selectedAssetsInAlbumViewer: {...state.selectedAssetsInAlbumViewer, ...assets},
|
||||
selectedAssetsInAlbumViewer: {
|
||||
...state.selectedAssetsInAlbumViewer,
|
||||
...assets
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -108,6 +124,7 @@ class AssetSelectionNotifier extends StateNotifier<AssetSelectionState> {
|
|||
}
|
||||
}
|
||||
|
||||
final assetSelectionProvider = StateNotifierProvider<AssetSelectionNotifier, AssetSelectionState>((ref) {
|
||||
final assetSelectionProvider =
|
||||
StateNotifierProvider<AssetSelectionNotifier, AssetSelectionState>((ref) {
|
||||
return AssetSelectionNotifier();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,11 @@ class AlbumActionOutlinedButton extends StatelessWidget {
|
|||
final String labelText;
|
||||
final IconData iconData;
|
||||
|
||||
const AlbumActionOutlinedButton({Key? key, this.onPressed, required this.labelText, required this.iconData})
|
||||
const AlbumActionOutlinedButton(
|
||||
{Key? key,
|
||||
this.onPressed,
|
||||
required this.labelText,
|
||||
required this.iconData})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
|
|
@ -26,7 +30,8 @@ class AlbumActionOutlinedButton extends StatelessWidget {
|
|||
icon: Icon(iconData, size: 15),
|
||||
label: Text(
|
||||
labelText,
|
||||
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold, color: Colors.black87),
|
||||
style: const TextStyle(
|
||||
fontSize: 12, fontWeight: FontWeight.bold, color: Colors.black87),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ class AlbumTitleTextField extends ConsumerWidget {
|
|||
ref.watch(albumTitleProvider.notifier).setAlbumTitle(v);
|
||||
},
|
||||
focusNode: albumTitleTextFieldFocusNode,
|
||||
style: TextStyle(fontSize: 28, color: Colors.grey[700], fontWeight: FontWeight.bold),
|
||||
style: TextStyle(
|
||||
fontSize: 28, color: Colors.grey[700], fontWeight: FontWeight.bold),
|
||||
controller: albumTitleController,
|
||||
onTap: () {
|
||||
isAlbumTitleTextFieldFocus.value = true;
|
||||
|
|
|
|||
|
|
@ -7,11 +7,14 @@ import 'package:immich_mobile/modules/sharing/providers/album_viewer.provider.da
|
|||
class AlbumViewerEditableTitle extends HookConsumerWidget {
|
||||
final SharedAlbum 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) {
|
||||
final titleTextEditController = useTextEditingController(text: albumInfo.albumName);
|
||||
final titleTextEditController =
|
||||
useTextEditingController(text: albumInfo.albumName);
|
||||
|
||||
void onFocusModeChange() {
|
||||
if (!titleFocusNode.hasFocus && titleTextEditController.text.isEmpty) {
|
||||
|
|
@ -40,7 +43,9 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
|
|||
onTap: () {
|
||||
FocusScope.of(context).requestFocus(titleFocusNode);
|
||||
|
||||
ref.watch(albumViewerProvider.notifier).setEditTitleText(albumInfo.albumName);
|
||||
ref
|
||||
.watch(albumViewerProvider.notifier)
|
||||
.setEditTitleText(albumInfo.albumName);
|
||||
ref.watch(albumViewerProvider.notifier).enableEditAlbum();
|
||||
|
||||
if (titleTextEditController.text == 'Untitled') {
|
||||
|
|
|
|||
|
|
@ -22,8 +22,10 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
|
|||
var thumbnailRequestUrl =
|
||||
'${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true';
|
||||
var deviceId = ref.watch(authenticationProvider).deviceId;
|
||||
final selectedAssetsInAlbumViewer = ref.watch(assetSelectionProvider).selectedAssetsInAlbumViewer;
|
||||
final isMultiSelectionEnable = ref.watch(assetSelectionProvider).isMultiselectEnable;
|
||||
final selectedAssetsInAlbumViewer =
|
||||
ref.watch(assetSelectionProvider).selectedAssetsInAlbumViewer;
|
||||
final isMultiSelectionEnable =
|
||||
ref.watch(assetSelectionProvider).isMultiselectEnable;
|
||||
|
||||
_viewAsset() {
|
||||
if (asset.type == 'IMAGE') {
|
||||
|
|
@ -39,7 +41,8 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
|
|||
} else {
|
||||
AutoRouter.of(context).push(
|
||||
VideoViewerRoute(
|
||||
videoUrl: '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}',
|
||||
videoUrl:
|
||||
'${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}',
|
||||
asset: asset),
|
||||
);
|
||||
}
|
||||
|
|
@ -58,7 +61,9 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
|
|||
|
||||
_enableMultiSelection() {
|
||||
ref.watch(assetSelectionProvider.notifier).enableMultiselection();
|
||||
ref.watch(assetSelectionProvider.notifier).addAssetsInAlbumViewer([asset]);
|
||||
ref
|
||||
.watch(assetSelectionProvider.notifier)
|
||||
.addAssetsInAlbumViewer([asset]);
|
||||
}
|
||||
|
||||
_disableMultiSelection() {
|
||||
|
|
@ -96,7 +101,9 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
|
|||
right: 10,
|
||||
bottom: 5,
|
||||
child: Icon(
|
||||
(deviceId != asset.deviceId) ? Icons.cloud_done_outlined : Icons.photo_library_rounded,
|
||||
(deviceId != asset.deviceId)
|
||||
? Icons.cloud_done_outlined
|
||||
: Icons.photo_library_rounded,
|
||||
color: Colors.white,
|
||||
size: 18,
|
||||
),
|
||||
|
|
@ -136,7 +143,8 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
|
|||
imageUrl: thumbnailRequestUrl,
|
||||
httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
|
||||
fadeInDuration: const Duration(milliseconds: 250),
|
||||
progressIndicatorBuilder: (context, url, downloadProgress) => Transform.scale(
|
||||
progressIndicatorBuilder: (context, url, downloadProgress) =>
|
||||
Transform.scale(
|
||||
scale: 0.2,
|
||||
child: CircularProgressIndicator(value: downloadProgress.progress),
|
||||
),
|
||||
|
|
@ -152,13 +160,17 @@ class AlbumViewerThumbnail extends HookConsumerWidget {
|
|||
|
||||
_handleSelectionGesture() {
|
||||
if (selectedAssetsInAlbumViewer.contains(asset)) {
|
||||
ref.watch(assetSelectionProvider.notifier).removeAssetsInAlbumViewer([asset]);
|
||||
ref
|
||||
.watch(assetSelectionProvider.notifier)
|
||||
.removeAssetsInAlbumViewer([asset]);
|
||||
|
||||
if (selectedAssetsInAlbumViewer.isEmpty) {
|
||||
_disableMultiSelection();
|
||||
}
|
||||
} else {
|
||||
ref.watch(assetSelectionProvider.notifier).addAssetsInAlbumViewer([asset]);
|
||||
ref
|
||||
.watch(assetSelectionProvider.notifier)
|
||||
.addAssetsInAlbumViewer([asset]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ import 'package:immich_mobile/shared/models/immich_asset.model.dart';
|
|||
|
||||
class AssetGridByMonth extends HookConsumerWidget {
|
||||
final List<ImmichAsset> assetGroup;
|
||||
const AssetGridByMonth({Key? key, required this.assetGroup}) : super(key: key);
|
||||
const AssetGridByMonth({Key? key, required this.assetGroup})
|
||||
: super(key: key);
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return SliverGrid(
|
||||
|
|
|
|||
|
|
@ -8,12 +8,15 @@ class MonthGroupTitle extends HookConsumerWidget {
|
|||
final String month;
|
||||
final List<ImmichAsset> 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) {
|
||||
final selectedDateGroup = ref.watch(assetSelectionProvider).selectedMonths;
|
||||
final selectedAssets = ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum;
|
||||
final selectedAssets =
|
||||
ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum;
|
||||
final isAlbumExist = ref.watch(assetSelectionProvider).isAlbumExist;
|
||||
|
||||
_handleTitleIconClick() {
|
||||
|
|
@ -21,10 +24,16 @@ class MonthGroupTitle extends HookConsumerWidget {
|
|||
|
||||
if (isAlbumExist) {
|
||||
if (selectedDateGroup.contains(month)) {
|
||||
ref.watch(assetSelectionProvider.notifier).removeAssetsInMonth(month, []);
|
||||
ref.watch(assetSelectionProvider.notifier).removeSelectedAdditionalAssets(assetGroup);
|
||||
ref
|
||||
.watch(assetSelectionProvider.notifier)
|
||||
.removeAssetsInMonth(month, []);
|
||||
ref
|
||||
.watch(assetSelectionProvider.notifier)
|
||||
.removeSelectedAdditionalAssets(assetGroup);
|
||||
} else {
|
||||
ref.watch(assetSelectionProvider.notifier).addAllAssetsInMonth(month, []);
|
||||
ref
|
||||
.watch(assetSelectionProvider.notifier)
|
||||
.addAllAssetsInMonth(month, []);
|
||||
|
||||
// Deep clone assetGroup
|
||||
var assetGroupWithNewItems = [...assetGroup];
|
||||
|
|
@ -33,13 +42,19 @@ class MonthGroupTitle extends HookConsumerWidget {
|
|||
assetGroupWithNewItems.removeWhere((a) => a.id == selectedAsset.id);
|
||||
}
|
||||
|
||||
ref.watch(assetSelectionProvider.notifier).addAdditionalAssets(assetGroupWithNewItems);
|
||||
ref
|
||||
.watch(assetSelectionProvider.notifier)
|
||||
.addAdditionalAssets(assetGroupWithNewItems);
|
||||
}
|
||||
} else {
|
||||
if (selectedDateGroup.contains(month)) {
|
||||
ref.watch(assetSelectionProvider.notifier).removeAssetsInMonth(month, assetGroup);
|
||||
ref
|
||||
.watch(assetSelectionProvider.notifier)
|
||||
.removeAssetsInMonth(month, assetGroup);
|
||||
} else {
|
||||
ref.watch(assetSelectionProvider.notifier).addAllAssetsInMonth(month, assetGroup);
|
||||
ref
|
||||
.watch(assetSelectionProvider.notifier)
|
||||
.addAllAssetsInMonth(month, assetGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -59,7 +74,8 @@ class MonthGroupTitle extends HookConsumerWidget {
|
|||
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 29.0, bottom: 29.0, left: 14.0, right: 8.0),
|
||||
padding: const EdgeInsets.only(
|
||||
top: 29.0, bottom: 29.0, left: 14.0, right: 8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ import 'package:immich_mobile/shared/models/immich_asset.model.dart';
|
|||
class SelectionThumbnailImage extends HookConsumerWidget {
|
||||
final ImmichAsset asset;
|
||||
|
||||
const SelectionThumbnailImage({Key? key, required this.asset}) : super(key: key);
|
||||
const SelectionThumbnailImage({Key? key, required this.asset})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
|
@ -18,8 +19,10 @@ class SelectionThumbnailImage extends HookConsumerWidget {
|
|||
var box = Hive.box(userInfoBox);
|
||||
var thumbnailRequestUrl =
|
||||
'${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=true';
|
||||
var selectedAsset = ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum;
|
||||
var newAssetsForAlbum = ref.watch(assetSelectionProvider).selectedAdditionalAssetsForAlbum;
|
||||
var selectedAsset =
|
||||
ref.watch(assetSelectionProvider).selectedNewAssetsForAlbum;
|
||||
var newAssetsForAlbum =
|
||||
ref.watch(assetSelectionProvider).selectedAdditionalAssetsForAlbum;
|
||||
var isAlbumExist = ref.watch(assetSelectionProvider).isAlbumExist;
|
||||
|
||||
Widget _buildSelectionIcon(ImmichAsset asset) {
|
||||
|
|
@ -72,15 +75,21 @@ class SelectionThumbnailImage extends HookConsumerWidget {
|
|||
// Operation for existing album
|
||||
if (!selectedAsset.contains(asset)) {
|
||||
if (newAssetsForAlbum.contains(asset)) {
|
||||
ref.watch(assetSelectionProvider.notifier).removeSelectedAdditionalAssets([asset]);
|
||||
ref
|
||||
.watch(assetSelectionProvider.notifier)
|
||||
.removeSelectedAdditionalAssets([asset]);
|
||||
} else {
|
||||
ref.watch(assetSelectionProvider.notifier).addAdditionalAssets([asset]);
|
||||
ref
|
||||
.watch(assetSelectionProvider.notifier)
|
||||
.addAdditionalAssets([asset]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Operation for new album
|
||||
if (selectedAsset.contains(asset)) {
|
||||
ref.watch(assetSelectionProvider.notifier).removeSelectedNewAssets([asset]);
|
||||
ref
|
||||
.watch(assetSelectionProvider.notifier)
|
||||
.removeSelectedNewAssets([asset]);
|
||||
} else {
|
||||
ref.watch(assetSelectionProvider.notifier).addNewAssets([asset]);
|
||||
}
|
||||
|
|
@ -97,11 +106,15 @@ class SelectionThumbnailImage extends HookConsumerWidget {
|
|||
memCacheHeight: asset.type == 'IMAGE' ? 150 : 150,
|
||||
fit: BoxFit.cover,
|
||||
imageUrl: thumbnailRequestUrl,
|
||||
httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
|
||||
httpHeaders: {
|
||||
"Authorization": "Bearer ${box.get(accessTokenKey)}"
|
||||
},
|
||||
fadeInDuration: const Duration(milliseconds: 250),
|
||||
progressIndicatorBuilder: (context, url, downloadProgress) => Transform.scale(
|
||||
progressIndicatorBuilder: (context, url, downloadProgress) =>
|
||||
Transform.scale(
|
||||
scale: 0.2,
|
||||
child: CircularProgressIndicator(value: downloadProgress.progress),
|
||||
child:
|
||||
CircularProgressIndicator(value: downloadProgress.progress),
|
||||
),
|
||||
errorWidget: (context, url, error) {
|
||||
return Icon(
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ import 'package:immich_mobile/shared/models/immich_asset.model.dart';
|
|||
class SharedAlbumThumbnailImage extends HookConsumerWidget {
|
||||
final ImmichAsset asset;
|
||||
|
||||
const SharedAlbumThumbnailImage({Key? key, required this.asset}) : super(key: key);
|
||||
const SharedAlbumThumbnailImage({Key? key, required this.asset})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
|
@ -34,9 +35,11 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget {
|
|||
imageUrl: thumbnailRequestUrl,
|
||||
httpHeaders: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
|
||||
fadeInDuration: const Duration(milliseconds: 250),
|
||||
progressIndicatorBuilder: (context, url, downloadProgress) => Transform.scale(
|
||||
progressIndicatorBuilder: (context, url, downloadProgress) =>
|
||||
Transform.scale(
|
||||
scale: 0.2,
|
||||
child: CircularProgressIndicator(value: downloadProgress.progress),
|
||||
child:
|
||||
CircularProgressIndicator(value: downloadProgress.progress),
|
||||
),
|
||||
errorWidget: (context, url, error) {
|
||||
return Icon(
|
||||
|
|
|
|||
|
|
@ -37,11 +37,13 @@ class SharingSliverAppBar extends StatelessWidget {
|
|||
padding: const EdgeInsets.only(right: 4.0),
|
||||
child: TextButton.icon(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(Theme.of(context).primaryColor.withAlpha(20)),
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
Theme.of(context).primaryColor.withAlpha(20)),
|
||||
// foregroundColor: MaterialStateProperty.all(Colors.white),
|
||||
),
|
||||
onPressed: () {
|
||||
AutoRouter.of(context).push(const CreateSharedAlbumRoute());
|
||||
AutoRouter.of(context)
|
||||
.push(const CreateSharedAlbumRoute());
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.photo_album_outlined,
|
||||
|
|
@ -49,7 +51,8 @@ class SharingSliverAppBar extends StatelessWidget {
|
|||
),
|
||||
label: const Text(
|
||||
"Create shared album",
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
|
||||
style:
|
||||
TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -59,7 +62,8 @@ class SharingSliverAppBar extends StatelessWidget {
|
|||
padding: const EdgeInsets.only(left: 4.0),
|
||||
child: TextButton.icon(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(Theme.of(context).primaryColor.withAlpha(20)),
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
Theme.of(context).primaryColor.withAlpha(20)),
|
||||
// foregroundColor: MaterialStateProperty.all(Colors.white),
|
||||
),
|
||||
onPressed: null,
|
||||
|
|
@ -69,7 +73,8 @@ class SharingSliverAppBar extends StatelessWidget {
|
|||
),
|
||||
label: const Text(
|
||||
"Share with partner",
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
|
||||
style:
|
||||
TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -10,15 +10,18 @@ import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
|||
class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
||||
final SharedAlbum albumInfo;
|
||||
|
||||
const SelectAdditionalUserForSharingPage({Key? key, required this.albumInfo}) : super(key: key);
|
||||
const SelectAdditionalUserForSharingPage({Key? key, required this.albumInfo})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
AsyncValue<List<User>> suggestedShareUsers = ref.watch(suggestedSharedUsersProvider);
|
||||
AsyncValue<List<User>> suggestedShareUsers =
|
||||
ref.watch(suggestedSharedUsersProvider);
|
||||
final sharedUsersList = useState<Set<User>>({});
|
||||
|
||||
_addNewUsersHandler() {
|
||||
AutoRouter.of(context).pop(sharedUsersList.value.map((e) => e.id).toList());
|
||||
AutoRouter.of(context)
|
||||
.pop(sharedUsersList.value.map((e) => e.id).toList());
|
||||
}
|
||||
|
||||
_buildTileIcon(User user) {
|
||||
|
|
@ -32,7 +35,8 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
);
|
||||
} else {
|
||||
return CircleAvatar(
|
||||
backgroundImage: const AssetImage('assets/immich-logo-no-outline.png'),
|
||||
backgroundImage:
|
||||
const AssetImage('assets/immich-logo-no-outline.png'),
|
||||
backgroundColor: Theme.of(context).primaryColor.withAlpha(50),
|
||||
);
|
||||
}
|
||||
|
|
@ -49,7 +53,10 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
backgroundColor: Theme.of(context).primaryColor.withOpacity(0.15),
|
||||
label: Text(
|
||||
user.email,
|
||||
style: const TextStyle(fontSize: 12, color: Colors.black87, fontWeight: FontWeight.bold),
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -65,7 +72,10 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
padding: EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'Suggestions',
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
ListView.builder(
|
||||
|
|
@ -75,14 +85,20 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
leading: _buildTileIcon(users[index]),
|
||||
title: Text(
|
||||
users[index].email,
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||
style: const TextStyle(
|
||||
fontSize: 14, fontWeight: FontWeight.bold),
|
||||
),
|
||||
onTap: () {
|
||||
if (sharedUsersList.value.contains(users[index])) {
|
||||
sharedUsersList.value =
|
||||
sharedUsersList.value.where((selectedUser) => selectedUser.id != users[index].id).toSet();
|
||||
sharedUsersList.value = sharedUsersList.value
|
||||
.where((selectedUser) =>
|
||||
selectedUser.id != users[index].id)
|
||||
.toSet();
|
||||
} else {
|
||||
sharedUsersList.value = {...sharedUsersList.value, users[index]};
|
||||
sharedUsersList.value = {
|
||||
...sharedUsersList.value,
|
||||
users[index]
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
@ -109,7 +125,8 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: sharedUsersList.value.isEmpty ? null : _addNewUsersHandler,
|
||||
onPressed:
|
||||
sharedUsersList.value.isEmpty ? null : _addNewUsersHandler,
|
||||
child: const Text(
|
||||
"Add",
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||
|
|
@ -120,7 +137,8 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
body: suggestedShareUsers.when(
|
||||
data: (users) {
|
||||
for (var sharedUsers in albumInfo.sharedUsers) {
|
||||
users.removeWhere((u) => u.id == sharedUsers.id || u.id == albumInfo.ownerId);
|
||||
users.removeWhere(
|
||||
(u) => u.id == sharedUsers.id || u.id == albumInfo.ownerId);
|
||||
}
|
||||
|
||||
return _buildUserList(users);
|
||||
|
|
|
|||
|
|
@ -29,12 +29,14 @@ class SharingPage extends HookConsumerWidget {
|
|||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
String thumbnailUrl = sharedAlbums[index].albumThumbnailAssetId != null
|
||||
String thumbnailUrl = sharedAlbums[index].albumThumbnailAssetId !=
|
||||
null
|
||||
? "$thumbnailRequestUrl/${sharedAlbums[index].albumThumbnailAssetId}"
|
||||
: "https://images.unsplash.com/photo-1612178537253-bccd437b730e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8Ymxhbmt8ZW58MHx8MHx8&auto=format&fit=crop&w=700&q=60";
|
||||
|
||||
return ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
|
||||
leading: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: FadeInImage(
|
||||
|
|
@ -44,7 +46,9 @@ class SharingPage extends HookConsumerWidget {
|
|||
placeholder: MemoryImage(kTransparentImage),
|
||||
image: NetworkImage(
|
||||
thumbnailUrl,
|
||||
headers: {"Authorization": "Bearer ${box.get(accessTokenKey)}"},
|
||||
headers: {
|
||||
"Authorization": "Bearer ${box.get(accessTokenKey)}"
|
||||
},
|
||||
),
|
||||
fadeInDuration: const Duration(milliseconds: 200),
|
||||
fadeOutDuration: const Duration(milliseconds: 200),
|
||||
|
|
@ -54,10 +58,14 @@ class SharingPage extends HookConsumerWidget {
|
|||
sharedAlbums[index].albumName,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.grey.shade800),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey.shade800),
|
||||
),
|
||||
onTap: () {
|
||||
AutoRouter.of(context).push(AlbumViewerRoute(albumId: sharedAlbums[index].id));
|
||||
AutoRouter.of(context)
|
||||
.push(AlbumViewerRoute(albumId: sharedAlbums[index].id));
|
||||
},
|
||||
);
|
||||
},
|
||||
|
|
@ -134,7 +142,9 @@ class SharingPage extends HookConsumerWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
sharedAlbums.isNotEmpty ? _buildAlbumList() : _buildEmptyListIndication()
|
||||
sharedAlbums.isNotEmpty
|
||||
? _buildAlbumList()
|
||||
: _buildEmptyListIndication()
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue