From 831ffe46d2f53467835d4f1c368399e64f268e13 Mon Sep 17 00:00:00 2001
From: Stone_Red <56473591+Stone-Red-Code@users.noreply.github.com>
Date: Fri, 17 Jul 2026 16:22:02 +0200
Subject: [PATCH] Add image viewer window
---
src/Decho/ViewModels/MainWindowViewModel.cs | 5 +-
src/Decho/Views/ImageViewerWindow.axaml | 29 +++++++++++
src/Decho/Views/ImageViewerWindow.axaml.cs | 34 ++++++++++++
src/Decho/Views/MessageItemView.axaml | 4 +-
src/Decho/Views/MessageItemView.axaml.cs | 57 +++++++++++++++++++++
5 files changed, 127 insertions(+), 2 deletions(-)
create mode 100644 src/Decho/Views/ImageViewerWindow.axaml
create mode 100644 src/Decho/Views/ImageViewerWindow.axaml.cs
diff --git a/src/Decho/ViewModels/MainWindowViewModel.cs b/src/Decho/ViewModels/MainWindowViewModel.cs
index 8227375..963f6ac 100644
--- a/src/Decho/ViewModels/MainWindowViewModel.cs
+++ b/src/Decho/ViewModels/MainWindowViewModel.cs
@@ -797,7 +797,10 @@ public sealed class MainWindowViewModel : ViewModelBase
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
{
- serverVm.Channels.Add(channelVm);
+ if (!serverVm.Channels.Any(c => c.Name == channel.Name))
+ {
+ serverVm.Channels.Add(channelVm);
+ }
serverVm.SelectedChannel = channelVm;
foreach (MessageModel msg in joinResult.History)
diff --git a/src/Decho/Views/ImageViewerWindow.axaml b/src/Decho/Views/ImageViewerWindow.axaml
new file mode 100644
index 0000000..abed49e
--- /dev/null
+++ b/src/Decho/Views/ImageViewerWindow.axaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/src/Decho/Views/ImageViewerWindow.axaml.cs b/src/Decho/Views/ImageViewerWindow.axaml.cs
new file mode 100644
index 0000000..befc64d
--- /dev/null
+++ b/src/Decho/Views/ImageViewerWindow.axaml.cs
@@ -0,0 +1,34 @@
+using Avalonia.Controls;
+using Avalonia.Media.Imaging;
+
+namespace Decho.Views;
+
+public sealed partial class ImageViewerWindow : Window
+{
+ public ImageViewerWindow()
+ {
+ InitializeComponent();
+ }
+
+ public ImageViewerWindow(Bitmap bitmap, string fileName)
+ {
+ InitializeComponent();
+ Title = fileName;
+ ImageView.Source = bitmap;
+
+ Opened += (_, _) =>
+ {
+ SizeToContent = SizeToContent.Manual;
+ if (Width > MaxWidth) Width = MaxWidth;
+ if (Height > MaxHeight) Height = MaxHeight;
+
+ MaxHeight = double.MaxValue;
+ MaxWidth = double.MaxValue;
+ };
+ }
+
+ private void OnCloseClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
+ {
+ Close();
+ }
+}
diff --git a/src/Decho/Views/MessageItemView.axaml b/src/Decho/Views/MessageItemView.axaml
index 791c2de..440110b 100644
--- a/src/Decho/Views/MessageItemView.axaml
+++ b/src/Decho/Views/MessageItemView.axaml
@@ -37,7 +37,9 @@
+ HorizontalAlignment="Left"
+ Cursor="Hand"
+ PointerPressed="OnAttachmentImagePointerPressed" />
diff --git a/src/Decho/Views/MessageItemView.axaml.cs b/src/Decho/Views/MessageItemView.axaml.cs
index 0852a70..dddcc61 100644
--- a/src/Decho/Views/MessageItemView.axaml.cs
+++ b/src/Decho/Views/MessageItemView.axaml.cs
@@ -321,4 +321,61 @@ public partial class MessageItemView : UserControl
// Ignore if temp file cannot be deleted
}
}
+
+ private async void OnAttachmentImagePointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)
+ {
+ if (sender is not Image image)
+ {
+ return;
+ }
+
+ if (image.DataContext is not AttachmentDto att)
+ {
+ return;
+ }
+
+ if (att.Kind != AttachmentKind.Image)
+ {
+ return;
+ }
+
+ if (DataContext is not MessageViewModel msg)
+ {
+ return;
+ }
+
+ if (msg.ImageCache.TryGetValue(att.Url, out Bitmap? cached))
+ {
+ ImageViewerWindow viewer = new ImageViewerWindow(cached, att.FileName);
+ if (TopLevel.GetTopLevel(this) is Window parent)
+ {
+ await viewer.ShowDialog(parent);
+ }
+ return;
+ }
+
+ MainWindowViewModel? mainVm = this.GetMainWindowViewModel();
+ if (mainVm is null)
+ {
+ return;
+ }
+
+ byte[]? bytes = await mainVm.ConnectionService.DownloadImageBytesAsync(
+ msg.ServerUrl ?? "", msg.Model.ChannelName, att.Url);
+
+ if (bytes is null || bytes.Length == 0)
+ {
+ return;
+ }
+
+ using MemoryStream stream = new MemoryStream(bytes);
+ Bitmap bitmap = new Bitmap(stream);
+ msg.ImageCache[att.Url] = bitmap;
+
+ ImageViewerWindow viewerWindow = new ImageViewerWindow(bitmap, att.FileName);
+ if (TopLevel.GetTopLevel(this) is Window parentWindow)
+ {
+ await viewerWindow.ShowDialog(parentWindow);
+ }
+ }
}
\ No newline at end of file