| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- using Avalonia.Collections;
- using Avalonia.Controls.ApplicationLifetimes;
- using Avalonia.Platform.Storage;
- using Avalonia.Threading;
- using FluentAvalonia.UI.Controls;
- using Ryujinx.Ava.Common.Locale;
- using Ryujinx.Ava.UI.Helpers;
- using Ryujinx.HLE.FileSystem;
- using Ryujinx.UI.App.Common;
- using Ryujinx.UI.Common.Models;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Threading.Tasks;
- using Application = Avalonia.Application;
- namespace Ryujinx.Ava.UI.ViewModels
- {
- public record TitleUpdateViewNoUpdateSentinal();
- public class TitleUpdateViewModel : BaseModel
- {
- private ApplicationLibrary ApplicationLibrary { get; }
- private ApplicationData ApplicationData { get; }
- private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
- private AvaloniaList<object> _views = new();
- private object _selectedUpdate = new TitleUpdateViewNoUpdateSentinal();
- private bool _showBundledContentNotice = false;
- public AvaloniaList<TitleUpdateModel> TitleUpdates
- {
- get => _titleUpdates;
- set
- {
- _titleUpdates = value;
- OnPropertyChanged();
- }
- }
- public AvaloniaList<object> Views
- {
- get => _views;
- set
- {
- _views = value;
- OnPropertyChanged();
- }
- }
- public object SelectedUpdate
- {
- get => _selectedUpdate;
- set
- {
- _selectedUpdate = value;
- OnPropertyChanged();
- }
- }
- public bool ShowBundledContentNotice
- {
- get => _showBundledContentNotice;
- set
- {
- _showBundledContentNotice = value;
- OnPropertyChanged();
- }
- }
- public IStorageProvider StorageProvider;
- public TitleUpdateViewModel(ApplicationLibrary applicationLibrary, ApplicationData applicationData)
- {
- ApplicationLibrary = applicationLibrary;
- ApplicationData = applicationData;
- if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- StorageProvider = desktop.MainWindow.StorageProvider;
- }
- LoadUpdates();
- }
- private void LoadUpdates()
- {
- var updates = ApplicationLibrary.TitleUpdates.Items
- .Where(it => it.TitleUpdate.TitleIdBase == ApplicationData.IdBase);
- bool hasBundledContent = false;
- SelectedUpdate = new TitleUpdateViewNoUpdateSentinal();
- foreach ((TitleUpdateModel update, bool isSelected) in updates)
- {
- TitleUpdates.Add(update);
- hasBundledContent = hasBundledContent || update.IsBundled;
- if (isSelected)
- {
- SelectedUpdate = update;
- }
- }
- ShowBundledContentNotice = hasBundledContent;
- SortUpdates();
- }
- public void SortUpdates()
- {
- var sortedUpdates = TitleUpdates.OrderByDescending(update => update.Version);
- // NOTE(jpr): this works around a bug where calling Views.Clear also clears SelectedUpdate for
- // some reason. so we save the item here and restore it after
- var selected = SelectedUpdate;
- Views.Clear();
- Views.Add(new TitleUpdateViewNoUpdateSentinal());
- Views.AddRange(sortedUpdates);
- SelectedUpdate = selected;
- if (SelectedUpdate is TitleUpdateViewNoUpdateSentinal)
- {
- SelectedUpdate = Views[0];
- }
- // this is mainly to handle a scenario where the user removes the selected update
- else if (!TitleUpdates.Contains((TitleUpdateModel)SelectedUpdate))
- {
- SelectedUpdate = Views.Count > 1 ? Views[1] : Views[0];
- }
- }
- private bool AddUpdate(string path, out int numUpdatesAdded)
- {
- numUpdatesAdded = 0;
- if (!File.Exists(path))
- {
- return false;
- }
- if (!ApplicationLibrary.TryGetTitleUpdatesFromFile(path, out var updates))
- {
- return false;
- }
- var updatesForThisGame = updates.Where(it => it.TitleIdBase == ApplicationData.Id).ToList();
- if (updatesForThisGame.Count == 0)
- {
- return false;
- }
- foreach (var update in updatesForThisGame)
- {
- if (!TitleUpdates.Contains(update))
- {
- TitleUpdates.Add(update);
- SelectedUpdate = update;
- numUpdatesAdded++;
- }
- }
- if (numUpdatesAdded > 0)
- {
- SortUpdates();
- }
- return true;
- }
- public void RemoveUpdate(TitleUpdateModel update)
- {
- if (!update.IsBundled)
- {
- TitleUpdates.Remove(update);
- }
- else if (update == SelectedUpdate as TitleUpdateModel)
- {
- SelectedUpdate = new TitleUpdateViewNoUpdateSentinal();
- }
- SortUpdates();
- }
- public async Task Add()
- {
- var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
- {
- AllowMultiple = true,
- FileTypeFilter = new List<FilePickerFileType>
- {
- new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
- {
- Patterns = new[] { "*.nsp" },
- AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
- MimeTypes = new[] { "application/x-nx-nsp" },
- },
- },
- });
- var totalUpdatesAdded = 0;
- foreach (var file in result)
- {
- if (!AddUpdate(file.Path.LocalPath, out var newUpdatesAdded))
- {
- await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
- }
- totalUpdatesAdded += newUpdatesAdded;
- }
- if (totalUpdatesAdded > 0)
- {
- await ShowNewUpdatesAddedDialog(totalUpdatesAdded);
- }
- }
- public void Save()
- {
- var updates = TitleUpdates.Select(it => (it, it == SelectedUpdate as TitleUpdateModel)).ToList();
- ApplicationLibrary.SaveTitleUpdatesForGame(ApplicationData, updates);
- }
- private Task ShowNewUpdatesAddedDialog(int numAdded)
- {
- var msg = string.Format(LocaleManager.Instance[LocaleKeys.UpdateWindowUpdateAddedMessage], numAdded);
- return Dispatcher.UIThread.InvokeAsync(async () =>
- {
- await ContentDialogHelper.ShowTextDialog(LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle], msg, "", "", "", LocaleManager.Instance[LocaleKeys.InputDialogOk], (int)Symbol.Checkmark);
- });
- }
- }
- }
|