mirror of
https://github.com/GalaxyViewer/GalaxyViewer.git
synced 2026-08-14 00:57:53 +00:00
✨ Add Preferences and saving, tweak LoginView code
This commit is contained in:
Vendored
+7
@@ -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": []
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 _);
|
||||
}
|
||||
}
|
||||
@@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user