2025-07-05 00:38:06 +05:30
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
2026-05-14 08:19:00 +01:00
|
|
|
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
2025-07-05 00:38:06 +05:30
|
|
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
2026-08-01 08:06:41 +05:30
|
|
|
import 'package:immich_mobile/presentation/actions/action.dart';
|
2026-08-01 08:06:41 +05:30
|
|
|
import 'package:immich_mobile/presentation/actions/delete.action.dart';
|
2026-08-01 08:06:42 +05:30
|
|
|
import 'package:immich_mobile/presentation/actions/edit_asset.action.dart';
|
2026-08-01 08:06:40 +05:30
|
|
|
import 'package:immich_mobile/presentation/actions/restore.action.dart';
|
2026-08-01 08:06:42 +05:30
|
|
|
import 'package:immich_mobile/presentation/actions/share.action.dart';
|
2026-08-01 08:06:43 +05:30
|
|
|
import 'package:immich_mobile/presentation/actions/upload.action.dart';
|
2026-04-15 09:26:40 -05:00
|
|
|
import 'package:immich_mobile/presentation/widgets/action_buttons/add_action_button.widget.dart';
|
2026-06-09 13:20:18 -05:00
|
|
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/ocr_toggle_button.widget.dart';
|
2026-03-03 16:28:07 +00:00
|
|
|
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
|
2025-08-28 13:30:15 -04:00
|
|
|
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
|
2026-05-14 08:19:00 +01:00
|
|
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
2025-08-06 07:58:54 -05:00
|
|
|
import 'package:immich_mobile/providers/routes.provider.dart';
|
2025-07-09 20:04:25 +05:30
|
|
|
import 'package:immich_mobile/widgets/asset_viewer/video_controls.dart';
|
2026-08-01 08:06:41 +05:30
|
|
|
import 'package:immich_ui/immich_ui.dart';
|
|
|
|
|
|
|
|
|
|
// Resolved here rather than as an ActionColumnButton so a hidden restore
|
|
|
|
|
// takes no slot in the spaceEvenly row below.
|
|
|
|
|
List<Widget> _actionColumnButtons(BuildContext context, WidgetRef ref, List<ActionBuilder> actions) => actions
|
|
|
|
|
.map((a) => a.create(context, ref))
|
|
|
|
|
.nonNulls
|
|
|
|
|
.map((item) => ImmichColumnButton(icon: item.icon, label: item.label, onPressed: item.onAction))
|
|
|
|
|
.toList(growable: false);
|
2025-07-05 00:38:06 +05:30
|
|
|
|
|
|
|
|
class ViewerBottomBar extends ConsumerWidget {
|
|
|
|
|
const ViewerBottomBar({super.key});
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
2026-03-03 16:28:07 +00:00
|
|
|
final asset = ref.watch(assetViewerProvider.select((s) => s.currentAsset));
|
2025-07-05 00:38:06 +05:30
|
|
|
if (asset == null) {
|
|
|
|
|
return const SizedBox.shrink();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-28 13:30:15 -04:00
|
|
|
final isReadonlyModeEnabled = ref.watch(readonlyModeProvider);
|
2026-02-17 15:24:34 +00:00
|
|
|
final showingDetails = ref.watch(assetViewerProvider.select((s) => s.showingDetails));
|
2025-08-06 07:58:54 -05:00
|
|
|
final isInLockedView = ref.watch(inLockedViewProvider);
|
2026-05-14 08:19:00 +01:00
|
|
|
final isInTrash = ref.read(timelineServiceProvider).origin == TimelineOrigin.trash;
|
2025-07-05 00:38:06 +05:30
|
|
|
|
2025-12-09 05:29:31 +09:00
|
|
|
final originalTheme = context.themeData;
|
|
|
|
|
|
2025-07-05 00:38:06 +05:30
|
|
|
final actions = <Widget>[
|
2026-08-01 08:06:44 +05:30
|
|
|
..._actionColumnButtons(context, ref, const [RestoreAction(source: .viewer), ShareAction(source: .viewer)]),
|
2025-11-14 22:11:47 +01:00
|
|
|
|
2025-12-16 07:44:27 +09:00
|
|
|
if (!isInLockedView) ...[
|
2026-05-14 08:19:00 +01:00
|
|
|
if (!isInTrash) ...[
|
2026-08-01 08:06:44 +05:30
|
|
|
..._actionColumnButtons(context, ref, const [
|
|
|
|
|
UploadAction(source: .viewer, showProgress: true),
|
|
|
|
|
EditAssetAction(source: .viewer),
|
|
|
|
|
]),
|
2026-05-14 08:19:00 +01:00
|
|
|
if (asset.hasRemote) AddActionButton(originalTheme: originalTheme),
|
|
|
|
|
],
|
2026-08-01 08:06:41 +05:30
|
|
|
..._actionColumnButtons(context, ref, const [DeleteAction(source: .viewer)]),
|
2025-08-18 10:20:08 -05:00
|
|
|
],
|
2025-07-05 00:38:06 +05:30
|
|
|
];
|
|
|
|
|
|
2026-02-17 15:24:34 +00:00
|
|
|
return AnimatedSwitcher(
|
|
|
|
|
duration: Durations.short4,
|
|
|
|
|
child: showingDetails
|
|
|
|
|
? const SizedBox.shrink()
|
|
|
|
|
: Theme(
|
|
|
|
|
data: context.themeData.copyWith(
|
|
|
|
|
iconTheme: const IconThemeData(size: 22, color: Colors.white),
|
|
|
|
|
textTheme: context.themeData.textTheme.copyWith(
|
|
|
|
|
labelLarge: context.themeData.textTheme.labelLarge?.copyWith(color: Colors.white),
|
|
|
|
|
),
|
|
|
|
|
),
|
2026-05-09 05:47:41 +02:00
|
|
|
child: Stack(
|
|
|
|
|
children: [
|
|
|
|
|
const Positioned.fill(
|
|
|
|
|
child: IgnorePointer(
|
|
|
|
|
child: DecoratedBox(
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
gradient: LinearGradient(
|
|
|
|
|
begin: Alignment.bottomCenter,
|
|
|
|
|
end: Alignment.topCenter,
|
|
|
|
|
colors: [Colors.black45, Colors.black12, Colors.transparent],
|
|
|
|
|
stops: [0.0, 0.7, 1.0],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
2026-03-10 15:55:31 +00:00
|
|
|
),
|
2026-05-09 05:47:41 +02:00
|
|
|
SafeArea(
|
|
|
|
|
top: false,
|
|
|
|
|
child: Padding(
|
|
|
|
|
padding: const EdgeInsets.only(top: 16),
|
|
|
|
|
child: Column(
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
children: [
|
2026-07-21 22:23:04 +02:00
|
|
|
if (asset.isImage) OcrToggleButton(asset: asset),
|
2026-05-09 05:47:41 +02:00
|
|
|
if (asset.isVideo) VideoControls(videoPlayerName: asset.heroTag),
|
|
|
|
|
if (!isReadonlyModeEnabled)
|
|
|
|
|
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: actions),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
2026-03-10 15:55:31 +00:00
|
|
|
),
|
2026-05-09 05:47:41 +02:00
|
|
|
],
|
2026-02-17 15:24:34 +00:00
|
|
|
),
|
|
|
|
|
),
|
2025-07-05 00:38:06 +05:30
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|