Преглед на файлове

added basic network exceptions handling

Veloe преди 1 година
родител
ревизия
08ba4b4288
променени са 14 файла, в които са добавени 197 реда и са изтрити 97 реда
  1. 3 3
      VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp.Android/VeloeAvaloniaKemonoPartyApp.Android.csproj
  2. 2 2
      VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp.Browser/VeloeAvaloniaKemonoPartyApp.Browser.csproj
  3. 2 2
      VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp.Desktop/VeloeAvaloniaKemonoPartyApp.Desktop.csproj
  4. 2 2
      VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp.iOS/VeloeAvaloniaKemonoPartyApp.iOS.csproj
  5. 1 0
      VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/App.axaml
  6. 2 7
      VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/Models/Post.cs
  7. 27 13
      VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/Services/HttpClient.cs
  8. 3 2
      VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp.csproj
  9. 52 18
      VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/ViewModels/CreatorPostsViewModel.cs
  10. 75 27
      VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/ViewModels/CreatorsViewModel.cs
  11. 1 0
      VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/ViewModels/ImageZoomViewModel.cs
  12. 5 3
      VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/Views/CreatorPostsView.axaml
  13. 21 17
      VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/Views/CreatorsView.axaml
  14. 1 1
      VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/Views/CreatorsView.axaml.cs

+ 3 - 3
VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp.Android/VeloeAvaloniaKemonoPartyApp.Android.csproj

@@ -6,11 +6,11 @@
     <Nullable>enable</Nullable>
     <ApplicationId>com.Veloe.AvaloniaKemonoParty</ApplicationId>
     <ApplicationVersion>1</ApplicationVersion>
-    <ApplicationDisplayVersion>1.0.0.117</ApplicationDisplayVersion>
+    <ApplicationDisplayVersion>1.0.0.149</ApplicationDisplayVersion>
     <AndroidPackageFormat>apk</AndroidPackageFormat>
     <AndroidEnableProfiledAot>False</AndroidEnableProfiledAot>
-    <AssemblyVersion>1.0.0.117</AssemblyVersion>
-    <FileVersion>1.0.0.117</FileVersion>
+    <AssemblyVersion>1.0.0.149</AssemblyVersion>
+    <FileVersion>1.0.0.149</FileVersion>
   </PropertyGroup>
 
   <ItemGroup>

+ 2 - 2
VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp.Browser/VeloeAvaloniaKemonoPartyApp.Browser.csproj

@@ -4,8 +4,8 @@
     <RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
     <WasmMainJSPath>AppBundle\main.js</WasmMainJSPath>
     <OutputType>Exe</OutputType>
-    <AssemblyVersion>1.0.0.8</AssemblyVersion>
-    <FileVersion>1.0.0.8</FileVersion>
+    <AssemblyVersion>1.0.0.9</AssemblyVersion>
+    <FileVersion>1.0.0.9</FileVersion>
   </PropertyGroup>
 
   <ItemGroup>

+ 2 - 2
VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp.Desktop/VeloeAvaloniaKemonoPartyApp.Desktop.csproj

@@ -10,8 +10,8 @@
 
   <PropertyGroup>
     <ApplicationManifest>app.manifest</ApplicationManifest>
-    <AssemblyVersion>1.0.0.97</AssemblyVersion>
-    <FileVersion>1.0.0.97</FileVersion>
+    <AssemblyVersion>1.0.0.107</AssemblyVersion>
+    <FileVersion>1.0.0.107</FileVersion>
   </PropertyGroup>
 
   <ItemGroup>

+ 2 - 2
VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp.iOS/VeloeAvaloniaKemonoPartyApp.iOS.csproj

@@ -4,8 +4,8 @@
     <TargetFramework>net8.0-ios</TargetFramework>
     <SupportedOSPlatformVersion>13.0</SupportedOSPlatformVersion>
     <Nullable>enable</Nullable>
-    <AssemblyVersion>1.0.0.8</AssemblyVersion>
-    <FileVersion>1.0.0.8</FileVersion>
+    <AssemblyVersion>1.0.0.9</AssemblyVersion>
+    <FileVersion>1.0.0.9</FileVersion>
   </PropertyGroup>
 
   <ItemGroup>

+ 1 - 0
VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/App.axaml

@@ -12,6 +12,7 @@
     <Application.Styles>
         <FluentTheme />
 		<StyleInclude Source="avares://Avalonia.Controls.TreeDataGrid/Themes/Fluent.axaml"/>
+		<StyleInclude Source="avares://Notification.Avalonia/Themes/Generic.xaml" />
     </Application.Styles>
 
 </Application>

+ 2 - 7
VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/Models/Post.cs

@@ -83,7 +83,7 @@ namespace VeloeAvaloniaKemonoPartyApp.Models
 
         private string FileName => Path.Split('/').Last();
 
-        public async Task<Stream> LoadCoverBitmapAsync()
+        public async Task<Stream?> LoadCoverBitmapAsync()
         {
             if (System.IO.File.Exists(System.IO.Path.Combine(await RegisteredServices.StorageService.GetCacheFolderAsync(),  FileName + ".bmp")))
             {
@@ -111,13 +111,8 @@ namespace VeloeAvaloniaKemonoPartyApp.Models
 
                 return new MemoryStream(data);
             }
-            catch (System.Net.Http.HttpRequestException ex)
-            {
-                return null;
-            }
-            catch (OperationCanceledException ex)
+            catch (Exception)
             {
-                //System.IO.File.Delete(CachePath + ".bmp");
                 return null;
             }
             

+ 27 - 13
VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/Services/HttpClient.cs

@@ -40,30 +40,44 @@ namespace VeloeKemonoPartyApp.Services
                 }
             }
 
-            using (HttpResponseMessage response = await httpClient.GetAsync("https://kemono.su/api/v1/creators.txt"))
-            using (HttpContent content = response.Content)
+            try
             {
-                using (var stream = System.IO.File.OpenWrite(await RegisteredServices.StorageService.GetCacheFolderAsync() + "/creators.json"))
+                using (HttpResponseMessage response = await httpClient.GetAsync("https://kemono.su/api/v1/creators.txt"))
+                using (HttpContent content = response.Content)
                 {
-                    stream.Write(await response.Content.ReadAsByteArrayAsync());
-                }
+                    using (var stream = System.IO.File.OpenWrite(await RegisteredServices.StorageService.GetCacheFolderAsync() + "/creators.json"))
+                    {
+                        stream.Write(await response.Content.ReadAsByteArrayAsync());
+                    }
 
-                return await JsonSerializer.DeserializeAsync<List<Creator>>(await response.Content.ReadAsStreamAsync());
+                    return await JsonSerializer.DeserializeAsync<List<Creator>>(await response.Content.ReadAsStreamAsync());
+                }
+            }
+            catch (Exception)
+            {
+                throw;
             }
         }
 
         public async Task<List<Post>> GetPostsList(string id, string service, int start, string search = "")
         {
-            using (HttpResponseMessage response = await httpClient.GetAsync($"https://kemono.su/api/v1/{service}/user/{id}?o={start}" + (!string.IsNullOrEmpty(search) ? $"&q={search}" : string.Empty)))
-            using (HttpContent content = response.Content)
+            try
             {
-                if (response.IsSuccessStatusCode)
+                using (HttpResponseMessage response = await httpClient.GetAsync($"https://kemono.su/api/v1/{service}/user/{id}?o={start}" + (!string.IsNullOrEmpty(search) ? $"&q={search}" : string.Empty)))
+                using (HttpContent content = response.Content)
                 {
-                    string jsonString = await response.Content.ReadAsStringAsync();
-                    // ... Read the string.
-                    return JsonSerializer.Deserialize<List<Post>>(jsonString);
+                    if (response.IsSuccessStatusCode)
+                    {
+                        string jsonString = await response.Content.ReadAsStringAsync();
+                        // ... Read the string.
+                        return JsonSerializer.Deserialize<List<Post>>(jsonString);
+                    }
+                    return new List<Post>();
                 }
-                return new List<Post>();
+            }
+            catch (Exception)
+            {
+                throw;
             }
         }
 

+ 3 - 2
VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp.csproj

@@ -4,8 +4,8 @@
     <Nullable>enable</Nullable>
     <LangVersion>latest</LangVersion>
     <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
-    <AssemblyVersion>1.0.0.268</AssemblyVersion>
-    <FileVersion>1.0.0.268</FileVersion>
+    <AssemblyVersion>1.0.0.313</AssemblyVersion>
+    <FileVersion>1.0.0.313</FileVersion>
   </PropertyGroup>
 
   
@@ -26,6 +26,7 @@
     <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.6" />
     <PackageReference Include="HanumanInstitute.MvvmDialogs.Avalonia" Version="2.1.0" />
     <PackageReference Include="HanumanInstitute.MvvmDialogs.Avalonia.MessageBox" Version="2.1.0" />
+    <PackageReference Include="Notification.Avalonia" Version="2.1.0" />
     <PackageReference Include="Splat.DependencyInjection.SourceGenerator" Version="1.1.93">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

+ 52 - 18
VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/ViewModels/CreatorPostsViewModel.cs

@@ -1,9 +1,11 @@
-using HanumanInstitute.MvvmDialogs;
+using Avalonia.Notification;
+using HanumanInstitute.MvvmDialogs;
 using ReactiveUI;
 using System;
 using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.Linq;
+using System.Net.Http;
 using System.Reactive.Linq;
 using System.Threading;
 using System.Threading.Tasks;
@@ -46,6 +48,8 @@ namespace VeloeAvaloniaKemonoPartyApp.ViewModels
             set => this.RaiseAndSetIfChanged(ref _searchText, value);
         }
 
+        public INotificationMessageManager Manager { get; } = new NotificationMessageManager();
+
         private Creator _creator;
         public Creator Creator 
         {
@@ -68,17 +72,32 @@ namespace VeloeAvaloniaKemonoPartyApp.ViewModels
 
             if (_creator is not null)
             {
-                var posts = await Post.LoadPostsAsync(_creator,0,search);
-
-                foreach (var post in posts)
+                try
                 {
-                    var vm = new PostViewModel(_dialogService,this,post);
-                    Posts.Add(vm);
+                    var posts = await Post.LoadPostsAsync(_creator, 0, search);
+
+                    foreach (var post in posts)
+                    {
+                        var vm = new PostViewModel(_dialogService, this, post);
+                        Posts.Add(vm);
+                    }
+
+                    if (!cancellationToken.IsCancellationRequested)
+                    {
+                        LoadAvatars(cancellationToken);
+                    }
                 }
-
-                if (!cancellationToken.IsCancellationRequested)
+                catch (HttpRequestException ex)
                 {
-                    LoadAvatars(cancellationToken);
+                    Manager
+                        .CreateMessage()
+                        .Accent("#1751C3")
+                        .Animates(true)
+                        .Background("#333")
+                        .HasBadge("Error")
+                        .HasMessage(ex.Message)
+                        .Dismiss().WithDelay(TimeSpan.FromSeconds(5))
+                        .Queue();
                 }
             }
 
@@ -98,19 +117,34 @@ namespace VeloeAvaloniaKemonoPartyApp.ViewModels
 
             if (_creator is not null)
             {
-                var startIndex = Posts.Count;
+                try
+                {
+                    var startIndex = Posts.Count;
 
-                var posts = await Post.LoadPostsAsync(_creator,startIndex,SearchText);
+                    var posts = await Post.LoadPostsAsync(_creator, startIndex, SearchText);
 
-                foreach (var post in posts)
-                {
-                    var vm = new PostViewModel(_dialogService,this,post);
-                    Posts.Add(vm);
-                }
+                    foreach (var post in posts)
+                    {
+                        var vm = new PostViewModel(_dialogService, this, post);
+                        Posts.Add(vm);
+                    }
 
-                if (!cancellationToken.IsCancellationRequested)
+                    if (!cancellationToken.IsCancellationRequested)
+                    {
+                        LoadAvatars(cancellationToken, startIndex);
+                    }
+                }
+                catch (HttpRequestException ex)
                 {
-                    LoadAvatars(cancellationToken,startIndex);
+                    Manager
+                        .CreateMessage()
+                        .Accent("#1751C3")
+                        .Animates(true)
+                        .Background("#333")
+                        .HasBadge("Error")
+                        .HasMessage(ex.Message)
+                        .Dismiss().WithDelay(TimeSpan.FromSeconds(5))
+                        .Queue();
                 }
             }
 

+ 75 - 27
VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/ViewModels/CreatorsViewModel.cs

@@ -1,16 +1,13 @@
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Input;
-using Avalonia.Interactivity;
+using Avalonia.Input;
+using Avalonia.Notification;
 using Avalonia.Threading;
 using DynamicData.Binding;
 using HanumanInstitute.MvvmDialogs;
 using ReactiveUI;
 using System;
-using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Linq;
 using System.Reactive.Linq;
-using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using VeloeAvaloniaKemonoPartyApp.Models;
@@ -39,10 +36,29 @@ namespace VeloeAvaloniaKemonoPartyApp.ViewModels
             this.WhenPropertyChanged(x => x.SelectedAlbum).Subscribe(async x => 
             {
                 if (x.Value is null) return;
-                var vm = _dialogService.CreateViewModel<CreatorPostsViewModel>();
-                vm.Creator = x.Value.Creator;
-                Dispatcher.UIThread.Post(() => { SelectedAlbum = null; });
-                await _dialogService.ShowDialogAsync(this, vm);
+                try
+                {
+                    var vm = _dialogService.CreateViewModel<CreatorPostsViewModel>();
+                    vm.Creator = x.Value.Creator;
+
+                    await _dialogService.ShowDialogAsync(this, vm);
+                }
+                catch (Exception ex)
+                {
+                    Manager
+                        .CreateMessage()
+                        .Accent("#1751C3")
+                        .Animates(true)
+                        .Background("#333")
+                        .HasBadge("Error")
+                        .HasMessage(ex.Message)
+                        .Dismiss().WithDelay(TimeSpan.FromSeconds(5))
+                        .Queue();
+                }
+                finally 
+                {
+                    Dispatcher.UIThread.Post(() => { SelectedAlbum = null; });
+                }
             });
         }
 
@@ -61,6 +77,8 @@ namespace VeloeAvaloniaKemonoPartyApp.ViewModels
             set => this.RaiseAndSetIfChanged(ref _isBusy, value);
         }
 
+        public INotificationMessageManager Manager { get; } = new NotificationMessageManager();
+
         private CreatorViewModel _selectedAlbum;
 
         public ObservableCollection<CreatorViewModel> SearchResults { get; } = new();
@@ -82,26 +100,41 @@ namespace VeloeAvaloniaKemonoPartyApp.ViewModels
 
             if (!string.IsNullOrWhiteSpace(s))
             {
-                var creators = await Creator.SearchAsync(s,false);
-
-                foreach (var creator in creators)
+                try
                 {
-                    var vm = new CreatorViewModel(creator);
-                    SearchResults.Add(vm);
+                    var creators = await Creator.SearchAsync(s, false);
+
+                    foreach (var creator in creators)
+                    {
+                        var vm = new CreatorViewModel(creator);
+                        SearchResults.Add(vm);
+                    }
+
+                    if (!cancellationToken.IsCancellationRequested)
+                    {
+                        LoadAvatars(cancellationToken);
+                    }
                 }
-
-                if (!cancellationToken.IsCancellationRequested)
+                catch (Exception ex)
                 {
-                    LoadAvatars(cancellationToken);
+                    Manager
+                        .CreateMessage()
+                        .Accent("#1751C3")
+                        .Animates(true)
+                        .Background("#333")
+                        .HasBadge("Error")
+                        .HasMessage(ex.Message)
+                        .Dismiss().WithDelay(TimeSpan.FromSeconds(5))
+                        .Queue();
                 }
             }
 
             IsBusy = false;
         }
 
-        public async Task ReloadCreatorsSource(object? sender, PullGestureEventArgs e)
+        public async Task ReloadCreatorsSource(object? sender, PullGestureEndedEventArgs e)
         {
-            if (IsBusy) return;
+            if (IsBusy || e.PullDirection != PullDirection.TopToBottom) return;
 
             _cancellationTokenSource?.Cancel();
             _cancellationTokenSource = new CancellationTokenSource();
@@ -112,17 +145,32 @@ namespace VeloeAvaloniaKemonoPartyApp.ViewModels
 
             if (!string.IsNullOrWhiteSpace(SearchText))
             {
-                var creators = await Creator.SearchAsync(SearchText,true);
-
-                foreach (var creator in creators)
+                try
                 {
-                    var vm = new CreatorViewModel(creator);
-                    SearchResults.Add(vm);
+                    var creators = await Creator.SearchAsync(SearchText, true);
+
+                    foreach (var creator in creators)
+                    {
+                        var vm = new CreatorViewModel(creator);
+                        SearchResults.Add(vm);
+                    }
+
+                    if (!cancellationToken.IsCancellationRequested)
+                    {
+                        LoadAvatars(cancellationToken);
+                    }
                 }
-
-                if (!cancellationToken.IsCancellationRequested)
+                catch (Exception ex)
                 {
-                    LoadAvatars(cancellationToken);
+                    Manager
+                        .CreateMessage()
+                        .Accent("#1751C3")
+                        .Animates(true)
+                        .Background("#333")
+                        .HasBadge("Error")
+                        .HasMessage(ex.Message)
+                        .Dismiss().WithDelay(TimeSpan.FromSeconds(5))
+                        .Queue();
                 }
             }
 

+ 1 - 0
VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/ViewModels/ImageZoomViewModel.cs

@@ -41,6 +41,7 @@ namespace VeloeAvaloniaKemonoPartyApp.ViewModels
             await using (var imageStream = await _attachment.LoadCoverBitmapAsync())
             {
                 if (imageStream is null) return;
+
                 Cover = await Task.Run(() =>
                 {
                     Bitmap? bitmap;

+ 5 - 3
VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/Views/CreatorPostsView.axaml

@@ -4,6 +4,7 @@
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 			 xmlns:m="using:VeloeAvaloniaKemonoPartyApp.Models"
 			 xmlns:vm="clr-namespace:VeloeAvaloniaKemonoPartyApp.ViewModels"
+			 xmlns:notif="using:Avalonia.Notification.Controls"
              mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="854"
              x:Class="VeloeAvaloniaKemonoPartyApp.Views.CreatorPostsView"
 			 MaxWidth="{Binding $self.Bounds.Height}"
@@ -13,8 +14,9 @@
          to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
 		<vm:CreatorPostsViewModel />
 	</Design.DataContext>
-	<DockPanel>
-		<ScrollViewer>
+	<Panel>
+		<notif:NotificationMessageContainer VerticalAlignment="Top" HorizontalAlignment="Stretch" Manager="{Binding Manager}" ZIndex="999"/>
+		<ScrollViewer VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
 			<StackPanel>
 				<StackPanel Margin="10 10 10 10">
 					<TextBox Watermark="Search in posts..." Text="{Binding SearchText}"/>
@@ -37,5 +39,5 @@
 				</Button>
 			</StackPanel>
 		</ScrollViewer>
-	</DockPanel>
+	</Panel>
 </UserControl>

+ 21 - 17
VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/Views/CreatorsView.axaml

@@ -4,6 +4,7 @@
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 			 xmlns:m="using:VeloeAvaloniaKemonoPartyApp.Models"
 			 xmlns:vm="clr-namespace:VeloeAvaloniaKemonoPartyApp.ViewModels"
+			 xmlns:notif="using:Avalonia.Notification.Controls"
              mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="854"
              x:Class="VeloeAvaloniaKemonoPartyApp.Views.CreatorsView"
 			 x:DataType="vm:CreatorsViewModel">
@@ -12,21 +13,24 @@
          to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
 		<vm:CreatorsViewModel />
 	</Design.DataContext>
-	<DockPanel>
-		<StackPanel DockPanel.Dock="Top" Margin="10 10 10 0">
-			<TextBox Watermark="Search for creators..." Text="{Binding SearchText}"/>
-			<ProgressBar IsIndeterminate="True" IsVisible="{Binding IsBusy}"/>
-		</StackPanel>
-		<ListBox ItemsSource="{Binding SearchResults}" SelectedItem="{Binding SelectedAlbum}" SelectionMode="Single"
-				 Background="Transparent" Margin="0 10" Gestures.PullGesture="ReloadCreatorsSource">
-			<ListBox.ItemsPanel>
-				<ItemsPanelTemplate>
-					<StackPanel/>
-				</ItemsPanelTemplate>
-			</ListBox.ItemsPanel>
-			<ListBox.GestureRecognizers>
-				<PullGestureRecognizer PullDirection="TopToBottom"/>
-			</ListBox.GestureRecognizers>
-		</ListBox>
-	</DockPanel>
+	<Panel>
+		<notif:NotificationMessageContainer VerticalAlignment="Top" HorizontalAlignment="Stretch"  Manager="{Binding Manager}" ZIndex="999" />
+		<DockPanel>
+			<StackPanel DockPanel.Dock="Top" Margin="10 10 10 0">
+				<TextBox Watermark="Search for creators..." Text="{Binding SearchText}"/>
+				<ProgressBar IsIndeterminate="True" IsVisible="{Binding IsBusy}"/>
+			</StackPanel>
+			<ListBox ItemsSource="{Binding SearchResults}" SelectedItem="{Binding SelectedAlbum}" SelectionMode="Single"
+						Background="Transparent" Margin="0 10" Gestures.PullGestureEnded="ReloadCreatorsSource">
+				<ListBox.ItemsPanel>
+					<ItemsPanelTemplate>
+							<StackPanel/>
+					</ItemsPanelTemplate>
+				</ListBox.ItemsPanel>
+				<ListBox.GestureRecognizers>
+					<PullGestureRecognizer PullDirection="TopToBottom"/>
+				</ListBox.GestureRecognizers>
+			</ListBox>		
+		</DockPanel>
+	</Panel>
 </UserControl>

+ 1 - 1
VeloeAvaloniaKemonoPartyApp/VeloeAvaloniaKemonoPartyApp/Views/CreatorsView.axaml.cs

@@ -17,7 +17,7 @@ public partial class CreatorsView : UserControl
         InitializeComponent();
     }
 
-    public async void ReloadCreatorsSource(object? sender, PullGestureEventArgs e)
+    public async void ReloadCreatorsSource(object? sender, PullGestureEndedEventArgs e)
     {
         await ((CreatorsViewModel)this.DataContext).ReloadCreatorsSource(sender, e); 
     }