import 'package:flutter/material.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/image_viewer/image_viewer.dart'; import 'package:fluffychat/utils/file_description.dart'; import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; import '../../../widgets/blur_hash.dart'; class ImageBubble extends StatelessWidget { final Event event; final bool tapToView; final BoxFit fit; final Color? backgroundColor; final Color? textColor; final Color? linkColor; final bool thumbnailOnly; final bool animated; final double width; final double height; final void Function()? onTap; final BorderRadius? borderRadius; final Timeline? timeline; const ImageBubble( this.event, { this.tapToView = true, this.backgroundColor, this.fit = BoxFit.contain, this.thumbnailOnly = true, this.width = 400, this.height = 300, this.animated = false, this.onTap, this.borderRadius, this.timeline, this.textColor, this.linkColor, super.key, }); Widget _buildPlaceholder(BuildContext context) { final String blurHashString = event.infoMap['xyz.amorgan.blurhash'] is String ? event.infoMap['xyz.amorgan.blurhash'] : 'LEHV6nWB2yk8pyo0adR*.7kCMdnj'; return SizedBox( width: width, height: height, child: BlurHash( blurhash: blurHashString, width: width, height: height, fit: fit, ), ); } void _onTap(BuildContext context) { if (onTap != null) { onTap!(); return; } if (!tapToView) return; showDialog( context: context, builder: (_) => ImageViewer( event, timeline: timeline, outerContext: context, ), ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); var borderRadius = this.borderRadius ?? BorderRadius.circular(AppConfig.borderRadius); final fileDescription = event.fileDescription; final textColor = this.textColor; if (fileDescription != null) { borderRadius = borderRadius.copyWith( bottomLeft: Radius.zero, bottomRight: Radius.zero, ); } return Column( mainAxisSize: MainAxisSize.min, spacing: 8, children: [ Material( color: Colors.transparent, clipBehavior: Clip.hardEdge, shape: RoundedRectangleBorder( borderRadius: borderRadius, side: BorderSide( color: event.messageType == MessageTypes.Sticker ? Colors.transparent : theme.dividerColor, ), ), child: InkWell( onTap: () => _onTap(context), borderRadius: borderRadius, child: Hero( tag: event.eventId, child: MxcImage( event: event, width: width, height: height, fit: fit, animated: animated, isThumbnail: thumbnailOnly, placeholder: event.messageType == MessageTypes.Sticker ? null : _buildPlaceholder, ), ), ), ), if (fileDescription != null && textColor != null) SizedBox( width: width, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), child: Linkify( text: fileDescription, textScaleFactor: MediaQuery.textScalerOf(context).scale(1), style: TextStyle( color: textColor, fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, ), options: const LinkifyOptions(humanize: false), linkStyle: TextStyle( color: linkColor, fontSize: AppConfig.fontSizeFactor * AppConfig.messageFontSize, decoration: TextDecoration.underline, decorationColor: linkColor, ), onOpen: (url) => UrlLauncher(context, url.url).launchUrl(), ), ), ), ], ); } }