From 2dcca0772592d1ca9af4b30e167d369bae075e70 Mon Sep 17 00:00:00 2001 From: Lyndon Jetten Date: Wed, 4 Aug 2021 17:04:18 +0200 Subject: [PATCH 1/2] Cache optimiert. Maximale Breite von Chatnachrichtlistitem auf 500 gesetzt. --- ChatController/ChatController.csproj | 3 +++ ChatController/ChatKlassen/ChatMessage.cs | 3 +++ ChatController/ChatMainControl.xaml | 2 +- ChatController/Core/OwnChatCache.cs | 25 ++++++++++++++++------ ChatController/HauptKlassen/Chat.cs | 10 +++++++++ ChatController/Utilities/Utils.cs | 4 ++-- PrototypChat/.vs/PrototypChat/v16/.suo | Bin 257536 -> 229888 bytes 7 files changed, 37 insertions(+), 10 deletions(-) diff --git a/ChatController/ChatController.csproj b/ChatController/ChatController.csproj index adde9fb..2e5e772 100644 --- a/ChatController/ChatController.csproj +++ b/ChatController/ChatController.csproj @@ -37,6 +37,9 @@ + + False + ..\libs\Newtonsoft.Json.dll diff --git a/ChatController/ChatKlassen/ChatMessage.cs b/ChatController/ChatKlassen/ChatMessage.cs index bbeb2db..a2c9432 100644 --- a/ChatController/ChatKlassen/ChatMessage.cs +++ b/ChatController/ChatKlassen/ChatMessage.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.Diagnostics; using System.Text.RegularExpressions; using System.Windows; using System.Windows.Media; @@ -275,6 +276,8 @@ namespace ChatController.ChatKlassen { if(IsHyperlink(UserMessage)) { + Debug.WriteLine($"Link: {UserMessage}"); + if(!UserMessage.Contains("http")) { return new Uri("http://" + UserMessage); diff --git a/ChatController/ChatMainControl.xaml b/ChatController/ChatMainControl.xaml index 0774e61..3c687ba 100644 --- a/ChatController/ChatMainControl.xaml +++ b/ChatController/ChatMainControl.xaml @@ -299,7 +299,7 @@ - + diff --git a/ChatController/Core/OwnChatCache.cs b/ChatController/Core/OwnChatCache.cs index 92e6335..cec0077 100644 --- a/ChatController/Core/OwnChatCache.cs +++ b/ChatController/Core/OwnChatCache.cs @@ -66,7 +66,7 @@ namespace ChatController.Core } } - public void AddProfilePictureToCache(string key, ImageSource imageSource) + public void AddProfilePictureToCache(string key, ImageSource imageSource, string profilePicturePath) { lock(_Lock) { @@ -75,17 +75,25 @@ namespace ChatController.Core _ImageSourceCache.Add(key, new ImageSourceCacheStorage()); } - _ImageSourceCache[key].SetCachedProfilePicture(imageSource); + _ImageSourceCache[key].SetCachedProfilePicture(imageSource, profilePicturePath); } } - public ImageSource GetCachedProfilePicture(string key) + public ImageSource GetCachedProfilePicture(string key, string uri) { lock(_Lock) { + foreach (var kvp in _ImageSourceCache) + { + if(!string.IsNullOrWhiteSpace(uri) && uri.ToLower().Contains("ownchat") && kvp.Value.ProfilePicturePath == uri) + { + return kvp.Value.GetCachedProfilePicture(); + } + } + return _ImageSourceCache.ContainsKey(key) ? - _ImageSourceCache[key].GetCachedProfilePicture() : - null; + _ImageSourceCache[key].GetCachedProfilePicture() : + null; } } } @@ -94,15 +102,18 @@ namespace ChatController.Core { private ImageSourceCacheObject _ProfilePicture; + public string ProfilePicturePath { get; set; } + public ImageSource GetCachedProfilePicture() { return _ProfilePicture?.ExpirationDate > DateTime.Now ? _ProfilePicture.Image : null; } - public void SetCachedProfilePicture(ImageSource avatarImageSource) + public void SetCachedProfilePicture(ImageSource avatarImageSource, string profilePicturePath) { - if(avatarImageSource != null) + if(avatarImageSource != null && !string.IsNullOrWhiteSpace(profilePicturePath)) { + ProfilePicturePath = profilePicturePath; _ProfilePicture = new ImageSourceCacheObject(avatarImageSource, CacheCategory.ProfilePicture); } } diff --git a/ChatController/HauptKlassen/Chat.cs b/ChatController/HauptKlassen/Chat.cs index 2f873cd..888e8e7 100644 --- a/ChatController/HauptKlassen/Chat.cs +++ b/ChatController/HauptKlassen/Chat.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.IO; @@ -145,6 +146,9 @@ namespace ChatController.HauptKlassen client.ExecuteAsync(request, response => { + // TODO: Entfernen + Debug.WriteLine(response.Content); + _UserMessages = JsonConvert.DeserializeObject(response.Content); if (_UserMessages.Response.HasMorePages) @@ -196,6 +200,9 @@ namespace ChatController.HauptKlassen client.ExecuteAsync(request, response => { + // TODO: Entfernen + Debug.WriteLine(response.Content); + _AdditionalUserMessages = null; _AdditionalUserMessages = JsonConvert.DeserializeObject(response.Content); @@ -290,6 +297,9 @@ namespace ChatController.HauptKlassen client.ExecuteAsync(request, response => { + // TODO: Entfernen + Debug.WriteLine(response.Content); + var messageCounter = JsonConvert.DeserializeObject(response.Content); callback?.Invoke(messageCounter.MessageCount); diff --git a/ChatController/Utilities/Utils.cs b/ChatController/Utilities/Utils.cs index de8909f..4519508 100644 --- a/ChatController/Utilities/Utils.cs +++ b/ChatController/Utilities/Utils.cs @@ -30,7 +30,7 @@ namespace ChatController.Utilities { var cache = OwnChatCache.GetInstance(); - var cachedProfileImage = cache.GetCachedProfilePicture(key); + var cachedProfileImage = cache.GetCachedProfilePicture(key, pathToProfilePicture); if(cachedProfileImage != null) { @@ -58,7 +58,7 @@ namespace ChatController.Utilities bitmap.EndInit(); bitmap.Freeze(); - cache.AddProfilePictureToCache(key, bitmap); + cache.AddProfilePictureToCache(key, bitmap, pathToProfilePicture); callback?.Invoke(bitmap); } diff --git a/PrototypChat/.vs/PrototypChat/v16/.suo b/PrototypChat/.vs/PrototypChat/v16/.suo index f1e1a229949b9d215f373017f6c2153ca9535196..06aa56a3815e09d2f3d0464b9d523df4ccdafcfc 100644 GIT binary patch delta 18370 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 ps*fGXD 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 V9 Date: Tue, 7 Sep 2021 19:32:10 +0200 Subject: [PATCH 2/2] - Links in Nachrichten klickbar - Profilbilder werden in Gruppenchats (mehr als 2 Teilnehmer) angezeigt - Profilbilder werden im Cache gespeichert - Verbesserungen am Code --- ChatController/ChatController.csproj | 2 + ChatController/ChatKlassen/ChatMessage.cs | 220 +++++------------- ChatController/ChatKlassen/Contact.cs | 11 +- ChatController/ChatMainControl.xaml | 113 +++++---- ChatController/ChatMainControl.xaml.cs | 82 ++++--- .../UserProfilePictureVisibilityConverter.cs | 30 +++ ChatController/Core/BindableTextBlock.cs | 31 +++ ChatController/Core/OwnChatCache.cs | 37 +++ ChatController/HauptKlassen/Chat.cs | 159 ++++++------- ChatController/LoginControl.xaml | 31 ++- ChatController/LoginControl.xaml.cs | 16 +- ChatController/LoginKlassen/UserDaten.cs | 20 ++ ChatController/Utilities/Utils.cs | 155 +++++++++--- PrototypChat/.vs/PrototypChat/v16/.suo | Bin 229888 -> 288256 bytes 14 files changed, 535 insertions(+), 372 deletions(-) create mode 100644 ChatController/Converter/UserProfilePictureVisibilityConverter.cs create mode 100644 ChatController/Core/BindableTextBlock.cs diff --git a/ChatController/ChatController.csproj b/ChatController/ChatController.csproj index 2e5e772..eae6e56 100644 --- a/ChatController/ChatController.csproj +++ b/ChatController/ChatController.csproj @@ -72,6 +72,8 @@ + + diff --git a/ChatController/ChatKlassen/ChatMessage.cs b/ChatController/ChatKlassen/ChatMessage.cs index a2c9432..7fc0259 100644 --- a/ChatController/ChatKlassen/ChatMessage.cs +++ b/ChatController/ChatKlassen/ChatMessage.cs @@ -1,12 +1,14 @@ using System; +using System.Collections.ObjectModel; using System.ComponentModel; -using System.Diagnostics; -using System.Text.RegularExpressions; -using System.Windows; +using System.Windows.Documents; using System.Windows.Media; using ChatController.Core; +using ChatController.HauptKlassen; using ChatController.Utilities; using static System.Windows.HorizontalAlignment; +using Application = System.Windows.Application; +using HorizontalAlignment = System.Windows.HorizontalAlignment; namespace ChatController.ChatKlassen { @@ -22,108 +24,82 @@ namespace ChatController.ChatKlassen public bool IsSeparator { get; set; } - public long FileSize { get; set; } + public long SenderId { get; } - // Separator - public ChatMessage(DateTime sendTime) + private ObservableCollection _MessageInlines; + + public ObservableCollection MessageInlines { - Id = Guid.NewGuid(); - IsSeparator = true; - SendTime = sendTime; - - MessageType = ChatMessageType.Separator; + get => _MessageInlines; + set + { + if(false == (_MessageInlines?.Equals(value) ?? false)) + { + _MessageInlines = value; + OnPropertyChanged(nameof(MessageInlines)); + } + } } - //Chat-Ansicht - public ChatMessage(string username, string message, DateTime sendtime, bool isme, long groupId, long fileSize, long messageId) + public ChatMessage(long groupId, long messageId, string userName, string messageText, DateTime sendTime, bool isOwnMessage, long fileSize, string filePath, + string originalImage, string imageName, string thumbnailPath, ImageSource logo, ChatMessageType chatMessageType, long senderId) { - MessageId = messageId; - Id = Guid.NewGuid(); - Username = username; - UserMessage = message; - SendTime = sendtime; - IsMyMessage = isme; + SenderId = senderId; + + MessageType = chatMessageType; + + MessageId = messageId; GroupId = groupId; + Username = userName; - MessageType = IsHyperlink(message) ? ChatMessageType.Hyperlink : ChatMessageType.Message; - } - - // Image-Ansicht - public ChatMessage(string username, string message, DateTime sendtime, bool isme, string filePath, string originalImage, string imageName, long groupId, long fileSize, long messageId) - { - PicturePlaceholderHeight = Utils.GetHeightFromThumbnailUri(filePath); - PictureSource = Utils.GetPicturePlaceholder(); + UserMessage = messageText; + if(!string.IsNullOrEmpty(messageText)) + { + Application.Current.Dispatcher.Invoke(() => + { + MessageInlines = Utils.ConvertLinksToHyperlinks(messageText); + }); + } - MessageId = messageId; - - Id = Guid.NewGuid(); - Username = username; - UserMessage = message; - SendTime = sendtime; - - IsMyMessage = isme; + SendTime = sendTime; + IsMyMessage = isOwnMessage; OriginalImage = originalImage; OriginalImageName = imageName; - - GroupId = groupId; - MessageType = ChatMessageType.Image; - - Utils.DownloadImageAsync(filePath, $"group-{groupId}", CacheCategory.Thumbnail, imageSource => + if(MessageId == 0 && MessageType == ChatMessageType.Image && logo != null) { - PictureSource = imageSource; - }); - } - - //Dokumenten-Ansicht - public ChatMessage(string username, string message, DateTime sendtime, string thumbnailPath, bool isme, string filePath, long groupId, long fileSize, long messageId) - { - ThumbnailPlaceholderHeight = Utils.GetHeightFromThumbnailUri(thumbnailPath); - Thumbnail = Utils.GetPicturePlaceholder(); - - MessageId = messageId; - - Id = Guid.NewGuid(); - Username = username; - UserMessage = message; - SendTime = sendtime; - IsMyMessage = isme; + PictureSource = logo; + PicturePlaceholderHeight = (int) logo.Height; + } FilePath = filePath; - - Utils.DownloadImageAsync(thumbnailPath, $"group-{groupId}", CacheCategory.Thumbnail, imageSource => + + if(!string.IsNullOrWhiteSpace(thumbnailPath)) { - Thumbnail = imageSource; - }); + ThumbnailPlaceholderHeight = Utils.GetHeightFromThumbnailUri(thumbnailPath); + Thumbnail = Utils.GetPicturePlaceholder(); - GroupId = groupId; + Utils.DownloadImageAsync(thumbnailPath, $"group-{groupId}", CacheCategory.Thumbnail, imageSource => + { + Thumbnail = imageSource; + }); + } - MessageType = ChatMessageType.Document; - } + if(MessageType == ChatMessageType.Image && !string.IsNullOrWhiteSpace(filePath)) + { + PicturePlaceholderHeight = Utils.GetHeightFromThumbnailUri(filePath); + PictureSource = Utils.GetPicturePlaceholder(); - // Mit Logo - public ChatMessage(string username, string message, DateTime sendtime, bool isme, ImageSource logo, string originalImage, string imageName, long groupId, long fileSize, long messageId) - { - MessageId = messageId; + Utils.DownloadImageAsync(filePath, $"group-{groupId}", CacheCategory.Thumbnail, imageSource => + { + PictureSource = imageSource; + }); + } - Id = Guid.NewGuid(); - Username = username; - UserMessage = message; - SendTime = sendtime; - - IsMyMessage = isme; - - OriginalImage = originalImage; - OriginalImageName = imageName; - - GroupId = groupId; - - MessageType = ChatMessageType.Image; - - PictureSource = logo; + IsSeparator = MessageType == ChatMessageType.Separator; } public ChatMessageType MessageType { get; } @@ -157,47 +133,6 @@ namespace ChatController.ChatKlassen } } - private static readonly Regex _UrlRegex = new Regex(@"(?#Protocol)^(http(?:s?)\:(\/\/|\\\\)|(w){3}(2|3)?\.{1})(?#Subdomains)(?:(?:[-\w]+\.)+(?#TopLevel Domains)(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))(?#Port)(?::[\d]{1,5})?(?#Directories)(?:(?:(?:/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|/)+|\?|#)?(?#Query)(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?#Anchor)(?:#(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)?"); - - public static bool IsHyperlink(string word) - { - if(word == null) - { - return false; - } - - try - { - if (word.IndexOfAny(@":.\/".ToCharArray()) != -1) - { - if (_UrlRegex.IsMatch(word)) - { - var uri = new Uri(word, UriKind.RelativeOrAbsolute); - - if (!uri.IsAbsoluteUri) - { - uri = new Uri(@"http://" + word, UriKind.Absolute); - } - - if (uri.IsAbsoluteUri) - { - return true; - } - } - else - { - return false; - } - } - } - catch (Exception e) - { - return false; - } - - return false; - } - public bool IsMyMessage { get; set; } public string Username { get; } @@ -223,16 +158,6 @@ namespace ChatController.ChatKlassen public string OriginalImage { get; } public string OriginalImageName { get; } - - public Visibility ChatVisibility => MessageType == ChatMessageType.Message ? Visibility.Visible : Visibility.Collapsed; - - public Visibility HyperlinkVisibility => MessageType == ChatMessageType.Hyperlink ? Visibility.Visible : Visibility.Collapsed; - - public Visibility PictureVisibility => MessageType == ChatMessageType.Image ? Visibility.Visible : Visibility.Collapsed; - - public Visibility PicturePlaceHolderVisibility => MessageType == ChatMessageType.DefaultImage ? Visibility.Visible : Visibility.Collapsed; - - public Visibility DokumentVisibility => MessageType == ChatMessageType.Document ? Visibility.Visible : Visibility.Collapsed; private ImageSource _PictureSource; public ImageSource PictureSource @@ -262,7 +187,7 @@ namespace ChatController.ChatKlassen } } - public object FilePath { get; } + public string FilePath { get; } public long GroupId { get; } @@ -270,28 +195,7 @@ namespace ChatController.ChatKlassen public Guid Id { get; } - public Uri UserMessageAsUri - { - get - { - if(IsHyperlink(UserMessage)) - { - Debug.WriteLine($"Link: {UserMessage}"); - - if(!UserMessage.Contains("http")) - { - return new Uri("http://" + UserMessage); - } - - if(UserMessage.Contains("https") || UserMessage.Contains("http")) - { - return new Uri(UserMessage); - } - } - - return null; - } - } + public ImageSource ProfilePicture => OwnChatCache.GetInstance().GetUserProfilePictureFromCache(SenderId)?.ProfilePicture; protected virtual void OnPropertyChanged(string propertyName) { @@ -308,7 +212,7 @@ namespace ChatController.ChatKlassen return SendTime == message.SendTime; } - return MessageId.Equals(message.MessageId); + return Id.Equals(message.Id) && MessageId.Equals(message.MessageId); } return false; diff --git a/ChatController/ChatKlassen/Contact.cs b/ChatController/ChatKlassen/Contact.cs index 26eb9a2..74a5c02 100644 --- a/ChatController/ChatKlassen/Contact.cs +++ b/ChatController/ChatKlassen/Contact.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Windows.Media; using ChatController.LoginKlassen; @@ -82,7 +83,7 @@ namespace ChatController.ChatKlassen public bool IsChatMessageInputGridVisible { get; } - public Contact(string pName, GroupLatestMessage pLatestMessage, DateTime pTimeStamp, int pGroupId, string pUserIdManage, bool pOnlyEmployees, int pUserCount, bool isChatMessageInputGridVisible, string accentColorBrushString, string profilePicturePath, string key) + public Contact(string pName, GroupLatestMessage pLatestMessage, DateTime pTimeStamp, int pGroupId, string pUserIdManage, bool pOnlyEmployees, int pUserCount, bool isChatMessageInputGridVisible, string accentColorBrushString, string profilePicturePath, string key, Dictionary userId2Uri) { Name = pName; ReceivedMessage = pLatestMessage; @@ -108,15 +109,21 @@ namespace ChatController.ChatKlassen accentColorBrushString = "000000"; } } - + AccentColorBrush = new BrushConverter().ConvertFromString($"#{accentColorBrushString}") as SolidColorBrush; + IsGroupChat = pUserCount > 2; + + Utils.DownloadUserProfilePictures(userId2Uri); + Utils.DownloadProfilePictureAsync(profilePicturePath, key, delegate(ImageSource imageSource) { Image = imageSource; }); } + public bool IsGroupChat { get; } + public override bool Equals(object obj) { if(obj is Contact y) diff --git a/ChatController/ChatMainControl.xaml b/ChatController/ChatMainControl.xaml index 3c687ba..5a7c57d 100644 --- a/ChatController/ChatMainControl.xaml +++ b/ChatController/ChatMainControl.xaml @@ -5,6 +5,8 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:converter="clr-namespace:ChatController.Converter" xmlns:chatKlassen="clr-namespace:ChatController.ChatKlassen" + xmlns:core="clr-namespace:ChatController.Core" + xmlns:chatController="clr-namespace:ChatController" mc:Ignorable="d" x:Name="ChatControlling" d:DesignHeight="300" d:DesignWidth="600" Loaded="ChatMainControl_OnLoaded" @@ -13,10 +15,10 @@ + - - + diff --git a/ChatController/ChatMainControl.xaml.cs b/ChatController/ChatMainControl.xaml.cs index 6984223..7068509 100644 --- a/ChatController/ChatMainControl.xaml.cs +++ b/ChatController/ChatMainControl.xaml.cs @@ -484,21 +484,21 @@ namespace ChatController else { var contextItems = ChatListBox.ContextMenu.Items; - var ContextItemSpeichernUnter = (MenuItem)contextItems[0]; - ContextItemSpeichernUnter.Visibility = Visibility.Collapsed; + var contextItemSpeichernUnter = (MenuItem)contextItems[0]; + contextItemSpeichernUnter.Visibility = Visibility.Collapsed; } if(item.PictureSource == null && item.FilePath == null) { var contextItems = ChatListBox.ContextMenu.Items; - var ContextItemSpeichernUnter = (MenuItem)contextItems[2]; - ContextItemSpeichernUnter.Visibility = Visibility.Visible; + var contextItemSpeichernUnter = (MenuItem)contextItems[2]; + contextItemSpeichernUnter.Visibility = Visibility.Visible; } else { var contextItems = ChatListBox.ContextMenu.Items; - var ContextItemSpeichernUnter = (MenuItem)contextItems[2]; - ContextItemSpeichernUnter.Visibility = Visibility.Collapsed; + var contextItemSpeichernUnter = (MenuItem)contextItems[2]; + contextItemSpeichernUnter.Visibility = Visibility.Collapsed; } } } @@ -506,8 +506,8 @@ namespace ChatController { //Schalte speichern unter Aus var contextItems = ChatListBox.ContextMenu.Items; - var ContextItemSpeichernUnter = (MenuItem)contextItems[0]; - ContextItemSpeichernUnter.Visibility = Visibility.Collapsed; + var contextItemSpeichernUnter = (MenuItem)contextItems[0]; + contextItemSpeichernUnter.Visibility = Visibility.Collapsed; } //prüfe ob was in dem Speicher vorhanden ist //Einfügen @@ -516,40 +516,40 @@ namespace ChatController if(dataObject != null && dataObject.GetDataPresent(DataFormats.FileDrop)) { var contextItems = ChatListBox.ContextMenu.Items; - var ContextItemSpeichernUnter = (MenuItem) contextItems[1]; - ContextItemSpeichernUnter.Visibility = Visibility.Visible; + var contextItemSpeichernUnter = (MenuItem) contextItems[1]; + contextItemSpeichernUnter.Visibility = Visibility.Visible; } else if(dataObject != null && dataObject.GetDataPresent(DataFormats.Text)) { var contextItems = ChatListBox.ContextMenu.Items; - var ContextItemSpeichernUnter = (MenuItem) contextItems[1]; - ContextItemSpeichernUnter.Visibility = Visibility.Visible; + var contextItemSpeichernUnter = (MenuItem) contextItems[1]; + contextItemSpeichernUnter.Visibility = Visibility.Visible; } else if(dataObject != null && dataObject.GetDataPresent(DataFormats.Bitmap)) { var contextItems = ChatListBox.ContextMenu.Items; - var ContextItemSpeichernUnter = (MenuItem) contextItems[1]; - ContextItemSpeichernUnter.Visibility = Visibility.Visible; + var contextItemSpeichernUnter = (MenuItem) contextItems[1]; + contextItemSpeichernUnter.Visibility = Visibility.Visible; } else { var contextItems = ChatListBox.ContextMenu.Items; - var ContextItemSpeichernUnter = (MenuItem)contextItems[1]; - ContextItemSpeichernUnter.Visibility = Visibility.Collapsed; + var contextItemSpeichernUnter = (MenuItem)contextItems[1]; + contextItemSpeichernUnter.Visibility = Visibility.Collapsed; } // Prüfe, ob Dokumentation erlaubt if(CurrentContact.UserIdManage == null && ChatListBox.ContextMenu.Items.Count > 3) { var contextItems = ChatListBox.ContextMenu.Items; - var ContextItemSpeichernUnter = (MenuItem)contextItems[3]; - ContextItemSpeichernUnter.Visibility = Visibility.Collapsed; + var contextItemSpeichernUnter = (MenuItem)contextItems[3]; + contextItemSpeichernUnter.Visibility = Visibility.Collapsed; } else if(ChatListBox.ContextMenu.Items.Count > 3) { var contextItems = ChatListBox.ContextMenu.Items; - var ContextItemSpeichernUnter = (MenuItem)contextItems[3]; - ContextItemSpeichernUnter.Visibility = Visibility.Visible; + var contextItemSpeichernUnter = (MenuItem)contextItems[3]; + contextItemSpeichernUnter.Visibility = Visibility.Visible; } if(ChatListBox.ContextMenu.Items.Count == 0) @@ -563,13 +563,13 @@ namespace ChatController { if(CurrentContact != null) { - var fileToOpen = Chat.OpenFile(); + var pathToOriginalImage = Chat.OpenFile(); - if(fileToOpen != null) + if(pathToOriginalImage != null) { - var filePath = FileUtils.ScaleImage(fileToOpen, Path.GetExtension(fileToOpen.ToUpperInvariant()), Chat.ChatDaten.MaxUploadSize); + var pathToScaledImage = FileUtils.ScaleImage(pathToOriginalImage, Path.GetExtension(pathToOriginalImage.ToUpperInvariant()), Chat.ChatDaten.MaxUploadSize); - var isFileSizeTooLarge = FileUtils.CheckFileSize(filePath, Chat.ChatDaten.MaxUploadSize); + var isFileSizeTooLarge = FileUtils.CheckFileSize(pathToScaledImage, Chat.ChatDaten.MaxUploadSize); if(isFileSizeTooLarge) { @@ -577,9 +577,9 @@ namespace ChatController return; } - if (!string.IsNullOrWhiteSpace(filePath)) + if (!string.IsNullOrWhiteSpace(pathToScaledImage)) { - _ChatMessages.AddRangeIfElementsNotIn(Chat.AddNewFile(filePath, fileToOpen, CurrentContact.GroupId, GetFirstMessage())); + _ChatMessages.AddRangeIfElementsNotIn(Chat.AddNewFile(pathToScaledImage, pathToOriginalImage, CurrentContact.GroupId, GetFirstMessage())); ChatMessages.MoveCurrentToLast(); @@ -587,11 +587,11 @@ namespace ChatController ChatListBox.ScrollIntoView(currentChatMessage); - Chat.SendFileToContact(CurrentContact, filePath,fileToOpen); + Chat.SendFileToContact(CurrentContact, pathToScaledImage,pathToOriginalImage); - if (!string.IsNullOrWhiteSpace(filePath) && !filePath.Equals(fileToOpen) && File.Exists(filePath)) + if (!string.IsNullOrWhiteSpace(pathToScaledImage) && !pathToScaledImage.Equals(pathToOriginalImage) && File.Exists(pathToScaledImage)) { - File.Delete(filePath); + File.Delete(pathToScaledImage); } } } @@ -606,8 +606,10 @@ namespace ChatController { if(CurrentContact != null && !string.IsNullOrEmpty(Chatbox.Text)) { - _ChatMessages.AddRangeIfElementsNotIn(Chat.AddNewMessage(Chatbox.Text, CurrentContact.GroupId, GetFirstMessage())); + var messages2Add = Chat.AddNewMessage(Chatbox.Text, CurrentContact.GroupId, GetFirstMessage()); + _ChatMessages.AddRangeIfElementsNotIn(messages2Add); + OnPropertyChanged(nameof(ChatMessages)); ChatMessages.MoveCurrentToLast(); ChatListBox.ScrollIntoView(ChatMessages.CurrentItem); @@ -753,7 +755,6 @@ namespace ChatController { foreach (var newContact in updatedContacts) { - if (contact.GroupId == newContact.GroupId) { // ReceivedMessage kann null sein! @@ -869,8 +870,19 @@ namespace ChatController if(selectedItem is ChatMessage selectedMessage) { - if(!string.IsNullOrEmpty(selectedMessage.OriginalImage)) + if (!string.IsNullOrEmpty(selectedMessage.OriginalImage)) { + var uri = new Uri(selectedMessage.OriginalImage); + + if(uri.IsFile) + { + var bitmapImage = new BitmapImage(uri); + bitmapImage.Freeze(); + Chat.ShowPictureWindow(bitmapImage, "Test"); + + return; + } + StartWaitingImmediately(); Chat.ShowPicture(selectedMessage.OriginalImage, CurrentContact.GroupId, (imageSource, windowTitle) => @@ -963,12 +975,6 @@ namespace ChatController }); } - private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs e) - { - Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)); - e.Handled = true; - } - private void ButtonReloadGruppen_OnClick(object sender, RoutedEventArgs e) { DoSynchro(); diff --git a/ChatController/Converter/UserProfilePictureVisibilityConverter.cs b/ChatController/Converter/UserProfilePictureVisibilityConverter.cs new file mode 100644 index 0000000..cabb8bf --- /dev/null +++ b/ChatController/Converter/UserProfilePictureVisibilityConverter.cs @@ -0,0 +1,30 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace ChatController.Converter +{ + public class UserProfilePictureVisibilityConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if(values != null && values.Length == 2 && values[0] is bool isMyMessage && values[1] is bool isGroupChat) + { + if(parameter is string test && test == "reverse") + { + return isMyMessage && isGroupChat ? Visibility.Visible : Visibility.Collapsed; + } + + return !isMyMessage && isGroupChat ? Visibility.Visible : Visibility.Collapsed; + } + + return Visibility.Collapsed; + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/ChatController/Core/BindableTextBlock.cs b/ChatController/Core/BindableTextBlock.cs new file mode 100644 index 0000000..e089037 --- /dev/null +++ b/ChatController/Core/BindableTextBlock.cs @@ -0,0 +1,31 @@ +using System.Collections.ObjectModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; + +namespace ChatController.Core +{ + public class BindableTextBlock : TextBlock + { + public ObservableCollection InlineList + { + get => (ObservableCollection) GetValue(InlineListProperty); + set => SetValue(InlineListProperty, value); + } + + public static readonly DependencyProperty InlineListProperty = DependencyProperty.Register("InlineList", typeof(ObservableCollection), typeof(BindableTextBlock), new UIPropertyMetadata(null, OnPropertyChanged)); + + public static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + if(e.NewValue == null) + { + return; + } + + var textBlock = (BindableTextBlock)sender; + + textBlock.Inlines.Clear(); + textBlock.Inlines.AddRange((ObservableCollection) e.NewValue); + } + } +} diff --git a/ChatController/Core/OwnChatCache.cs b/ChatController/Core/OwnChatCache.cs index cec0077..5699320 100644 --- a/ChatController/Core/OwnChatCache.cs +++ b/ChatController/Core/OwnChatCache.cs @@ -11,6 +11,8 @@ namespace ChatController.Core // Key-> "group-127" oder "user-938" private Dictionary _ImageSourceCache = new Dictionary(); + private Dictionary _UserProfilePictures = new Dictionary(); + private static OwnChatCache _Instance; private OwnChatCache() { } @@ -96,6 +98,41 @@ namespace ChatController.Core null; } } + + public void AddUserProfilePictureToCache(ImageSource imageSource, long userId, string uri) + { + lock(_Lock) + { + if(!_UserProfilePictures.ContainsKey(userId)) + { + _UserProfilePictures.Add(userId, new ProfilePictureObject(userId, uri, imageSource)); + } + } + } + + public ProfilePictureObject GetUserProfilePictureFromCache(long userId) + { + lock(_Lock) + { + return _UserProfilePictures.ContainsKey(userId) ? _UserProfilePictures[userId] : null; + } + } + } + + public class ProfilePictureObject + { + public long UserId { get; set; } + + public string Uri { get; set; } + + public ImageSource ProfilePicture { get; set; } + + public ProfilePictureObject(long userId, string uri, ImageSource profilePicture) + { + UserId = userId; + Uri = uri; + ProfilePicture = profilePicture; + } } public class ImageSourceCacheStorage diff --git a/ChatController/HauptKlassen/Chat.cs b/ChatController/HauptKlassen/Chat.cs index 888e8e7..0b8ba4e 100644 --- a/ChatController/HauptKlassen/Chat.cs +++ b/ChatController/HauptKlassen/Chat.cs @@ -3,7 +3,6 @@ using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; @@ -21,7 +20,6 @@ using System.Windows.Data; using System.Windows.Forms; using System.Windows.Media; using System.Windows.Media.Imaging; -using System.Windows.Threading; using ChatController.Core; using ChatController.Extensions; using ChatController.LoginKlassen; @@ -45,7 +43,7 @@ namespace ChatController.HauptKlassen private readonly List _Contacts = new List(); - public ChatDatenUebergabe ChatDaten { get; set; } + public static ChatDatenUebergabe ChatDaten { get; set; } private UserMessages _UserMessages; private UserMessages _AdditionalUserMessages; @@ -146,9 +144,6 @@ namespace ChatController.HauptKlassen client.ExecuteAsync(request, response => { - // TODO: Entfernen - Debug.WriteLine(response.Content); - _UserMessages = JsonConvert.DeserializeObject(response.Content); if (_UserMessages.Response.HasMorePages) @@ -297,12 +292,9 @@ namespace ChatController.HauptKlassen client.ExecuteAsync(request, response => { - // TODO: Entfernen - Debug.WriteLine(response.Content); - var messageCounter = JsonConvert.DeserializeObject(response.Content); - callback?.Invoke(messageCounter.MessageCount); + callback?.Invoke(messageCounter?.MessageCount ?? 0); }); } catch(Exception exception) @@ -357,25 +349,12 @@ namespace ChatController.HauptKlassen { var result = new List(); - if(previousMessage != null) - { - result.Add(previousMessage); - } - try { - var username = string.Empty; - - var user = ChatDaten.LoggedInUser.Response.User; - - if(user != null) - { - username = $"{user.Firstname} {user.Lastname}"; - } - var time = DateTime.Now; - result.Add(new ChatMessage(username, pMessage, time, true, pGroupId, 0, 0)); + var chatMessage = new ChatMessage(pGroupId, 0, ChatDaten.LoggedInUser.Response.User.UserName, pMessage, time, true, 0, null, null, null, null, null, ChatMessageType.Message, ChatDaten.LoggedInUser.Response.User.Oid); + result.Add(chatMessage); return AddSeparators(result); } @@ -526,81 +505,75 @@ namespace ChatController.HauptKlassen } } - public List AddNewFile(string pFile, string pOriginalFilePath, long pGroupId, ChatMessage previousMessage) + public List AddNewFile(string pathToFile, string originalFilePath, long groupId, ChatMessage previousMessage) { var result = new List(); - if (previousMessage != null) - { - result.Add(previousMessage); - } - try { - if (string.IsNullOrEmpty(pFile) || string.IsNullOrEmpty(pOriginalFilePath)) + if (string.IsNullOrEmpty(pathToFile) || string.IsNullOrEmpty(originalFilePath)) { return result; } - var message = _UserMessages.Response.Messages.Last(); + var latestMessage = _UserMessages.Response.Messages.Last(); var sendTime = DateTime.Now; - if (Constants.ImageFileExtensions.Contains(Path.GetExtension(pFile).ToUpperInvariant())) + if (Constants.ImageFileExtensions.Contains(Path.GetExtension(pathToFile).ToUpperInvariant())) { byte[] file; - using (Stream reader = File.OpenRead(pFile)) + using (Stream reader = File.OpenRead(pathToFile)) { file = Utils.ReadFully(reader); } - - var memoryStream = new MemoryStream(file); - var image = Image.FromStream(memoryStream); - - using (var bitmap = new Bitmap(image)) + + using(var memoryStream = new MemoryStream(file)) { - using (var stream = new MemoryStream()) + var image = Image.FromStream(memoryStream); + + using (var bitmap = new Bitmap(image)) { - short orient = 0; - const int orientationId = 0x0112; - - if (image.PropertyIdList.Contains(orientationId)) + using (var stream = new MemoryStream()) { - var item = image.GetPropertyItem(orientationId); + short orient = 0; + const int orientationId = 0x0112; - orient = BitConverter.ToInt16(item.Value, 0); + if (image.PropertyIdList.Contains(orientationId)) + { + var item = image.GetPropertyItem(orientationId); + + orient = BitConverter.ToInt16(item.Value, 0); - bitmap.SetPropertyItem(item); + bitmap.SetPropertyItem(item); + } + + var flipType = Utils.OrientationToFlipType(orient.ToString()); + + bitmap.RotateFlip(flipType); + + bitmap.Save(stream, ImageFormat.Jpeg); + + + var bitmapImage = new BitmapImage(); + + bitmapImage.BeginInit(); + bitmapImage.StreamSource = new MemoryStream(stream.ToArray()); + bitmapImage.EndInit(); + bitmapImage.Freeze(); + + var chatMessage = new ChatMessage(groupId, 0, ChatDaten.LoggedInUser.Response.User.UserName, Path.GetFileName(pathToFile), sendTime, true, memoryStream.Length, null, pathToFile, Path.GetFileName(pathToFile), null, bitmapImage, ChatMessageType.Image, ChatDaten.LoggedInUser.Response.User.Oid); + result.Add(chatMessage); } - - var flipType = Utils.OrientationToFlipType(orient.ToString()); - - bitmap.RotateFlip(flipType); - - bitmap.Save(stream, ImageFormat.Jpeg); - - - var bitmapImage = new BitmapImage(); - - bitmapImage.BeginInit(); - bitmapImage.StreamSource = new MemoryStream(stream.ToArray()); - bitmapImage.EndInit(); - bitmapImage.Freeze(); - - var filename = Path.GetFileName(pOriginalFilePath).ToLower(); - - filename = filename.Replace(".bmp", ".jpg"); - - result.Add(new ChatMessage(message.User_Name, filename, sendTime, true, bitmapImage, pFile, filename, pGroupId, file.LongLength, message.Id)); } } - - memoryStream.Dispose(); - memoryStream.Close(); } else { - result.Add(new ChatMessage(message.User_Name, Path.GetFileName(pFile), sendTime, message.Smaller_Image, true, pFile, pGroupId, 0, message.Id)); + var linkToThumbnail = $"{ChatDaten.ServerUrl}/document.png"; + + var chatMessage = new ChatMessage(groupId, 0, ChatDaten.LoggedInUser.Response.User.UserName, Path.GetFileName(pathToFile), sendTime, true, 0, pathToFile, null, null, linkToThumbnail, null, ChatMessageType.Document, ChatDaten.LoggedInUser.Response.User.Oid); + result.Add(chatMessage); } return AddSeparators(result); @@ -613,11 +586,11 @@ namespace ChatController.HauptKlassen return result; } - public List AddMediumContextDerNachrichtView(byte[] pFile, string pFileName, long pGroupId, ChatMessage previousMessage) + public List AddFileToMessage(byte[] file, string fileName, long groupId, ChatMessage previousMessage) { var result = new List(); - if (string.IsNullOrEmpty(pFileName)) + if (string.IsNullOrEmpty(fileName)) { return result; } @@ -631,10 +604,10 @@ namespace ChatController.HauptKlassen var sendtime = DateTime.Now; - if (Constants.ImageFileExtensions.Contains(Path.GetExtension(pFileName).ToUpperInvariant())) + if (Constants.ImageFileExtensions.Contains(Path.GetExtension(fileName).ToUpperInvariant())) { var bitmapImage = new BitmapImage(); - var memoryStream = new MemoryStream(pFile); + var memoryStream = new MemoryStream(file); bitmapImage.BeginInit(); bitmapImage.StreamSource = memoryStream; @@ -644,11 +617,16 @@ namespace ChatController.HauptKlassen memoryStream.Close(); ImageSource logo = bitmapImage; - result.Add(new ChatMessage(message.User_Name, Path.GetFileName(pFileName), sendtime, true, logo, pFileName, Path.GetFileName(pFileName), pGroupId, memoryStream.Length, message.Id)); + // Mit Logo + var chatMessage = new ChatMessage(groupId, 0, ChatDaten.LoggedInUser.Response.User.UserName, Path.GetFileName(fileName), sendtime, true, memoryStream.Length, null, fileName, Path.GetFileName(fileName), null, logo, ChatMessageType.Image, ChatDaten.LoggedInUser.Response.User.Oid); + + result.Add(chatMessage); } else { - result.Add(new ChatMessage(message.User_Name, Path.GetFileName(pFileName), sendtime, message.Smaller_Image, true, pFileName, pGroupId, 0, message.Id)); + // Dokument + var chatMessage = new ChatMessage(groupId, 0, ChatDaten.LoggedInUser.Response.User.UserName, Path.GetFileName(fileName), sendtime, true, 0, fileName, null, null, message.Smaller_Image, null, ChatMessageType.Document, ChatDaten.LoggedInUser.Response.User.Oid); + result.Add(chatMessage); } return AddSeparators(result); @@ -847,7 +825,7 @@ namespace ChatController.HauptKlassen var mediaFile = File.ReadAllBytes(fileList[0]); - pChatMainControl.AddMessages(AddMediumContextDerNachrichtView(mediaFile, fileName, pGroupOid, pChatMainControl.GetFirstMessage())); + pChatMainControl.AddMessages(AddFileToMessage(mediaFile, fileName, pGroupOid, pChatMainControl.GetFirstMessage())); CollectionViewSource.GetDefaultView(pChatMainControl.ChatListBox.ItemsSource).Refresh(); @@ -886,7 +864,7 @@ namespace ChatController.HauptKlassen Image image = bitmap; - pChatMainControl.AddMessages(AddMediumContextDerNachrichtView(Utils.ImageToByteArray(image), imageName, pGroupOid, pChatMainControl.GetFirstMessage())); + pChatMainControl.AddMessages(AddFileToMessage(Utils.ImageToByteArray(image), imageName, pGroupOid, pChatMainControl.GetFirstMessage())); CollectionViewSource.GetDefaultView(pChatMainControl.ChatListBox.ItemsSource).Refresh(); @@ -1095,6 +1073,8 @@ namespace ChatController.HauptKlassen var key = $"group-{groupInput.Oid}"; + + contacts.Add(new Contact( groupInput.Name, groupInput.LastMessage, @@ -1106,7 +1086,7 @@ namespace ChatController.HauptKlassen groupInput.CanWrite, groupInput.AccentColor, groupInput.Avatar, - key)); + key, groupInput.Users.ToDictionary(user => user.Oid, user => user.Picture))); if(pShouldUpdateLastTimeStamp) { @@ -1129,11 +1109,6 @@ namespace ChatController.HauptKlassen { var result = new List(); - if(previousMessage != null) - { - result.Add(previousMessage); - } - foreach (var message in pMessages) { var time = DateTime.Parse(message.Timestamp.Date); @@ -1145,16 +1120,16 @@ namespace ChatController.HauptKlassen if (!string.IsNullOrEmpty(message.File)) { var message2Add = Constants.ImageFileExtensions.Contains(Path.GetExtension(message.File).ToUpperInvariant()) ? //SmallerImage ist null - new ChatMessage(message.User_Name, messageText, formattedTime, isLoggedInUsersMessage, message.Smaller_Image, message.File, message.Original_Filename, message.GroupId, message.FileSize, message.Id) : - new ChatMessage(message.User_Name, messageText, formattedTime, message.Smaller_Image, isLoggedInUsersMessage, message.File, message.GroupId, message.FileSize, message.Id); + new ChatMessage(message.GroupId, message.Id, message.User_Name, messageText, formattedTime, isLoggedInUsersMessage, message.FileSize, message.Smaller_Image, message.File, message.Original_Filename, null, null, ChatMessageType.Image, message.UserId) : + new ChatMessage(message.GroupId, message.Id, message.User_Name, messageText, formattedTime, isLoggedInUsersMessage, 0, message.File, null, null, message.Smaller_Image, null, ChatMessageType.Document, message.UserId); result.AddIfNotIn(message2Add); } else { - var message2Add2 = new ChatMessage(message.User_Name, message.Text, formattedTime, isLoggedInUsersMessage, message.GroupId, message.FileSize, message.Id); - - result.AddIfNotIn(message2Add2); + var chatMessage = new ChatMessage(message.GroupId, message.Id, message.User_Name, message.Text, formattedTime, isLoggedInUsersMessage, message.FileSize, null, null, null, null, null, ChatMessageType.Message, message.UserId); + + result.AddIfNotIn(chatMessage); } } @@ -1174,7 +1149,7 @@ namespace ChatController.HauptKlassen { if(message.SendTime.Date != previousMessage.SendTime.Date) { - var separatorMessage = new ChatMessage(null, null, message.SendTime.Date, false, 0, 0, -1) { IsSeparator = true }; + var separatorMessage = new ChatMessage(0, -1, null, null, message.SendTime.Date, false, 0, null, null, null, null, null, ChatMessageType.Separator, 0); separators.Add(separatorMessage); diff --git a/ChatController/LoginControl.xaml b/ChatController/LoginControl.xaml index 3fda31a..49a8ad7 100644 --- a/ChatController/LoginControl.xaml +++ b/ChatController/LoginControl.xaml @@ -4,6 +4,9 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d"> + + + @@ -48,12 +51,30 @@ diff --git a/ChatController/LoginControl.xaml.cs b/ChatController/LoginControl.xaml.cs index 6cfae31..2342200 100644 --- a/ChatController/LoginControl.xaml.cs +++ b/ChatController/LoginControl.xaml.cs @@ -92,10 +92,20 @@ namespace ChatController LeseDateiFallsVorhanden(); #if DEBUG + //_Kundennummer = "4368658436"; + //_ChatCode = "ZMCKb-BckTt"; + //_Benutzername = "muellerp"; + //Password = "F5aTk9Co47JFJCWB2Z7Y"; + + //_Kundennummer = "9988776655"; + //_ChatCode = "K2UPV-qrirb"; + //_Benutzername = "lyndon"; + //Password = "lyndon2travemünde"; + _Kundennummer = "4368658436"; - _ChatCode = "ZMCKb-BckTt"; - _Benutzername = "muellerp"; - Password = "F5aTk9Co47JFJCWB2Z7Y"; + _ChatCode = "B7x0b-FhhJA"; + _Benutzername = "jettenl"; + Password = "n8-yR+3Q=6nX#"; #endif } diff --git a/ChatController/LoginKlassen/UserDaten.cs b/ChatController/LoginKlassen/UserDaten.cs index 5ac46ef..1c41b9e 100644 --- a/ChatController/LoginKlassen/UserDaten.cs +++ b/ChatController/LoginKlassen/UserDaten.cs @@ -53,5 +53,25 @@ namespace ChatController.LoginKlassen public bool IsEmployee { get; set; } public string Token { get; set; } + + public string UserName + { + get + { + var result = string.Empty; + + if(!string.IsNullOrWhiteSpace(Firstname)) + { + result = $"{Firstname} "; + } + + if(!string.IsNullOrWhiteSpace(Lastname)) + { + result += Lastname; + } + + return result; + } + } } } diff --git a/ChatController/Utilities/Utils.cs b/ChatController/Utilities/Utils.cs index 4519508..08b1f7c 100644 --- a/ChatController/Utilities/Utils.cs +++ b/ChatController/Utilities/Utils.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.IO; @@ -7,7 +9,9 @@ using System.Linq; using System.Net; using System.Reflection; using System.Runtime.InteropServices; +using System.Text.RegularExpressions; using System.Windows; +using System.Windows.Documents; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; @@ -38,9 +42,16 @@ namespace ChatController.Utilities return; } - var url = pathToProfilePicture; - - var client = new RestClient(url); + DownloadImage(pathToProfilePicture, imageSource => + { + cache.AddProfilePictureToCache(key, imageSource, pathToProfilePicture); + callback?.Invoke(imageSource); + }); + } + + private static void DownloadImage(string uri, Action callback) + { + var client = new RestClient(uri); var request = new RestRequest { ResponseWriter = stream => @@ -58,8 +69,6 @@ namespace ChatController.Utilities bitmap.EndInit(); bitmap.Freeze(); - cache.AddProfilePictureToCache(key, bitmap, pathToProfilePicture); - callback?.Invoke(bitmap); } }; @@ -70,6 +79,33 @@ namespace ChatController.Utilities client.ExecuteAsync(request, response => { /*Das übernimmt der ResponseWriter von oben*/ }); } + public static void DownloadUserProfilePictures(Dictionary userId2Uris) + { + var cache = OwnChatCache.GetInstance(); + + foreach(var id2Uri in userId2Uris) + { + var userProfilePicture = cache.GetUserProfilePictureFromCache(id2Uri.Key); + + if(userProfilePicture == null) + { + try + { + var uri = id2Uri.Value; + + DownloadImage(uri, profilePicture => + { + cache.AddUserProfilePictureToCache(profilePicture, id2Uri.Key, uri); + }); + } + catch(Exception) + { + + } + } + } + } + public static void DownloadImageAsync(string uri, string key, CacheCategory cacheCategory, Action callback) { if(string.IsNullOrEmpty(uri)) @@ -88,35 +124,11 @@ namespace ChatController.Utilities return; } - var url = uri; - var client = new RestClient(url); - var request = new RestRequest + DownloadImage(uri, imageSource => { - ResponseWriter = stream => - { - var bitmap = new BitmapImage(); - - var localStream = new MemoryStream(); - - stream.CopyTo(localStream); - - localStream.Position = 0; - - bitmap.BeginInit(); - bitmap.StreamSource = localStream; - bitmap.EndInit(); - bitmap.Freeze(); - - cache.AddImageToCache(key, bitmap, cacheCategory, uri); - - callback?.Invoke(bitmap); - } - }; - - request.AddHeader(Constants.Token, AuthToken); - request.AddHeader(Constants.CustomerId, Tenant); - - client.ExecuteAsync(request, response => { }); + cache.AddImageToCache(key, imageSource, cacheCategory, uri); + callback?.Invoke(imageSource); + }); } private static ImageSource GetDefaultImageSource(bool pIsGroup, bool pIsEmployee) @@ -438,5 +450,82 @@ namespace ChatController.Utilities return 100; } + + private static readonly Regex _UrlRegex = new Regex(@"(?#Protocol)(http(?:s?)\:(\/\/|\\\\)|(w){3}(2|3)?\.{1})(?#Subdomains)(?:(?:[-\w]+\.)+(?#TopLevel Domains)(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))(?#Port)(?::[\d]{1,5})?(?#Directories)(?:(?:(?:/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|/)+|\?|#)?(?#Query)(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?#Anchor)(?:#(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)?", RegexOptions.Compiled | RegexOptions.IgnoreCase); + + public static ObservableCollection ConvertLinksToHyperlinks(string messageText) + { + var result = new ObservableCollection(); + + if(messageText == null) + { + return result; + } + + var guidString = Guid.NewGuid().ToString(); + var replacementString = Guid.NewGuid().ToString(); + + var matches = _UrlRegex.Matches(messageText); + + var index2Link = new Dictionary(); + + for(var i = 0; i < matches.Count; i++) + { + var link = matches[i].Value; + + if(!link.Contains("http")) + { + link = $"http://{link}"; + } + + var hyperlink = new Hyperlink(new Run(link)) + { + NavigateUri = new Uri(link, UriKind.Absolute) + }; + + hyperlink.RequestNavigate += (s, e) => + { + Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)); + e.Handled = true; + }; + + index2Link[i] = hyperlink; + } + + var splitMessage = _UrlRegex.Replace(messageText, replacementString + guidString).Split(new[] {guidString}, StringSplitOptions.None); + + var linkIndex = 0; + foreach(var text in splitMessage) + { + if(text.Equals(replacementString)) + { + result.Add(index2Link[linkIndex]); + linkIndex++; + } + else if(text.StartsWith(replacementString) || text.EndsWith(replacementString)) + { + var splitLine = text.Split(new[] { replacementString }, StringSplitOptions.None); + + foreach(var partLine in splitLine) + { + if(string.Empty.Equals(partLine)) + { + result.Add(index2Link[linkIndex]); + linkIndex++; + } + else + { + result.Add(new Run(partLine)); + } + } + } + else + { + result.Add(new Run(text)); + } + } + + return result; + } } } diff --git a/PrototypChat/.vs/PrototypChat/v16/.suo b/PrototypChat/.vs/PrototypChat/v16/.suo index 06aa56a3815e09d2f3d0464b9d523df4ccdafcfc..e1984218a243ca5b13bf2db1937308b2f5701833 100644 GIT binary patch delta 12841 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 delta 8862 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