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 06aa56a..e198421 100644
Binary files a/PrototypChat/.vs/PrototypChat/v16/.suo and b/PrototypChat/.vs/PrototypChat/v16/.suo differ