using System; using System.Collections.Generic; using ChatComponents; using uiwidgets; using Unity.UIWidgets.async; using Unity.UIWidgets.foundation; using Unity.UIWidgets.material; using Unity.UIWidgets.painting; using Unity.UIWidgets.ui; using Unity.UIWidgets.widgets; using UnityEngine; using Color = Unity.UIWidgets.ui.Color; namespace UIWidgetsSample { public delegate string CustomDateHeaderText(DateTime dateTime); public delegate void OnAttachmentPressed(); public delegate Future OnEndReached(); public delegate void OnMessageLongPress(ChatComponents.Message message); public delegate void OnMessageTap(ChatComponents.Message message); public delegate void OnPreviewDataFetched( PreviewData previewData,ChatComponents.TextMessage textMessage = null); public delegate void OnSendPressed(PartialText partialText); public delegate void OnTextChanged(string _str); public delegate Widget BuildCustomMessage(ChatComponents.Message message); public class Chat : StatefulWidget { /// See [Message.buildCustomMessage] /// If [dateFormat], [dateLocale] and/or [timeFormat] is not enough to /// customize date headers in your case, use this to return an arbitrary /// string based on a [DateTime] of a particular message. Can be helpful to /// return "Today" if [DateTime] is today. IMPORTANT: this will replace /// all default date headers, so you must handle all cases yourself, like /// for example today, yesterday and before. Or you can just return the same /// date header for any message. public readonly BuildCustomMessage buildCustomMessage; public readonly CustomDateHeaderText customDateHeaderText; /// Allows you to customize the date format. IMPORTANT: only for the date, /// do not return time here. See [timeFormat] to customize the time format. /// [dateLocale] will be ignored if you use this, so if you want a localized date /// make sure you initialize your [DateFormat] with a locale. See [customDateHeaderText] /// for more customization. public readonly DateTime? dateFormat; /// Locale will be passed to the `Intl` package. Make sure you initialized /// date formatting in your app before passing any locale here, otherwise /// an error will be thrown. Also see [customDateHeaderText], [dateFormat], [timeFormat]. public readonly string dateLocale; /// Disable automatic image preview on tap. public readonly bool? disableImageGallery; /// See [Input.isAttachmentUploading] public readonly bool? isAttachmentUploading; /// See [ChatList.isLastPage] public readonly bool? isLastPage; /// Localized copy. Extend [ChatL10n] class to create your own copy or use /// existing one, like the default [ChatL10nEn]. You can customize only /// certain variables, see more here [ChatL10nEn]. public readonly ChatL10n l10n; /// List of [types.Message] to render in the chat widget public readonly List messages; /// See [Input.onAttachmentPressed] public readonly OnAttachmentPressed onAttachmentPressed; /// See [ChatList.onEndReached] public readonly OnEndReached onEndReached; /// See [ChatList.onEndReachedThreshold] public readonly float? onEndReachedThreshold; /// See [Message.onMessageLongPress] public readonly OnMessageLongPress onMessageLongPress; /// See [Message.onMessageTap] public readonly OnMessageTap onMessageTap; /// See [Message.onPreviewDataFetched] public readonly OnPreviewDataFetched onPreviewDataFetched; /// See [Input.onSendPressed] public readonly OnSendPressed onSendPressed; /// See [Input.onTextChanged] public readonly OnTextChanged onTextChanged; /// See [Message.showUserAvatars] public readonly bool showUserAvatars; /// Show user names for received messages. Useful for a group chat. Will be /// shown only on text messages. public readonly bool showUserNames; /// Chat theme. Extend [ChatTheme] class to create your own theme or use /// existing one, like the [DefaultChatTheme]. You can customize only certain /// variables, see more here [DefaultChatTheme]. public readonly ChatTheme theme; /// Allows you to customize the time format. IMPORTANT: only for the time, /// do not return date here. See [dateFormat] to customize the date format. /// [dateLocale] will be ignored if you use this, so if you want a localized time /// make sure you initialize your [DateFormat] with a locale. See [customDateHeaderText] /// for more customization. public readonly DateTime? timeFormat; /// See [Message.usePreviewData] public readonly bool usePreviewData; /// See [InheritedUser.user] public readonly User user; /// Creates a chat widget public Chat( List messages, OnSendPressed onSendPressed, User user, Key key = null, BuildCustomMessage buildCustomMessage = null, CustomDateHeaderText customDateHeaderText = null, DateTime? dateFormat = null, string dateLocale = null, bool? disableImageGallery = null, bool? isAttachmentUploading = null, bool? isLastPage = null, ChatL10nEn l10n = null, OnAttachmentPressed onAttachmentPressed = null, OnEndReached onEndReached = null, float? onEndReachedThreshold = null, OnMessageLongPress onMessageLongPress = null, OnMessageTap onMessageTap = null, OnPreviewDataFetched onPreviewDataFetched = null, OnTextChanged onTextChanged = null, bool showUserAvatars = false, bool showUserNames = false, ChatTheme theme = null, DateTime? timeFormat = null, bool usePreviewData = true ) : base(key) { this.buildCustomMessage = buildCustomMessage; this.customDateHeaderText = customDateHeaderText; this.dateFormat = dateFormat; this.dateLocale = dateLocale; this.disableImageGallery = disableImageGallery; this.isAttachmentUploading = isAttachmentUploading; this.isLastPage = isLastPage; this.l10n = l10n == null ? new ChatL10nEn() : l10n; this.messages = messages; this.onAttachmentPressed = onAttachmentPressed; this.onEndReached = onEndReached; this.onEndReachedThreshold = onEndReachedThreshold; this.onMessageLongPress = onMessageLongPress; this.onMessageTap = onMessageTap; this.onPreviewDataFetched = onPreviewDataFetched; this.onSendPressed = onSendPressed; this.onTextChanged = onTextChanged; this.showUserAvatars = showUserAvatars; this.showUserNames = showUserNames; this.theme = theme == null ? new DefaultChatTheme() : theme; this.timeFormat = timeFormat; this.usePreviewData = usePreviewData; this.user = user; } public override State createState() { return new _ChatState(); } } /// [Chat] widget state public class _ChatState : State { private List _chatMessages = new List(); private List _gallery = new List(); private int _imageViewIndex; private bool _isImageViewVisible; public override void initState() { base.initState(); didUpdateWidget(widget); } public override void didUpdateWidget(StatefulWidget oldWidget) { oldWidget = (Chat) oldWidget; base.didUpdateWidget((Chat)oldWidget); if (widget.messages.isNotEmpty()) { var result = ChatUtils.calculateChatMessages( widget.messages, widget.user, customDateHeaderText: widget.customDateHeaderText, dateFormat: widget.dateFormat, dateLocale: widget.dateLocale, showUserNames: widget.showUserNames, timeFormat: widget.timeFormat ); _chatMessages = result[0] as List; _gallery = result[1] as List; } } /*private Widget _buildImageGallery() { return new Dismissible( GlobalKey.key("photo_view_gallery"), direction: DismissDirection.down, onDismissed: direction => _onCloseGalleryPressed(), child: new Stack( children: new List { PhotoViewGallery.builder( builder: (BuildContext context, int index) => PhotoViewGalleryPageOptions( imageProvider: new BrowserConditional().getProvider(_gallery[index].uri) ), itemCount: _gallery.Count, loadingBuilder: (_context, _event) => _imageGalleryLoadingBuilder(_context, _event), onPageChanged: _onPageChanged, pageController: new PageController(_imageViewIndex), scrollPhysics: new ClampingScrollPhysics() ), new Positioned( right: 16, top: 56, child: new CloseButton( color: Colors.white, onPressed: _onCloseGalleryPressed ) ) } ) ); }*/ private Widget _buildMessage(object _object) { if (_object is DateHeader) { return new Container( alignment: Alignment.center, margin: EdgeInsets.only( bottom: 32, top: 16 ), child: new Text( ((DateHeader) _object).text, style: widget.theme.dateDividerTextStyle ) ); } else if (_object is MessageSpacer) { var height = ((MessageSpacer) _object).height; return new SizedBox( height: height ); } else { var map = _object as Dictionary; var message = map["message"] as ChatComponents.Message; var _messageWidth = widget.showUserAvatars && message.author.id != widget.user.id ? Mathf.Min(MediaQuery.of(context).size.width * 0.72f, 440).floor() : Mathf.Min(MediaQuery.of(context).size.width * 0.78f, 440).floor(); return new Message( key: new ValueKey(message.id), buildCustomMessage: widget.buildCustomMessage, message: message, messageWidth: _messageWidth, onMessageLongPress: widget.onMessageLongPress, onMessageTap: tappedMessage => { if (tappedMessage is ImageMessage && widget.disableImageGallery != true) _onImagePressed((ImageMessage) tappedMessage); widget.onMessageTap?.Invoke(tappedMessage); }, onPreviewDataFetched: (previewData,textMessage)=> { _onPreviewDataFetched( textMessage,previewData); }, roundBorder: true, //(map["nextMessageInGroup"] is bool ? (bool) map["nextMessageInGroup"] : false), showAvatar: widget.showUserAvatars && false, //(map["nextMessageInGroup"] is bool ? (bool) map["nextMessageInGroup"] : false) == false, showName: false, //(map["showName"] is bool ? (bool) map["showName"] : false), showStatus: false, //(map["showStatus"] is bool ? (bool) map["showStatus"] : false), showUserAvatars: widget.showUserAvatars, usePreviewData: widget.usePreviewData ); } } private Widget _imageGalleryLoadingBuilder( BuildContext context, ImageChunkEvent _event = null ) { return new Center( child: new SizedBox( width: 20.0f, height: 20.0f, child: new CircularProgressIndicator( value: _event == null || _event.expectedTotalBytes == null ? 0 : _event.cumulativeBytesLoaded / _event.expectedTotalBytes ) ) ); } private void _onCloseGalleryPressed() { setState(() => { _isImageViewVisible = false; }); } private void _onImagePressed(ImageMessage message) { setState(() => { /*_imageViewIndex = _gallery.Where( element => element.id == message.id && element.uri == message.uri );*/ foreach (var element in _gallery) if (element.id == message.id && element.uri == message.uri) { _imageViewIndex = _gallery.IndexOf(element); break; } _isImageViewVisible = true; }); } private void _onPageChanged(int index) { setState(() => { _imageViewIndex = index; }); } private void _onPreviewDataFetched( ChatComponents.TextMessage message, PreviewData previewData ) { widget.onPreviewDataFetched?.Invoke(previewData,message); } public override Widget build(BuildContext context) { var results = new List(); results.Add(new Container( color: Color.fromARGB(0, 0, 0, 0), child: new SafeArea( bottom: false, child: new Column( children: new List { new Flexible( child: widget.messages.isEmpty() ? (Widget) SizedBox.expand( child: new Container( //color: Colors.yellow, alignment: Alignment.center, margin: EdgeInsets.symmetric( horizontal: 24 ), child: new Text( widget.l10n.emptyChatPlaceholder, style: widget.theme.emptyChatPlaceholderTextStyle, textAlign: TextAlign.center ) ) ) : new GestureDetector( onTap: () => FocusManager.instance.primaryFocus?.unfocus(), child: new ChatList( isLastPage: widget.isLastPage, itemBuilder: (item, index) => _buildMessage(item), items: _chatMessages, onEndReached: widget.onEndReached, onEndReachedThreshold: widget.onEndReachedThreshold ) ) ), new Input( isAttachmentUploading: widget.isAttachmentUploading, onAttachmentPressed: widget.onAttachmentPressed, onSendPressed: widget.onSendPressed, onTextChanged: widget.onTextChanged ) } ) ) )); // if (_isImageViewVisible) // results.Add(_buildImageGallery()); var test = widget.l10n; return new InheritedUser( widget.user, new InheritedChatTheme( widget.theme, new InheritedL10n( widget.l10n, new Stack( children: results ) ) ) ); } } }