feat: manual stack assets (#4198)

This commit is contained in:
shenlong 2023-10-22 02:38:07 +00:00 committed by GitHub
parent 5ead4af2dc
commit cf08ac7538
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
59 changed files with 2190 additions and 138 deletions

View file

@ -32,6 +32,7 @@ class ImmichAssetGrid extends HookConsumerWidget {
final Widget? topWidget;
final bool shrinkWrap;
final bool showDragScroll;
final bool showStack;
const ImmichAssetGrid({
super.key,
@ -51,6 +52,7 @@ class ImmichAssetGrid extends HookConsumerWidget {
this.topWidget,
this.shrinkWrap = false,
this.showDragScroll = true,
this.showStack = false,
});
@override
@ -114,6 +116,7 @@ class ImmichAssetGrid extends HookConsumerWidget {
heroOffset: heroOffset(),
shrinkWrap: shrinkWrap,
showDragScroll: showDragScroll,
showStack: showStack,
),
);
}

View file

@ -37,6 +37,7 @@ class ImmichAssetGridView extends StatefulWidget {
final int heroOffset;
final bool shrinkWrap;
final bool showDragScroll;
final bool showStack;
const ImmichAssetGridView({
super.key,
@ -56,6 +57,7 @@ class ImmichAssetGridView extends StatefulWidget {
this.heroOffset = 0,
this.shrinkWrap = false,
this.showDragScroll = true,
this.showStack = false,
});
@override
@ -71,7 +73,7 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
bool _scrolling = false;
final Set<Asset> _selectedAssets =
HashSet(equals: (a, b) => a.id == b.id, hashCode: (a) => a.id);
LinkedHashSet(equals: (a, b) => a.id == b.id, hashCode: (a) => a.id);
Set<Asset> _getSelectedAssets() {
return Set.from(_selectedAssets);
@ -90,7 +92,13 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
void _deselectAssets(List<Asset> assets) {
setState(() {
_selectedAssets.removeAll(assets);
_selectedAssets.removeAll(
assets.where(
(a) =>
widget.canDeselect ||
!(widget.preselectedAssets?.contains(a) ?? false),
),
);
_callSelectionListener(_selectedAssets.isNotEmpty);
});
}
@ -129,6 +137,7 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
useGrayBoxPlaceholder: true,
showStorageIndicator: widget.showStorageIndicator,
heroOffset: widget.heroOffset,
showStack: widget.showStack,
);
}
@ -377,10 +386,6 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
setState(() {
_selectedAssets.clear();
});
} else if (widget.preselectedAssets != null) {
setState(() {
_selectedAssets.addAll(widget.preselectedAssets!);
});
}
}

View file

@ -12,6 +12,7 @@ class ThumbnailImage extends StatelessWidget {
final Asset Function(int index) loadAsset;
final int totalAssets;
final bool showStorageIndicator;
final bool showStack;
final bool useGrayBoxPlaceholder;
final bool isSelected;
final bool multiselectEnabled;
@ -26,6 +27,7 @@ class ThumbnailImage extends StatelessWidget {
required this.loadAsset,
required this.totalAssets,
this.showStorageIndicator = true,
this.showStack = false,
this.useGrayBoxPlaceholder = false,
this.isSelected = false,
this.multiselectEnabled = false,
@ -93,6 +95,35 @@ class ThumbnailImage extends StatelessWidget {
);
}
Widget buildStackIcon() {
return Positioned(
top: 5,
right: 5,
child: Row(
children: [
if (asset.stackCount > 1)
Text(
"${asset.stackCount}",
style: const TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
if (asset.stackCount > 1)
const SizedBox(
width: 3,
),
const Icon(
Icons.burst_mode_rounded,
color: Colors.white,
size: 18,
),
],
),
);
}
Widget buildImage() {
final image = SizedBox(
width: 300,
@ -113,9 +144,9 @@ class ThumbnailImage extends StatelessWidget {
decoration: BoxDecoration(
border: Border.all(
width: 0,
color: assetContainerColor,
color: onDeselect == null ? Colors.grey : assetContainerColor,
),
color: assetContainerColor,
color: onDeselect == null ? Colors.grey : assetContainerColor,
),
child: ClipRRect(
borderRadius: const BorderRadius.only(
@ -144,6 +175,7 @@ class ThumbnailImage extends StatelessWidget {
loadAsset: loadAsset,
totalAssets: totalAssets,
heroOffset: heroOffset,
showStack: showStack,
),
);
}
@ -196,6 +228,7 @@ class ThumbnailImage extends StatelessWidget {
),
),
if (!asset.isImage) buildVideoIcon(),
if (asset.isImage && asset.stackCount > 0) buildStackIcon(),
],
),
);

View file

@ -4,9 +4,9 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/album/ui/add_to_album_sliverlist.dart';
import 'package:immich_mobile/modules/home/models/selection_state.dart';
import 'package:immich_mobile/modules/home/ui/delete_dialog.dart';
import 'package:immich_mobile/modules/home/ui/upload_dialog.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
import 'package:immich_mobile/shared/ui/drag_sheet.dart';
import 'package:immich_mobile/shared/models/album.dart';
@ -19,11 +19,12 @@ class ControlBottomAppBar extends ConsumerWidget {
final Function(Album album) onAddToAlbum;
final void Function() onCreateNewAlbum;
final void Function() onUpload;
final void Function() onStack;
final List<Album> albums;
final List<Album> sharedAlbums;
final bool enabled;
final AssetState selectionAssetState;
final SelectionAssetState selectionAssetState;
const ControlBottomAppBar({
Key? key,
@ -36,19 +37,24 @@ class ControlBottomAppBar extends ConsumerWidget {
required this.onAddToAlbum,
required this.onCreateNewAlbum,
required this.onUpload,
this.selectionAssetState = AssetState.remote,
required this.onStack,
this.selectionAssetState = const SelectionAssetState(),
this.enabled = true,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
var isDarkMode = Theme.of(context).brightness == Brightness.dark;
var hasRemote = selectionAssetState == AssetState.remote;
var hasRemote =
selectionAssetState.hasRemote || selectionAssetState.hasMerged;
var hasLocal = selectionAssetState.hasLocal;
final trashEnabled =
ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash));
Widget renderActionButtons() {
return Row(
return Wrap(
spacing: 10,
runSpacing: 15,
children: [
ControlBoxButton(
iconData: Platform.isAndroid
@ -92,7 +98,7 @@ class ControlBottomAppBar extends ConsumerWidget {
if (!hasRemote)
ControlBoxButton(
iconData: Icons.backup_outlined,
label: "Upload",
label: "control_bottom_app_bar_upload".tr(),
onPressed: enabled
? () => showDialog(
context: context,
@ -104,6 +110,12 @@ class ControlBottomAppBar extends ConsumerWidget {
)
: null,
),
if (!hasLocal)
ControlBoxButton(
iconData: Icons.filter_none_rounded,
label: "control_bottom_app_bar_stack".tr(),
onPressed: enabled ? onStack : null,
),
],
);
}
@ -111,7 +123,7 @@ class ControlBottomAppBar extends ConsumerWidget {
return DraggableScrollableSheet(
initialChildSize: hasRemote ? 0.30 : 0.18,
minChildSize: 0.18,
maxChildSize: hasRemote ? 0.57 : 0.18,
maxChildSize: hasRemote ? 0.60 : 0.18,
snap: true,
builder: (
BuildContext context,