Add Preferences and saving, tweak LoginView code

This commit is contained in:
GalaxyLittlepaws
2024-06-25 23:01:53 -04:00
parent 7838dda857
commit ba3470ffb2
9 changed files with 372 additions and 36 deletions
+7
View File
@@ -0,0 +1,7 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": []
}
+34
View File
@@ -0,0 +1,34 @@
using System;
namespace GalaxyViewer.Models
{
public class Grid
{
public string GridNick { get; set; }
public string GridName { get; set; }
public string Platform { get; set; }
public string LoginUri { get; set; }
public string LoginPage { get; set; }
public string HelperUri { get; set; }
public string Website { get; set; }
public string Support { get; set; }
public string Register { get; set; }
public string Password { get; set; }
public string Version { get; set; }
public Grid(string gridNick, string gridName, string platform, string loginUri, string loginPage, string helperUri, string website, string support, string register, string password, string version)
{
GridNick = !string.IsNullOrEmpty(gridNick) ? gridNick : throw new ArgumentNullException(nameof(gridNick));
GridName = !string.IsNullOrEmpty(gridName) ? gridName : throw new ArgumentNullException(nameof(gridName));
Platform = !string.IsNullOrEmpty(platform) ? platform : throw new ArgumentNullException(nameof(platform));
LoginUri = !string.IsNullOrEmpty(loginUri) ? loginUri : throw new ArgumentNullException(nameof(loginUri));
LoginPage = !string.IsNullOrEmpty(loginPage) ? loginPage : throw new ArgumentNullException(nameof(loginPage));
HelperUri = !string.IsNullOrEmpty(helperUri) ? helperUri : throw new ArgumentNullException(nameof(helperUri));
Website = !string.IsNullOrEmpty(website) ? website : throw new ArgumentNullException(nameof(website));
Support = !string.IsNullOrEmpty(support) ? support : throw new ArgumentNullException(nameof(support));
Register = !string.IsNullOrEmpty(register) ? register : throw new ArgumentNullException(nameof(register));
Password = !string.IsNullOrEmpty(password) ? password : throw new ArgumentNullException(nameof(password));
Version = !string.IsNullOrEmpty(version) ? version : throw new ArgumentNullException(nameof(version));
}
}
}
+61
View File
@@ -0,0 +1,61 @@
using System;
namespace GalaxyViewer.Models
{
public enum ThemeOptions
{
Light,
Dark,
System
}
public enum LoginLocationOptions
{
Home,
LastLocation
}
[Serializable]
public class PreferencesModel
{
private string _theme = Enum.TryParse(typeof(ThemeOptions), "System", out _) ? "System" : "Light";
private string _loginLocation = Enum.TryParse(typeof(LoginLocationOptions), "LastLocation", out _) ? "LastLocation" : "Home";
public long LastSavedEpoch { get; set; } // Hidden from UI, but stored
public string Theme
{
get => _theme;
set
{
if (IsValidTheme(value))
{
_theme = value;
}
else
{
throw new ArgumentException($"Invalid theme value: {value}");
}
}
}
public string LoginLocation
{
get => _loginLocation;
set
{
if (IsValidLoginLocation(value))
{
_loginLocation = value;
}
else
{
throw new ArgumentException($"Invalid login location value: {value}");
}
}
}
// Assuming ThemeOptions and LoginLocationOptions are enums or similar
private bool IsValidTheme(string theme) => Enum.TryParse(typeof(ThemeOptions), theme, out _);
private bool IsValidLoginLocation(string location) => Enum.TryParse(typeof(LoginLocationOptions), location, out _);
}
}
+85 -9
View File
@@ -1,8 +1,15 @@
using Avalonia.Controls;
using System;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using GalaxyViewer.Views;
using ReactiveUI;
using System.Reactive;
using System.Threading.Tasks;
using OpenMetaverse;
using System.IO;
using System.Windows.Input;
using Serilog;
using Avalonia;
namespace GalaxyViewer.ViewModels
{
@@ -11,6 +18,14 @@ namespace GalaxyViewer.ViewModels
private UserControl? _currentView;
private bool _isLoggedIn;
private GridClient _client = new GridClient();
// Define properties for Username, Password, LoginLocation, and Grid.
public string? Username { get; set; }
public string? Password { get; set; }
public string? LoginLocation { get; set; }
public string? Grid { get; set; }
public UserControl? CurrentView
{
get => _currentView;
@@ -29,30 +44,91 @@ namespace GalaxyViewer.ViewModels
public ReactiveCommand<Unit, Unit> LogoutCommand { get; }
public ReactiveCommand<Unit, Unit> LoginCommand { get; }
public ReactiveCommand<Unit, Unit> LoginWithCurrentValues { get; }
public ICommand ShowPreferencesCommand { get; }
public ICommand ExitCommand { get; }
public MainViewModel()
{
_currentView = new LoginView();
IsLoggedIn = false; // Set this to true when the user logs in
IsLoggedIn = false; // By default you aren't logged in
LogoutCommand = ReactiveCommand.Create(Logout);
LoginCommand = ReactiveCommand.CreateFromTask(Login);
LoginCommand = ReactiveCommand.Create(DisplayLoginView);
LoginWithCurrentValues = ReactiveCommand.CreateFromTask(Login);
ShowPreferencesCommand = ReactiveCommand.Create(ShowPreferences);
ExitCommand = ReactiveCommand.Create(ExitApplication);
}
private void Logout()
{
// Perform logout operation here
_client.Network.Logout();
IsLoggedIn = false;
}
private Task Login()
private void DisplayLoginView()
{
// Perform login operation here
// If login is successful, set IsLoggedIn to true
IsLoggedIn = true;
// If it doesn't work, we will return an error message
CurrentView = new LoginView();
}
return Task.CompletedTask;
public async Task Login()
{
try
{
// Validate properties before using them
if (string.IsNullOrEmpty(Username) || string.IsNullOrEmpty(Password))
{
// Handle invalid login parameters
// For example, you might want to show an error message
// or log the invalid login attempt to a file
File.AppendAllText("error.log", "Invalid login parameters for user: " + Username);
}
string userAgent = "GalaxyViewer/0.1.0";
LoginParams libreMetaverseLoginParams = _client.Network.DefaultLoginParams(Username, Password, userAgent, LoginLocation, Grid);
bool loginSuccess = await Task.Run(() => _client.Network.Login(libreMetaverseLoginParams));
if (loginSuccess)
{
IsLoggedIn = true;
}
else
{
// Handle failed login
Log.Error("Failed to login user: {Username}", Username);
}
}
catch (Exception ex)
{
// Handle any exceptions that occur during login
Log.Error(ex, "An error occurred while logging in user: {Username}", Username);
}
}
private void ShowPreferences()
{
CurrentView = new PreferencesView
{
DataContext = new PreferencesViewModel()
};
}
private void ExitApplication()
{
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
{
desktopLifetime.Shutdown();
}
}
public void Dispose()
{
_client.Network.Logout();
}
}
}
@@ -0,0 +1,124 @@
using GalaxyViewer.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Threading.Tasks;
using System.Xml.Serialization;
using ReactiveUI;
using System.Reactive;
namespace GalaxyViewer.ViewModels
{
public class PreferencesViewModel : ReactiveObject
{
public IEnumerable<string> ThemeOptions => Enum.GetNames(typeof(ThemeOptions));
public IEnumerable<string> LoginLocationOptions => Enum.GetNames(typeof(LoginLocationOptions));
private Lazy<PreferencesModel> _lazyPreferences;
private readonly string _preferencesFilePath;
public ReactiveCommand<Unit, Unit> SaveCommand { get; }
public PreferencesViewModel()
{
_preferencesFilePath = GetPreferencesFilePath();
_lazyPreferences = new Lazy<PreferencesModel>(LoadOrCreatePreferences);
SaveCommand = ReactiveCommand.CreateFromTask(SavePreferencesAsync);
}
public static async Task<PreferencesViewModel> CreateAsync()
{
var viewModel = new PreferencesViewModel();
await viewModel.LoadPreferencesAsync();
return viewModel;
}
private PreferencesModel Preferences => _lazyPreferences.Value;
private static string GetPreferencesFilePath()
{
var appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var applicationFolder = Path.Combine(appDataFolder, "GalaxyViewer");
Directory.CreateDirectory(applicationFolder); // CreateDirectory is no-op if exists
return Path.Combine(applicationFolder, "preferences.xml");
}
private PreferencesModel LoadOrCreatePreferences()
{
if (File.Exists(_preferencesFilePath))
{
using var stream = new FileStream(_preferencesFilePath, FileMode.Open);
var serializer = new XmlSerializer(typeof(PreferencesModel));
return serializer.Deserialize(stream) as PreferencesModel ?? new PreferencesModel();
}
return new PreferencesModel
{
Theme = Enum.TryParse(typeof(ThemeOptions), "System", out object themeResult) ? themeResult.ToString() : Models.ThemeOptions.Light.ToString(),
LoginLocation = Enum.TryParse(typeof(LoginLocationOptions), "LastLocation", out object loginLocationResult) ? loginLocationResult.ToString() : Models.LoginLocationOptions.Home.ToString()
};
}
private string _theme;
public string Theme
{
get => Preferences.Theme;
set
{
if (Preferences.Theme != value)
{
Preferences.Theme = value;
this.RaisePropertyChanged(nameof(Theme));
}
}
}
private string _loginLocation;
public string LoginLocation
{
get => Preferences.LoginLocation;
set
{
if (Preferences.LoginLocation != value)
{
Preferences.LoginLocation = value;
this.RaisePropertyChanged(nameof(LoginLocation));
}
}
}
private string _statusMessage = string.Empty;
public string StatusMessage
{
get => _statusMessage;
set => this.RaiseAndSetIfChanged(ref _statusMessage, value);
}
public async Task LoadPreferencesAsync()
{
await Task.Run(() =>
{
_lazyPreferences = new Lazy<PreferencesModel>(LoadOrCreatePreferences);
});
}
public async Task SavePreferencesAsync()
{
try
{
Preferences.LastSavedEpoch = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
using var stream = new FileStream(_preferencesFilePath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true);
var serializer = new XmlSerializer(typeof(PreferencesModel));
await Task.Run(() => serializer.Serialize(stream, Preferences));
StatusMessage = "Preferences saved";
}
catch (Exception ex)
{
StatusMessage = $"Error saving preferences: {ex.Message}";
}
}
}
}
+3 -5
View File
@@ -1,7 +1,6 @@
using System;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using GalaxyViewer.ViewModels;
using Avalonia.ReactiveUI;
namespace GalaxyViewer.Views
{
@@ -17,10 +16,9 @@ namespace GalaxyViewer.Views
AvaloniaXamlLoader.Load(this);
}
private void LoginButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
private void LoginButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{
var viewModel = (MainViewModel)DataContext!;
viewModel.LoginCommand.Execute().Subscribe();
// Your event handling code here, which attempts the Login method
}
}
}
+20 -22
View File
@@ -7,29 +7,27 @@
x:Class="GalaxyViewer.Views.MainView"
x:DataType="vm:MainViewModel">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainViewModel />
<vm:MainViewModel />
</Design.DataContext>
<DockPanel>
<DockPanel LastChildFill="True">
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header="New Window"/>
<Separator/>
<MenuItem Header="Upload Blinn-Phong Texture" IsEnabled="{Binding IsLoggedIn}"/>
<MenuItem Header="Upload PBR Material" IsEnabled="{Binding IsLoggedIn}"/>
<MenuItem Header="Upload Mesh" IsEnabled="{Binding IsLoggedIn}"/>
<MenuItem Header="Import Object" IsEnabled="{Binding IsLoggedIn}"/>
<MenuItem Header="Script Editor" IsEnabled="{Binding IsLoggedIn}"/>
<Separator/>
<MenuItem Header="Login"/>
<MenuItem Header="Logout" Command="{Binding LogoutCommand}" IsEnabled="{Binding IsLoggedIn}"/>
<MenuItem Header="Relog"/>
<MenuItem Header="Preferences"/>
<Separator/>
<MenuItem Header="Exit"/>
</MenuItem>
<MenuItem Header="File">
<MenuItem Header="New Window"/>
<Separator/>
<MenuItem Header="Upload Blinn-Phong Texture" IsEnabled="{Binding IsLoggedIn}"/>
<MenuItem Header="Upload PBR Material" IsEnabled="{Binding IsLoggedIn}"/>
<MenuItem Header="Upload Mesh" IsEnabled="{Binding IsLoggedIn}"/>
<MenuItem Header="Import Object" IsEnabled="{Binding IsLoggedIn}"/>
<MenuItem Header="Script Editor" IsEnabled="{Binding IsLoggedIn}"/>
<Separator/>
<MenuItem Header="Login" Command="{Binding LoginCommand}" IsEnabled="{Binding !IsLoggedIn}"/>
<MenuItem Header="Logout" Command="{Binding LogoutCommand}" IsEnabled="{Binding IsLoggedIn}"/>
<MenuItem Header="Relog" IsEnabled="{Binding !IsLoggedIn}"/>
<MenuItem Header="Preferences" Command="{Binding ShowPreferencesCommand}"/>
<Separator/>
<MenuItem Header="Exit" Command="{Binding ExitCommand}"/>
</MenuItem>
<MenuItem Header="World" IsEnabled="{Binding IsLoggedIn}">
<MenuItem Header="Create new Landmark Here"/>
<MenuItem Header="Landmarks"/>
@@ -55,6 +53,6 @@
<!-- Add more menu items as needed -->
</Menu>
<ContentControl Content="{Binding CurrentView}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</DockPanel>
<ContentControl Content="{Binding CurrentView}"/>
</DockPanel>
</UserControl>
+17
View File
@@ -0,0 +1,17 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:GalaxyViewer.ViewModels"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"
x:Class="GalaxyViewer.Views.PreferencesView"
x:DataType="vm:PreferencesViewModel">
<StackPanel Margin="20">
<TextBlock Text="Preferences" />
<ComboBox ItemsSource="{Binding ThemeOptions}" SelectedItem="{Binding Theme}" />
<ComboBox ItemsSource="{Binding LoginLocationOptions}" SelectedItem="{Binding LoginLocation}" />
<!-- Add more UI elements as needed -->
<Button Content="Save Preferences" Command="{Binding SaveCommand}" HorizontalAlignment="Left" />
<TextBlock Text="{Binding StatusMessage}" />
</StackPanel>
</UserControl>
@@ -0,0 +1,21 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using GalaxyViewer.ViewModels;
namespace GalaxyViewer.Views
{
public partial class PreferencesView : UserControl
{
public PreferencesView()
{
InitializeComponent();
this.DataContext = new PreferencesViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}