Skip to content

Commit

Permalink
attachments (images, files)
Browse files Browse the repository at this point in the history
  • Loading branch information
exp111 committed Nov 28, 2023
1 parent d13087e commit 2b104c9
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 9 deletions.
27 changes: 24 additions & 3 deletions Turbulence.Core/ViewModels/Design/DesignMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public DesignMessage()
Timestamp = DateTime.Now;
Reactions = new Reaction[]
{
new Reaction()
new()
{
Count = 1,
Me = true,
Expand All @@ -31,7 +31,7 @@ public DesignMessage()
Name = "\U0001F629", // :weary:
}
},
new Reaction()
new()
{
Count = 1,
Me = false,
Expand All @@ -41,7 +41,7 @@ public DesignMessage()
Name = "\U0001F914", // :think:
}
},
new Reaction()
new()
{
Count = 1,
Me = false,
Expand All @@ -52,6 +52,27 @@ public DesignMessage()
}
},
};
Attachments = new Attachment[]
{
new()
{
Id = new(0),
Filename = "file.jpg",
ContentType = "image/jpeg",
Size = 0,
Url = "https://localhost",
ProxyUrl = "https://localhost",
},
new()
{
Id = new(1),
Filename = "file.txt",
ContentType = "text/plain; charset=utf-8",
Size = 0,
Url = "https://localhost",
ProxyUrl = "https://localhost",
},
};
ReferencedMessage = CreateMessage("Reply to this.", MessageType.DEFAULT, Author, DateTimeOffset.Now - new TimeSpan(0, 1, 0));
}

Expand Down
40 changes: 40 additions & 0 deletions Turbulence.Desktop/Converters/ImageUrlConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Avalonia.Controls;
using Avalonia.Data.Converters;
using Avalonia.Platform;
using System.Globalization;
using Turbulence.Discord;
using Avalonia.Media.Imaging;
using CommunityToolkit.Mvvm.DependencyInjection;

namespace Turbulence.Desktop.Converters;

public class ImageUrlConverter : IValueConverter
{
private readonly IPlatformClient _client = Ioc.Default.GetService<IPlatformClient>()!;

public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is not string url)
throw new Exception("Not a string.");

// Debug image
if (Design.IsDesignMode)
{
return new Bitmap(AssetLoader.Open(new Uri("resm:Avalonia.Skia.Assets.NoiseAsset_256X256_PNG.png?assembly=Avalonia.Skia")));
}

var data = Task.Run(async () => await _client.GetImageAsync(url)).Result;
var bmp = new Bitmap(new MemoryStream(data));
/*if (bmp.PixelSize.Height > 80)
{
bmp = bmp.CreateScaledBitmap(new PixelSize(80, 80));
}*/

return bmp;
}

public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
26 changes: 26 additions & 0 deletions Turbulence.Desktop/Converters/MessageVisibleConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Avalonia.Data.Converters;
using CommunityToolkit.Mvvm.DependencyInjection;
using System.Globalization;
using Turbulence.Discord;
using Turbulence.Discord.Models.DiscordChannel;

namespace Turbulence.Desktop.Converters;

public class MessageVisibleConverter : IValueConverter
{
private readonly IPlatformClient _client = Ioc.Default.GetService<IPlatformClient>()!;

public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is not Message message)
return null;

//TODO: can we optimize this by not needing call this again or not needing a new converter
return !string.IsNullOrEmpty(_client.GetMessageContent(message));
}

public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
27 changes: 27 additions & 0 deletions Turbulence.Desktop/DataTemplates/AttachmentTypeSelector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Metadata;
using Turbulence.Discord.Models.DiscordChannel;

namespace Turbulence.Desktop.DataTemplates;

public class AttachmentTypeSelector : IDataTemplate
{
[Content]
public Dictionary<string, IDataTemplate> Templates { get; } = new Dictionary<string, IDataTemplate>();

public bool Match(object? data) => data is Attachment;

Control? ITemplate<object?, Control?>.Build(object? param)
{
var attachment = (Attachment)param!;
var type = attachment.ContentType switch
{
"image/png" or "image/jpeg" => "image",
_ => "default",
};
if (!Templates.TryGetValue(type, out var template))
return Templates["unknown"].Build(param);
return template.Build(param);
}
}
35 changes: 31 additions & 4 deletions Turbulence.Desktop/Views/Main/MessageView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
xmlns:dvm="clr-namespace:Turbulence.Core.ViewModels.Design;assembly=Turbulence.Core"
xmlns:channel="clr-namespace:Turbulence.Discord.Models.DiscordChannel;assembly=Turbulence.Discord"
xmlns:converters="clr-namespace:Turbulence.Desktop.Converters"
xmlns:dataTemplates="clr-namespace:Turbulence.Desktop.DataTemplates"
mc:Ignorable="d" d:DesignWidth="500"
x:DataType="channel:Message"
x:Class="Turbulence.Desktop.Views.Main.MessageView"
Expand All @@ -16,14 +17,16 @@
<UserControl.Resources>
<converters:MessageAuthorNameConverter x:Key="AuthorConverter" />
<converters:MessageContentConverter x:Key="MessageConverter" />
<converters:MessageVisibleConverter x:Key="MessageVisibleConverter" />
<converters:UserAvatarConverter x:Key="AvatarConverter" />
<converters:EmojiImageConverter x:Key="EmojiConverter" />
<converters:ImageUrlConverter x:Key="ImageUrlConverter" />
</UserControl.Resources>
<UserControl.ContextFlyout>
<v:MessageContextMenu />
</UserControl.ContextFlyout>
<UserControl.Styles>
<Style Selector="Button">
<Style Selector="Button.JumpTo">
<Setter Property="FontSize" Value="10" />
<Setter Property="Padding" Value="2" />
<Setter Property="Background" Value="#1E1F22" />
Expand All @@ -44,8 +47,13 @@
<Style Selector="Button.Reaction.Selected:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="BorderBrush" Value="#5865F2" />
</Style>
<Style Selector="Button.Link">
<Setter Property="CornerRadius" Value="0" />
<Setter Property="Padding" Value="4" />
</Style>
</UserControl.Styles>
<Grid ColumnDefinitions="Auto,*" Classes="Message">
<!-- Avatar -->
<DockPanel>
<Panel IsVisible="{Binding !!ReferencedMessage}" Height="20" DockPanel.Dock="Top">
<!--TODO: add weird spine border thingy here-->
Expand All @@ -54,24 +62,44 @@
<Image Name="Image" UseLayoutRounding="False" Source="{Binding Author, Converter={StaticResource AvatarConverter}}" />
</Border>
</DockPanel>
<!-- Actual Message -->
<StackPanel Grid.Column="1"
Orientation="Vertical"
Classes="MessageContent">
<!-- Reply Message Preview -->
<StackPanel Name="MessageReference" Height="20" Orientation="Horizontal" IsVisible="{Binding !!ReferencedMessage}">
<TextBlock FontWeight="SemiBold" Text="{Binding ReferencedMessage, Converter={StaticResource AuthorConverter}, StringFormat='@{0} '}" />
<TextBlock Foreground="#B5BAC1" Text="{Binding ReferencedMessage, Converter={StaticResource MessageConverter}}" />
</StackPanel>
<!-- Author Name, Timestamp etc -->
<DockPanel>
<!-- Keep jump button first so it takes priority over others and is always fully visible -->
<Button IsVisible="{Binding #Message.ShowJumpToMessage}" DockPanel.Dock="Right">Jump To Message</Button>
<Button Classes="JumpTo" IsVisible="{Binding #Message.ShowJumpToMessage}" DockPanel.Dock="Right">Jump To Message</Button>
<TextBlock Name="Author" Classes="Author" Text="{Binding ., Converter={StaticResource AuthorConverter}}" DockPanel.Dock="Left" />
<TextBlock Name="Timestamp" Classes="Timestamp" Text="{Binding Timestamp, StringFormat='G'}" DockPanel.Dock="Left">
<ToolTip.Tip>
<Label Content="{Binding Timestamp, StringFormat='F'}" />
</ToolTip.Tip>
</TextBlock>
</DockPanel>
<TextBlock Name="Content" TextWrapping="Wrap" Classes="MessageContent" Text="{Binding ., Converter={StaticResource MessageConverter}}" />
<!-- Message Content -->
<TextBlock Name="Content" TextWrapping="Wrap" Classes="MessageContent"
Text="{Binding ., Converter={StaticResource MessageConverter}}"
IsVisible="{Binding ., Converter={StaticResource MessageVisibleConverter}}" />
<!-- Attachments (images, files) -->
<ItemsControl ItemsSource="{Binding Attachments}" IsVisible="{Binding !!Attachments.Length}">
<ItemsControl.ItemTemplate>
<dataTemplates:AttachmentTypeSelector>
<DataTemplate x:Key="image" x:DataType="channel:Attachment">
<Image Source="{Binding ProxyUrl, Converter={StaticResource ImageUrlConverter}}" MaxWidth="100" MaxHeight="100" HorizontalAlignment="Left" />
</DataTemplate>
<DataTemplate x:Key="default" x:DataType="channel:Attachment">
<Button Classes="Link" Content="{Binding Filename}" Click="OnAttachmentButton" />
</DataTemplate>
</dataTemplates:AttachmentTypeSelector>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Reactions -->
<ItemsControl ItemsSource="{Binding Reactions}" IsVisible="{Binding !!Reactions}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
Expand All @@ -92,7 +120,6 @@
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

</StackPanel>
</Grid>
</UserControl>
21 changes: 21 additions & 0 deletions Turbulence.Desktop/Views/Main/MessageView.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using System.Diagnostics;
using Turbulence.Discord.Models.DiscordChannel;

namespace Turbulence.Desktop.Views.Main;

Expand All @@ -19,4 +22,22 @@ public MessageView()
{
InitializeComponent();
}

public void OnAttachmentButton(object? sender, RoutedEventArgs args)
{
if (sender is not Control control ||
control.DataContext is not Attachment attachment)
return;

var url = new Uri(attachment.Url);
// check if its a valid url before running it as a process...
if (url.Scheme == Uri.UriSchemeHttp || url.Scheme == Uri.UriSchemeHttps)
{
Process.Start(new ProcessStartInfo
{
FileName = url.AbsoluteUri,
UseShellExecute = true
});
}
}
}
23 changes: 21 additions & 2 deletions Turbulence.Discord/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public class Client : IPlatformClient

private ClientWebSocket WebSocket { get; set; }
private const string UserAgent = "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/116.0"; // idk where to move this

public Client()
{
// Set up http client
Expand Down Expand Up @@ -499,7 +499,7 @@ public async Task<byte[]> GetAvatarAsync(User user, int size = 128)
_cache.SetAvatar(user.Id, size, avatar);
return avatar;
}

public async Task<byte[]> GetEmojiAsync(Emoji emoji, int size = 32)
{
if (emoji.Id == null)
Expand Down Expand Up @@ -559,5 +559,24 @@ public string GetMessageContent(Message message)
_ => message.Content,
};
}

public async Task<byte[]> GetImageAsync(string url)
{
//TODO: cache
/*if (_cache.GetAvatar(user.Id, size) is { } avatar)
return avatar;*/

var req = new HttpRequestMessage(HttpMethod.Get, url);
var response = await CdnClient.SendAsync(req);
if (!response.IsSuccessStatusCode)
{
throw new ApiException($"Got error while fetching image: {response.StatusCode}, {response.ReasonPhrase}");
}
var image = await response.Content.ReadAsByteArrayAsync();
_logger?.Log($"Requested media image {url}", LogType.Images, LogLevel.Debug);

//_cache.SetAvatar(user.Id, size, avatar);
return image;
}
}
}
1 change: 1 addition & 0 deletions Turbulence.Discord/IPlatformClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public interface IPlatformClient
public event EventHandler<Event<TypingStartEvent>>? TypingStart;

public User? CurrentUser { get; set; }
public Task<byte[]> GetImageAsync(string url);
public Task<byte[]> GetAvatarAsync(User user, int size = 128);
public Task<byte[]> GetEmojiAsync(Emoji emoji, int size = 32);
public Task<string> GetChannelName(Channel channel);
Expand Down

0 comments on commit 2b104c9

Please sign in to comment.