CreatorPostsViewModel.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. using Avalonia.Notification;
  2. using HanumanInstitute.MvvmDialogs;
  3. using ReactiveUI;
  4. using System;
  5. using System.Collections.ObjectModel;
  6. using System.ComponentModel;
  7. using System.Linq;
  8. using System.Net.Http;
  9. using System.Reactive.Linq;
  10. using System.Threading;
  11. using System.Threading.Tasks;
  12. using VeloeAvaloniaKemonoPartyApp.Models;
  13. namespace VeloeAvaloniaKemonoPartyApp.ViewModels
  14. {
  15. public class CreatorPostsViewModel : ViewModelBase, IModalDialogViewModel, IViewClosing, IViewLoaded, ICloseable
  16. {
  17. private readonly IDialogService _dialogService;
  18. public event EventHandler? RequestClose;
  19. public bool? DialogResult => true;
  20. public ObservableCollection<PostViewModel> Posts { get; } = new();
  21. public CreatorPostsViewModel(IDialogService dialogService)
  22. {
  23. _dialogService = dialogService;
  24. Close = ReactiveCommand.Create(CloseImpl);
  25. this.WhenAnyValue(x => x.SearchText)
  26. .Throttle(TimeSpan.FromMilliseconds(400))
  27. .ObserveOn(RxApp.MainThreadScheduler)
  28. .Subscribe(InitPosts!);
  29. }
  30. private CancellationTokenSource? _cancellationTokenSource;
  31. private bool _isBusy = false;
  32. public bool IsBusy
  33. {
  34. get => _isBusy;
  35. set => this.RaiseAndSetIfChanged(ref _isBusy, value);
  36. }
  37. private string _searchText = string.Empty;
  38. public string SearchText
  39. {
  40. get => _searchText;
  41. set => this.RaiseAndSetIfChanged(ref _searchText, value);
  42. }
  43. public INotificationMessageManager Manager { get; } = new NotificationMessageManager();
  44. private Creator _creator;
  45. public Creator Creator
  46. {
  47. get => _creator;
  48. set
  49. {
  50. _creator = value;
  51. InitPosts();
  52. }
  53. }
  54. private async void InitPosts(string search = "")
  55. {
  56. _cancellationTokenSource?.Cancel();
  57. _cancellationTokenSource = new CancellationTokenSource();
  58. var cancellationToken = _cancellationTokenSource.Token;
  59. IsBusy = true;
  60. Posts.Clear();
  61. if (_creator is not null)
  62. {
  63. try
  64. {
  65. var posts = await Post.LoadPostsAsync(_creator, 0, search);
  66. foreach (var post in posts)
  67. {
  68. var vm = new PostViewModel(_dialogService, this, post);
  69. Posts.Add(vm);
  70. }
  71. if (!cancellationToken.IsCancellationRequested)
  72. {
  73. LoadAvatars(cancellationToken);
  74. }
  75. }
  76. catch (HttpRequestException ex)
  77. {
  78. Manager
  79. .CreateMessage()
  80. .Accent("#1751C3")
  81. .Animates(true)
  82. .Background("#333")
  83. .HasBadge("Error")
  84. .HasMessage(ex.Message)
  85. .Dismiss().WithDelay(TimeSpan.FromSeconds(5))
  86. .Queue();
  87. }
  88. }
  89. IsBusy = false;
  90. }
  91. public async void LoadMorePosts()
  92. {
  93. if (IsBusy) return;
  94. if (_cancellationTokenSource is null)
  95. _cancellationTokenSource = new();
  96. var cancellationToken = _cancellationTokenSource.Token;
  97. IsBusy = true;
  98. if (_creator is not null)
  99. {
  100. try
  101. {
  102. var startIndex = Posts.Count;
  103. var posts = await Post.LoadPostsAsync(_creator, startIndex, SearchText);
  104. foreach (var post in posts)
  105. {
  106. var vm = new PostViewModel(_dialogService, this, post);
  107. Posts.Add(vm);
  108. }
  109. if (!cancellationToken.IsCancellationRequested)
  110. {
  111. LoadAvatars(cancellationToken, startIndex);
  112. }
  113. }
  114. catch (HttpRequestException ex)
  115. {
  116. Manager
  117. .CreateMessage()
  118. .Accent("#1751C3")
  119. .Animates(true)
  120. .Background("#333")
  121. .HasBadge("Error")
  122. .HasMessage(ex.Message)
  123. .Dismiss().WithDelay(TimeSpan.FromSeconds(5))
  124. .Queue();
  125. }
  126. }
  127. IsBusy = false;
  128. }
  129. private async void LoadAvatars(CancellationToken cancellationToken,int startIndex = 0)
  130. {
  131. foreach (var images in Posts.Skip(startIndex).SelectMany(x=>x.Images.Concat(x.ContentViewModels.OfType<PostImageViewModel>())).ToList())
  132. {
  133. await images.LoadAvatar();
  134. if (cancellationToken.IsCancellationRequested)
  135. {
  136. return;
  137. }
  138. }
  139. }
  140. public ReactiveCommand<System.Reactive.Unit, System.Reactive.Unit> Close { get; }
  141. public void OnLoaded()
  142. {
  143. }
  144. public void OnClosing(CancelEventArgs e)
  145. {
  146. e.Cancel = true;
  147. }
  148. private void CloseImpl()
  149. {
  150. RequestClose?.Invoke(this, EventArgs.Empty);
  151. }
  152. public async Task OnClosingAsync(CancelEventArgs e)
  153. {
  154. foreach (var post in Posts)
  155. {
  156. post.Attachments.Clear();
  157. post.Images.Clear();
  158. post.ContentViewModels.Clear();
  159. }
  160. Posts.Clear();
  161. GC.Collect();
  162. //var result = await _dialogService.ShowMessageBoxAsync(this, "Do you want to close it?", "Confirmation", MessageBoxButton.YesNo);
  163. _cancellationTokenSource?.Cancel();
  164. e.Cancel = false;
  165. }
  166. }
  167. }