NavigationDialogHost.axaml.cs
1 using Avalonia; 2 using Avalonia.Controls; 3 using Avalonia.Styling; 4 using Avalonia.Threading; 5 using FluentAvalonia.UI.Controls; 6 using LibHac; 7 using LibHac.Common; 8 using LibHac.Fs; 9 using LibHac.Fs.Shim; 10 using Ryujinx.Ava.Common.Locale; 11 using Ryujinx.Ava.UI.Helpers; 12 using Ryujinx.Ava.UI.ViewModels; 13 using Ryujinx.Ava.UI.Views.User; 14 using Ryujinx.HLE.FileSystem; 15 using Ryujinx.HLE.HOS.Services.Account.Acc; 16 using System; 17 using System.Collections.Generic; 18 using System.Linq; 19 using System.Threading.Tasks; 20 using UserId = Ryujinx.HLE.HOS.Services.Account.Acc.UserId; 21 using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; 22 23 namespace Ryujinx.Ava.UI.Controls 24 { 25 public partial class NavigationDialogHost : UserControl 26 { 27 public AccountManager AccountManager { get; } 28 public ContentManager ContentManager { get; } 29 public VirtualFileSystem VirtualFileSystem { get; } 30 public HorizonClient HorizonClient { get; } 31 public UserProfileViewModel ViewModel { get; set; } 32 33 public NavigationDialogHost() 34 { 35 InitializeComponent(); 36 } 37 38 public NavigationDialogHost(AccountManager accountManager, ContentManager contentManager, 39 VirtualFileSystem virtualFileSystem, HorizonClient horizonClient) 40 { 41 AccountManager = accountManager; 42 ContentManager = contentManager; 43 VirtualFileSystem = virtualFileSystem; 44 HorizonClient = horizonClient; 45 ViewModel = new UserProfileViewModel(); 46 LoadProfiles(); 47 48 if (contentManager.GetCurrentFirmwareVersion() != null) 49 { 50 Task.Run(() => 51 { 52 UserFirmwareAvatarSelectorViewModel.PreloadAvatars(contentManager, virtualFileSystem); 53 }); 54 } 55 InitializeComponent(); 56 } 57 58 public void GoBack() 59 { 60 if (ContentFrame.BackStack.Count > 0) 61 { 62 ContentFrame.GoBack(); 63 } 64 65 LoadProfiles(); 66 } 67 68 public void Navigate(Type sourcePageType, object parameter) 69 { 70 ContentFrame.Navigate(sourcePageType, parameter); 71 } 72 73 public static async Task Show(AccountManager ownerAccountManager, ContentManager ownerContentManager, 74 VirtualFileSystem ownerVirtualFileSystem, HorizonClient ownerHorizonClient) 75 { 76 var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient); 77 ContentDialog contentDialog = new() 78 { 79 Title = LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle], 80 PrimaryButtonText = "", 81 SecondaryButtonText = "", 82 CloseButtonText = "", 83 Content = content, 84 Padding = new Thickness(0), 85 }; 86 87 contentDialog.Closed += (sender, args) => 88 { 89 content.ViewModel.Dispose(); 90 }; 91 92 Style footer = new(x => x.Name("DialogSpace").Child().OfType<Border>()); 93 footer.Setters.Add(new Setter(IsVisibleProperty, false)); 94 95 contentDialog.Styles.Add(footer); 96 97 await contentDialog.ShowAsync(); 98 } 99 100 protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) 101 { 102 base.OnAttachedToVisualTree(e); 103 104 Navigate(typeof(UserSelectorViews), this); 105 } 106 107 public void LoadProfiles() 108 { 109 ViewModel.Profiles.Clear(); 110 ViewModel.LostProfiles.Clear(); 111 112 var profiles = AccountManager.GetAllUsers().OrderBy(x => x.Name); 113 114 foreach (var profile in profiles) 115 { 116 ViewModel.Profiles.Add(new UserProfile(profile, this)); 117 } 118 119 var saveDataFilter = SaveDataFilter.Make(programId: default, saveType: SaveDataType.Account, default, saveDataId: default, index: default); 120 121 using var saveDataIterator = new UniqueRef<SaveDataIterator>(); 122 123 HorizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref, SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure(); 124 125 Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10]; 126 127 HashSet<UserId> lostAccounts = new(); 128 129 while (true) 130 { 131 saveDataIterator.Get.ReadSaveDataInfo(out long readCount, saveDataInfo).ThrowIfFailure(); 132 133 if (readCount == 0) 134 { 135 break; 136 } 137 138 for (int i = 0; i < readCount; i++) 139 { 140 var save = saveDataInfo[i]; 141 var id = new UserId((long)save.UserId.Id.Low, (long)save.UserId.Id.High); 142 if (ViewModel.Profiles.Cast<UserProfile>().FirstOrDefault(x => x.UserId == id) == null) 143 { 144 lostAccounts.Add(id); 145 } 146 } 147 } 148 149 foreach (var account in lostAccounts) 150 { 151 ViewModel.LostProfiles.Add(new UserProfile(new HLE.HOS.Services.Account.Acc.UserProfile(account, "", null), this)); 152 } 153 154 ViewModel.Profiles.Add(new BaseModel()); 155 } 156 157 public async void DeleteUser(UserProfile userProfile) 158 { 159 var lastUserId = AccountManager.LastOpenedUser.UserId; 160 161 if (userProfile.UserId == lastUserId) 162 { 163 // If we are deleting the currently open profile, then we must open something else before deleting. 164 var profile = ViewModel.Profiles.Cast<UserProfile>().FirstOrDefault(x => x.UserId != lastUserId); 165 166 if (profile == null) 167 { 168 static async void Action() 169 { 170 await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionWarningMessage]); 171 } 172 173 Dispatcher.UIThread.Post(Action); 174 175 return; 176 } 177 178 AccountManager.OpenUser(profile.UserId); 179 } 180 181 var result = await ContentDialogHelper.CreateConfirmationDialog( 182 LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionConfirmMessage], 183 "", 184 LocaleManager.Instance[LocaleKeys.InputDialogYes], 185 LocaleManager.Instance[LocaleKeys.InputDialogNo], 186 ""); 187 188 if (result == UserResult.Yes) 189 { 190 GoBack(); 191 AccountManager.DeleteUser(userProfile.UserId); 192 } 193 194 LoadProfiles(); 195 } 196 197 public void AddUser() 198 { 199 Navigate(typeof(UserEditorView), (this, (UserProfile)null, true)); 200 } 201 202 public void EditUser(UserProfile userProfile) 203 { 204 Navigate(typeof(UserEditorView), (this, userProfile, false)); 205 } 206 207 public void RecoverLostAccounts() 208 { 209 Navigate(typeof(UserRecovererView), this); 210 } 211 212 public void ManageSaves() 213 { 214 Navigate(typeof(UserSaveManagerView), (this, AccountManager, HorizonClient, VirtualFileSystem)); 215 } 216 } 217 }