Ryujinx-ryubing/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs

302 lines
9.3 KiB
C#

using Avalonia.Collections;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using DynamicData;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.HLE.FileSystem;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Application = Avalonia.Application;
namespace Ryujinx.Ava.UI.ViewModels
{
public class DownloadableContentManagerViewModel : BaseModel
{
private readonly ApplicationLibrary _applicationLibrary;
private AvaloniaList<DownloadableContentModel> _downloadableContents = new();
private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new();
private AvaloniaList<DownloadableContentModel> _views = new();
private bool _showBundledContentNotice = false;
private string _search;
private readonly ApplicationData _applicationData;
private readonly IStorageProvider _storageProvider;
public AvaloniaList<DownloadableContentModel> DownloadableContents
{
get => _downloadableContents;
set
{
_downloadableContents = value;
OnPropertyChanged();
OnPropertyChanged(nameof(UpdateCount));
Sort();
}
}
public AvaloniaList<DownloadableContentModel> Views
{
get => _views;
set
{
_views = value;
OnPropertyChanged();
}
}
public AvaloniaList<DownloadableContentModel> SelectedDownloadableContents
{
get => _selectedDownloadableContents;
set
{
_selectedDownloadableContents = value;
OnPropertyChanged();
}
}
public string Search
{
get => _search;
set
{
_search = value;
OnPropertyChanged();
Sort();
}
}
public string UpdateCount
{
get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count);
}
public bool ShowBundledContentNotice
{
get => _showBundledContentNotice;
set
{
_showBundledContentNotice = value;
OnPropertyChanged();
}
}
public DownloadableContentManagerViewModel(ApplicationLibrary applicationLibrary, ApplicationData applicationData)
{
_applicationLibrary = applicationLibrary;
_applicationData = applicationData;
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
_storageProvider = desktop.MainWindow.StorageProvider;
}
LoadDownloadableContents();
}
private void LoadDownloadableContents()
{
var dlcs = _applicationLibrary.DownloadableContents.Items
.Where(it => it.Dlc.TitleIdBase == _applicationData.IdBase);
bool hasBundledContent = false;
foreach ((DownloadableContentModel dlc, bool isEnabled) in dlcs)
{
DownloadableContents.Add(dlc);
hasBundledContent = hasBundledContent || dlc.IsBundled;
if (isEnabled)
{
SelectedDownloadableContents.Add(dlc);
}
OnPropertyChanged(nameof(UpdateCount));
}
ShowBundledContentNotice = hasBundledContent;
Sort();
}
public void Sort()
{
DownloadableContents
// Sort bundled last
.OrderBy(it => it.IsBundled ? 0 : 1)
.ThenBy(it => it.TitleId)
.AsObservableChangeSet()
.Filter(Filter)
.Bind(out var view).AsObservableList();
// NOTE(jpr): this works around a bug where calling _views.Clear also clears SelectedDownloadableContents for
// some reason. so we save the items here and add them back after
var items = SelectedDownloadableContents.ToArray();
_views.Clear();
_views.AddRange(view);
foreach (DownloadableContentModel item in items)
{
SelectedDownloadableContents.ReplaceOrAdd(item, item);
}
OnPropertyChanged(nameof(Views));
}
private bool Filter<T>(T arg)
{
if (arg is DownloadableContentModel content)
{
return string.IsNullOrWhiteSpace(_search) || content.FileName.ToLower().Contains(_search.ToLower()) || content.TitleIdStr.ToLower().Contains(_search.ToLower());
}
return false;
}
public async void Add()
{
var result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle],
AllowMultiple = true,
FileTypeFilter = new List<FilePickerFileType>
{
new("NSP")
{
Patterns = new[] { "*.nsp" },
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
MimeTypes = new[] { "application/x-nx-nsp" },
},
},
});
var totalDlcAdded = 0;
foreach (var file in result)
{
if (!AddDownloadableContent(file.Path.LocalPath, out var newDlcAdded))
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]);
}
totalDlcAdded += newDlcAdded;
}
if (totalDlcAdded > 0)
{
await ShowNewDlcAddedDialog(totalDlcAdded);
}
}
private bool AddDownloadableContent(string path, out int numDlcAdded)
{
numDlcAdded = 0;
if (!File.Exists(path))
{
return false;
}
if (!_applicationLibrary.TryGetDownloadableContentFromFile(path, out var dlcs) || dlcs.Count == 0)
{
return false;
}
var dlcsForThisGame = dlcs.Where(it => it.TitleIdBase == _applicationData.IdBase).ToList();
if (dlcsForThisGame.Count == 0)
{
return false;
}
foreach (var dlc in dlcsForThisGame)
{
if (!DownloadableContents.Contains(dlc))
{
DownloadableContents.Add(dlc);
SelectedDownloadableContents.ReplaceOrAdd(dlc, dlc);
numDlcAdded++;
}
}
if (numDlcAdded > 0)
{
OnPropertyChanged(nameof(UpdateCount));
Sort();
}
return true;
}
public void Remove(DownloadableContentModel model)
{
SelectedDownloadableContents.Remove(model);
if (!model.IsBundled)
{
DownloadableContents.Remove(model);
OnPropertyChanged(nameof(UpdateCount));
Sort();
}
}
public void RemoveAll()
{
SelectedDownloadableContents.Clear();
DownloadableContents.RemoveMany(DownloadableContents.Where(it => !it.IsBundled));
OnPropertyChanged(nameof(UpdateCount));
Sort();
}
public void EnableAll()
{
SelectedDownloadableContents.Clear();
SelectedDownloadableContents.AddRange(DownloadableContents);
}
public void DisableAll()
{
SelectedDownloadableContents.Clear();
}
public void Enable(DownloadableContentModel model)
{
SelectedDownloadableContents.ReplaceOrAdd(model, model);
}
public void Disable(DownloadableContentModel model)
{
SelectedDownloadableContents.Remove(model);
}
public void Save()
{
var dlcs = DownloadableContents.Select(it => (it, SelectedDownloadableContents.Contains(it))).ToList();
_applicationLibrary.SaveDownloadableContentsForGame(_applicationData, dlcs);
}
private Task ShowNewDlcAddedDialog(int numAdded)
{
var msg = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowDlcAddedMessage], numAdded);
return Dispatcher.UIThread.InvokeAsync(async () =>
{
await ContentDialogHelper.ShowTextDialog(
LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle],
msg,
string.Empty,
string.Empty,
string.Empty,
LocaleManager.Instance[LocaleKeys.InputDialogOk],
(int)Symbol.Checkmark);
});
}
}
}