365 lines
15 KiB
C#
365 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Windows.UI.Xaml.Media;
|
|
using Windows.UI.Xaml.Media.Imaging;
|
|
using PCUT.Entities;
|
|
using PCUT.Entities.ApiResponse;
|
|
using PCUT.Extensions;
|
|
using PCUT.Models;
|
|
using Http.Core;
|
|
using Http.Core.Extensions;
|
|
using static Http.Core.Constants.HttpConstants;
|
|
using Windows.ApplicationModel.Core;
|
|
using Http.Core.Exceptions;
|
|
using Windows.Storage;
|
|
|
|
namespace PCUT.ViewModels
|
|
{
|
|
internal class FileListViewModel : NotificationBase
|
|
{
|
|
public const string BannerFile = "banner.html";
|
|
public PaginationViewModel Pagination { get; }
|
|
public MetadataViewModel MetadataModel { get; }
|
|
|
|
private SelectableCollection<Category> _categories;
|
|
public SelectableCollection<Category> Categories
|
|
{
|
|
get => _categories;
|
|
set
|
|
{
|
|
if (_categories != null)
|
|
MetadataModel.UnbindPropertyChangedHandle(_categories);
|
|
SetProperty(ref _categories, value ?? new SelectableCollection<Category>(new Category { Name = "-None-" }));
|
|
MetadataModel.BindPropertyChangedHandle(_categories);
|
|
}
|
|
}
|
|
|
|
public SelectableCollection<CdrFileModel> FilteredFiles { get; }
|
|
private readonly IDictionary<string, ThumbnailSource> _thumbnailSources = new Dictionary<string, ThumbnailSource>();
|
|
private readonly FilterModel _filterModel;
|
|
|
|
private bool _filterMetadata;
|
|
private readonly Predicate<CategoryMetadata>[] _metadataFilters = new Predicate<CategoryMetadata>[6];
|
|
|
|
public FileListViewModel()
|
|
{
|
|
FilteredFiles = new SelectableCollection<CdrFileModel>();
|
|
MetadataModel = new MetadataViewModel();
|
|
|
|
_filterModel = new FilterModel();
|
|
|
|
_metadataFilters[0] = type => _filterModel.TypeIds.Contains(type.Id);
|
|
_metadataFilters[1] = brand => _filterModel.BrandIds.Contains(brand.Id);
|
|
_metadataFilters[2] = serie => _filterModel.SeriesIds.Contains(serie.Id);
|
|
_metadataFilters[3] = model => _filterModel.ModelIds.Contains(model.Id);
|
|
_metadataFilters[4] = subType => _filterModel.SubTypeIds.Contains(subType.Id);
|
|
_metadataFilters[5] = year => _filterModel.YearIds.Contains(year.Id);
|
|
|
|
MetadataModel.MetadataChanged += OnMetadataChanged;
|
|
MetadataModel.SetFilterChangedHandle();
|
|
|
|
Pagination = new PaginationViewModel(withSearch: true);
|
|
Pagination.TextSearched += async (sender, args) => await SearchFileAsync(args.Text);
|
|
Pagination.PageChanged += OnPageChanged;
|
|
}
|
|
|
|
public void SetFilterMetadataOnFileChange()
|
|
{
|
|
_filterMetadata = true;
|
|
}
|
|
|
|
public async Task GetCategoriesAsync()
|
|
{
|
|
using (var client = HttpClientFactory.CreateClient(ClientNames.ApiClient))
|
|
{
|
|
var categories = await client.GetCategoriesAsync(filterByRole: true);
|
|
Categories.Load(categories);
|
|
}
|
|
}
|
|
|
|
public async Task<bool> GetBannersAsync()
|
|
{
|
|
using (var client = HttpClientFactory.CreateClient(ClientNames.ApiClient))
|
|
{
|
|
try
|
|
{
|
|
var response = await client.GetAsync($"{Api.Banner}?page=1&pageSize={int.MaxValue}");
|
|
if (response.IsSuccessStatusCode)
|
|
{
|
|
var apiResponse = await response.DeserializeObjectAsync<PaginationResponse<IEnumerable<Banner>>>();
|
|
var banners = apiResponse.Data;
|
|
var contentBuilder = new StringBuilder();
|
|
contentBuilder.Append(
|
|
@"<!DOCTYPE html><html>
|
|
<head><style>
|
|
.marquee-container {display: flex;overflow: hidden;white-space: nowrap;animation: marquee 30s linear infinite;}
|
|
.marquee-text {width: 100%}
|
|
@keyframes marquee {0% {transform: translateX(100%);} 100% {transform: translateX(-100%);}}
|
|
</style></head>
|
|
<body><div class=""marquee-container"">");
|
|
foreach (var banner in banners)
|
|
{
|
|
contentBuilder.Append(@"<div class=""marquee-text"">").Append(banner.Content).Append("</div>");
|
|
}
|
|
contentBuilder.Append(@"</div></body></html>");
|
|
StorageFolder folder = await ApplicationData.Current.TemporaryFolder.CreateFolderAsync("banner", CreationCollisionOption.OpenIfExists);
|
|
StorageFile file = await folder.CreateFileAsync(BannerFile, CreationCollisionOption.ReplaceExisting);
|
|
await FileIO.WriteTextAsync(file, contentBuilder.ToString());
|
|
return true;
|
|
}
|
|
}
|
|
catch (Exception ex) when (!(ex is AppOutdatedException))
|
|
{
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public async Task Reload()
|
|
{
|
|
await GetFilesByFilterAsync(Pagination.Page, Pagination.PageSize, Pagination.SearchText);
|
|
var loadThumbnailTask = LoadThumbnailsAsync();
|
|
if (_filterMetadata)
|
|
FilterMetadata(null);
|
|
await loadThumbnailTask;
|
|
}
|
|
|
|
private async void OnPageChanged(object sender, PageChangedEventArgs args)
|
|
{
|
|
await GetFilesByFilterAsync(args.Page, args.PageSize, Pagination.SearchText);
|
|
var loadThumbnailTask = LoadThumbnailsAsync();
|
|
//if (_filterMetadata)
|
|
// FilterMetadata(null);
|
|
await loadThumbnailTask;
|
|
}
|
|
|
|
private async void OnMetadataChanged(object sender, MetadataChangedEventArgs args)
|
|
{
|
|
Pagination.PageChanged -= OnPageChanged;
|
|
Pagination.Page = 1;
|
|
_filterModel.Clear();
|
|
await GetFilesAsync(args.MetadataType);
|
|
Pagination.PageChanged += OnPageChanged;
|
|
}
|
|
|
|
public async Task GetFilesAsync(string metadataType = null)
|
|
{
|
|
await GetFilesByFilterAsync(Pagination.Page, Pagination.PageSize, Pagination.SearchText);
|
|
var loadThumbnailTask = LoadThumbnailsAsync();
|
|
if (_filterMetadata)
|
|
FilterMetadata(metadataType);
|
|
await loadThumbnailTask;
|
|
}
|
|
|
|
public async Task SearchFileAsync(string searchText)
|
|
{
|
|
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
|
|
{
|
|
await GetFilesByFilterAsync(Pagination.Page, Pagination.PageSize, searchText);
|
|
await LoadThumbnailsAsync();
|
|
});
|
|
}
|
|
|
|
private async Task GetFilesByFilterAsync(int page, int pageSize, string searchText)
|
|
{
|
|
var url = CreateFilterUrl(page, pageSize, searchText);
|
|
using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient))
|
|
{
|
|
try
|
|
{
|
|
var response = await httpClient.GetAsync(url);
|
|
if (response.IsSuccessStatusCode)
|
|
{
|
|
var apiResponse = await response.DeserializeObjectAsync<PaginationResponse<IEnumerable<CdrFileModel>>>();
|
|
var data = apiResponse.Data;
|
|
int totalPages;
|
|
if (_filterModel.IsLoaded)
|
|
{
|
|
totalPages = apiResponse.Pagination.TotalPages;
|
|
}
|
|
else
|
|
{
|
|
totalPages = (int)Math.Ceiling((double)apiResponse.Pagination.TotalRecords / pageSize);
|
|
_filterModel.Load(data);
|
|
data = data.Take(pageSize);
|
|
}
|
|
Pagination.Page = apiResponse.Pagination.Page;
|
|
Pagination.TotalPage = totalPages;
|
|
Pagination.TotalRecords = apiResponse.Pagination.TotalRecords;
|
|
|
|
int displayId = (apiResponse.Pagination.Page - 1) * pageSize + 1;
|
|
var files = data.Select(file =>
|
|
{
|
|
file.FileId = file.File?.Id;
|
|
file.ThumbnailSource = GetOrCreateThumbnailSource(file);
|
|
file.DisplayId = displayId.ToString();
|
|
|
|
displayId++;
|
|
return file;
|
|
});
|
|
FilteredFiles.Load(files);
|
|
}
|
|
}
|
|
catch (Exception ex) when (!(ex is AppOutdatedException))
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
private string CreateFilterUrl(int page, int pageSize, string searchText)
|
|
{
|
|
if (!_filterModel.IsLoaded)
|
|
pageSize = int.MaxValue;
|
|
|
|
var filterBuilder = new StringBuilder();
|
|
if (Categories.SelectedItem != default)
|
|
filterBuilder.AppendFilter("category", Categories.SelectedItem.Id);
|
|
if (MetadataModel.Brands.SelectedItem != default)
|
|
filterBuilder.AppendFilter("brand", MetadataModel.Brands.SelectedItem.Id);
|
|
if (MetadataModel.Types.SelectedItem != default)
|
|
filterBuilder.AppendFilter("type", MetadataModel.Types.SelectedItem.Id);
|
|
if (MetadataModel.Models.SelectedItem != default)
|
|
filterBuilder.AppendFilter("_model", MetadataModel.Models.SelectedItem.Id);
|
|
if (MetadataModel.Series.SelectedItem != default)
|
|
filterBuilder.AppendFilter("series", MetadataModel.Series.SelectedItem.Id);
|
|
if (MetadataModel.Years.SelectedItem != default)
|
|
filterBuilder.AppendFilter("year", MetadataModel.Years.SelectedItem.Id);
|
|
if (MetadataModel.SubTypes.SelectedItem != default)
|
|
filterBuilder.AppendFilter("subType", MetadataModel.SubTypes.SelectedItem.Id);
|
|
|
|
if (!string.IsNullOrEmpty(searchText))
|
|
filterBuilder.AppendFilter("name", "cn", searchText);
|
|
|
|
var filter = filterBuilder.BuildFilter().ToString();
|
|
|
|
return PathBuilder.FromRouteTemplate(Api.CdrFiles)
|
|
.AddQuery("filter", filter)
|
|
.AddQuery("page", page.ToString())
|
|
.AddQuery("pageSize", pageSize.ToString())
|
|
.AddQuery("sort", "createdAt:desc")
|
|
.Build();
|
|
}
|
|
|
|
private void FilterMetadata(string metadataType)
|
|
{
|
|
MetadataModel.FilterAll(metadataType, _metadataFilters);
|
|
}
|
|
|
|
private ImageSource GetOrCreateThumbnailSource(CdrFileModel file)
|
|
{
|
|
if (file.Thumbnail == null)
|
|
return null;
|
|
|
|
if (_thumbnailSources.TryGetValue(file.Thumbnail.Id, out var thumbnail))
|
|
return thumbnail.Source;
|
|
|
|
thumbnail = new ThumbnailSource(new BitmapImage());
|
|
_thumbnailSources[file.Thumbnail.Id] = thumbnail;
|
|
return thumbnail.Source;
|
|
}
|
|
|
|
private async Task LoadThumbnailsAsync()
|
|
{
|
|
try
|
|
{
|
|
await Task.WhenAll(
|
|
_thumbnailSources.Where(x => !x.Value.IsLoaded).Select(x => x.Value.LoadAsync(x.Key)));
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
|
|
public class CdrFileModel : CdrFile
|
|
{
|
|
public ImageSource ThumbnailSource { get; set; }
|
|
public string FileId { get; set; }
|
|
public string DisplayId { get; set; }
|
|
}
|
|
|
|
private class FilterModel
|
|
{
|
|
public readonly List<string> TypeIds = new List<string>();
|
|
public readonly List<string> BrandIds = new List<string>();
|
|
public readonly List<string> SeriesIds = new List<string>();
|
|
public readonly List<string> ModelIds = new List<string>();
|
|
public readonly List<string> SubTypeIds = new List<string>();
|
|
public readonly List<string> YearIds = new List<string>();
|
|
|
|
public bool IsLoaded { get; set; }
|
|
|
|
public void Clear()
|
|
{
|
|
TypeIds.Clear();
|
|
BrandIds.Clear();
|
|
SeriesIds.Clear();
|
|
ModelIds.Clear();
|
|
SubTypeIds.Clear();
|
|
YearIds.Clear();
|
|
IsLoaded = false;
|
|
}
|
|
|
|
public void Load(IEnumerable<CdrFileModel> data)
|
|
{
|
|
TypeIds.AddRange(data.Select(x => x.Type?.Id).Where(x => x != null).Distinct());
|
|
BrandIds.AddRange(data.Select(x => x.Brand?.Id).Where(x => x != null).Distinct());
|
|
SeriesIds.AddRange(data.Select(x => x.Series?.Id).Where(x => x != null).Distinct());
|
|
ModelIds.AddRange(data.Select(x => x.Model?.Id).Where(x => x != null).Distinct());
|
|
SubTypeIds.AddRange(data.Select(x => x.SubType?.Id).Where(x => x != null).Distinct());
|
|
YearIds.AddRange(data.Select(x => x.Year?.Id).Where(x => x != null).Distinct());
|
|
IsLoaded = true;
|
|
}
|
|
}
|
|
|
|
private class ThumbnailSource
|
|
{
|
|
public ImageSource Source { get; set; }
|
|
public bool IsLoaded { get; set; }
|
|
|
|
public ThumbnailSource(ImageSource source)
|
|
{
|
|
Source = source;
|
|
IsLoaded = false;
|
|
}
|
|
|
|
public async Task LoadAsync(string id)
|
|
{
|
|
if (!IsLoaded)
|
|
{
|
|
IsLoaded = true;
|
|
try
|
|
{
|
|
await LoadThumbnailAsync(id);
|
|
}
|
|
catch
|
|
{
|
|
IsLoaded = false;
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task LoadThumbnailAsync(string thumbnailId)
|
|
{
|
|
using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient))
|
|
{
|
|
using (var stream = new MemoryStream())
|
|
{
|
|
var response = await httpClient.DownloadAsync(Api.Download.FormatRoute(thumbnailId), stream);
|
|
if (response)
|
|
{
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
await Source.LoadStreamAsync(stream.AsRandomAccessStream());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|