using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Forms; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Threading; using ChatController.Annotations; using ChatController.ChatKlassen; using ChatController.Extensions; using ChatController.HauptKlassen; using ChatController.LoginKlassen; using ChatController.Utilities; using ChatController.Utilities.Extensions; using Application = System.Windows.Forms.Application; using Button = System.Windows.Controls.Button; using CheckBox = System.Windows.Controls.CheckBox; using Cursors = System.Windows.Input.Cursors; using DataFormats = System.Windows.DataFormats; using KeyEventArgs = System.Windows.Input.KeyEventArgs; using MenuItem = System.Windows.Controls.MenuItem; using MessageBox = System.Windows.MessageBox; using Orientation = System.Windows.Controls.Orientation; using Panel = System.Windows.Controls.Panel; using Path = System.IO.Path; using ScrollBar = System.Windows.Controls.Primitives.ScrollBar; using Timer = System.Windows.Forms.Timer; namespace ChatController { // Test public partial class ChatMainControl : INotifyPropertyChanged { private readonly Dictionary> _Group2NotifyIcons = new Dictionary>(); private readonly Dictionary _GroupId2DateTime = new Dictionary(); private string _ThreadExceptionMessage; public string ThreadExceptionMessage { get => _ThreadExceptionMessage; set { if(!Equals(_ThreadExceptionMessage, value)) { _ThreadExceptionMessage = value; OnPropertyChanged(nameof(ThreadExceptionMessage)); OnPropertyChanged(nameof(CurrentContactInformationString)); OnPropertyChanged(nameof(CurrencContactInfoForeground)); OnPropertyChanged(nameof(ThreadExceptionImageSource)); } } } private readonly ImageSource _ThreadExceptionImageSource; public ImageSource ThreadExceptionImageSource => !(ThreadExceptionMessage is null) ? _ThreadExceptionImageSource : CurrentContact?.Image; public SolidColorBrush CurrencContactInfoForeground => !(ThreadExceptionMessage is null) ? new SolidColorBrush(Color.FromRgb(185, 65, 0)) : CurrentContact?.AccentColorBrush ?? new SolidColorBrush(Colors.Transparent); public string CurrentContactInformationString => ThreadExceptionMessage ?? CurrentContact?.Name; private Window _ContainingWindow; public Window ContainingWindow { get => _ContainingWindow; set { _ContainingWindow = value; if(!(value is null)) { _ContainingWindow.Deactivated += ContainingWindowOnDeactivated; _ContainingWindow.Activated += ContainingWindowOnActivated; } } } private Chat _Chat; public Chat Chat { get => _Chat; set { _Chat = value; OnPropertyChanged(nameof(Chat)); OnPropertyChanged(nameof(ChatMessageInputGridVisibility)); OnPropertyChanged(nameof(ChatListBoxVisibility)); OnPropertyChanged(nameof(IsBroadcastButtonEnabled)); OnPropertyChanged(nameof(StapelmodusChatGridVisibility)); } } private Contact _CurrentContact; public Contact CurrentContact { get => _CurrentContact; set { if(!Equals(_CurrentContact, value)) { _CurrentContact = value; OnPropertyChanged(nameof(CurrentContact)); OnPropertyChanged(nameof(ChatMessageInputGridVisibility)); OnPropertyChanged(nameof(Chat)); OnPropertyChanged(nameof(CurrentContactInformationString)); OnPropertyChanged(nameof(CurrencContactInfoForeground)); OnPropertyChanged(nameof(ThreadExceptionImageSource)); } } } public ObservableSortCollection ContactList { get => _ContactList ?? (_ContactList = new ObservableSortCollection()); set { _ContactList = new ObservableSortCollection(value); _ContactList.Sort((x, y) => DateTime.Compare(y.Contact.TimeStamp, x.Contact.TimeStamp)); OnPropertyChanged(nameof(ContactList)); } } public Visibility ChatMessageInputGridVisibility { get { if(_CurrentContact is null && Chat?.IsInBroadcastMode == false) { return Visibility.Collapsed; } return false == (Chat?.IsInBroadcastMode ?? false) && !(_CurrentContact is null) ? _CurrentContact.IsChatMessageInputGridVisible ? Visibility.Visible : Visibility.Collapsed : Chat?.IsInBroadcastMode ?? false ? Visibility.Visible : Visibility.Collapsed; } } private ObservableSortCollection _ContactList; private bool _ScrollPrueferAktivieren; public bool ShouldInterruptContactsThread = true; private readonly List _CreatedTempFiles = new List(); private readonly Dictionary> _MenuItemName2Callback = new Dictionary>(); private bool _IsDesktopVersion = true; private bool _IsInBackground; public ChatMainControl() { InitializeComponent(); _ThreadExceptionImageSource = new BitmapImage(new Uri("pack://application:,,,/ChatController;component/Ressourcen/warning-exclamation-mark.png", UriKind.Absolute)); BroadcastButtonImageSource = new BitmapImage(new Uri("pack://application:,,,/ChatController;component/Ressourcen/tasks-solid.png", UriKind.Absolute)); InvertedBroadcastButtonImageSource = new BitmapImage(new Uri("pack://application:,,,/ChatController;component/Ressourcen/tasks-solid-inverted.png", UriKind.Absolute)); _ChatMessages = new ObservableCollection(); ChatMessages = CollectionViewSource.GetDefaultView(_ChatMessages) as ListCollectionView; if(!(ChatMessages is null)) { ChatMessages.CustomSort = new ChatMessageComparer(); } DataContext = this; ProgressOverlayVisibility = Visibility.Collapsed; StartWaitingImmediately(); var deleteImagesTask = new Task(() => { try { var files = Directory.GetFiles(Path.GetTempPath(), "*_tmp_img_owch.*", SearchOption.TopDirectoryOnly); foreach(var file in files) { if(File.Exists(file)) { try { File.Delete(file); } catch(IOException) { } } } } finally { this.Dispatch(() => { EndWaiting(); }); } }); deleteImagesTask.Start(); } private void ContainingWindowOnDeactivated(object sender, EventArgs e) { _IsInBackground = true; } private void ContainingWindowOnActivated(object sender, EventArgs e) { _IsInBackground = false; } private readonly ObservableCollection _ChatMessages; public ListCollectionView ChatMessages { get; set; } public void InitMitChatdaten(ChatDatenUebergabe cdu) { Chat = new Chat(cdu, exception => { this.Dispatch( () => { EndWaiting(); MessageBox.Show($"Fehler: {exception.Message}", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); }); }, exception => { this.Dispatch( () => { if(exception is null) { ThreadExceptionMessage = null; } ThreadExceptionMessage = exception?.Message?.Contains("500") ?? false ? "ownChat ist vorrübergehend nicht verfügbar. Bitte versuchen Sie es später noch einmal." : null; }); }); OnPropertyChanged(nameof(Chat)); _ContactList = new ObservableSortCollection(); // ToDo: Hier tritt eine NullPointer-Exception auf! foreach(var contact in Chat.AddContacts()) { ContactList.Add(new ContactDependencyObject(contact)); } ContactList.Sort((x, y) => DateTime.Compare(y.Contact.TimeStamp, x.Contact.TimeStamp)); OnPropertyChanged(nameof(ContactList)); ReadNewSyncFile(); foreach(var contactDependencyObject in _ContactList) { if(_GroupId2DateTime.ContainsKey(contactDependencyObject.Contact.GroupId)) { var isUnread = _GroupId2DateTime[contactDependencyObject.Contact.GroupId].IsUnread; contactDependencyObject.Contact.IsNewMessage = isUnread; contactDependencyObject.Contact.HasUnreadMessages = contactDependencyObject.Contact.IsNewMessage; } } GlobalListeningThreadtimer?.Stop(); GlobalListeningThreadtimer = null; InitGlobalListeningThread(); Clientlist.Items.Refresh(); } public void ShowNotification(string pTitle, GroupLatestMessage pMessage, int pGroupId) { var notificationIcon = Resource.ownchat_favicon; var contact = _ContactList.FirstOrDefault(a => a.Contact.GroupId == pGroupId); if (!(contact is null)) { notificationIcon = Utils.ImageSourceToIcon(contact.Contact.Image); } var notifyIcon = new NotifyIcon { Icon = notificationIcon, Visible = true, Tag = pMessage }; notifyIcon.BalloonTipClicked += (sender, args) => { DisposeAndRemoveNotification((NotifyIcon)sender, pGroupId); if(_ContainingWindow is null) { return; } if (_ContainingWindow.WindowState == WindowState.Minimized) { _ContainingWindow.WindowState = WindowState.Normal; } _ContainingWindow.Activate(); var selectedContact = _ContactList?.FirstOrDefault(f => f?.Contact.GroupId == pGroupId); if(!(selectedContact is null)) { SelectContact(selectedContact.Contact); } }; notifyIcon.BalloonTipClosed += (sender, args) => { // TODO: wird beim ausblenden bzw. automatischem Schließen des "Balloons" ausgelöst DisposeAndRemoveNotification((NotifyIcon)sender, pGroupId); }; _Group2NotifyIcons.AddOrUpdateValueInDictionary(pGroupId, notifyIcon); _Group2NotifyIcons[pGroupId].Find(f => f.Tag is GroupLatestMessage message && message.Id.Equals(pMessage.Id))?.ShowBalloonTip(Constants.NotificationTimeout, pTitle, "Sie haben eine neue Nachricht", ToolTipIcon.None); } public void ClearNotifications() { foreach(var group2NotifyIcon in _Group2NotifyIcons) { foreach(var notifyIcon in group2NotifyIcon.Value) { notifyIcon.Icon = null; notifyIcon.Dispose(); Application.DoEvents(); } } _Group2NotifyIcons.Clear(); } private void DisposeAndRemoveNotification(IDisposable pNotification, long pGroupId) { pNotification.Dispose(); _Group2NotifyIcons.Remove(pGroupId); Application.DoEvents(); } private void CurrentContactImage_OnMouseDoubleClick(object sender, MouseButtonEventArgs e) { if(e.ClickCount == 2) { Chat.ShowProfilePicture(_CurrentContact); } } private void Clientlist_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if(Chat.IsInBroadcastMode) { return; } if(Clientlist?.SelectedItem is ContactDependencyObject selectedContactDependencyObject) { SelectContact(selectedContactDependencyObject.Contact); } } private void SelectContact(Contact contact) { try { if(contact is null) { return; } ChatMessageFileWrapper = null; ShouldInterruptContactsThread = true; StartWaitingImmediately(); _ScrollPrueferAktivieren = false; if(contact.HasUnreadMessages) { contact.HasUnreadMessages = false; Clientlist.Items.Refresh(); OnPropertyChanged(nameof(ContactList)); } var currentContact = contact; contact.IsNewMessage = false; var shouldChangeIcon = _ContactList.Any(contactDependencyObject => contactDependencyObject.Contact.IsNewMessage); if(shouldChangeIcon) { ContainingWindow.Icon = Utils.ConvertIconToImageSource(Resource.ownchat_favicon); } CurrentContact = currentContact; OnPropertyChanged(nameof(IsSendButtonEnabled)); foreach(ContactDependencyObject item in Clientlist.Items) { if(item.Contact.Equals(contact)) { Clientlist.SelectedItem = item; } } _ChatMessages.Clear(); OnPropertyChanged(nameof(_ChatMessages)); Chat.LoadChatMessagesForContactAsync(currentContact, GetFirstMessage(), chatMessages => { this.Dispatch(() => { _ChatMessages.Clear(); _ChatMessages.AddRangeIfElementsNotIn(chatMessages); OnPropertyChanged(nameof(_ChatMessages)); WpfUtils.ScrollToBottomOfListBox(ChatListBox); ChatListBox.ContextMenu = Chat.ErstelleKontextMenue(); SetContextHandler(); _ScrollPrueferAktivieren = true; ListenForMessages(); Cursor = Cursors.Arrow; WriteToNewSyncFile(); ShouldInterruptContactsThread = false; EndWaiting(); }); }); } catch(Exception exception) { #if DEBUG var desktop = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); using(var fileStream = new FileStream(Path.Combine(desktop, "ownchat_log.txt"), FileMode.OpenOrCreate)) { using(var streamWriter = new StreamWriter(fileStream)) { streamWriter.WriteLine(exception.ToString()); } } #endif MessageBox.Show(exception.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } } private void SetContextHandler() { var contextItems = ChatListBox?.ContextMenu?.Items; if(contextItems is null) { return; } foreach(var menuItemText in _MenuItemName2Callback.Keys) { var callback = _MenuItemName2Callback[menuItemText]; var menuItem = new MenuItem(); menuItem.Click += (s, e2) => { callback(menuItemText); }; menuItem.Header = menuItemText; contextItems.Add(menuItem); } if(contextItems.Count > 0) { var saveAsMenuItem = (MenuItem) contextItems[0]; saveAsMenuItem.Click += SaveAsOnClick; } if(contextItems.Count > 1) { var pasteMenuItem = (MenuItem) contextItems[1]; pasteMenuItem.Click += PasteOnClick; } // neu und vor Einfügen setzen und anpassen if(contextItems.Count > 2) { var copyMenuItem = (MenuItem) contextItems[2]; copyMenuItem.Click += CopyOnClick; } } private void SaveAsOnClick(object sender, RoutedEventArgs routedEventArgs) { foreach (var items in ChatListBox.SelectedItems) { var chatMessage = (ChatMessage) items; Chat.SaveFileAs(chatMessage); } } private async void PasteOnClick(object sender, RoutedEventArgs routedEventArgs) { await Chat.Paste(CurrentContact.GroupId,this); } private void CopyOnClick(object sender, RoutedEventArgs routedEventArgs) { var textToCopy = string.Empty; foreach (var items in ChatListBox.SelectedItems) { var item = (ChatMessage) items; textToCopy += item.UserMessage + Environment.NewLine; } if(!string.IsNullOrWhiteSpace(textToCopy)) { Chat.Copy(textToCopy, 1); } } private void Chat_OnContextMenuOpening(object sender, ContextMenuEventArgs e) { if(ChatListBox?.ContextMenu is null) { return; } if(ChatListBox.SelectedItems is List chatMessages) { if(chatMessages.Count > 0) { foreach(var chatMessage in chatMessages) { if(!(chatMessage?.PictureSource is null)) { if(!(ChatListBox.ContextMenu is null)) { var contextItems = ChatListBox.ContextMenu.Items; ((MenuItem)contextItems[0]).Visibility = Visibility.Visible; } } else { var contextItems = ChatListBox.ContextMenu.Items; var contextItemSpeichernUnter = (MenuItem)contextItems[0]; contextItemSpeichernUnter.Visibility = Visibility.Collapsed; } if(chatMessage?.PictureSource is null && chatMessage?.FilePath is null) { var contextItems = ChatListBox.ContextMenu.Items; var contextItemSpeichernUnter = (MenuItem)contextItems[2]; contextItemSpeichernUnter.Visibility = Visibility.Visible; } else { var contextItems = ChatListBox.ContextMenu.Items; var contextItemSpeichernUnter = (MenuItem)contextItems[2]; contextItemSpeichernUnter.Visibility = Visibility.Collapsed; } } } else { //Schalte speichern unter Aus var contextItems = ChatListBox.ContextMenu.Items; var contextItemSpeichernUnter = (MenuItem)contextItems[0]; contextItemSpeichernUnter.Visibility = Visibility.Collapsed; } //prüfe ob was in dem Speicher vorhanden ist //Einfügen var dataObject = System.Windows.Forms.Clipboard.GetDataObject(); if(!(dataObject is null) && dataObject.GetDataPresent(DataFormats.FileDrop)) { var contextItems = ChatListBox.ContextMenu.Items; var contextItemSpeichernUnter = (MenuItem) contextItems[1]; contextItemSpeichernUnter.Visibility = Visibility.Visible; } else if(!(dataObject is null) && dataObject.GetDataPresent(DataFormats.Text)) { var contextItems = ChatListBox.ContextMenu.Items; var contextItemSpeichernUnter = (MenuItem) contextItems[1]; contextItemSpeichernUnter.Visibility = Visibility.Visible; } else if(!(dataObject is null) && dataObject.GetDataPresent(DataFormats.Bitmap)) { var contextItems = ChatListBox.ContextMenu.Items; var contextItemSpeichernUnter = (MenuItem) contextItems[1]; contextItemSpeichernUnter.Visibility = Visibility.Visible; } else { var contextItems = ChatListBox.ContextMenu.Items; var contextItemSpeichernUnter = (MenuItem)contextItems[1]; contextItemSpeichernUnter.Visibility = Visibility.Collapsed; } // Prüfe, ob Dokumentation erlaubt if(CurrentContact.UserIdManage is null && ChatListBox.ContextMenu.Items.Count > 3) { var contextItems = ChatListBox.ContextMenu.Items; 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; } if(ChatListBox.ContextMenu.Items.Count == 0) { ChatListBox.ContextMenu.Visibility = Visibility.Collapsed; } } } private void MediaButton_OnClick(object sender, RoutedEventArgs e) { if(!(CurrentContact is null)) { var pathToOriginalImage = Chat.OpenFile(); if(File.Exists(pathToOriginalImage)) { StartWaitingImmediately("Skaliere Bild ..."); var scalingTask = new Task(() => { try { var pathToScaledImage = FileUtils.ScaleImage(pathToOriginalImage, Path.GetExtension(pathToOriginalImage.ToUpperInvariant()), Chat.ChatDaten.MaxUploadSize); var isFileSizeTooLarge = FileUtils.CheckFileSize(pathToScaledImage, Chat.ChatDaten.MaxUploadSize); if(isFileSizeTooLarge) { this.Dispatch(() => { EndWaiting(); MessageBox.Show($"Die ausgewählte Datei ist zu groß. Die maximale Größe beträgt {Chat.ChatDaten.MaxUploadSize / 1000 / 1000} MB", "Senden nicht möglich", MessageBoxButton.OK, MessageBoxImage.Warning); }); return; } if(File.Exists(pathToScaledImage)) { ChatMessageFileWrapper = new ChatMessageFileWrapper(pathToOriginalImage, pathToScaledImage, $"{Chat.ChatDaten.ServerUrl}/document.png"); } } finally { this.Dispatch(EndWaiting); } }); scalingTask.Start(); } } else { MessageBox.Show("Bitte wählen Sie einen Kontakt aus.","ownChat",MessageBoxButton.OK, MessageBoxImage.Information); } } private async Task SendMessage() { if(CurrentContact is null || (string.IsNullOrEmpty(Chatbox.Text) && string.IsNullOrWhiteSpace(ChatMessageFileWrapper?.ScaledFilePath))) { return; } var isFileInMessage = !(ChatMessageFileWrapper is null) && File.Exists(ChatMessageFileWrapper.ScaledFilePath); var messageText = Chatbox.Text; var newMessages = isFileInMessage ? Chat.AddNewFile(ChatMessageFileWrapper.ScaledFilePath, ChatMessageFileWrapper.OriginalFilePath, CurrentContact.GroupId, GetFirstMessage(), messageText) : Chat.AddNewMessage(Chatbox.Text, CurrentContact.GroupId, GetFirstMessage()); _ChatMessages.AddRangeIfElementsNotIn(newMessages); OnPropertyChanged(nameof(ChatMessages)); ChatMessages.MoveCurrentToLast(); ChatListBox.ScrollIntoView(ChatMessages.CurrentItem); WpfUtils.ScrollToBottomOfListBox(ChatListBox); if(isFileInMessage) { await Chat.SendFileToContact(CurrentContact, ChatMessageFileWrapper.ScaledFilePath, ChatMessageFileWrapper.OriginalFilePath, Chatbox.Text); } else { await Chat.SendMessage(Chatbox.Text, CurrentContact.GroupId); } ChatMessageFileWrapper = null; Chatbox.Text = string.Empty; //DoSynchro(); } public Visibility ChatListBoxVisibility => (Chat?.IsInBroadcastMode ?? false) || !(ChatMessageFileWrapper is null) ? Visibility.Collapsed : Visibility.Visible; public Visibility FilePreviewGridVisibility => string.IsNullOrWhiteSpace(ChatMessageFileWrapper?.OriginalFilePath) ? Visibility.Collapsed : Visibility.Visible; private ChatMessageFileWrapper _ChatMessageFileWrapper; public ChatMessageFileWrapper ChatMessageFileWrapper { get => _ChatMessageFileWrapper; set { _ChatMessageFileWrapper = value; OnPropertyChanged(nameof(ChatMessageFileWrapper)); OnPropertyChanged(nameof(IsSendButtonEnabled)); OnPropertyChanged(nameof(FilePreviewGridVisibility)); OnPropertyChanged(nameof(ChatListBoxVisibility)); } } private async void SendButton_OnClick(object sender, RoutedEventArgs e) { if(Chat.IsInBroadcastMode) { SendBroadcastMessage(); return; } await SendMessage(); } private void Chatbox_OnGotFocus(object sender, RoutedEventArgs e) { if (Chatbox.Text.Equals("Nachricht schreiben")) { Chatbox.Text = string.Empty; Chatbox.Foreground = new SolidColorBrush(Colors.Black); } } private void Chatbox_OnKeyDownHandler(object sender, KeyEventArgs e) { try { //if (e.Key == Key.Return) //{ // if(CurrentContact != null && !string.IsNullOrEmpty(Chatbox.Text)) // { // Chat.AddNewMessage(Chatbox.Text, CurrentContact.GroupId); // OnPropertyChanged(nameof(CurrentChatMessages)); // ChatListBox.Items.MoveCurrentToLast(); // ChatListBox.ScrollIntoView(ChatListBox.Items.CurrentItem); // Chat.SendMessage(Chatbox.Text, CurrentContact.GroupId); // Chatbox.Text = string.Empty; // } //} OnPropertyChanged(nameof(IsBroadcastButtonEnabled)); OnPropertyChanged(nameof(IsSendButtonEnabled)); } catch (Exception ed) { MessageBox.Show("Beim Senden einer Nachricht ist ein Fehler aufgetreten. " + ed.Message + "\n" + ed.StackTrace, "Fehler", MessageBoxButton.OK); } } private void Chatbox_OnKeyUpHandler(object sender, KeyEventArgs e) { OnPropertyChanged(nameof(IsBroadcastButtonEnabled)); OnPropertyChanged(nameof(IsSendButtonEnabled)); } public delegate void EmojiiDelegate(Button button); public event EmojiiDelegate OnEmojii; private void EmojiButton_OnClick(object sender, RoutedEventArgs e) { OnEmojii?.Invoke(EmojiButton); } private void Chat_OnScrollChanged(object sender, ScrollChangedEventArgs e) { var scrollBarList = Utils.GetVisualChildCollection(ChatListBox); foreach (var scrollBar in scrollBarList) { if (scrollBar.Orientation == Orientation.Horizontal) { _ScrollPrueferAktivieren = true; } else { scrollBar.ValueChanged += VerticalScrollbarChanged; } } } private void VerticalScrollbarChanged(object sender, RoutedPropertyChangedEventArgs routedPropertyChangedEventArgs) { if (_ScrollPrueferAktivieren) { var scrollBar = (ScrollBar) sender; if(!(scrollBar.Value > 0)) { StartWaitingImmediately(); _ScrollPrueferAktivieren = false; ChatMessages.MoveCurrentToFirst(); var currentChatMessage = ChatMessages.CurrentItem; if(!(Clientlist.SelectedItem is null)) { var selectedContact = ((ContactDependencyObject) Clientlist.SelectedItem).Contact; Chat.LoadMoreMessagesAsync(selectedContact, GetFirstMessage(), chatMessages => { this.Dispatch(() => { EndWaiting(); _ChatMessages.AddRangeIfElementsNotIn(chatMessages); ChatListBox.ScrollIntoView(currentChatMessage); }); }); } } } } private void ListenForMessages() { EmojisListeningThreadTimer?.Stop(); EmojisListeningThreadTimer = null; InitListeningThread(CurrentContact); } public Timer EmojisListeningThreadTimer; public Timer GlobalListeningThreadtimer { get; set; } private void InitGlobalListeningThread() { GlobalListeningThreadtimer = new Timer(); GlobalListeningThreadtimer.Tick += (s, e) => { ListenGloballyForMessages(); }; GlobalListeningThreadtimer.Interval = 10000; GlobalListeningThreadtimer.Start(); } private void ListenGloballyForMessages() { Chat.GetNumberOfAllNewMessagesAsync(newMessagesCount => { if(newMessagesCount > 0) { Chat.UpdateContactList(updatedContactList => { var updatedContacts = updatedContactList.ToList(); this.Dispatch(() => { foreach (var contactDependencyObject in _ContactList) { foreach (var newContact in updatedContacts) { if (contactDependencyObject.Contact.GroupId == newContact.GroupId) { // ReceivedMessage kann null sein! if (!(contactDependencyObject.Contact.ReceivedMessage is null) && !contactDependencyObject.Contact.ReceivedMessage.Id.Equals(newContact.ReceivedMessage?.Id)) { contactDependencyObject.Contact.ReceivedMessage = newContact.ReceivedMessage; contactDependencyObject.Contact.TimeStamp = newContact.TimeStamp; Contact selectedContact = null; if(Clientlist.SelectedItem is ContactDependencyObject selectedContactDependencyObject) { selectedContact = selectedContactDependencyObject.Contact; } // Angemeldeter Benutzer var userOid = Chat.ChatDaten.LoggedInUser.Response.User.Oid; var senderId = contactDependencyObject.Contact.ReceivedMessage?.UserId; var receiverId = userOid; var receiverGroupId = newContact.GroupId; var selectedGroupId = selectedContact?.GroupId; if (senderId != receiverId && (_IsInBackground || receiverGroupId != selectedGroupId)) { if (!(ContainingWindow is null)) { ContainingWindow.Icon = Utils.ConvertIconToImageSource(Resource.oC_favico_NewMessage); } ShowNotification(newContact.Name, newContact.ReceivedMessage, newContact.GroupId); } // Wenn der momentan ausgewählte Kontakt der aktuelle Kontakt in der Schleife ist, werden die Nachrichten nicht als ungelesen angezeigt, sonst schon. if (!(CurrentContact is null) && newContact.GroupId == CurrentContact.GroupId) { contactDependencyObject.Contact.HasUnreadMessages = false; } else { contactDependencyObject.Contact.HasUnreadMessages = true; contactDependencyObject.Contact.IsNewMessage = true; } } } } if (!updatedContacts.Contains(contactDependencyObject.Contact)) { _ContactList.ToList().Remove(contactDependencyObject); } } ContactList.Sort((x, y) => DateTime.Compare(y.Contact.TimeStamp, x.Contact.TimeStamp)); OnPropertyChanged(nameof(ChatMessages)); OnPropertyChanged(nameof(ContactList)); }); }); } }); } private void InitListeningThread(Contact currentContact) { EmojisListeningThreadTimer = new Timer {Tag = currentContact}; EmojisListeningThreadTimer.Tick += ListeningThreadTimerTickEvent; EmojisListeningThreadTimer.Interval = 10000; EmojisListeningThreadTimer.Start(); } private void ListeningThreadTimerTickEvent(object sender, EventArgs e) { ListenForMessagesForCurrentContact((Contact) ((Timer) sender).Tag); } private void ListenForMessagesForCurrentContact(Contact pCurrentContact) { if(!ShouldInterruptContactsThread && !(CurrentContact is null)) { Chat.CheckIfNewMessagesExistAsync(CurrentContact.GroupId, hasNewMessages => { if(!hasNewMessages) { return; } Chat.LoadChatMessagesForContactAsync(pCurrentContact, GetFirstMessage(), chatMessages => { this.Dispatch(() => { _ChatMessages.Clear(); _ChatMessages.AddRangeIfElementsNotIn(chatMessages); WpfUtils.ScrollToBottomOfListBox(ChatListBox); }); }); }); } } private void Suche_OnTextChanged(object sender, TextChangedEventArgs e) { CollectionViewSource.GetDefaultView(Clientlist.ItemsSource).Refresh(); } private bool UserFilter(object item) { if (string.IsNullOrEmpty(Suche.Text)) { return true; } if(item is ContactDependencyObject contactDependencyObject) { return contactDependencyObject.Contact.Name.IndexOf(Suche.Text, StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private void Chat_OnMouseDoubleClick(object sender, MouseButtonEventArgs e) { if(ChatListBox.SelectedItems.Count > 0) { var selectedItem = ChatListBox.SelectedItems[0]; if(selectedItem is ChatMessage selectedMessage) { 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) => { this.Dispatch(() => { EndWaiting(); Chat.ShowPictureWindow(imageSource, windowTitle); }); }); } else if(!(selectedMessage.FilePath is null)) { StartWaitingImmediately(); LoadDocumentAsync(selectedMessage.FilePath, Path.GetFileName(selectedMessage.FilePath), EndWaiting); } } } } private void LoadDocumentAsync(string uri, string fileName, Action callback) { try { var path = Path.Combine(Path.GetTempPath(), fileName); _CreatedTempFiles.Add(path); if(!File.Exists(path)) { Chat.DownloadFileAsync(uri, path, () => { this.Dispatch(() => { if (File.Exists(path)) { Process.Start(path); } callback?.Invoke(); }); }); } else { Process.Start(path); callback?.Invoke(); } } catch(Exception exception) { EndWaiting(); MessageBox.Show(exception.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } } private ChatControlWaitLayer _WaitLayer; public void StartWaiting() { Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action) StartWaitingImmediately); } public void StartWaitingImmediately(string dialogText = null) { if (_WaitLayer is null) { _WaitLayer = new ChatControlWaitLayer(dialogText); Grid.SetRowSpan(_WaitLayer, 3); RootGrid.Children.Add(_WaitLayer); Panel.SetZIndex(_WaitLayer, int.MaxValue); _WaitLayer.RefreshChatUI(); } } public void EndWaiting() { Dispatcher.BeginInvoke( DispatcherPriority.Background, (Action) delegate { if(!(_WaitLayer is null)) { RootGrid.Children.Remove(_WaitLayer); _WaitLayer = null; } }); } private void ButtonReloadGruppen_OnClick(object sender, RoutedEventArgs e) { DoSynchro(); } private void DoSynchro() { try { StartWaitingImmediately(); WriteToNewSyncFile(); Chat.ReloadGroupsAsync(data => { this.Dispatch(() => { ContactList = new ObservableSortCollection(); foreach(var contact in Chat.UpdateContacts(data)) { ContactList.Add(new ContactDependencyObject(contact)); } ContactList.Sort((x, y) => DateTime.Compare(y.Contact.TimeStamp, x.Contact.TimeStamp)); if(!(CurrentContact is null)) { var selectedElement = ContactList.FirstOrDefault(f => f.Contact.GroupId == CurrentContact.GroupId); if(!(selectedElement is null)) { Clientlist.SelectedItem = selectedElement; } } OnPropertyChanged(nameof(ContactList)); ReadNewSyncFile(); AddViewFilterToClientlist(); EndWaiting(); }); }); } catch (Exception e) { EndWaiting(); MessageBox.Show("Ein Fehler bei der Synchronisation ist aufgetreten.\nFehler:\n" + e.Message, "Fehler", MessageBoxButton.OK); } } // Speichert die TimeStamps der zuletzt empfangenen Nachrichten public void WriteToNewSyncFile() { try { if (File.Exists(Path.Combine(Utils.GetAndCreateUserAppDataPath(), Resource.SyncFile2Name))) { File.SetAttributes(Path.Combine(Utils.GetAndCreateUserAppDataPath(), Resource.SyncFile2Name), FileAttributes.Normal); File.Delete(Path.Combine(Utils.GetAndCreateUserAppDataPath(), Resource.SyncFile2Name)); } using (var streamWriter = new StreamWriter(Path.Combine(Utils.GetAndCreateUserAppDataPath(), Resource.SyncFile2Name))) { Encoding enc = new UTF8Encoding(); foreach (var contact in _ContactList.ToList().Where(contact => !(contact.Contact.ReceivedMessage is null))) { var text = contact.Contact.GroupId + ";" + contact.Contact.TimeStamp.ToUniversalTime() + ";" + contact.Contact.IsNewMessage + ";"; var bytes = enc.GetBytes(text); var base64String = Convert.ToBase64String(bytes); streamWriter.WriteLine(base64String); } File.SetAttributes(Path.Combine(Utils.GetAndCreateUserAppDataPath(), Resource.SyncFile2Name), FileAttributes.ReadOnly); } } catch (Exception) { } } public void ReadNewSyncFile() { try { if(File.Exists(Path.Combine(Utils.GetAndCreateUserAppDataPath(), Resource.SyncFile2Name))) { using (var streamReader = new StreamReader(Path.Combine(Utils.GetAndCreateUserAppDataPath(), Resource.SyncFile2Name), true)) { string line; _GroupId2DateTime.Clear(); while (!((line = streamReader.ReadLine()) is null)) { var encodedTextBytes = Convert.FromBase64String(line); var plainText = Encoding.UTF8.GetString(encodedTextBytes); var splitPlainText = plainText.Split(';'); // Ist kein Datum vorhanden -> 01.01.0001 00:00:00 var groupId = int.Parse(splitPlainText[0]); var dateTime = DateTime.Parse(splitPlainText[1]); var isUnread = splitPlainText.Length <= 2 || string.IsNullOrEmpty(splitPlainText[2]) || bool.Parse(splitPlainText[2]); _GroupId2DateTime.Add(groupId, new SyncFileInfoStruct(isUnread, dateTime)); } } var latestDateTime = Utils.DefaultDate; foreach(var id2dateTime in _GroupId2DateTime) { if(id2dateTime.Value.TimeStamp > latestDateTime) { latestDateTime = id2dateTime.Value.TimeStamp; } } Chat.LastTimeStamp = latestDateTime.GetUnixTimeStamp(); } } catch(Exception) { } } // Wird im BeWoPlaner benutzt public List GetContactsWithoutUnreadMessages() { var contacts = new List(); Clientlist.Items.Refresh(); foreach(var contactItem in Clientlist.Items) { if(contactItem is ContactDependencyObject contactDependencyObject && contactDependencyObject.Contact.HasUnreadMessages && !string.IsNullOrEmpty(contactDependencyObject.Contact.ReceivedMessage?.Text)) { contacts.Add(contactDependencyObject.Contact); } } return contacts; } // Wird im BeWoPlaner benutzt public void ResetNumberOfUnreadMessages(Dictionary pGroupId2DateTime) { if(!(pGroupId2DateTime is null)) { foreach(var contactDependencyObject in _ContactList) { foreach(var groupId2DateTime in pGroupId2DateTime) { if(contactDependencyObject.Contact.GroupId == groupId2DateTime.Key) { contactDependencyObject.Contact.HasUnreadMessages = false; } } } } } // Wird im BeWoPlaner benutzt public List GetSelectedMessages() { var messages = (from object selectedItem in ChatListBox.SelectedItems select selectedItem as ChatMessage).ToList(); return messages.Count > 0 ? messages : null; } // Wird im BeWoPlaner benutzt public Contact GetCurrentContactWithUserIdManage() { return !(CurrentContact?.UserIdManage is null) ? CurrentContact : null; } // Wird im BeWoPlaner benutzt public Contact GetCurrentContact() { return CurrentContact; } // Wird im BeWoPlaner benutzt public void AddContextMenu(string menuItemText, Action callBackAction) { if (!_MenuItemName2Callback.ContainsKey(menuItemText)) { _MenuItemName2Callback.Add(menuItemText, callBackAction); } } public void DeleteTempFiles() { foreach (var file in _CreatedTempFiles) { try { if (File.Exists(file)) { File.Delete(file); } } catch (Exception) { //ignore okeee } } } public event PropertyChangedEventHandler PropertyChanged; [NotifyPropertyChangedInvocator] protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private void ChatMainControl_OnLoaded(object sender, RoutedEventArgs e) { AddViewFilterToClientlist(); } private void AddViewFilterToClientlist() { var view = (CollectionView)CollectionViewSource.GetDefaultView(Clientlist.ItemsSource); view.Filter = UserFilter; } private void Chatbox_OnTextChanged(object sender, TextChangedEventArgs e) { if(string.IsNullOrWhiteSpace(Chatbox.Text)) { Chatbox.Height = 25; } OnPropertyChanged(nameof(IsBroadcastButtonEnabled)); OnPropertyChanged(nameof(IsSendButtonEnabled)); } public void AddMessages(List newMessages) { _ChatMessages.AddRangeIfElementsNotIn(newMessages); } public ChatMessage GetFirstMessage() { return _ChatMessages.FirstOrDefault(); } #region Stapelnachricht public ImageSource BroadcastButtonImageSource { get; } public ImageSource InvertedBroadcastButtonImageSource { get; } private Visibility _ProgressOverlayVisibility; public Visibility ProgressOverlayVisibility { get => _ProgressOverlayVisibility; set { _ProgressOverlayVisibility = value; OnPropertyChanged(nameof(ProgressOverlayVisibility)); OnPropertyChanged(nameof(NormalViewVisibility)); OnPropertyChanged(nameof(StapelmodusChatGridVisibility)); OnPropertyChanged(nameof(ChatListBoxVisibility)); } } public Visibility NormalViewVisibility => ProgressOverlayVisibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible; public Visibility StapelmodusChatGridVisibility => (Chat?.IsInBroadcastMode ?? false) && ProgressOverlayVisibility == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed; public bool IsSendButtonEnabled => (!string.IsNullOrEmpty(Chatbox.Text) || File.Exists(ChatMessageFileWrapper?.ScaledFilePath)) && !(CurrentContact is null); public bool IsBroadcastButtonEnabled { get { var isInBroadcastMode = Chat?.IsInBroadcastMode ?? false; var hasSelectedContacts = GetSelectedContacts().Any(); var hasTextOrFile = !string.IsNullOrEmpty(Chatbox.Text) || File.Exists(BroadcastMessageFileWrapper?.ScaledFilePath); return isInBroadcastMode && hasSelectedContacts && hasTextOrFile; } } private Contact _PreviousContact; private CancellationTokenSource _CancellationTokenSource; private string _DefaultBroadcastInfoText = "Sie befinden Sich im Stapel-Modus. In diesem Modus können Sie eine Nachricht in einem Schritt an mehrere Empfänger & Gruppen senden. Bitte setzen Sie dazu in der Kontaktliste bei den gewünschten Empfängern & Gruppen ein Häkchen und schreiben Sie Ihre Nachricht wie gewohnt. Wenn Sie auf senden klicken, wird die Nachricht an alle ausgewählten Empfänger & Gruppen gesendet."; private string _BroadcastInfoText; public string BroadcastInfoText { get => _BroadcastInfoText ?? (_BroadcastInfoText = _DefaultBroadcastInfoText); set { _BroadcastInfoText = $"{_DefaultBroadcastInfoText}{Environment.NewLine}{value}"; OnPropertyChanged(BroadcastInfoText); } } private double _BroadcastProgressValue; public double BroadcastProgressValue { get => _BroadcastProgressValue; set { _BroadcastProgressValue = value; OnPropertyChanged(nameof(BroadcastProgressValue)); } } public Visibility RemoveFileFromBroadcastMessageVisibility => BroadcastMessageFileWrapper is null ? Visibility.Collapsed : Visibility; private ChatMessageFileWrapper _BroadcastMessageFileWrapper; public ChatMessageFileWrapper BroadcastMessageFileWrapper { get => _BroadcastMessageFileWrapper; set { _BroadcastMessageFileWrapper = value; OnPropertyChanged(nameof(BroadcastMessageFileWrapper)); OnPropertyChanged(nameof(IsBroadcastButtonEnabled)); OnPropertyChanged(nameof(RemoveFileFromBroadcastMessageVisibility)); } } private void BroadcastToggleButton_OnClick(object sender, RoutedEventArgs e) { Chat.IsInBroadcastMode = !Chat.IsInBroadcastMode; BroadcastProgressValue = 0; DeselectAllContacts(); if (Chat.IsInBroadcastMode) { _CancellationTokenSource = new CancellationTokenSource(); _PreviousContact = CurrentContact; CurrentContact = null; ToggleButtonImage.Source = InvertedBroadcastButtonImageSource; } else { CloseBroadcastMode(); } OnPropertyChanged(nameof(StapelmodusChatGridVisibility)); OnPropertyChanged(nameof(ChatListBoxVisibility)); OnPropertyChanged(nameof(IsSendButtonEnabled)); OnPropertyChanged(nameof(ChatMessageInputGridVisibility)); OnPropertyChanged(nameof(BroadcastMessageFileWrapper)); } private async void SendBroadcastMessage() { var selectedContacts = GetSelectedContacts(); if(selectedContacts.Count == 0 || string.IsNullOrEmpty(Chatbox.Text) && BroadcastMessageFileWrapper.ChatMessageImageSource is null) { return; } var result = new OwnChatMessageBox("JA", "ABBRECHEN", $"Sind Sie sicher? Ihre Nachricht wird an {selectedContacts.Count} Empfänger & Gruppen verschickt!", "Stapelnachricht") { Owner = ContainingWindow }.ShowDialog(); if(result != true) { return; } var wasCompleted = false; var progress = new Progress(); progress.ProgressChanged += ReportProgress; try { ProgressOverlayVisibility = Visibility.Visible; wasCompleted = await SendBroadcast(progress, _CancellationTokenSource.Token); } catch(OperationCanceledException) { BroadcastInfoText = "Der Broadcast wurde abgebrochen."; } finally { ProgressOverlayVisibility = Visibility.Collapsed; } DoSynchro(); if(wasCompleted) { new OwnChatMessageBox(null, "OK", $"Die Nachricht wurde erfolgreich an {selectedContacts.Count} Empfänger & Gruppen versandt.", "Stapelnachricht") { Owner = ContainingWindow }.ShowDialog(); } CloseBroadcastMode(); } private async Task SendBroadcast(IProgress progress, CancellationToken cancellationToken) { var count = 0; var report = new BroadcastProgressReportModel(); var selectedContacts = GetSelectedContacts(); foreach(var contactDependencyObject in selectedContacts.Where(selectedContact => selectedContact.Contact.IsChatMessageInputGridVisible)) { if(BroadcastMessageFileWrapper is null) { await Chat.SendMessage(Chatbox.Text, contactDependencyObject.Contact.GroupId); } else { await Chat.SendFileToContact(contactDependencyObject.Contact, BroadcastMessageFileWrapper.ScaledFilePath, BroadcastMessageFileWrapper.OriginalFilePath, Chatbox.Text); } contactDependencyObject.SetValue(ListItemHelper.IsCheckedProperty, false); cancellationToken.ThrowIfCancellationRequested(); count++; report.PercentageComplete = count * 100 / selectedContacts.Count; progress.Report(report); } return count == selectedContacts.Count; } private void ReportProgress(object sender, BroadcastProgressReportModel e) { BroadcastProgressValue = e.PercentageComplete; } private void SelectAllContacts_OnClick(object sender, RoutedEventArgs e) { if(sender is CheckBox checkBox) { var isChecked = checkBox.IsChecked ?? false; foreach (var contactDependencyObject in ContactList) { contactDependencyObject.SetValue(ListItemHelper.IsCheckedProperty, isChecked); } OnPropertyChanged(nameof(ContactList)); OnPropertyChanged(nameof(IsBroadcastButtonEnabled)); } } private void SendBroatcastButton_OnClick(object sender, RoutedEventArgs e) { SendBroadcastMessage(); } private void CancelBroadcastButton_OnClick(object sender, RoutedEventArgs e) { _CancellationTokenSource?.Cancel(); BroadcastMessageFileWrapper = null; Chatbox.Text = string.Empty; } private void AddMediaFileToBroadcastMessage_OnClick(object sender, RoutedEventArgs e) { if(Chat.IsInBroadcastMode) { var originalFilePath = Chat.OpenFile(); if(File.Exists(originalFilePath)) { StartWaitingImmediately("Skaliere Bild ..."); var scalingTask = new Task(() => { try { var scaledImagePath = FileUtils.ScaleImage(originalFilePath, Path.GetExtension(originalFilePath.ToUpperInvariant()), Chat.ChatDaten.MaxUploadSize); var isFileSizeTooLarge = FileUtils.CheckFileSize(scaledImagePath, Chat.ChatDaten.MaxUploadSize); if(isFileSizeTooLarge) { MessageBox.Show($"Die ausgewählte Datei ist zu groß. Die maximale Größe beträgt {Chat.ChatDaten.MaxUploadSize / 1000 / 1000} MB", "Senden nicht möglich", MessageBoxButton.OK, MessageBoxImage.Warning); return; } if(File.Exists(scaledImagePath)) { BroadcastMessageFileWrapper = new ChatMessageFileWrapper(originalFilePath, scaledImagePath, $"{Chat.ChatDaten.ServerUrl}/document.png"); } } finally { this.Dispatch(EndWaiting); } }); scalingTask.Start(); } } } private void CloseBroadcastMode() { Chatbox.Text = string.Empty; BroadcastToggleButton.IsChecked = false; Chat.IsInBroadcastMode = false; _CancellationTokenSource = null; BroadcastMessageFileWrapper = null; BroadcastProgressValue = 0; ProgressOverlayVisibility = Visibility.Collapsed; DeselectAllContacts(); OnPropertyChanged(nameof(IsSendButtonEnabled)); OnPropertyChanged(nameof(ChatMessageInputGridVisibility)); OnPropertyChanged(nameof(BroadcastMessageFileWrapper)); ToggleButtonImage.Source = BroadcastButtonImageSource; OnPropertyChanged(nameof(StapelmodusChatGridVisibility)); if(!(_PreviousContact is null)) { SelectContact(_PreviousContact); } } private void ContactCheckBox_OnClick(object sender, RoutedEventArgs e) { OnPropertyChanged(nameof(IsBroadcastButtonEnabled)); } private void ContactCheckBox_OnChecked(object sender, RoutedEventArgs e) { UpdateContactSelection(); } private void UpdateContactSelection() { OnPropertyChanged(nameof(ContactList)); OnPropertyChanged(nameof(IsBroadcastButtonEnabled)); bool? newValue = null; var selectedContacts = GetSelectedContacts(); var allSelectableContacts = ContactList.Where(contactDependencyObject => contactDependencyObject.Contact.IsChatMessageInputGridVisible).ToList(); if(selectedContacts.Count == allSelectableContacts.Count) { newValue = true; } if(selectedContacts.Count == 0) { newValue = false; } SelectAllContactsCheckBox.IsChecked = newValue; } private List GetSelectedContacts() { var result = new List(); foreach(var contactDependencyObject in ContactList.Where(contactDependencyObject => contactDependencyObject.Contact.IsChatMessageInputGridVisible)) { if(contactDependencyObject.GetValue(ListItemHelper.IsCheckedProperty) is bool isChecked && isChecked) { result.AddIfNotIn(contactDependencyObject); } } return result; } private void DeselectAllContacts() { foreach (var contactDependencyObject in ContactList) { contactDependencyObject.SetValue(ListItemHelper.IsCheckedProperty, false); } } private void RemoveBroadcastFileAttachmentButton_Click(object sender, RoutedEventArgs e) { BroadcastMessageFileWrapper = null; } #endregion private void RemoveFileAttachmentButton_Click(object sender, RoutedEventArgs e) { ChatMessageFileWrapper = null; } } }