// SPDX-FileCopyrightText: 2019-Present Christian Kußowski // SPDX-FileCopyrightText: 2019-Present Contributors to FluffyChat // // SPDX-License-Identifier: AGPL-3.0-or-later import 'package:tatchat/l10n/l10n.dart'; import 'package:tatchat/pages/image_viewer/video_player.dart'; import 'package:tatchat/utils/platform_infos.dart'; import 'package:tatchat/widgets/hover_builder.dart'; import 'package:tatchat/widgets/mxc_image.dart'; import 'package:material_ui/material_ui.dart'; import 'package:matrix/matrix.dart'; import 'image_viewer.dart'; class ImageViewerView extends StatelessWidget { final ImageViewerController controller; const ImageViewerView(this.controller, {super.key}); @override Widget build(BuildContext context) { final iconButtonStyle = IconButton.styleFrom( backgroundColor: Colors.black.withAlpha(200), foregroundColor: Colors.white, ); return GestureDetector( onTap: controller.closeAction, child: Scaffold( backgroundColor: Colors.black.withAlpha(128), extendBodyBehindAppBar: true, appBar: AppBar( elevation: 0, leading: IconButton( style: iconButtonStyle, icon: const Icon(Icons.close), onPressed: controller.closeAction, color: Colors.white, tooltip: L10n.of(context).close, ), backgroundColor: Colors.transparent, actions: [ IconButton( style: iconButtonStyle, icon: const Icon(Icons.reply_outlined), onPressed: controller.forwardAction, color: Colors.white, tooltip: L10n.of(context).share, ), const SizedBox(width: 8), IconButton( style: iconButtonStyle, icon: const Icon(Icons.download_outlined), onPressed: () => controller.saveFileAction(context), color: Colors.white, tooltip: L10n.of(context).downloadFile, ), const SizedBox(width: 8), if (PlatformInfos.isMobile) // Use builder context to correctly position the share dialog on iPad Padding( padding: const EdgeInsets.only(right: 8.0), child: Builder( builder: (context) => IconButton( style: iconButtonStyle, onPressed: () => controller.shareFileAction(context), tooltip: L10n.of(context).share, color: Colors.white, icon: Icon(Icons.adaptive.share_outlined), ), ), ), ], ), body: HoverBuilder( builder: (context, hovered) => Stack( children: [ KeyboardListener( focusNode: controller.focusNode, onKeyEvent: controller.onKeyEvent, child: Listener( onPointerDown: (_) => controller.onPointerDown(), onPointerUp: (_) => controller.onPointerUp(), onPointerCancel: (_) => controller.onPointerUp(), child: PageView.builder( scrollDirection: Axis.vertical, controller: controller.pageController, physics: controller.canSwipeToNextImage ? const PageScrollPhysics() : const NeverScrollableScrollPhysics(), itemCount: controller.allEvents.length, itemBuilder: (context, i) { final event = controller.allEvents[i]; switch (event.messageType) { case MessageTypes.Video: return Padding( padding: const EdgeInsets.only(top: 52.0), child: Center( child: GestureDetector( // Ignore taps to not go back here: onTap: () {}, child: EventVideoPlayer(event), ), ), ); case MessageTypes.Image: case MessageTypes.Sticker: default: return InteractiveViewer( transformationController: controller .transformationControllerFor(event), minScale: 1.0, maxScale: 10.0, onInteractionEnd: controller.onInteractionEnds, child: Center( child: Hero( tag: event.eventId, child: GestureDetector( // Ignore taps to not go back here: onTap: () {}, child: MxcImage( key: ValueKey(event.eventId), event: event, fit: BoxFit.contain, isThumbnail: false, animated: true, ), ), ), ), ); } }, ), ), ), if (hovered) Align( alignment: Alignment.centerRight, child: Column( mainAxisSize: .min, children: [ if (controller.canGoBack) Padding( padding: const EdgeInsets.all(12.0), child: IconButton( style: iconButtonStyle, tooltip: L10n.of(context).previous, icon: const Icon(Icons.arrow_upward_outlined), onPressed: controller.prevImage, ), ), if (controller.canGoNext) Padding( padding: const EdgeInsets.all(12.0), child: IconButton( style: iconButtonStyle, tooltip: L10n.of(context).next, icon: const Icon(Icons.arrow_downward_outlined), onPressed: controller.nextImage, ), ), ], ), ), ], ), ), ), ); } }