mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 11:56:30 +00:00
Workshop - Simplify upload to a busy indicator, show images in profiles
This commit is contained in:
1 parent
e33ae8a066
commit
e304d67035
21 files changed
+112
-229
No files matched your search
@@ -47,6 +47,7 @@ public static class Constants
|
|||||||
/// The full path to the Artemis logs folder
|
/// The full path to the Artemis logs folder
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly string LogsFolder = Path.Combine(DataFolder, "Logs");
|
public static readonly string LogsFolder = Path.Combine(DataFolder, "Logs");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The full path to the Artemis logs folder
|
/// The full path to the Artemis logs folder
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -71,9 +72,9 @@ public static class Constants
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current version of the application
|
/// The current version of the application
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly string CurrentVersion = CoreAssembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!.InformationalVersion != "1.0.0"
|
public static readonly string CurrentVersion = CoreAssembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!.InformationalVersion.StartsWith("1.0.0")
|
||||||
? CoreAssembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!.InformationalVersion
|
? "local"
|
||||||
: "local";
|
: CoreAssembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!.InformationalVersion;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The plugin info used by core components of Artemis
|
/// The plugin info used by core components of Artemis
|
||||||
|
|||||||
@@ -30,6 +30,9 @@
|
|||||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="AsyncImageLoader.Avalonia">
|
||||||
|
<HintPath>..\..\..\..\Users\Robert\.nuget\packages\asyncimageloader.avalonia\3.2.1\lib\netstandard2.1\AsyncImageLoader.Avalonia.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="RGB.NET.Layout">
|
<Reference Include="RGB.NET.Layout">
|
||||||
<HintPath>..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll</HintPath>
|
<HintPath>..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
// Heavily based on:
|
|
||||||
// SkyClip
|
|
||||||
// - ProgressableStreamContent.cs
|
|
||||||
// --------------------------------------------------------------------
|
|
||||||
// Author: Jeff Hansen <jeff@jeffijoe.com>
|
|
||||||
// Copyright (C) Jeff Hansen 2015. All rights reserved.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Artemis.UI.Shared.Utilities;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides HTTP content based on a stream with support for IProgress.
|
|
||||||
/// </summary>
|
|
||||||
public class ProgressableStreamContent : StreamContent
|
|
||||||
{
|
|
||||||
private const int DEFAULT_BUFFER_SIZE = 4096;
|
|
||||||
|
|
||||||
private readonly int _bufferSize;
|
|
||||||
private readonly IProgress<StreamProgress> _progress;
|
|
||||||
private readonly Stream _streamToWrite;
|
|
||||||
private bool _contentConsumed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="ProgressableStreamContent" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="streamToWrite">The stream to write.</param>
|
|
||||||
/// <param name="progress">The downloader.</param>
|
|
||||||
public ProgressableStreamContent(Stream streamToWrite, IProgress<StreamProgress> progress) : this(streamToWrite, DEFAULT_BUFFER_SIZE, progress)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="ProgressableStreamContent" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="streamToWrite">The stream to write.</param>
|
|
||||||
/// <param name="bufferSize">The buffer size.</param>
|
|
||||||
/// <param name="progress">The downloader.</param>
|
|
||||||
public ProgressableStreamContent(Stream streamToWrite, int bufferSize, IProgress<StreamProgress> progress) : base(streamToWrite, bufferSize)
|
|
||||||
{
|
|
||||||
if (bufferSize <= 0)
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(bufferSize));
|
|
||||||
|
|
||||||
_streamToWrite = streamToWrite;
|
|
||||||
_bufferSize = bufferSize;
|
|
||||||
_progress = progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
_streamToWrite.Dispose();
|
|
||||||
|
|
||||||
base.Dispose(disposing);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context)
|
|
||||||
{
|
|
||||||
await SerializeToStreamAsync(stream, context, CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
PrepareContent();
|
|
||||||
|
|
||||||
byte[] buffer = new byte[_bufferSize];
|
|
||||||
long size = _streamToWrite.Length;
|
|
||||||
int uploaded = 0;
|
|
||||||
|
|
||||||
await using (_streamToWrite)
|
|
||||||
{
|
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
int length = await _streamToWrite.ReadAsync(buffer, cancellationToken);
|
|
||||||
if (length <= 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
uploaded += length;
|
|
||||||
_progress.Report(new StreamProgress(uploaded, size));
|
|
||||||
await stream.WriteAsync(buffer, 0, length, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override bool TryComputeLength(out long length)
|
|
||||||
{
|
|
||||||
length = _streamToWrite.Length;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Prepares the content.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="System.InvalidOperationException">The stream has already been read.</exception>
|
|
||||||
private void PrepareContent()
|
|
||||||
{
|
|
||||||
if (_contentConsumed)
|
|
||||||
{
|
|
||||||
// If the content needs to be written to a target stream a 2nd time, then the stream must support
|
|
||||||
// seeking (e.g. a FileStream), otherwise the stream can't be copied a second time to a target
|
|
||||||
// stream (e.g. a NetworkStream).
|
|
||||||
if (_streamToWrite.CanSeek)
|
|
||||||
_streamToWrite.Position = 0;
|
|
||||||
else
|
|
||||||
throw new InvalidOperationException("The stream has already been read.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_contentConsumed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"v":"5.9.6","fr":29.9700012207031,"ip":0,"op":17.0000006924242,"w":1080,"h":1080,"nm":"dog","ddd":0,"assets":[{"id":"comp_0","nm":"1","fr":29.9700012207031,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":50,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[571.5,705,0],"ix":2,"l":2},"a":{"a":0,"k":[31.5,132,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[103,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":4,"s":[78,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":9,"s":[103,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":12,"s":[78,100,100]},{"t":17.0000006924242,"s":[103,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-206,132],[269,132]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":22,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600.000024438501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"ball Outlines","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[26.5,35.867,0],"ix":2,"l":2},"a":{"a":0,"k":[13.842,13.842,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-7.507],[7.507,0],[0,7.507],[-7.506,0]],"o":[[0,7.507],[-7.506,0],[0,-7.507],[7.507,0]],"v":[[13.592,0],[0,13.592],[-13.592,0],[0,-13.592]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.203373763141,0.203600565592,0.203484329523,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[13.842,13.842],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600.000024438501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"wire Outlines","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":3,"s":[-28]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":6,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[-34]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[5]},{"t":17.0000006924242,"s":[0]}],"ix":10},"p":{"a":0,"k":[85.395,20.574,0],"ix":2,"l":2},"a":{"a":0,"k":[17.5,17.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.103,-3.49]],"o":[[0,0],[0,0]],"v":[[-2.663,-4.868],[2.663,4.868]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.999998743394,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":7,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[20.163,22.368],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600.000024438501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"band Outlines","parent":12,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":3,"s":[-4]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":5,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[-4]},{"t":17.0000006924242,"s":[0]}],"ix":10},"p":{"a":0,"k":[44.581,150.745,0],"ix":2,"l":2},"a":{"a":0,"k":[52.884,15.431,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.98,0.163],[0,0],[-0.163,2.98],[0,0],[-2.98,-0.163],[0,0],[0.163,-2.98],[0,0]],"o":[[Line truncated
|
||||||
@@ -7,18 +7,25 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.Workshop.Entries.Details.EntryImageView"
|
x:Class="Artemis.UI.Screens.Workshop.Entries.Details.EntryImageView"
|
||||||
x:DataType="details:EntryImageViewModel">
|
x:DataType="details:EntryImageViewModel">
|
||||||
<Border Classes="card" Padding="0" Width="300" ClipToBounds="True" Margin="0 5 0 0">
|
<Border Classes="card" Padding="0">
|
||||||
<Grid RowDefinitions="230,*">
|
<Grid RowDefinitions="230,*">
|
||||||
<Rectangle Grid.Row="0" Fill="{DynamicResource CheckerboardBrush}" />
|
<Border Grid.Row="0" ClipToBounds="True" CornerRadius="4 4 0 0" Padding="0">
|
||||||
<Image Grid.Row="0"
|
<Rectangle RenderOptions.BitmapInterpolationMode="HighQuality">
|
||||||
VerticalAlignment="Center"
|
<Rectangle.Fill>
|
||||||
HorizontalAlignment="Center"
|
<ImageBrush asyncImageLoader:ImageBrushLoader.Source="{CompiledBinding ThumbnailUrl}" Stretch="UniformToFill" />
|
||||||
RenderOptions.BitmapInterpolationMode="HighQuality"
|
</Rectangle.Fill>
|
||||||
asyncImageLoader:ImageLoader.Source="{CompiledBinding ThumbnailUrl, Mode=OneWay}" />
|
</Rectangle>
|
||||||
<StackPanel Grid.Row="1" Margin="12">
|
</Border>
|
||||||
|
<Border Grid.Row="1" ClipToBounds="True" CornerRadius="0 0 4 4" Background="{DynamicResource ControlFillColorDefaultBrush}">
|
||||||
|
<StackPanel Margin="16">
|
||||||
<TextBlock Text="{CompiledBinding Image.Name}" TextTrimming="CharacterEllipsis" />
|
<TextBlock Text="{CompiledBinding Image.Name}" TextTrimming="CharacterEllipsis" />
|
||||||
<TextBlock Classes="subtitle" Text="{CompiledBinding Image.Description}" TextWrapping="Wrap" />
|
<TextBlock Classes="subtitle"
|
||||||
|
Text="{CompiledBinding Image.Description}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
IsVisible="{CompiledBinding Image.Description, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
using Artemis.WebClient.Workshop;
|
using Artemis.UI.Shared;
|
||||||
|
using Artemis.WebClient.Workshop;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.Workshop.Entries.Details;
|
namespace Artemis.UI.Screens.Workshop.Entries.Details;
|
||||||
|
|
||||||
public class EntryImageViewModel
|
public class EntryImageViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
public EntryImageViewModel(IImage image)
|
public EntryImageViewModel(IImage image)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,21 +8,23 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Artemis.UI.Screens.Workshop.Entries.Details.EntryImagesView"
|
x:Class="Artemis.UI.Screens.Workshop.Entries.Details.EntryImagesView"
|
||||||
x:DataType="details:EntryImagesViewModel">
|
x:DataType="details:EntryImagesViewModel">
|
||||||
<ItemsControl ItemsSource="{CompiledBinding Images}" Margin="0 -16 0 0">
|
<ScrollViewer Classes="with-padding" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||||
|
<ItemsControl ItemsSource="{CompiledBinding Images}" Width="300">
|
||||||
|
<ItemsControl.Styles>
|
||||||
|
<Styles>
|
||||||
|
<Style Selector="ItemsControl > ContentPresenter">
|
||||||
|
<Setter Property="Margin" Value="0 0 0 10"></Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ItemsControl > ContentPresenter:nth-last-child(1)">
|
||||||
|
<Setter Property="Margin" Value="0 0 0 0"></Setter>
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
||||||
|
</ItemsControl.Styles>
|
||||||
<ItemsControl.ItemsPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<VirtualizingStackPanel />
|
<VirtualizingStackPanel />
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
</ItemsControl.ItemsPanel>
|
</ItemsControl.ItemsPanel>
|
||||||
<ItemsControl.DataTemplates>
|
|
||||||
<DataTemplate x:DataType="details:EntryImageViewModel">
|
|
||||||
<Border CornerRadius="6"
|
|
||||||
Margin="0 16 0 0"
|
|
||||||
MaxWidth="300"
|
|
||||||
ClipToBounds="True">
|
|
||||||
<Image Stretch="UniformToFill" asyncImageLoader:ImageLoader.Source="{CompiledBinding ThumbnailUrl, Mode=OneWay}" />
|
|
||||||
</Border>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.DataTemplates>
|
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
</Styles>
|
</Styles>
|
||||||
</controls:NavigationView.Styles>
|
</controls:NavigationView.Styles>
|
||||||
|
|
||||||
<controls:Frame Name="TabFrame" IsNavigationStackEnabled="False" CacheSize="0" Padding="20">
|
<controls:Frame Name="TabFrame" IsNavigationStackEnabled="False" CacheSize="0" Padding="20 20 10 20">
|
||||||
<controls:Frame.NavigationPageFactory>
|
<controls:Frame.NavigationPageFactory>
|
||||||
<ui:PageFactory/>
|
<ui:PageFactory/>
|
||||||
</controls:Frame.NavigationPageFactory>
|
</controls:Frame.NavigationPageFactory>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.Workshop.Image;
|
namespace Artemis.UI.Screens.Workshop.Image;
|
||||||
|
|
||||||
public partial class ImagePropertiesDialogView : UserControl
|
public partial class ImagePropertiesDialogView : ReactiveUserControl<ImagePropertiesDialogViewModel>
|
||||||
{
|
{
|
||||||
public ImagePropertiesDialogView()
|
public ImagePropertiesDialogView()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
Source="{CompiledBinding Bitmap}" />
|
Source="{CompiledBinding Bitmap}" />
|
||||||
<StackPanel Grid.Row="1" Margin="12">
|
<StackPanel Grid.Row="1" Margin="12">
|
||||||
<TextBlock Text="{CompiledBinding Name, FallbackValue=Unnamed image}" TextTrimming="CharacterEllipsis" />
|
<TextBlock Text="{CompiledBinding Name, FallbackValue=Unnamed image}" TextTrimming="CharacterEllipsis" />
|
||||||
<TextBlock TextWrapping="Wrap" Classes="subtitle" Text="{CompiledBinding Description}" />
|
<TextBlock TextWrapping="Wrap" Classes="subtitle" Text="{CompiledBinding Description, FallbackValue='No description'}" />
|
||||||
<Separator Margin="-4 10" />
|
<Separator Margin="-4 10" />
|
||||||
<TextBlock TextWrapping="Wrap" Classes="subtitle">
|
<TextBlock TextWrapping="Wrap" Classes="subtitle">
|
||||||
<Run Text="{CompiledBinding ImageDimensions}" /> - <Run Text="{CompiledBinding FileSize, Converter={StaticResource BytesToStringConverter}, Mode=OneWay}" />
|
<Run Text="{CompiledBinding ImageDimensions}" /> - <Run Text="{CompiledBinding FileSize, Converter={StaticResource BytesToStringConverter}, Mode=OneWay}" />
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ using System.Threading.Tasks;
|
|||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
using Artemis.UI.Shared.Services.Builders;
|
|
||||||
using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
|
using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
using PropertyChanged.SourceGenerator;
|
using PropertyChanged.SourceGenerator;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using ReactiveUI.Validation.Extensions;
|
using ReactiveUI.Validation.Extensions;
|
||||||
|
using ContentDialogButton = Artemis.UI.Shared.Services.Builders.ContentDialogButton;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.Workshop.Image;
|
namespace Artemis.UI.Screens.Workshop.Image;
|
||||||
|
|
||||||
@@ -30,30 +31,35 @@ public partial class ImageSubmissionViewModel : ValidatableViewModelBase
|
|||||||
_image = image;
|
_image = image;
|
||||||
_windowService = windowService;
|
_windowService = windowService;
|
||||||
|
|
||||||
|
FileSize = _image.File.Length;
|
||||||
|
Name = _image.Name;
|
||||||
|
Description = _image.Description;
|
||||||
|
|
||||||
this.WhenActivated(d =>
|
this.WhenActivated(d =>
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Invoke(() =>
|
Dispatcher.UIThread.Invoke(() =>
|
||||||
{
|
{
|
||||||
_image.File.Seek(0, SeekOrigin.Begin);
|
_image.File.Seek(0, SeekOrigin.Begin);
|
||||||
Bitmap = new Bitmap(_image.File);
|
Bitmap = new Bitmap(_image.File);
|
||||||
FileSize = _image.File.Length;
|
|
||||||
ImageDimensions = Bitmap.Size.Width + "x" + Bitmap.Size.Height;
|
ImageDimensions = Bitmap.Size.Width + "x" + Bitmap.Size.Height;
|
||||||
Name = _image.Name;
|
|
||||||
Description = _image.Description;
|
|
||||||
|
|
||||||
Bitmap.DisposeWith(d);
|
Bitmap.DisposeWith(d);
|
||||||
}, DispatcherPriority.Background);
|
}, DispatcherPriority.Background);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Edit()
|
public async Task<ContentDialogResult> Edit()
|
||||||
{
|
{
|
||||||
await _windowService.CreateContentDialog()
|
ContentDialogResult result = await _windowService.CreateContentDialog()
|
||||||
.WithTitle("Edit image properties")
|
.WithTitle("Edit image properties")
|
||||||
.WithViewModel(out ImagePropertiesDialogViewModel vm, _image)
|
.WithViewModel(out ImagePropertiesDialogViewModel vm, _image)
|
||||||
.HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Confirm))
|
.HavingPrimaryButton(b => b.WithText("Confirm").WithCommand(vm.Confirm))
|
||||||
.WithCloseButtonText("Cancel")
|
.WithCloseButtonText("Cancel")
|
||||||
.WithDefaultButton(ContentDialogButton.Primary)
|
.WithDefaultButton(ContentDialogButton.Primary)
|
||||||
.ShowAsync();
|
.ShowAsync();
|
||||||
|
|
||||||
|
Name = _image.Name;
|
||||||
|
Description = _image.Description;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,7 +158,7 @@ public partial class SubmissionDetailViewModel : RoutableScreen<WorkshopDetailPa
|
|||||||
{
|
{
|
||||||
using MemoryStream stream = new();
|
using MemoryStream stream = new();
|
||||||
EntrySpecificationsViewModel.IconBitmap.Save(stream);
|
EntrySpecificationsViewModel.IconBitmap.Save(stream);
|
||||||
ImageUploadResult imageResult = await _workshopService.SetEntryIcon(Entry.Id, new Progress<StreamProgress>(), stream, cancellationToken);
|
ImageUploadResult imageResult = await _workshopService.SetEntryIcon(Entry.Id, stream, cancellationToken);
|
||||||
if (!imageResult.IsSuccess)
|
if (!imageResult.IsSuccess)
|
||||||
throw new ArtemisWorkshopException("Failed to upload image. " + imageResult.Message);
|
throw new ArtemisWorkshopException("Failed to upload image. " + imageResult.Message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
x:Class="Artemis.UI.Screens.Workshop.Profile.ProfileDetailsView"
|
x:Class="Artemis.UI.Screens.Workshop.Profile.ProfileDetailsView"
|
||||||
x:DataType="profile:ProfileDetailsViewModel">
|
x:DataType="profile:ProfileDetailsViewModel">
|
||||||
<Grid ColumnDefinitions="300,*, 300" RowDefinitions="Auto,*">
|
<Grid ColumnDefinitions="300,*, 300" RowDefinitions="Auto,*">
|
||||||
<StackPanel Grid.Row="1" Grid.Column="0" Margin="0 0 10 0" Spacing="10">
|
<StackPanel Grid.Row="1" Grid.Column="0" Spacing="10">
|
||||||
<Border Classes="card" VerticalAlignment="Top">
|
<Border Classes="card" VerticalAlignment="Top">
|
||||||
<ContentControl Content="{CompiledBinding EntryInfoViewModel}" />
|
<ContentControl Content="{CompiledBinding EntryInfoViewModel}" />
|
||||||
</Border>
|
</Border>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
</Border>
|
</Border>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<Border Classes="card" Grid.Row="1" Grid.Column="1">
|
<Border Classes="card" Grid.Row="1" Grid.Column="1" Margin="10 0">
|
||||||
<mdxaml:MarkdownScrollViewer Markdown="{CompiledBinding Entry.Description}" MarkdownStyleName="FluentAvalonia">
|
<mdxaml:MarkdownScrollViewer Markdown="{CompiledBinding Entry.Description}" MarkdownStyleName="FluentAvalonia">
|
||||||
<mdxaml:MarkdownScrollViewer.Styles>
|
<mdxaml:MarkdownScrollViewer.Styles>
|
||||||
<StyleInclude Source="/Styles/Markdown.axaml" />
|
<StyleInclude Source="/Styles/Markdown.axaml" />
|
||||||
@@ -25,8 +25,6 @@
|
|||||||
</mdxaml:MarkdownScrollViewer>
|
</mdxaml:MarkdownScrollViewer>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<StackPanel Grid.Row="1" Grid.Column="2" IsVisible="{CompiledBinding Entry.Images.Count}">
|
<ContentControl Grid.Row="1" Grid.Column="2" IsVisible="{CompiledBinding Entry.Images.Count}" Content="{CompiledBinding EntryImagesViewModel}" />
|
||||||
<ContentControl Content="{CompiledBinding EntryImagesViewModel}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -9,6 +9,7 @@ using Artemis.UI.Screens.Workshop.Image;
|
|||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
|
using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
||||||
@@ -49,7 +50,7 @@ public class ImagesStepViewModel : SubmissionViewModel
|
|||||||
private ImageSubmissionViewModel CreateImageSubmissionViewModel(ImageUploadRequest image)
|
private ImageSubmissionViewModel CreateImageSubmissionViewModel(ImageUploadRequest image)
|
||||||
{
|
{
|
||||||
ImageSubmissionViewModel viewModel = _getImageSubmissionViewModel(image);
|
ImageSubmissionViewModel viewModel = _getImageSubmissionViewModel(image);
|
||||||
viewModel.Remove = ReactiveCommand.Create(() => _stateImages.Remove(image));
|
viewModel.Remove = ReactiveCommand.Create(() => RemoveImage(image));
|
||||||
return viewModel;
|
return viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,8 +74,23 @@ public class ImagesStepViewModel : SubmissionViewModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImageUploadRequest request = new(stream, Path.GetFileName(path), string.Empty);
|
ImageUploadRequest request = new(stream, Path.GetFileName(path), string.Empty);
|
||||||
_stateImages.Add(request);
|
AddImage(request);
|
||||||
State.Images.Add(request);
|
|
||||||
|
// Show the dialog to give the image a name and description
|
||||||
|
if (await Images.Last().Edit() != ContentDialogResult.Primary)
|
||||||
|
RemoveImage(request); // user did not click confirm, remove again
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddImage(ImageUploadRequest image)
|
||||||
|
{
|
||||||
|
_stateImages.Add(image);
|
||||||
|
State.Images.Add(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveImage(ImageUploadRequest image)
|
||||||
|
{
|
||||||
|
_stateImages.Remove(image);
|
||||||
|
State.Images.Remove(image);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -16,16 +16,10 @@
|
|||||||
</Styles>
|
</Styles>
|
||||||
</StackPanel.Styles>
|
</StackPanel.Styles>
|
||||||
|
|
||||||
<TextBlock IsVisible="{CompiledBinding !Finished}" Theme="{StaticResource TitleTextBlockStyle}" TextAlignment="Center" TextWrapping="Wrap">
|
<StackPanel IsVisible="{CompiledBinding !Finished}">
|
||||||
Uploading your submission...
|
<Lottie Path="/Assets/Animations/busy.json" Width="250" Height="250" Margin="0 100" ></Lottie>
|
||||||
</TextBlock>
|
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">Uploading your submission...</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
<ProgressBar IsVisible="{CompiledBinding !Finished}"
|
|
||||||
Margin="0 15 0 0"
|
|
||||||
Width="380"
|
|
||||||
IsIndeterminate="{CompiledBinding ProgressIndeterminate}"
|
|
||||||
Value="{CompiledBinding ProgressPercentage}">
|
|
||||||
</ProgressBar>
|
|
||||||
|
|
||||||
<StackPanel IsVisible="{CompiledBinding Succeeded}">
|
<StackPanel IsVisible="{CompiledBinding Succeeded}">
|
||||||
<Lottie Path="/Assets/Animations/success.json" RepeatCount="1" Width="250" Height="250" Margin="0 100"></Lottie>
|
<Lottie Path="/Assets/Animations/success.json" RepeatCount="1" Width="250" Height="250" Margin="0 100"></Lottie>
|
||||||
|
|||||||
@@ -23,9 +23,6 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
|||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly EntryUploadHandlerFactory _entryUploadHandlerFactory;
|
private readonly EntryUploadHandlerFactory _entryUploadHandlerFactory;
|
||||||
private readonly Progress<StreamProgress> _progress = new();
|
|
||||||
private readonly ObservableAsPropertyHelper<bool> _progressIndeterminate;
|
|
||||||
private readonly ObservableAsPropertyHelper<int> _progressPercentage;
|
|
||||||
private readonly IRouter _router;
|
private readonly IRouter _router;
|
||||||
private readonly IWindowService _windowService;
|
private readonly IWindowService _windowService;
|
||||||
private readonly IWorkshopClient _workshopClient;
|
private readonly IWorkshopClient _workshopClient;
|
||||||
@@ -54,19 +51,9 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
|||||||
ContinueText = "Finish";
|
ContinueText = "Finish";
|
||||||
Continue = ReactiveCommand.CreateFromTask(ExecuteContinue, this.WhenAnyValue(vm => vm.Finished));
|
Continue = ReactiveCommand.CreateFromTask(ExecuteContinue, this.WhenAnyValue(vm => vm.Finished));
|
||||||
|
|
||||||
_progressPercentage = Observable.FromEventPattern<StreamProgress>(x => _progress.ProgressChanged += x, x => _progress.ProgressChanged -= x)
|
|
||||||
.Select(e => e.EventArgs.ProgressPercentage)
|
|
||||||
.ToProperty(this, vm => vm.ProgressPercentage);
|
|
||||||
_progressIndeterminate = Observable.FromEventPattern<StreamProgress>(x => _progress.ProgressChanged += x, x => _progress.ProgressChanged -= x)
|
|
||||||
.Select(e => e.EventArgs.ProgressPercentage == 0)
|
|
||||||
.ToProperty(this, vm => vm.ProgressIndeterminate);
|
|
||||||
|
|
||||||
this.WhenActivated(d => Observable.FromAsync(ExecuteUpload).Subscribe().DisposeWith(d));
|
this.WhenActivated(d => Observable.FromAsync(ExecuteUpload).Subscribe().DisposeWith(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ProgressPercentage => _progressPercentage.Value;
|
|
||||||
public bool ProgressIndeterminate => _progressIndeterminate.Value;
|
|
||||||
|
|
||||||
private async Task ExecuteUpload(CancellationToken cancellationToken)
|
private async Task ExecuteUpload(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Use the existing entry or create a new one
|
// Use the existing entry or create a new one
|
||||||
@@ -79,7 +66,7 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
IEntryUploadHandler uploadHandler = _entryUploadHandlerFactory.CreateHandler(State.EntryType);
|
IEntryUploadHandler uploadHandler = _entryUploadHandlerFactory.CreateHandler(State.EntryType);
|
||||||
EntryUploadResult uploadResult = await uploadHandler.CreateReleaseAsync(_entryId.Value, State.EntrySource!, _progress, cancellationToken);
|
EntryUploadResult uploadResult = await uploadHandler.CreateReleaseAsync(_entryId.Value, State.EntrySource!, cancellationToken);
|
||||||
if (!uploadResult.IsSuccess)
|
if (!uploadResult.IsSuccess)
|
||||||
{
|
{
|
||||||
string? message = uploadResult.Message;
|
string? message = uploadResult.Message;
|
||||||
@@ -109,6 +96,8 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
|||||||
|
|
||||||
private async Task<long?> CreateEntry(CancellationToken cancellationToken)
|
private async Task<long?> CreateEntry(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
await Task.Delay(2000);
|
||||||
|
|
||||||
IOperationResult<IAddEntryResult> result = await _workshopClient.AddEntry.ExecuteAsync(new CreateEntryInput
|
IOperationResult<IAddEntryResult> result = await _workshopClient.AddEntry.ExecuteAsync(new CreateEntryInput
|
||||||
{
|
{
|
||||||
EntryType = State.EntryType,
|
EntryType = State.EntryType,
|
||||||
@@ -127,52 +116,35 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
{
|
|
||||||
State.ChangeScreen<SubmitStepViewModel>();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (ImageUploadRequest image in State.Images.ToList())
|
foreach (ImageUploadRequest image in State.Images.ToList())
|
||||||
{
|
{
|
||||||
// Upload image
|
await TryImageUpload(async () => await _workshopService.UploadEntryImage(entryId.Value, image, cancellationToken));
|
||||||
try
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
{
|
|
||||||
ImageUploadResult imageUploadResult = await _workshopService.UploadEntryImage(entryId.Value, image, _progress, cancellationToken);
|
|
||||||
if (!imageUploadResult.IsSuccess)
|
|
||||||
throw new ArtemisWorkshopException(imageUploadResult.Message);
|
|
||||||
State.Images.Remove(image);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// It's not critical if this fails
|
|
||||||
await _windowService.ShowConfirmContentDialog("Failed to upload image", "Your submission will continue, you can try upload a new image afterwards\r\n" + e.Message, "Continue", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
State.ChangeScreen<SubmitStepViewModel>();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (State.Icon == null)
|
if (State.Icon == null)
|
||||||
return entryId;
|
return entryId;
|
||||||
|
|
||||||
// Upload icon
|
// Upload icon
|
||||||
|
await TryImageUpload(async () => await _workshopService.SetEntryIcon(entryId.Value, State.Icon, cancellationToken));
|
||||||
|
|
||||||
|
return entryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task TryImageUpload(Func<Task<ImageUploadResult>> action)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ImageUploadResult imageUploadResult = await _workshopService.SetEntryIcon(entryId.Value, _progress, State.Icon, cancellationToken);
|
ImageUploadResult result = await action();
|
||||||
if (!imageUploadResult.IsSuccess)
|
if (!result.IsSuccess)
|
||||||
throw new ArtemisWorkshopException(imageUploadResult.Message);
|
throw new ArtemisWorkshopException(result.Message);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
// It's not critical if this fails
|
// It's not critical if this fails
|
||||||
await _windowService.ShowConfirmContentDialog("Failed to upload icon", "Your submission will continue, you can try upload a new image afterwards\r\n" + e.Message, "Continue", null);
|
await _windowService.ShowConfirmContentDialog("Failed to upload", "Your submission will continue, you can try upload a new image afterwards\r\n" + e.Message, "Continue", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entryId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExecuteContinue()
|
private async Task ExecuteContinue()
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ namespace Artemis.WebClient.Workshop.Handlers.UploadHandlers;
|
|||||||
|
|
||||||
public interface IEntryUploadHandler
|
public interface IEntryUploadHandler
|
||||||
{
|
{
|
||||||
Task<EntryUploadResult> CreateReleaseAsync(long entryId, IEntrySource entrySource, Progress<StreamProgress> progress, CancellationToken cancellationToken);
|
Task<EntryUploadResult> CreateReleaseAsync(long entryId, IEntrySource entrySource, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
+2
-2
@@ -19,7 +19,7 @@ public class LayoutEntryUploadHandler : IEntryUploadHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<EntryUploadResult> CreateReleaseAsync(long entryId, IEntrySource entrySource, Progress<StreamProgress> progress, CancellationToken cancellationToken)
|
public async Task<EntryUploadResult> CreateReleaseAsync(long entryId, IEntrySource entrySource, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (entrySource is not LayoutEntrySource source)
|
if (entrySource is not LayoutEntrySource source)
|
||||||
throw new InvalidOperationException("Can only create releases for layouts");
|
throw new InvalidOperationException("Can only create releases for layouts");
|
||||||
@@ -67,7 +67,7 @@ public class LayoutEntryUploadHandler : IEntryUploadHandler
|
|||||||
|
|
||||||
// Construct the request
|
// Construct the request
|
||||||
MultipartFormDataContent content = new();
|
MultipartFormDataContent content = new();
|
||||||
ProgressableStreamContent streamContent = new(archiveStream, progress);
|
StreamContent streamContent = new(archiveStream);
|
||||||
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
|
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
|
||||||
content.Add(streamContent, "file", "file.zip");
|
content.Add(streamContent, "file", "file.zip");
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -18,7 +18,7 @@ public class ProfileEntryUploadHandler : IEntryUploadHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<EntryUploadResult> CreateReleaseAsync(long entryId, IEntrySource entrySource, Progress<StreamProgress> progress, CancellationToken cancellationToken)
|
public async Task<EntryUploadResult> CreateReleaseAsync(long entryId, IEntrySource entrySource, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (entrySource is not ProfileEntrySource source)
|
if (entrySource is not ProfileEntrySource source)
|
||||||
throw new InvalidOperationException("Can only create releases for profile configurations");
|
throw new InvalidOperationException("Can only create releases for profile configurations");
|
||||||
@@ -30,7 +30,7 @@ public class ProfileEntryUploadHandler : IEntryUploadHandler
|
|||||||
|
|
||||||
// Construct the request
|
// Construct the request
|
||||||
MultipartFormDataContent content = new();
|
MultipartFormDataContent content = new();
|
||||||
ProgressableStreamContent streamContent = new(archiveStream, progress);
|
StreamContent streamContent = new(archiveStream);
|
||||||
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
|
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
|
||||||
content.Add(streamContent, "file", "file.zip");
|
content.Add(streamContent, "file", "file.zip");
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ namespace Artemis.WebClient.Workshop.Services;
|
|||||||
public interface IWorkshopService
|
public interface IWorkshopService
|
||||||
{
|
{
|
||||||
Task<Stream?> GetEntryIcon(long entryId, CancellationToken cancellationToken);
|
Task<Stream?> GetEntryIcon(long entryId, CancellationToken cancellationToken);
|
||||||
Task<ImageUploadResult> SetEntryIcon(long entryId, Progress<StreamProgress> progress, Stream icon, CancellationToken cancellationToken);
|
Task<ImageUploadResult> SetEntryIcon(long entryId, Stream icon, CancellationToken cancellationToken);
|
||||||
Task<ImageUploadResult> UploadEntryImage(long entryId, ImageUploadRequest request, Progress<StreamProgress> progress, CancellationToken cancellationToken);
|
Task<ImageUploadResult> UploadEntryImage(long entryId, ImageUploadRequest request, CancellationToken cancellationToken);
|
||||||
Task<WorkshopStatus> GetWorkshopStatus(CancellationToken cancellationToken);
|
Task<WorkshopStatus> GetWorkshopStatus(CancellationToken cancellationToken);
|
||||||
Task<bool> ValidateWorkshopStatus(CancellationToken cancellationToken);
|
Task<bool> ValidateWorkshopStatus(CancellationToken cancellationToken);
|
||||||
Task NavigateToEntry(long entryId, EntryType entryType);
|
Task NavigateToEntry(long entryId, EntryType entryType);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class WorkshopService : IWorkshopService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ImageUploadResult> SetEntryIcon(long entryId, Progress<StreamProgress> progress, Stream icon, CancellationToken cancellationToken)
|
public async Task<ImageUploadResult> SetEntryIcon(long entryId, Stream icon, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
icon.Seek(0, SeekOrigin.Begin);
|
icon.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ public class WorkshopService : IWorkshopService
|
|||||||
|
|
||||||
// Construct the request
|
// Construct the request
|
||||||
MultipartFormDataContent content = new();
|
MultipartFormDataContent content = new();
|
||||||
ProgressableStreamContent streamContent = new(icon, progress);
|
StreamContent streamContent = new(icon);
|
||||||
streamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
|
streamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
|
||||||
content.Add(streamContent, "file", "file.png");
|
content.Add(streamContent, "file", "file.png");
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ public class WorkshopService : IWorkshopService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<ImageUploadResult> UploadEntryImage(long entryId, ImageUploadRequest request, Progress<StreamProgress> progress, CancellationToken cancellationToken)
|
public async Task<ImageUploadResult> UploadEntryImage(long entryId, ImageUploadRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
request.File.Seek(0, SeekOrigin.Begin);
|
request.File.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ public class WorkshopService : IWorkshopService
|
|||||||
|
|
||||||
// Construct the request
|
// Construct the request
|
||||||
MultipartFormDataContent content = new();
|
MultipartFormDataContent content = new();
|
||||||
ProgressableStreamContent streamContent = new(request.File, progress);
|
StreamContent streamContent = new(request.File);
|
||||||
streamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
|
streamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
|
||||||
content.Add(streamContent, "file", "file.png");
|
content.Add(streamContent, "file", "file.png");
|
||||||
content.Add(new StringContent(request.Name), "Name");
|
content.Add(new StringContent(request.Name), "Name");
|
||||||
|
|||||||
Reference in new issue
Block a user