UI - Avalonia Part 3 (#3441)

* Add all other windows

* addreesed review

* Prevent "No Update" option from being deleted

* Select no update is the current update is removed from the title update window

* fix amiibo crash
This commit is contained in:
Emmanuel Hansen 2022-07-08 18:47:11 +00:00 committed by GitHub
parent 9418c33488
commit 07be08f26f
26 changed files with 2901 additions and 23 deletions

View file

@ -0,0 +1,450 @@
using Avalonia;
using Avalonia.Collections;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace Ryujinx.Ava.Ui.ViewModels
{
public class AmiiboWindowViewModel : BaseModel, IDisposable
{
private const string DefaultJson = "{ \"amiibo\": [] }";
private const float AmiiboImageSize = 350f;
private readonly string _amiiboJsonPath;
private readonly byte[] _amiiboLogoBytes;
private readonly HttpClient _httpClient;
private readonly StyleableWindow _owner;
private Bitmap _amiiboImage;
private List<Amiibo.AmiiboApi> _amiiboList;
private AvaloniaList<Amiibo.AmiiboApi> _amiibos;
private ObservableCollection<string> _amiiboSeries;
private int _amiiboSelectedIndex;
private int _seriesSelectedIndex;
private bool _enableScanning;
private bool _showAllAmiibo;
private bool _useRandomUuid;
private string _usage;
public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId)
{
_owner = owner;
_httpClient = new HttpClient { Timeout = TimeSpan.FromMilliseconds(5000) };
LastScannedAmiiboId = lastScannedAmiiboId;
TitleId = titleId;
Directory.CreateDirectory(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo"));
_amiiboJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json");
_amiiboList = new List<Amiibo.AmiiboApi>();
_amiiboSeries = new ObservableCollection<string>();
_amiibos = new AvaloniaList<Amiibo.AmiiboApi>();
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png");
_ = LoadContentAsync();
}
public AmiiboWindowViewModel() { }
public string TitleId { get; set; }
public string LastScannedAmiiboId { get; set; }
public UserResult Response { get; private set; }
public bool UseRandomUuid
{
get => _useRandomUuid;
set
{
_useRandomUuid = value;
OnPropertyChanged();
}
}
public bool ShowAllAmiibo
{
get => _showAllAmiibo;
set
{
_showAllAmiibo = value;
#pragma warning disable 4014
ParseAmiiboData();
#pragma warning restore 4014
OnPropertyChanged();
}
}
public AvaloniaList<Amiibo.AmiiboApi> AmiiboList
{
get => _amiibos;
set
{
_amiibos = value;
OnPropertyChanged();
}
}
public ObservableCollection<string> AmiiboSeries
{
get => _amiiboSeries;
set
{
_amiiboSeries = value;
OnPropertyChanged();
}
}
public int SeriesSelectedIndex
{
get => _seriesSelectedIndex;
set
{
_seriesSelectedIndex = value;
FilterAmiibo();
OnPropertyChanged();
}
}
public int AmiiboSelectedIndex
{
get => _amiiboSelectedIndex;
set
{
_amiiboSelectedIndex = value;
EnableScanning = _amiiboSelectedIndex >= 0 && _amiiboSelectedIndex < _amiibos.Count;
SetAmiiboDetails();
OnPropertyChanged();
}
}
public Bitmap AmiiboImage
{
get => _amiiboImage;
set
{
_amiiboImage = value;
OnPropertyChanged();
}
}
public string Usage
{
get => _usage;
set
{
_usage = value;
OnPropertyChanged();
}
}
public bool EnableScanning
{
get => _enableScanning;
set
{
_enableScanning = value;
OnPropertyChanged();
}
}
public void Dispose()
{
_httpClient.Dispose();
}
private async Task LoadContentAsync()
{
string amiiboJsonString = DefaultJson;
if (File.Exists(_amiiboJsonPath))
{
amiiboJsonString = File.ReadAllText(_amiiboJsonPath);
if (await NeedsUpdate(JsonSerializer.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).LastUpdated))
{
amiiboJsonString = await DownloadAmiiboJson();
}
}
else
{
try
{
amiiboJsonString = await DownloadAmiiboJson();
}
catch
{
ShowInfoDialog();
}
}
_amiiboList = JsonSerializer.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).Amiibo;
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
ParseAmiiboData();
}
private void ParseAmiiboData()
{
_amiiboSeries.Clear();
_amiibos.Clear();
for (int i = 0; i < _amiiboList.Count; i++)
{
if (!_amiiboSeries.Contains(_amiiboList[i].AmiiboSeries))
{
if (!ShowAllAmiibo)
{
foreach (Amiibo.AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch)
{
if (game != null)
{
if (game.GameId.Contains(TitleId))
{
AmiiboSeries.Add(_amiiboList[i].AmiiboSeries);
break;
}
}
}
}
else
{
AmiiboSeries.Add(_amiiboList[i].AmiiboSeries);
}
}
}
if (LastScannedAmiiboId != "")
{
SelectLastScannedAmiibo();
}
else
{
SeriesSelectedIndex = 0;
}
}
private void SelectLastScannedAmiibo()
{
Amiibo.AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId);
SeriesSelectedIndex = AmiiboSeries.IndexOf(scanned.AmiiboSeries);
AmiiboSelectedIndex = AmiiboList.IndexOf(scanned);
}
private void FilterAmiibo()
{
_amiibos.Clear();
if (_seriesSelectedIndex < 0)
{
return;
}
List<Amiibo.AmiiboApi> amiiboSortedList = _amiiboList
.Where(amiibo => amiibo.AmiiboSeries == _amiiboSeries[SeriesSelectedIndex])
.OrderBy(amiibo => amiibo.Name).ToList();
for (int i = 0; i < amiiboSortedList.Count; i++)
{
if (!_amiibos.Contains(amiiboSortedList[i]))
{
if (!_showAllAmiibo)
{
foreach (Amiibo.AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch)
{
if (game != null)
{
if (game.GameId.Contains(TitleId))
{
_amiibos.Add(amiiboSortedList[i]);
break;
}
}
}
}
else
{
_amiibos.Add(amiiboSortedList[i]);
}
}
}
AmiiboSelectedIndex = 0;
}
private void SetAmiiboDetails()
{
ResetAmiiboPreview();
Usage = string.Empty;
if (_amiiboSelectedIndex < 0)
{
return;
}
Amiibo.AmiiboApi selected = _amiibos[_amiiboSelectedIndex];
string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image;
string usageString = "";
for (int i = 0; i < _amiiboList.Count; i++)
{
if (_amiiboList[i].Equals(selected))
{
bool writable = false;
foreach (Amiibo.AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch)
{
if (item.GameId.Contains(TitleId))
{
foreach (Amiibo.AmiiboApiUsage usageItem in item.AmiiboUsage)
{
usageString += Environment.NewLine +
$"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}";
writable = usageItem.Write;
}
}
}
if (usageString.Length == 0)
{
usageString = LocaleManager.Instance["Unknown"] + ".";
}
Usage = $"{LocaleManager.Instance["Usage"]} {(writable ? $" ({LocaleManager.Instance["Writable"]})" : "")} : {usageString}";
}
}
_ = UpdateAmiiboPreview(imageUrl);
}
private async Task<bool> NeedsUpdate(DateTime oldLastModified)
{
try
{
HttpResponseMessage response =
await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, "https://amiibo.ryujinx.org/"));
if (response.IsSuccessStatusCode)
{
return response.Content.Headers.LastModified != oldLastModified;
}
return false;
}
catch
{
ShowInfoDialog();
return false;
}
}
private async Task<string> DownloadAmiiboJson()
{
HttpResponseMessage response = await _httpClient.GetAsync("https://amiibo.ryujinx.org/");
if (response.IsSuccessStatusCode)
{
string amiiboJsonString = await response.Content.ReadAsStringAsync();
using (FileStream dlcJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough))
{
dlcJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString));
}
return amiiboJsonString;
}
await ContentDialogHelper.CreateInfoDialog(_owner, LocaleManager.Instance["DialogAmiiboApiTitle"],
LocaleManager.Instance["DialogAmiiboApiFailFetchMessage"],
LocaleManager.Instance["InputDialogOk"],
"",
LocaleManager.Instance["RyujinxInfo"]);
Close();
return DefaultJson;
}
private void Close()
{
Dispatcher.UIThread.Post(_owner.Close);
}
private async Task UpdateAmiiboPreview(string imageUrl)
{
HttpResponseMessage response = await _httpClient.GetAsync(imageUrl);
if (response.IsSuccessStatusCode)
{
byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync();
using (MemoryStream memoryStream = new(amiiboPreviewBytes))
{
Bitmap bitmap = new(memoryStream);
double ratio = Math.Min(AmiiboImageSize / bitmap.Size.Width,
AmiiboImageSize / bitmap.Size.Height);
int resizeHeight = (int)(bitmap.Size.Height * ratio);
int resizeWidth = (int)(bitmap.Size.Width * ratio);
AmiiboImage = bitmap.CreateScaledBitmap(new PixelSize(resizeWidth, resizeHeight));
}
}
}
private void ResetAmiiboPreview()
{
using (MemoryStream memoryStream = new(_amiiboLogoBytes))
{
Bitmap bitmap = new(memoryStream);
AmiiboImage = bitmap;
}
}
private async void ShowInfoDialog()
{
await ContentDialogHelper.CreateInfoDialog(_owner, LocaleManager.Instance["DialogAmiiboApiTitle"],
LocaleManager.Instance["DialogAmiiboApiConnectErrorMessage"],
LocaleManager.Instance["InputDialogOk"],
"",
LocaleManager.Instance["RyujinxInfo"]);
}
}
}

View file

@ -0,0 +1,363 @@
using Avalonia.Media;
using DynamicData;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.HLE.FileSystem;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Color = Avalonia.Media.Color;
namespace Ryujinx.Ava.Ui.ViewModels
{
internal class AvatarProfileViewModel : BaseModel, IDisposable
{
private const int MaxImageTasks = 4;
private static readonly Dictionary<string, byte[]> _avatarStore = new();
private static bool _isPreloading;
private static Action _loadCompleteAction;
private ObservableCollection<ProfileImageModel> _images;
private Color _backgroundColor = Colors.White;
private int _selectedIndex;
private int _imagesLoaded;
private bool _isActive;
private byte[] _selectedImage;
private bool _isIndeterminate = true;
public bool IsActive
{
get => _isActive;
set => _isActive = value;
}
public AvatarProfileViewModel()
{
_images = new ObservableCollection<ProfileImageModel>();
}
public AvatarProfileViewModel(Action loadCompleteAction)
{
_images = new ObservableCollection<ProfileImageModel>();
if (_isPreloading)
{
_loadCompleteAction = loadCompleteAction;
}
else
{
ReloadImages();
}
}
public Color BackgroundColor
{
get => _backgroundColor;
set
{
_backgroundColor = value;
IsActive = false;
ReloadImages();
}
}
public ObservableCollection<ProfileImageModel> Images
{
get => _images;
set
{
_images = value;
OnPropertyChanged();
}
}
public bool IsIndeterminate
{
get => _isIndeterminate;
set
{
_isIndeterminate = value;
OnPropertyChanged();
}
}
public int ImageCount => _avatarStore.Count;
public int ImagesLoaded
{
get => _imagesLoaded;
set
{
_imagesLoaded = value;
OnPropertyChanged();
}
}
public int SelectedIndex
{
get => _selectedIndex;
set
{
_selectedIndex = value;
if (_selectedIndex == -1)
{
SelectedImage = null;
}
else
{
SelectedImage = _images[_selectedIndex].Data;
}
OnPropertyChanged();
}
}
public byte[] SelectedImage
{
get => _selectedImage;
private set => _selectedImage = value;
}
public void ReloadImages()
{
if (_isPreloading)
{
IsIndeterminate = false;
return;
}
Task.Run(() =>
{
IsActive = true;
Images.Clear();
int selectedIndex = _selectedIndex;
int index = 0;
ImagesLoaded = 0;
IsIndeterminate = false;
var keys = _avatarStore.Keys.ToList();
var newImages = new List<ProfileImageModel>();
var tasks = new List<Task>();
for (int i = 0; i < MaxImageTasks; i++)
{
var start = i;
tasks.Add(Task.Run(() => ImageTask(start)));
}
Task.WaitAll(tasks.ToArray());
Images.AddRange(newImages);
void ImageTask(int start)
{
for (int i = start; i < keys.Count; i += MaxImageTasks)
{
if (!IsActive)
{
return;
}
var key = keys[i];
var image = _avatarStore[keys[i]];
var data = ProcessImage(image);
newImages.Add(new ProfileImageModel(key, data));
if (index++ == selectedIndex)
{
SelectedImage = data;
}
Interlocked.Increment(ref _imagesLoaded);
OnPropertyChanged(nameof(ImagesLoaded));
}
}
});
}
private byte[] ProcessImage(byte[] data)
{
using (MemoryStream streamJpg = new())
{
Image avatarImage = Image.Load(data, new PngDecoder());
avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(BackgroundColor.R,
BackgroundColor.G,
BackgroundColor.B,
BackgroundColor.A)));
avatarImage.SaveAsJpeg(streamJpg);
return streamJpg.ToArray();
}
}
public static void PreloadAvatars(ContentManager contentManager, VirtualFileSystem virtualFileSystem)
{
try
{
if (_avatarStore.Count > 0)
{
return;
}
_isPreloading = true;
string contentPath =
contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem,
NcaContentType.Data);
string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath);
if (!string.IsNullOrWhiteSpace(avatarPath))
{
using (IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open))
{
Nca nca = new(virtualFileSystem.KeySet, ncaFileStream);
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
foreach (DirectoryEntryEx item in romfs.EnumerateEntries())
{
// TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy.
if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") &&
item.FullPath.Contains("szs"))
{
using var file = new UniqueRef<IFile>();
romfs.OpenFile(ref file.Ref(), ("/" + item.FullPath).ToU8Span(), OpenMode.Read)
.ThrowIfFailure();
using (MemoryStream stream = new())
using (MemoryStream streamPng = new())
{
file.Get.AsStream().CopyTo(stream);
stream.Position = 0;
Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256);
avatarImage.SaveAsPng(streamPng);
_avatarStore.Add(item.FullPath, streamPng.ToArray());
}
}
}
}
}
}
finally
{
_isPreloading = false;
_loadCompleteAction?.Invoke();
}
}
private static byte[] DecompressYaz0(Stream stream)
{
using (BinaryReader reader = new(stream))
{
reader.ReadInt32(); // Magic
uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32());
reader.ReadInt64(); // Padding
byte[] input = new byte[stream.Length - stream.Position];
stream.Read(input, 0, input.Length);
uint inputOffset = 0;
byte[] output = new byte[decodedLength];
uint outputOffset = 0;
ushort mask = 0;
byte header = 0;
while (outputOffset < decodedLength)
{
if ((mask >>= 1) == 0)
{
header = input[inputOffset++];
mask = 0x80;
}
if ((header & mask) != 0)
{
if (outputOffset == output.Length)
{
break;
}
output[outputOffset++] = input[inputOffset++];
}
else
{
byte byte1 = input[inputOffset++];
byte byte2 = input[inputOffset++];
uint dist = (uint)((byte1 & 0xF) << 8) | byte2;
uint position = outputOffset - (dist + 1);
uint length = (uint)byte1 >> 4;
if (length == 0)
{
length = (uint)input[inputOffset++] + 0x12;
}
else
{
length += 2;
}
uint gap = outputOffset - position;
uint nonOverlappingLength = length;
if (nonOverlappingLength > gap)
{
nonOverlappingLength = gap;
}
Buffer.BlockCopy(output, (int)position, output, (int)outputOffset, (int)nonOverlappingLength);
outputOffset += nonOverlappingLength;
position += nonOverlappingLength;
length -= nonOverlappingLength;
while (length-- > 0)
{
output[outputOffset++] = output[position++];
}
}
}
return output;
}
}
public void Dispose()
{
_loadCompleteAction = null;
IsActive = false;
}
}
}

View file

@ -67,6 +67,8 @@ namespace Ryujinx.Ava.Ui.ViewModels
private bool _isPaused;
private bool _showContent = true;
private bool _isLoadingIndeterminate = true;
private bool _showAll;
private string _lastScannedAmiiboId;
private ReadOnlyObservableCollection<ApplicationData> _appsObservableList;
public string TitleName { get; internal set; }
@ -695,15 +697,28 @@ namespace Ryujinx.Ava.Ui.ViewModels
}
}
public void OpenAmiiboWindow()
public async void OpenAmiiboWindow()
{
if (!_isAmiiboRequested)
{
return;
}
if (_owner.AppHost.Device.System.SearchingForAmiibo(out int deviceId))
{
string titleId = _owner.AppHost.Device.Application.TitleIdText.ToUpper();
AmiiboWindow window = new(_showAll, _lastScannedAmiiboId, titleId);
// TODO : Implement Amiibo window
ContentDialogHelper.ShowNotAvailableMessage(_owner);
await window.ShowDialog(_owner);
if (window.IsScanned)
{
_showAll = window.ViewModel.ShowAllAmiibo;
_lastScannedAmiiboId = window.ScannedAmiibo.GetId();
_owner.AppHost.Device.System.ScanAmiibo(deviceId, _lastScannedAmiiboId, window.ViewModel.UseRandomUuid);
}
}
}
public void HandleShaderProgress(Switch emulationContext)
@ -953,10 +968,11 @@ namespace Ryujinx.Ava.Ui.ViewModels
LoadConfigurableHotKeys();
}
public void ManageProfiles()
public async void ManageProfiles()
{
// TODO : Implement Profiles window
ContentDialogHelper.ShowNotAvailableMessage(_owner);
UserProfileWindow window = new(_owner.AccountManager, _owner.ContentManager, _owner.VirtualFileSystem);
await window.ShowDialog(_owner);
}
public async void OpenAboutWindow()
@ -1227,33 +1243,60 @@ namespace Ryujinx.Ava.Ui.ViewModels
}
}
public void OpenTitleUpdateManager()
public async void OpenTitleUpdateManager()
{
// TODO : Implement Update window
ContentDialogHelper.ShowNotAvailableMessage(_owner);
var selection = SelectedApplication;
if (selection != null)
{
TitleUpdateWindow titleUpdateManager =
new(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName);
await titleUpdateManager.ShowDialog(_owner);
}
}
public void OpenDlcManager()
public async void OpenDlcManager()
{
// TODO : Implement Dlc window
ContentDialogHelper.ShowNotAvailableMessage(_owner);
var selection = SelectedApplication;
if (selection != null)
{
DlcManagerWindow dlcManager = new(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName);
await dlcManager.ShowDialog(_owner);
}
}
public void OpenCheatManager()
public async void OpenCheatManager()
{
// TODO : Implement cheat window
ContentDialogHelper.ShowNotAvailableMessage(_owner);
var selection = SelectedApplication;
if (selection != null)
{
CheatWindow cheatManager = new(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName);
await cheatManager.ShowDialog(_owner);
}
}
public void OpenCheatManagerForCurrentApp()
public async void OpenCheatManagerForCurrentApp()
{
if (!IsGameRunning)
{
return;
}
// TODO : Implement cheat window
ContentDialogHelper.ShowNotAvailableMessage(_owner);
var application = _owner.AppHost.Device.Application;
if (application != null)
{
CheatWindow cheatManager = new(_owner.VirtualFileSystem, application.TitleIdText, application.TitleName);
await cheatManager.ShowDialog(_owner);
_owner.AppHost.Device.EnableCheats();
}
}
public void OpenDeviceSaveDirectory()

View file

@ -0,0 +1,166 @@
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using UserProfile = Ryujinx.Ava.Ui.Models.UserProfile;
namespace Ryujinx.Ava.Ui.ViewModels
{
public class UserProfileViewModel : BaseModel, IDisposable
{
private const uint MaxProfileNameLength = 0x20;
private readonly UserProfileWindow _owner;
private UserProfile _selectedProfile;
private string _tempUserName;
public UserProfileViewModel()
{
Profiles = new ObservableCollection<UserProfile>();
}
public UserProfileViewModel(UserProfileWindow owner) : this()
{
_owner = owner;
LoadProfiles();
}
public ObservableCollection<UserProfile> Profiles { get; set; }
public UserProfile SelectedProfile
{
get => _selectedProfile;
set
{
_selectedProfile = value;
OnPropertyChanged(nameof(SelectedProfile));
OnPropertyChanged(nameof(IsSelectedProfileDeletable));
}
}
public bool IsSelectedProfileDeletable =>
_selectedProfile != null && _selectedProfile.UserId != AccountManager.DefaultUserId;
public void Dispose()
{
}
public void LoadProfiles()
{
Profiles.Clear();
var profiles = _owner.AccountManager.GetAllUsers()
.OrderByDescending(x => x.AccountState == AccountState.Open);
foreach (var profile in profiles)
{
Profiles.Add(new UserProfile(profile));
}
SelectedProfile = Profiles.FirstOrDefault(x => x.UserId == _owner.AccountManager.LastOpenedUser.UserId);
if (SelectedProfile == null)
{
SelectedProfile = Profiles.First();
if (SelectedProfile != null)
{
_owner.AccountManager.OpenUser(_selectedProfile.UserId);
}
}
}
public async void ChooseProfileImage()
{
await SelectProfileImage();
}
public async Task SelectProfileImage(bool isNewUser = false)
{
ProfileImageSelectionDialog selectionDialog = new(_owner.ContentManager);
await selectionDialog.ShowDialog(_owner);
if (selectionDialog.BufferImageProfile != null)
{
if (isNewUser)
{
if (!string.IsNullOrWhiteSpace(_tempUserName))
{
_owner.AccountManager.AddUser(_tempUserName, selectionDialog.BufferImageProfile);
}
}
else if (SelectedProfile != null)
{
_owner.AccountManager.SetUserImage(SelectedProfile.UserId, selectionDialog.BufferImageProfile);
SelectedProfile.Image = selectionDialog.BufferImageProfile;
SelectedProfile = null;
}
LoadProfiles();
}
}
public async void AddUser()
{
var dlgTitle = LocaleManager.Instance["InputDialogAddNewProfileTitle"];
var dlgMainText = LocaleManager.Instance["InputDialogAddNewProfileHeader"];
var dlgSubText = string.Format(LocaleManager.Instance["InputDialogAddNewProfileSubtext"],
MaxProfileNameLength);
_tempUserName =
await ContentDialogHelper.CreateInputDialog(dlgTitle, dlgMainText, dlgSubText, _owner,
MaxProfileNameLength);
if (!string.IsNullOrWhiteSpace(_tempUserName))
{
await SelectProfileImage(true);
}
_tempUserName = String.Empty;
}
public async void DeleteUser()
{
if (_selectedProfile != null)
{
var lastUserId = _owner.AccountManager.LastOpenedUser.UserId;
if (_selectedProfile.UserId == lastUserId)
{
// If we are deleting the currently open profile, then we must open something else before deleting.
var profile = Profiles.FirstOrDefault(x => x.UserId != lastUserId);
if (profile == null)
{
ContentDialogHelper.CreateErrorDialog(_owner,
LocaleManager.Instance["DialogUserProfileDeletionWarningMessage"]);
return;
}
_owner.AccountManager.OpenUser(profile.UserId);
}
var result =
await ContentDialogHelper.CreateConfirmationDialog(_owner,
LocaleManager.Instance["DialogUserProfileDeletionConfirmMessage"], "",
LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], "");
if (result == UserResult.Yes)
{
_owner.AccountManager.DeleteUser(_selectedProfile.UserId);
}
}
LoadProfiles();
}
}
}