import 'package:bubble/bubble.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/utils/app_route.dart'; import 'package:fluffychat/views/image_view.dart'; import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_blurhash/flutter_blurhash.dart'; import 'package:flutter_advanced_networkimage/provider.dart'; import 'package:flutter_advanced_networkimage/transition.dart'; class ImageBubble extends StatefulWidget { final Event event; final bool tapToView; final BoxFit fit; final bool maxSize; final Color backgroundColor; final double radius; final bool thumbnailOnly; const ImageBubble( this.event, { this.tapToView = true, this.maxSize = true, this.backgroundColor, this.fit = BoxFit.cover, this.radius = 10.0, this.thumbnailOnly = true, Key key, }) : super(key: key); @override _ImageBubbleState createState() => _ImageBubbleState(); } class _ImageBubbleState extends State { bool get isUnencrypted => widget.event.content['url'] is String; static final Map _matrixFileMap = {}; MatrixFile get _file => _matrixFileMap[widget.event.eventId]; set _file(MatrixFile file) { if (file != null) { _matrixFileMap[widget.event.eventId] = file; } } static final Map _matrixThumbnailMap = {}; MatrixFile get _thumbnail => _matrixThumbnailMap[widget.event.eventId]; set _thumbnail(MatrixFile thumbnail) { if (thumbnail != null) { _matrixThumbnailMap[widget.event.eventId] = thumbnail; } } dynamic _error; bool _requestedFile = false; Future _getFile() async { _requestedFile = true; if (widget.thumbnailOnly) return null; if (_file != null) return _file; return widget.event.downloadAndDecryptAttachment(); } bool _requestedThumbnail = false; Future _getThumbnail() async { _requestedThumbnail = true; if (isUnencrypted) return null; if (_thumbnail != null) return _thumbnail; return widget.event .downloadAndDecryptAttachment(getThumbnail: widget.event.hasThumbnail); } @override Widget build(BuildContext context) { return Bubble( padding: BubbleEdges.all(0), radius: Radius.circular(widget.radius), color: widget.backgroundColor ?? Theme.of(context).secondaryHeaderColor, elevation: 0, child: Container( height: widget.maxSize ? 300 : null, width: widget.maxSize ? 400 : null, child: Builder( builder: (BuildContext context) { if (_error != null) { return Center( child: Text( _error.toString(), ), ); } if (_thumbnail == null && !_requestedThumbnail && !isUnencrypted) { _getThumbnail().then((MatrixFile thumbnail) { setState(() => _thumbnail = thumbnail); }, onError: (error, stacktrace) { setState(() => _error = error); }); } if (_file == null && !widget.thumbnailOnly && !_requestedFile) { _getFile().then((MatrixFile file) { setState(() => _file = file); }, onError: (error, stacktrace) { setState(() => _error = error); }); } final display = _file ?? _thumbnail; final generatePlaceholderWidget = () => Stack( children: [ if (widget.event.content['info'] is Map && widget.event.content['info']['xyz.amorgan.blurhash'] is String) BlurHash( hash: widget.event.content['info'] ['xyz.amorgan.blurhash']), Center( child: CircularProgressIndicator(), ), ], ); Widget renderWidget; if (display != null) { renderWidget = Image.memory( display.bytes, fit: widget.fit, ); } else if (isUnencrypted) { renderWidget = TransitionToImage( image: AdvancedNetworkImage( Uri.parse(widget.event.content['url']).getThumbnail( widget.event.room.client, width: 800, height: 800, method: ThumbnailMethod.scale), useDiskCache: !kIsWeb, ), loadingWidget: generatePlaceholderWidget(), fit: widget.fit, ); } else { renderWidget = generatePlaceholderWidget(); } return InkWell( onTap: () { if (!widget.tapToView) return; Navigator.of(context).push( AppRoute( ImageView(widget.event), ), ); }, child: Hero( tag: widget.event.eventId, child: renderWidget, ), ); }, ), ), ); } }