mirror of
https://github.com/GalaxyViewer/GalaxyViewer.git
synced 2026-08-14 09:02:08 +00:00
🚧 Session data is gathered correctly and not a ton of times
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
<svg width="35" height="35" viewBox="0 0 35 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M30.4661 34.928C30.5364 34.928 30.6052 34.928 30.6754 34.928C32.8596 34.928 34.654 33.2918 34.9053 31.1752L34.9356 16.9955C34.6872 7.56697 26.9662 0 17.4777 0C7.83263 0 0.0137329 7.8189 0.0137329 17.464C0.0137329 27.0059 7.66618 34.7631 17.1687 34.928H30.4661Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.5239 5.948C12.0268 5.948 7.42967 9.80117 6.286 14.954C7.38092 15.2609 8.18385 16.2664 8.18385 17.4593C8.18385 18.6523 7.38092 19.6577 6.286 19.9647C7.42966 25.1175 12.0268 28.9706 17.5239 28.9706C19.525 28.9706 21.4068 28.4601 23.0462 27.562V28.8927H29.0352V17.9365C29.0407 17.7908 29.0352 17.6063 29.0352 17.4593C29.0352 11.1018 23.8814 5.948 17.5239 5.948ZM12.0098 17.4593C12.0098 14.414 14.4786 11.9452 17.5239 11.9452C20.5693 11.9452 23.038 14.414 23.038 17.4593C23.038 20.5047 20.5693 22.9734 17.5239 22.9734C14.4786 22.9734 12.0098 20.5047 12.0098 17.4593Z" fill="#8B44AC"/>
|
||||
<path d="M7.36841 17.4517C7.36841 18.4691 6.54368 19.2938 5.52631 19.2938C4.50894 19.2938 3.6842 18.4691 3.6842 17.4517C3.6842 16.4343 4.50894 15.6096 5.52631 15.6096C6.54368 15.6096 7.36841 16.4343 7.36841 17.4517Z" fill="#8B44AC"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
+142
-125
@@ -16,153 +16,170 @@ using GalaxyViewer.Views;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Serilog;
|
||||
|
||||
namespace GalaxyViewer;
|
||||
|
||||
public class App : Application, IDisposable
|
||||
namespace GalaxyViewer
|
||||
{
|
||||
private static IServiceProvider? _serviceProvider;
|
||||
public static PreferencesManager? PreferencesManager { get; private set; }
|
||||
private static LiteDbService _liteDbService;
|
||||
private static SessionModel _session;
|
||||
|
||||
public App()
|
||||
public class App : Application, IDisposable
|
||||
{
|
||||
ConfigureLogging();
|
||||
}
|
||||
private static IServiceProvider? _serviceProvider;
|
||||
public static PreferencesManager? PreferencesManager { get; private set; }
|
||||
public static SessionManager? SessionManager { get; private set; }
|
||||
private static ILiteDbService _liteDbService;
|
||||
|
||||
private static void ConfigureLogging()
|
||||
{
|
||||
var logFilePath =
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"GalaxyViewer", "logs", "error.log");
|
||||
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.WriteTo.Console()
|
||||
.WriteTo.File(logFilePath, rollingInterval: RollingInterval.Day)
|
||||
.CreateLogger();
|
||||
}
|
||||
|
||||
private static void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<LiteDbService>();
|
||||
// Register other services here
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
ConfigureServices(serviceCollection);
|
||||
_serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
_liteDbService = _serviceProvider.GetService<LiteDbService>();
|
||||
if (_liteDbService == null)
|
||||
public App()
|
||||
{
|
||||
throw new InvalidOperationException("LiteDbService is not registered.");
|
||||
ConfigureLogging();
|
||||
}
|
||||
|
||||
PreferencesManager = new PreferencesManager(_liteDbService);
|
||||
PreferencesManager.PreferencesChanged += OnPreferencesChanged;
|
||||
|
||||
_session = _liteDbService.GetSession();
|
||||
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
public static bool IsLoggedIn
|
||||
{
|
||||
get => _session.IsLoggedIn;
|
||||
set
|
||||
private static void ConfigureLogging()
|
||||
{
|
||||
if (_session.IsLoggedIn != value)
|
||||
var logFilePath =
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"GalaxyViewer", "logs", "error.log");
|
||||
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.WriteTo.Console()
|
||||
.WriteTo.File(logFilePath, rollingInterval: RollingInterval.Day)
|
||||
.CreateLogger();
|
||||
}
|
||||
|
||||
private static void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<ILiteDbService, LiteDbService>();
|
||||
services.AddSingleton<SessionManager>();
|
||||
services.AddSingleton<IGridService, GridService>();
|
||||
services.AddSingleton<PreferencesViewModel>();
|
||||
services.AddSingleton<MainViewModel>();
|
||||
services.AddSingleton<LoginViewModel>();
|
||||
services.AddSingleton<LoggedInViewModel>();
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
ConfigureServices(serviceCollection);
|
||||
_serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
_liteDbService = _serviceProvider.GetRequiredService<ILiteDbService>();
|
||||
if (_liteDbService == null)
|
||||
{
|
||||
_session.IsLoggedIn = value;
|
||||
_liteDbService.SaveSession(_session);
|
||||
OnStaticPropertyChanged();
|
||||
throw new InvalidOperationException("LiteDbService is not registered.");
|
||||
}
|
||||
|
||||
PreferencesManager = new PreferencesManager(_liteDbService);
|
||||
PreferencesManager.PreferencesChanged += OnPreferencesChanged;
|
||||
|
||||
SessionManager = _serviceProvider.GetRequiredService<SessionManager>();
|
||||
if (SessionManager == null)
|
||||
{
|
||||
throw new InvalidOperationException("SessionManager is not registered.");
|
||||
}
|
||||
|
||||
SessionManager.SessionChanged += OnSessionChanged;
|
||||
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
public static bool IsLoggedIn
|
||||
{
|
||||
get => SessionManager?.Session.IsLoggedIn ?? false;
|
||||
set
|
||||
{
|
||||
if (SessionManager != null && SessionManager.Session.IsLoggedIn != value)
|
||||
{
|
||||
var session = SessionManager.Session;
|
||||
session.IsLoggedIn = value;
|
||||
SessionManager.Session = session;
|
||||
OnStaticPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
try
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
switch (ApplicationLifetime)
|
||||
var serviceCollection = new ServiceCollection();
|
||||
ConfigureServices(serviceCollection);
|
||||
_serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
case IClassicDesktopStyleApplicationLifetime desktop:
|
||||
Log.Information("Initializing MainWindow for desktop application.");
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = new MainViewModel(_liteDbService)
|
||||
};
|
||||
desktop.MainWindow.Show();
|
||||
break;
|
||||
case ISingleViewApplicationLifetime singleViewPlatform:
|
||||
Log.Information("Initializing MainView for single view application.");
|
||||
singleViewPlatform.MainView = new MainView
|
||||
{
|
||||
DataContext = new MainViewModel(_liteDbService)
|
||||
};
|
||||
break;
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = _serviceProvider.GetRequiredService<MainViewModel>()
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "An error occurred while initializing the main window.");
|
||||
throw; // Optionally rethrow the exception if you want to halt the application
|
||||
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
|
||||
{
|
||||
singleViewPlatform.MainView = new MainView
|
||||
{
|
||||
DataContext = _serviceProvider.GetRequiredService<MainViewModel>()
|
||||
};
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
public static event PropertyChangedEventHandler? StaticPropertyChanged;
|
||||
|
||||
public static event PropertyChangedEventHandler? StaticPropertyChanged;
|
||||
|
||||
private static void OnStaticPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
private void OnPreferencesChanged(object? sender, PreferencesModel preferences)
|
||||
{
|
||||
ApplyPreferences(preferences);
|
||||
RefreshThemeForAllWindows();
|
||||
}
|
||||
|
||||
private void ApplyPreferences(PreferencesModel preferences)
|
||||
{
|
||||
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() =>
|
||||
private static void OnStaticPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
RequestedThemeVariant = preferences.Theme switch
|
||||
{
|
||||
"Light" => ThemeVariant.Light,
|
||||
"Dark" => ThemeVariant.Dark,
|
||||
_ => ThemeVariant.Default
|
||||
};
|
||||
StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
// TODO: Apply other preferences
|
||||
});
|
||||
}
|
||||
|
||||
private async void RefreshThemeForAllWindows()
|
||||
{
|
||||
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktopLifetime)
|
||||
return;
|
||||
|
||||
await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
private void OnPreferencesChanged(object? sender, PreferencesModel preferences)
|
||||
{
|
||||
foreach (var window in desktopLifetime.Windows)
|
||||
ApplyPreferences(preferences);
|
||||
RefreshThemeForAllWindows();
|
||||
}
|
||||
|
||||
private void OnSessionChanged(object? sender, SessionModel session)
|
||||
{
|
||||
if (session.IsLoggedIn)
|
||||
{
|
||||
if (window is not BaseWindow baseWindow) continue;
|
||||
var resultTheme = (await PreferencesManager?.LoadPreferencesAsync())?.Theme;
|
||||
if (resultTheme != null)
|
||||
baseWindow.ApplyTheme(resultTheme);
|
||||
IsLoggedIn = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (IsLoggedIn)
|
||||
{
|
||||
IsLoggedIn = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
//PreferencesManager?.Dispose();
|
||||
(_serviceProvider as IDisposable)?.Dispose();
|
||||
private void ApplyPreferences(PreferencesModel preferences)
|
||||
{
|
||||
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
RequestedThemeVariant = preferences.Theme switch
|
||||
{
|
||||
"Light" => ThemeVariant.Light,
|
||||
"Dark" => ThemeVariant.Dark,
|
||||
_ => ThemeVariant.Default
|
||||
};
|
||||
|
||||
// TODO: Apply other preferences
|
||||
});
|
||||
}
|
||||
|
||||
private async void RefreshThemeForAllWindows()
|
||||
{
|
||||
if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktopLifetime)
|
||||
return;
|
||||
|
||||
await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
foreach (var window in desktopLifetime.Windows)
|
||||
{
|
||||
if (window is not BaseWindow baseWindow) continue;
|
||||
var resultTheme = (await PreferencesManager?.LoadPreferencesAsync())?.Theme;
|
||||
if (resultTheme != null)
|
||||
baseWindow.ApplyTheme(resultTheme);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
//PreferencesManager?.Dispose();
|
||||
(_serviceProvider as IDisposable)?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="61.167931mm"
|
||||
height="66.846802mm"
|
||||
viewBox="0 0 61.167931 66.846802"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
sodipodi:docname="Logo.svg"
|
||||
inkscape:export-filename="Logo.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="3.3763328"
|
||||
inkscape:cx="81.745497"
|
||||
inkscape:cy="150.01483"
|
||||
inkscape:window-width="2510"
|
||||
inkscape:window-height="1412"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g7"
|
||||
inkscape:export-bgcolor="#ffffff00" /><defs
|
||||
id="defs1" /><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-71.41723,-72.385321)"><g
|
||||
id="g7"
|
||||
transform="matrix(1.0018151,0,0,0.98691065,58.164341,-135.54191)"><g
|
||||
style="fill:#000000"
|
||||
id="g2-3"
|
||||
transform="matrix(0.13225757,0,0,0.13225757,9.8908307,210.68501)"><g
|
||||
id="SVGRepo_bgCarrier-8"
|
||||
stroke-width="0" /><g
|
||||
id="SVGRepo_tracerCarrier-99"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round" /><g
|
||||
id="SVGRepo_iconCarrier-1"
|
||||
style="stroke:none;stroke-width:0.998053;stroke-dasharray:none;stroke-opacity:1"> <g
|
||||
transform="translate(1)"
|
||||
id="g5-7"
|
||||
style="stroke:none;stroke-width:0.998053;stroke-dasharray:none;stroke-opacity:1"> <polygon
|
||||
style="fill:#aab3c7;fill-opacity:1;stroke:none;stroke-width:0.998053;stroke-dasharray:none;stroke-opacity:1"
|
||||
points="254.639,230.532 254.639,503.599 32.773,392.665 32.773,119.599 "
|
||||
id="polygon1-5" /> <polygon
|
||||
style="fill:#a3abc2;fill-opacity:1;stroke:none;stroke-width:0.998053;stroke-dasharray:none;stroke-opacity:1"
|
||||
points="254.639,230.532 254.639,503.599 476.506,392.665 476.506,119.599 "
|
||||
id="polygon2-3" /> <polygon
|
||||
style="fill:#000040;stroke:none;stroke-width:0.998053;stroke-dasharray:none;stroke-opacity:1"
|
||||
points="254.639,230.532 32.773,119.599 246.106,8.665 476.506,119.599 "
|
||||
id="polygon3-3" /> <path
|
||||
d="m 254.639,512.132 c -1.707,0 -2.56,0 -3.413,-0.853 L 29.359,400.345 c -3.413,-1.707 -5.12,-4.267 -5.12,-7.68 V 119.599 c 0,-2.56 1.707,-5.973 4.267,-7.68 2.56,-1.707 5.973,-1.707 8.533,0 l 221.867,110.933 c 2.56,1.707 5.12,4.267 5.12,7.68 v 273.067 c 0,2.56 -1.707,5.973 -4.267,7.68 -1.706,0 -3.413,0.853 -5.12,0.853 z M 41.306,387.545 l 204.8,102.4 V 235.652 l -204.8,-102.4 z"
|
||||
id="path3-4"
|
||||
style="stroke:none;stroke-width:0.998053;stroke-dasharray:none;stroke-opacity:1" /> <path
|
||||
d="m 254.639,512.132 c -1.707,0 -3.413,0 -4.267,-0.853 -2.56,-1.707 -4.267,-5.12 -4.267,-7.68 V 230.532 c 0,-3.413 1.707,-5.973 5.12,-7.68 L 473.092,111.919 c 2.56,-1.707 5.973,-0.853 8.533,0 2.56,1.707 4.267,4.267 4.267,7.68 v 273.067 c 0,3.413 -1.707,5.973 -5.12,7.68 L 258.906,511.279 c -1.707,0.853 -2.56,0.853 -4.267,0.853 z m 8.534,-276.48 v 254.293 l 204.8,-102.4 V 133.252 Z"
|
||||
id="path4-6"
|
||||
style="stroke:none;stroke-width:0.998053;stroke-dasharray:none;stroke-opacity:1" /> <path
|
||||
d="m 254.639,239.065 c -1.707,0 -2.56,0 -3.413,-0.853 L 29.359,127.279 c -3.413,-1.707 -5.12,-4.267 -5.12,-7.68 0,-3.413 1.707,-5.973 4.267,-7.68 L 241.839,0.985 c 2.56,-0.853 5.12,-1.707 7.68,0 l 230.4,110.933 c 2.56,1.707 5.12,4.267 5.12,7.68 0,3.413 -1.707,5.973 -5.12,7.68 L 258.053,238.212 c -0.854,0.853 -1.707,0.853 -3.414,0.853 z M 51.546,119.599 254.639,221.146 456.879,119.599 246.106,18.052 Z"
|
||||
id="path5-9"
|
||||
style="stroke:none;stroke-width:0.998053;stroke-dasharray:none;stroke-opacity:1" /> </g> </g></g><g
|
||||
style="fill:#000000"
|
||||
id="g3-0"
|
||||
transform="matrix(0.4612168,-0.2297859,0,0.52234532,44.594091,247.23515)"><g
|
||||
id="g1-9-2">
|
||||
<path
|
||||
d="M 51.751,23.435 V 5.474 H 44.645 V 31.449 H 40.562 V 19.958 H 30.103 V 12.096 H 20.906 V 30.538 H 18.624 V 24.796 H 7.902 V 16.934 H 0 v 7.862 5.742 24.195 h 16.371 2.253 2.282 7.561 1.636 5.268 5.191 19.645 V 38.826 31.449 23.434 H 51.751 Z M 4.843,26.899 H 2.239 v -6.183 h 2.604 z m 6.25,12.021 H 8.489 V 28.439 h 2.604 z m 4.297,-4.297 h -2.604 v -6.184 h 2.604 z m 9.765,-4.632 h -2.604 v -6.183 h 2.604 z m 0,-8.887 H 22.551 V 14.92 h 2.604 z m 4.428,17.528 H 26.979 V 32.45 h 2.604 z M 34.4,29.991 H 31.796 V 23.808 H 34.4 Z m 14.713,5.549 h -2.604 v -6.184 h 2.604 z m 0,-11.345 h -2.604 v -6.183 h 2.604 z m 0,-10.31 H 46.509 V 7.703 h 2.604 z m 5.469,26.894 H 51.978 V 30.3 h 2.604 z"
|
||||
id="path1-19" />
|
||||
</g></g><g
|
||||
style="fill:#000000;fill-opacity:1"
|
||||
id="g4-4"
|
||||
transform="matrix(0.31699982,0.16860553,0,0.35510983,13.001511,229.21359)"><g
|
||||
id="SVGRepo_bgCarrier-9-3"
|
||||
stroke-width="0"
|
||||
style="fill:#000000;fill-opacity:1" /><g
|
||||
id="SVGRepo_tracerCarrier-7-6"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
style="fill:#000000;fill-opacity:1" /><g
|
||||
id="SVGRepo_iconCarrier-7-4"
|
||||
style="fill:#000000;fill-opacity:1"> <g
|
||||
id="g1-99-1"
|
||||
style="fill:#000000;fill-opacity:1"> <path
|
||||
fill="#231f20"
|
||||
d="m 91.963,80.982 0.023,-0.013 -7.285,-12.617 h 2.867 v -0.013 c 0.598,0 1.083,-0.484 1.083,-1.082 0,-0.185 -0.059,-0.351 -0.14,-0.503 L 88.53,66.743 81.793,55.074 h 1.639 v -0.009 c 0.427,0 0.773,-0.347 0.773,-0.772 0,-0.132 -0.042,-0.25 -0.1,-0.359 l 0.013,-0.008 -9.802,-16.979 -0.01,0.006 c -0.216,-0.442 -0.66,-0.754 -1.186,-0.754 -0.524,0 -0.968,0.311 -1.185,0.752 l -0.005,-0.003 -9.802,16.978 0.002,10e-4 c -0.061,0.11 -0.105,0.231 -0.105,0.366 0,0.426 0.346,0.772 0.773,0.772 v 0.009 h 1.661 l -6.737,11.669 0.003,0.001 c -0.085,0.155 -0.147,0.324 -0.147,0.513 0,0.598 0.485,1.082 1.083,1.082 v 0.013 h 2.894 l -2.1,3.638 -8.399,-14.548 h 4.046 v -0.018 c 0.844,0 1.528,-0.685 1.528,-1.528 0,-0.26 -0.071,-0.502 -0.186,-0.717 L 56.459,55.17 46.952,38.703 h 2.313 v -0.012 c 0.603,0 1.091,-0.488 1.091,-1.092 0,-0.186 -0.059,-0.353 -0.141,-0.506 L 50.234,37.082 36.4,13.125 36.395,13.128 c -0.305,-0.625 -0.94,-1.06 -1.683,-1.06 -0.758,0 -1.408,0.452 -1.704,1.1 l -13.807,23.914 0.003,0.002 c -0.086,0.156 -0.148,0.326 -0.148,0.516 0,0.604 0.488,1.092 1.09,1.092 v 0.012 h 2.345 l -9.395,16.272 c -0.195,0.257 -0.316,0.573 -0.316,0.92 0,0.844 0.685,1.528 1.528,1.528 v 0.018 h 4.084 L 8.252,75.007 c -0.24,0.314 -0.387,0.702 -0.387,1.128 0,1.032 0.838,1.87 1.871,1.87 v 0.021 h 19.779 v 8.43 c 0,0.815 0.661,1.477 1.476,1.477 h 7.383 c 0.815,0 1.477,-0.661 1.477,-1.477 v -8.43 h 16.12 l -1.699,2.943 0.003,0.002 c -0.104,0.189 -0.18,0.396 -0.18,0.628 0,0.732 0.593,1.325 1.325,1.325 v 0.015 h 14.016 v 3.941 c 0,0.578 0.469,1.046 1.046,1.046 h 5.232 c 0.578,0 1.046,-0.468 1.046,-1.046 v -3.941 h 14.05 v -0.015 c 0.732,0 1.326,-0.593 1.326,-1.325 -10e-4,-0.227 -0.072,-0.431 -0.173,-0.617 z"
|
||||
id="path1-7-6"
|
||||
style="fill:#000000;fill-opacity:1" /> </g> </g></g><g
|
||||
style="fill:#a2aac3;fill-opacity:1"
|
||||
id="g6"
|
||||
transform="matrix(0.04838256,0,0.0277314,0.02641433,19.746211,217.7951)"><g
|
||||
id="SVGRepo_bgCarrier-3-7"
|
||||
stroke-width="0"
|
||||
style="fill:#a2aac3;fill-opacity:1" /><g
|
||||
id="SVGRepo_tracerCarrier-9-7"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
style="fill:#a2aac3;fill-opacity:1" /><g
|
||||
id="SVGRepo_iconCarrier-4-8"
|
||||
style="fill:#a2aac3;fill-opacity:1"> <g
|
||||
id="g1-4-4"
|
||||
style="fill:#a2aac3;fill-opacity:1"> <path
|
||||
d="M 611.502,203.367 C 607.613,150.151 580.581,102.529 518.226,78.361 448.865,51.477 366.266,65.328 301.456,95.174 233.672,126.39 150.143,189.109 129.774,266.779 c -7.962,30.357 -5.303,63.94 10.093,91.288 19.789,35.152 59.002,56.863 99.094,61.324 55.248,6.147 112.773,-16.493 155.435,-50.741 24.317,-19.521 46.5,-44.992 58.997,-73.774 11.212,-25.821 13.864,-56.419 1.925,-81.911 -16.243,-34.679 -54.683,-49.835 -91.121,-44.132 -4.787,0.735 -9.337,1.776 -13.657,3.016 -4.322,1.234 -8.419,2.662 -12.295,4.256 -7.765,3.148 -14.653,6.919 -20.773,10.925 -6.109,4.03 -11.447,8.348 -16.022,12.747 -1.037,1.043 -2.056,2.067 -3.054,3.071 -1.08,1.074 -2.021,2.094 -2.964,3.1 -1.877,2.012 -3.588,3.995 -5.207,5.882 -3.208,3.799 -5.955,7.307 -8.307,10.499 -4.731,6.35 -8.047,11.323 -10.284,14.671 -1.1,1.684 -1.934,2.961 -2.494,3.817 -0.55,0.863 -0.829,1.301 -0.829,1.301 -0.92,1.449 -0.97,3.367 0.036,4.894 1.366,2.076 4.157,2.652 6.233,1.285 0,0 0.433,-0.285 1.288,-0.846 0.846,-0.567 2.108,-1.413 3.772,-2.529 3.307,-2.238 8.187,-5.589 14.48,-9.753 6.333,-4.129 14.012,-9.048 23.248,-13.695 2.175,-1.069 4.431,-2.096 6.846,-3.028 2.399,-0.946 4.893,-1.86 7.522,-2.638 5.229,-1.598 10.883,-2.863 16.803,-3.589 2.961,-0.352 5.978,-0.597 9.027,-0.671 3.05,-0.067 6.128,0.03 9.183,0.299 10.759,1.003 24.149,4.372 30.659,13.714 4.245,6.092 4.815,14.123 3.391,21.409 -2.471,12.644 -10.28,23.585 -18.883,33.174 -30.278,33.744 -74.119,56.946 -119.526,60.718 -37.814,2.947 -73.075,-14.422 -78.121,-55.732 -3.607,-29.523 10.253,-58.809 29.349,-81.612 26.954,-32.186 60.518,-59.114 98.754,-76.011 14.09,-6.227 28.7,-11.46 43.667,-15.311 39.803,-10.321 86.856,-13.345 125.381,3.353 36.924,16.004 60.458,52.144 59.714,92.483 -0.223,11.808 -2.412,23.851 -6.114,35.878 -3.66,12.037 -8.996,24.017 -15.499,35.56 -6.539,11.542 -14.238,22.664 -22.764,33.124 C 454.222,364.819 387.62,404.605 326.223,430.7 252.114,462.198 110.559,499.434 57.635,412.857 30.047,367.726 40.167,309.994 55.133,262.396 c 0.541,-1.722 0.006,-3.684 -1.497,-4.867 -1.942,-1.528 -4.754,-1.193 -6.282,0.748 0,0 -1.277,1.622 -3.752,4.764 -0.624,0.768 -1.287,1.718 -2.039,2.741 -0.746,1.027 -1.563,2.153 -2.451,3.375 -22.006,30.295 -36.716,66.932 -38.848,104.465 -1.962,34.546 6.992,69.97 26.952,98.236 23.939,33.9 62.223,55.79 102.375,66.286 63.644,16.637 134.014,11.022 196.48,-7.885 14.716,-4.454 29.379,-9.645 43.893,-15.61 14.505,-5.984 28.826,-12.8 42.955,-20.315 51.202,-27.233 99.882,-64.099 136.409,-109.452 39.462,-48.997 66.735,-119.103 62.174,-181.515 z"
|
||||
id="path1-1-2"
|
||||
style="fill:#a2aac3;fill-opacity:1" /> </g> </g></g></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 10 KiB |
@@ -40,6 +40,9 @@
|
||||
<PackageReference Include="Irihi.Ursa.Themes.Semi" Version="1.6.0.2" />
|
||||
<PackageReference Include="LibreMetaverse" Version="2.1.3.735" />
|
||||
<PackageReference Include="LiteDB" Version="5.0.21" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
using LiteDB;
|
||||
|
||||
namespace GalaxyViewer.Models;
|
||||
|
||||
@@ -1,14 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LiteDB;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace GalaxyViewer.Models;
|
||||
|
||||
public class SessionModel
|
||||
public class SessionModel : INotifyPropertyChanged
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public bool IsLoggedIn { get; set; }
|
||||
public string AvatarName { get; set; }
|
||||
public UUID AvatarKey { get; set; }
|
||||
public int Balance { get; set; }
|
||||
public string CurrentLocation { get; set; }
|
||||
public string CurrentLocationWelcomeMessage { get; set; }
|
||||
[BsonId] public ObjectId Id { get; set; }
|
||||
|
||||
private bool _isLoggedIn;
|
||||
private string _avatarName;
|
||||
private UUID _avatarKey;
|
||||
private int _balance;
|
||||
private string _currentLocation;
|
||||
private string _currentLocationWelcomeMessage;
|
||||
|
||||
public bool IsLoggedIn
|
||||
{
|
||||
get => _isLoggedIn;
|
||||
set => SetField(ref _isLoggedIn, value);
|
||||
}
|
||||
|
||||
public string AvatarName
|
||||
{
|
||||
get => _avatarName;
|
||||
set => SetField(ref _avatarName, value);
|
||||
}
|
||||
|
||||
public UUID AvatarKey
|
||||
{
|
||||
get => _avatarKey;
|
||||
set => SetField(ref _avatarKey, value);
|
||||
}
|
||||
|
||||
public int Balance
|
||||
{
|
||||
get => _balance;
|
||||
set => SetField(ref _balance, value);
|
||||
}
|
||||
|
||||
public string CurrentLocation
|
||||
{
|
||||
get => _currentLocation;
|
||||
set => SetField(ref _currentLocation, value);
|
||||
}
|
||||
|
||||
public string CurrentLocationWelcomeMessage
|
||||
{
|
||||
get => _currentLocationWelcomeMessage;
|
||||
set => SetField(ref _currentLocationWelcomeMessage, value);
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
private void SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
if (!EqualityComparer<T>.Default.Equals(field, value))
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using LiteDB;
|
||||
using GalaxyViewer.Models;
|
||||
using LiteDB;
|
||||
|
||||
namespace GalaxyViewer.Services;
|
||||
|
||||
public class GridService
|
||||
namespace GalaxyViewer.Services
|
||||
{
|
||||
private readonly string _databasePath;
|
||||
|
||||
public GridService()
|
||||
public class GridService : IGridService
|
||||
{
|
||||
var appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "GalaxyViewer");
|
||||
Directory.CreateDirectory(appDataPath); // Ensure the directory exists
|
||||
_databasePath = Path.Combine(appDataPath, "data.db");
|
||||
}
|
||||
private readonly ILiteDbService _liteDbService;
|
||||
|
||||
public List<GridModel> GetAllGrids()
|
||||
{
|
||||
try
|
||||
public GridService(ILiteDbService liteDbService)
|
||||
{
|
||||
using var db = new LiteDatabase(_databasePath);
|
||||
var collection = db.GetCollection<GridModel>("grids");
|
||||
return collection.FindAll().ToList();
|
||||
_liteDbService = liteDbService;
|
||||
}
|
||||
catch (IOException ex) when (ex.Message.Contains("Read-only file system"))
|
||||
|
||||
public IEnumerable<GridModel> GetAllGrids()
|
||||
{
|
||||
// Handle read-only file system scenario
|
||||
Console.Error.WriteLine("Error: The file system is read-only. Please check the file system permissions.");
|
||||
return [];
|
||||
return _liteDbService.Database.GetCollection<GridModel>("grids").FindAll();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
public GridModel GetGridByNick(string gridNick)
|
||||
{
|
||||
// Handle other exceptions
|
||||
Console.Error.WriteLine($"An error occurred: {ex.Message}");
|
||||
throw;
|
||||
return _liteDbService.Database.GetCollection<GridModel>("grids").FindOne(g => g.GridNick == gridNick);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using GalaxyViewer.Models;
|
||||
|
||||
namespace GalaxyViewer.Services
|
||||
{
|
||||
public interface IGridService
|
||||
{
|
||||
IEnumerable<GridModel> GetAllGrids();
|
||||
GridModel GetGridByNick(string gridNick);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using GalaxyViewer.Models;
|
||||
using LiteDB;
|
||||
|
||||
namespace GalaxyViewer.Services;
|
||||
|
||||
public interface ILiteDbService
|
||||
{
|
||||
LiteDatabase Database { get; }
|
||||
ILiteCollection<T> GetCollection<T>(string name);
|
||||
SessionModel GetSession();
|
||||
void SaveSession(SessionModel session);
|
||||
}
|
||||
@@ -1,178 +1,136 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using LiteDB;
|
||||
using GalaxyViewer.Models;
|
||||
using LiteDB;
|
||||
using Serilog;
|
||||
|
||||
namespace GalaxyViewer.Services;
|
||||
|
||||
public class LiteDbService : IDisposable
|
||||
namespace GalaxyViewer.Services
|
||||
{
|
||||
private LiteDatabase? _database;
|
||||
private readonly string _databasePath;
|
||||
public LiteDatabase? Database => _database;
|
||||
|
||||
public LiteDbService()
|
||||
public class LiteDbService : ILiteDbService, IDisposable
|
||||
{
|
||||
_databasePath = GetDatabasePath();
|
||||
InitializeDatabase();
|
||||
}
|
||||
private readonly LiteDatabase _database;
|
||||
private readonly string _databasePath;
|
||||
|
||||
private static string GetDatabasePath()
|
||||
{
|
||||
var appDataPath =
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"GalaxyViewer");
|
||||
return Path.Combine(appDataPath, "data.db");
|
||||
}
|
||||
|
||||
private void InitializeDatabase()
|
||||
{
|
||||
try
|
||||
public LiteDbService()
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(_databasePath) ??
|
||||
throw new InvalidOperationException());
|
||||
_databasePath = GetDatabasePath();
|
||||
_database = new LiteDatabase(_databasePath);
|
||||
Log.Information("LiteDbService initialized with database path: {DbPath}",
|
||||
_databasePath);
|
||||
ClearSessionData();
|
||||
Log.Information("LiteDbService initialized with database path: {DbPath}", _databasePath);
|
||||
SeedDatabase();
|
||||
}
|
||||
catch (LiteException ex)
|
||||
{
|
||||
Log.Error(ex, "LiteDB exception occurred. Attempting to recreate the database.");
|
||||
HandleDatabaseCorruption();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to initialize LiteDbService");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDatabaseCorruption()
|
||||
{
|
||||
try
|
||||
public LiteDatabase Database => _database;
|
||||
|
||||
private static string GetDatabasePath()
|
||||
{
|
||||
if (File.Exists(_databasePath))
|
||||
var appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "GalaxyViewer");
|
||||
return Path.Combine(appDataPath, "data.db");
|
||||
}
|
||||
|
||||
private void SeedDatabase()
|
||||
{
|
||||
SeedPreferences();
|
||||
SeedGrids();
|
||||
}
|
||||
|
||||
private void SeedPreferences()
|
||||
{
|
||||
var preferencesCollection = _database.GetCollection<PreferencesModel>("preferences");
|
||||
if (preferencesCollection.Count() == 0)
|
||||
{
|
||||
File.Move(_databasePath, _databasePath + ".bak");
|
||||
var defaultPreferences = new PreferencesModel
|
||||
{
|
||||
Theme = "Default",
|
||||
LoginLocation = "Home",
|
||||
Font = "Atkinson Hyperlegible",
|
||||
Language = "en-US",
|
||||
SelectedGridNick = "agni",
|
||||
LastSavedEpoch = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
|
||||
};
|
||||
preferencesCollection.Insert(defaultPreferences);
|
||||
Log.Information("Database seeded with default preferences");
|
||||
}
|
||||
|
||||
_database = new LiteDatabase(_databasePath);
|
||||
SeedDatabase();
|
||||
Log.Information("Database recreated successfully.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to recreate the database.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void SeedDatabase()
|
||||
{
|
||||
SeedPreferences();
|
||||
SeedGrids();
|
||||
}
|
||||
|
||||
private void SeedPreferences()
|
||||
{
|
||||
var preferencesCollection = _database.GetCollection<PreferencesModel>("preferences");
|
||||
if (preferencesCollection.Count() == 0)
|
||||
private void SeedGrids()
|
||||
{
|
||||
var defaultPreferences = new PreferencesModel
|
||||
var gridsCollection = _database.GetCollection<GridModel>("grids");
|
||||
if (gridsCollection.Count() == 0)
|
||||
{
|
||||
Theme = "Default",
|
||||
LoginLocation = "Home",
|
||||
Font = "Atkinson Hyperlegible",
|
||||
Language = "en-US",
|
||||
SelectedGridNick = "agni",
|
||||
LastSavedEpoch = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
|
||||
};
|
||||
preferencesCollection.Insert(defaultPreferences);
|
||||
Log.Information("Database seeded with default preferences");
|
||||
var grids = new[]
|
||||
{
|
||||
new GridModel
|
||||
{
|
||||
GridNick = "agni",
|
||||
GridName = "Second Life (agni)",
|
||||
Platform = "SecondLife",
|
||||
LoginUri = "https://login.agni.lindenlab.com/cgi-bin/login.cgi",
|
||||
LoginPage = "http://secondlife.com/app/login/?channel=Second+Life+Release",
|
||||
HelperUri = "https://secondlife.com/helpers/",
|
||||
Website = "http://secondlife.com/",
|
||||
Support = "http://secondlife.com/support/",
|
||||
Register = "http://secondlife.com/registration/",
|
||||
Password = "http://secondlife.com/account/request.php",
|
||||
Version = "0"
|
||||
},
|
||||
new GridModel
|
||||
{
|
||||
GridNick = "aditi",
|
||||
GridName = "Second Life Beta (aditi)",
|
||||
Platform = "SecondLife",
|
||||
LoginUri = "https://login.aditi.lindenlab.com/cgi-bin/login.cgi",
|
||||
LoginPage = "http://secondlife.com/app/login/?channel=Second+Life+Beta",
|
||||
HelperUri = "http://aditi-secondlife.webdev.lindenlab.com/helpers/",
|
||||
Website = "http://secondlife.com/",
|
||||
Support = "http://secondlife.com/support/",
|
||||
Register = "http://secondlife.com/registration/",
|
||||
Password = "http://secondlife.com/account/request.php",
|
||||
Version = "1"
|
||||
}
|
||||
};
|
||||
gridsCollection.InsertBulk(grids);
|
||||
Log.Information("Database seeded with default grids");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SeedGrids()
|
||||
{
|
||||
var gridsCollection = _database.GetCollection<GridModel>("grids");
|
||||
if (gridsCollection.Count() == 0)
|
||||
private void ClearSessionData()
|
||||
{
|
||||
var grids = new[]
|
||||
// Commented out the code that clears the session data
|
||||
// ILiteCollection<SessionModel>? sessionCollection;
|
||||
// if (_database.CollectionExists("session"))
|
||||
// {
|
||||
// sessionCollection = _database.GetCollection<SessionModel>("session");
|
||||
// sessionCollection.DeleteAll();
|
||||
// Log.Information("Session data cleared on startup");
|
||||
// }
|
||||
|
||||
var sessionCollection = _database.GetCollection<SessionModel>("session");
|
||||
if (sessionCollection.Count() == 0)
|
||||
{
|
||||
new GridModel
|
||||
{
|
||||
GridNick = "agni",
|
||||
GridName = "Second Life (agni)",
|
||||
Platform = "SecondLife",
|
||||
LoginUri = "https://login.agni.lindenlab.com/cgi-bin/login.cgi",
|
||||
LoginPage = "http://secondlife.com/app/login/?channel=Second+Life+Release",
|
||||
HelperUri = "https://secondlife.com/helpers/",
|
||||
Website = "http://secondlife.com/",
|
||||
Support = "http://secondlife.com/support/",
|
||||
Register = "http://secondlife.com/registration/",
|
||||
Password = "http://secondlife.com/account/request.php",
|
||||
Version = "0"
|
||||
},
|
||||
new GridModel
|
||||
{
|
||||
GridNick = "aditi",
|
||||
GridName = "Second Life Beta (aditi)",
|
||||
Platform = "SecondLife",
|
||||
LoginUri = "https://login.aditi.lindenlab.com/cgi-bin/login.cgi",
|
||||
LoginPage = "http://secondlife.com/app/login/?channel=Second+Life+Beta",
|
||||
HelperUri = "http://aditi-secondlife.webdev.lindenlab.com/helpers/",
|
||||
Website = "http://secondlife.com/",
|
||||
Support = "http://secondlife.com/support/",
|
||||
Register = "http://secondlife.com/registration/",
|
||||
Password = "http://secondlife.com/account/request.php",
|
||||
Version = "1"
|
||||
}
|
||||
};
|
||||
gridsCollection.InsertBulk(grids);
|
||||
Log.Information("Database seeded with default grids");
|
||||
sessionCollection.Insert(new SessionModel());
|
||||
Log.Information("Session data created on startup");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearSessionData()
|
||||
{
|
||||
ILiteCollection<SessionModel>? sessionCollection;
|
||||
if (_database.CollectionExists("session"))
|
||||
public ILiteCollection<T> GetCollection<T>(string name)
|
||||
{
|
||||
sessionCollection = _database.GetCollection<SessionModel>("session");
|
||||
sessionCollection.DeleteAll();
|
||||
Log.Information("Session data cleared on startup");
|
||||
return _database.GetCollection<T>(name);
|
||||
}
|
||||
|
||||
sessionCollection = _database.GetCollection<SessionModel>("session");
|
||||
sessionCollection.Insert(new SessionModel());
|
||||
Log.Information("Session data created on startup");
|
||||
}
|
||||
public SessionModel GetSession()
|
||||
{
|
||||
return _database.GetCollection<SessionModel>("sessions").FindOne(Query.All()) ?? new SessionModel();
|
||||
}
|
||||
|
||||
public ILiteCollection<T> GetCollection<T>(string name)
|
||||
{
|
||||
Log.Information("Retrieving collection: {CollectionName}", name);
|
||||
return _database.GetCollection<T>(name);
|
||||
}
|
||||
public void SaveSession(SessionModel session)
|
||||
{
|
||||
_database.GetCollection<SessionModel>("sessions").Upsert(session);
|
||||
}
|
||||
|
||||
public SessionModel GetSession()
|
||||
{
|
||||
var collection = _database.GetCollection<SessionModel>("session");
|
||||
return collection.FindOne(Query.All()) ?? new SessionModel();
|
||||
}
|
||||
|
||||
public void SaveSession(SessionModel session)
|
||||
{
|
||||
var collection = _database.GetCollection<SessionModel>("session");
|
||||
collection.Upsert(session);
|
||||
Log.Information("Session data saved");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_database?.Dispose();
|
||||
Log.Information("LiteDbService disposed");
|
||||
public void Dispose()
|
||||
{
|
||||
_database?.Dispose();
|
||||
Log.Information("LiteDbService disposed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace GalaxyViewer.Services;
|
||||
|
||||
public class NavigationService
|
||||
{
|
||||
private readonly Dictionary<string, Type> _routes = new();
|
||||
private readonly ContentControl _contentControl;
|
||||
|
||||
public NavigationService(ContentControl contentControl)
|
||||
{
|
||||
_contentControl = contentControl;
|
||||
}
|
||||
|
||||
public void RegisterRoute(string uri, Type viewType)
|
||||
{
|
||||
_routes[uri] = viewType;
|
||||
}
|
||||
|
||||
public void NavigateTo(string uri)
|
||||
{
|
||||
if (_routes.TryGetValue(uri, out var viewType))
|
||||
{
|
||||
var view = (Control)Activator.CreateInstance(viewType)!;
|
||||
_contentControl.Content = view;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"No view registered for URI: {uri}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,92 +2,90 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using LiteDB;
|
||||
using GalaxyViewer.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GalaxyViewer.Services;
|
||||
|
||||
public sealed class PreferencesManager
|
||||
namespace GalaxyViewer.Services
|
||||
{
|
||||
private readonly ILiteCollection<PreferencesModel> _preferencesCollection;
|
||||
private readonly ILiteCollection<GridModel>? _gridsCollection;
|
||||
|
||||
public event EventHandler<PreferencesModel>? PreferencesChanged;
|
||||
|
||||
public PreferencesManager(LiteDbService? liteDbService)
|
||||
public sealed class PreferencesManager
|
||||
{
|
||||
Debug.Assert(liteDbService != null, nameof(liteDbService) + " != null");
|
||||
var database = liteDbService?.Database;
|
||||
Debug.Assert(database != null, nameof(database) + " != null");
|
||||
_preferencesCollection = database.GetCollection<PreferencesModel>("preferences");
|
||||
_gridsCollection = database.GetCollection<GridModel>("grids");
|
||||
}
|
||||
private readonly ILiteCollection<PreferencesModel> _preferencesCollection;
|
||||
private readonly ILiteCollection<GridModel> _gridsCollection;
|
||||
|
||||
public PreferencesModel CurrentPreferences
|
||||
{
|
||||
get
|
||||
public event EventHandler<PreferencesModel>? PreferencesChanged;
|
||||
|
||||
public PreferencesManager(ILiteDbService liteDbService)
|
||||
{
|
||||
var preferences = _preferencesCollection.FindOne(Query.All());
|
||||
return preferences ?? new PreferencesModel
|
||||
Debug.Assert(liteDbService != null, nameof(liteDbService) + " != null");
|
||||
_preferencesCollection = liteDbService.GetCollection<PreferencesModel>("preferences");
|
||||
_gridsCollection = liteDbService.GetCollection<GridModel>("grids");
|
||||
}
|
||||
|
||||
public PreferencesModel CurrentPreferences
|
||||
{
|
||||
get
|
||||
{
|
||||
Id = ObjectId.NewObjectId(),
|
||||
Theme = "Default",
|
||||
LoginLocation = "Home",
|
||||
Font = "Atkinson Hyperlegible",
|
||||
Language = "en-US",
|
||||
LastSavedEpoch = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
SelectedGridNick = string.Empty
|
||||
var preferences = _preferencesCollection.FindOne(Query.All());
|
||||
return preferences ?? new PreferencesModel
|
||||
{
|
||||
Id = ObjectId.NewObjectId(),
|
||||
Theme = "Default",
|
||||
LoginLocation = "Home",
|
||||
Font = "Atkinson Hyperlegible",
|
||||
Language = "en-US",
|
||||
LastSavedEpoch = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
SelectedGridNick = string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<PreferencesModel> LoadPreferencesAsync()
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
var preferences = _preferencesCollection.FindOne(Query.All());
|
||||
return preferences ?? new PreferencesModel
|
||||
{
|
||||
Id = ObjectId.NewObjectId(),
|
||||
Theme = "Default",
|
||||
LoginLocation = "Home",
|
||||
Font = "Atkinson Hyperlegible",
|
||||
Language = "en-US",
|
||||
SelectedGridNick = "agni",
|
||||
LastSavedEpoch = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public async Task SavePreferencesAsync(PreferencesModel preferences)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
_preferencesCollection.Upsert(preferences);
|
||||
OnPreferencesChanged(preferences);
|
||||
});
|
||||
}
|
||||
|
||||
public Dictionary<string, List<string>> GetCurrentPreferencesOptions()
|
||||
{
|
||||
return new Dictionary<string, List<string>>
|
||||
{
|
||||
{ "ThemeOptions", PreferencesOptions.ThemeOptions },
|
||||
{ "LoginLocationOptions", PreferencesOptions.LoginLocationOptions },
|
||||
{ "FontOptions", PreferencesOptions.FontOptions },
|
||||
{ "LanguageOptions", PreferencesOptions.LanguageOptions }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<PreferencesModel> LoadPreferencesAsync()
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
public List<string> GetGridOptions()
|
||||
{
|
||||
var preferences = _preferencesCollection.FindOne(Query.All());
|
||||
return preferences ?? new PreferencesModel
|
||||
{
|
||||
Id = ObjectId.NewObjectId(),
|
||||
Theme = "Default",
|
||||
LoginLocation = "Home",
|
||||
Font = "Atkinson Hyperlegible",
|
||||
Language = "en-US",
|
||||
SelectedGridNick = "agni",
|
||||
LastSavedEpoch = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||||
};
|
||||
});
|
||||
}
|
||||
return _gridsCollection.FindAll().Select(grid => grid.GridNick).ToList();
|
||||
}
|
||||
|
||||
public async Task SavePreferencesAsync(PreferencesModel preferences)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
private void OnPreferencesChanged(PreferencesModel preferences)
|
||||
{
|
||||
_preferencesCollection.Upsert(preferences);
|
||||
OnPreferencesChanged(preferences);
|
||||
});
|
||||
}
|
||||
|
||||
public Dictionary<string, List<string>> GetCurrentPreferencesOptions()
|
||||
{
|
||||
return new Dictionary<string, List<string>>
|
||||
{
|
||||
{ "ThemeOptions", PreferencesOptions.ThemeOptions },
|
||||
{ "LoginLocationOptions", PreferencesOptions.LoginLocationOptions },
|
||||
{ "FontOptions", PreferencesOptions.FontOptions },
|
||||
{ "LanguageOptions", PreferencesOptions.LanguageOptions }
|
||||
};
|
||||
}
|
||||
|
||||
public List<string> GetGridOptions()
|
||||
{
|
||||
return _gridsCollection?.FindAll().Select(grid => grid.GridNick).ToList() ??
|
||||
[];
|
||||
}
|
||||
|
||||
private void OnPreferencesChanged(PreferencesModel preferences)
|
||||
{
|
||||
PreferencesChanged?.Invoke(this, preferences);
|
||||
PreferencesChanged?.Invoke(this, preferences);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using GalaxyViewer.Models;
|
||||
using GalaxyViewer.Services;
|
||||
using Serilog;
|
||||
|
||||
namespace GalaxyViewer
|
||||
{
|
||||
public class SessionManager
|
||||
{
|
||||
private readonly ILiteDbService _liteDbService;
|
||||
private SessionModel _session;
|
||||
|
||||
public event EventHandler<SessionModel> SessionChanged;
|
||||
|
||||
public SessionManager(ILiteDbService liteDbService)
|
||||
{
|
||||
_liteDbService = liteDbService;
|
||||
_session = _liteDbService.GetSession();
|
||||
}
|
||||
|
||||
public SessionModel Session
|
||||
{
|
||||
get => _session;
|
||||
set
|
||||
{
|
||||
if (_session != value)
|
||||
{
|
||||
_session = value;
|
||||
_liteDbService.SaveSession(_session);
|
||||
OnSessionChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnSessionChanged()
|
||||
{
|
||||
SessionChanged?.Invoke(this, _session);
|
||||
Log.Information("Session changed: {@Session}", _session);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using GalaxyViewer.Models;
|
||||
using GalaxyViewer.Services;
|
||||
using LiteDB;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace GalaxyViewer.Tests;
|
||||
|
||||
public class SessionManagerTests
|
||||
{
|
||||
[Fact]
|
||||
public void SessionManager_Should_SaveSession_When_SessionChanges()
|
||||
{
|
||||
// Arrange
|
||||
var mockLiteDbService = new Mock<ILiteDbService>();
|
||||
var session = new SessionModel
|
||||
{
|
||||
IsLoggedIn = false,
|
||||
AvatarName = "TestAvatar",
|
||||
AvatarKey = new OpenMetaverse.UUID(),
|
||||
Balance = 100,
|
||||
CurrentLocation = "TestLocation",
|
||||
CurrentLocationWelcomeMessage = "Welcome to TestLocation"
|
||||
};
|
||||
|
||||
var mockSessionCollection = new Mock<ILiteCollection<SessionModel>>();
|
||||
mockLiteDbService.Setup(service => service.GetCollection<SessionModel>("sessions")).Returns(mockSessionCollection.Object);
|
||||
mockSessionCollection.Setup(collection => collection.FindOne(It.IsAny<Query>())).Returns(session);
|
||||
|
||||
var sessionManager = new SessionManager(mockLiteDbService.Object);
|
||||
|
||||
// Act
|
||||
sessionManager.Session = new SessionModel
|
||||
{
|
||||
IsLoggedIn = true,
|
||||
AvatarName = "TestAvatar",
|
||||
AvatarKey = new OpenMetaverse.UUID(),
|
||||
Balance = 100,
|
||||
CurrentLocation = "TestLocation",
|
||||
CurrentLocationWelcomeMessage = "Welcome to TestLocation"
|
||||
};
|
||||
|
||||
// Assert
|
||||
mockSessionCollection.Verify(collection => collection.Upsert(It.IsAny<SessionModel>()), Times.Once);
|
||||
}
|
||||
}
|
||||
@@ -1,67 +1,103 @@
|
||||
using System.ComponentModel;
|
||||
using GalaxyViewer.Models;
|
||||
using GalaxyViewer.Services;
|
||||
using OpenMetaverse;
|
||||
using ReactiveUI;
|
||||
using GalaxyViewer.Services;
|
||||
using GalaxyViewer.Models;
|
||||
|
||||
namespace GalaxyViewer.ViewModels;
|
||||
|
||||
public class LoggedInViewModel : ViewModelBase
|
||||
namespace GalaxyViewer.ViewModels
|
||||
{
|
||||
private readonly LiteDbService _liteDbService;
|
||||
private SessionModel _session;
|
||||
|
||||
public string CurrentLocation
|
||||
public class LoggedInViewModel : ViewModelBase
|
||||
{
|
||||
get => _session.CurrentLocation;
|
||||
set
|
||||
private readonly ILiteDbService _liteDbService;
|
||||
private SessionModel _session;
|
||||
|
||||
public LoggedInViewModel(ILiteDbService liteDbService)
|
||||
{
|
||||
if (_session.CurrentLocation != value)
|
||||
_liteDbService = liteDbService;
|
||||
_session = _liteDbService.GetSession();
|
||||
_session.PropertyChanged += OnSessionPropertyChanged;
|
||||
}
|
||||
|
||||
private void OnSessionPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
_session.CurrentLocation = value;
|
||||
_liteDbService.SaveSession(_session);
|
||||
var sessionCurrentLocation = _session.CurrentLocation;
|
||||
this.RaiseAndSetIfChanged(ref sessionCurrentLocation, value);
|
||||
case nameof(SessionModel.AvatarName):
|
||||
this.RaisePropertyChanged(nameof(AvatarName));
|
||||
break;
|
||||
case nameof(SessionModel.AvatarKey):
|
||||
this.RaisePropertyChanged(nameof(AvatarKey));
|
||||
break;
|
||||
case nameof(SessionModel.Balance):
|
||||
this.RaisePropertyChanged(nameof(Balance));
|
||||
break;
|
||||
case nameof(SessionModel.CurrentLocation):
|
||||
this.RaisePropertyChanged(nameof(CurrentLocation));
|
||||
break;
|
||||
case nameof(SessionModel.CurrentLocationWelcomeMessage):
|
||||
this.RaisePropertyChanged(nameof(CurrentLocationWelcomeMessage));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string CurrentLocationWelcomeMessage
|
||||
{
|
||||
get => _session.CurrentLocationWelcomeMessage;
|
||||
set
|
||||
public string AvatarName
|
||||
{
|
||||
if (_session.CurrentLocationWelcomeMessage != value)
|
||||
get => _session.AvatarName;
|
||||
set
|
||||
{
|
||||
_session.CurrentLocationWelcomeMessage = value;
|
||||
if (_session.AvatarName == value) return;
|
||||
_session.AvatarName = value;
|
||||
_liteDbService.SaveSession(_session);
|
||||
var sessionCurrentLocationWelcomeMessage = _session.CurrentLocationWelcomeMessage;
|
||||
this.RaiseAndSetIfChanged(ref sessionCurrentLocationWelcomeMessage, value);
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Balance
|
||||
{
|
||||
get => _session.Balance;
|
||||
set
|
||||
|
||||
public UUID AvatarKey
|
||||
{
|
||||
if (_session.Balance != value)
|
||||
get => _session.AvatarKey;
|
||||
set
|
||||
{
|
||||
if (_session.AvatarKey == value) return;
|
||||
_session.AvatarKey = value;
|
||||
_liteDbService.SaveSession(_session);
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public int Balance
|
||||
{
|
||||
get => _session.Balance;
|
||||
set
|
||||
{
|
||||
if (_session.Balance == value) return;
|
||||
_session.Balance = value;
|
||||
_liteDbService.SaveSession(_session);
|
||||
var sessionBalance = _session.Balance;
|
||||
this.RaiseAndSetIfChanged(ref sessionBalance, value);
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string CurrentLocation
|
||||
{
|
||||
get => _session.CurrentLocation;
|
||||
set
|
||||
{
|
||||
if (_session.CurrentLocation == value) return;
|
||||
_session.CurrentLocation = value;
|
||||
_liteDbService.SaveSession(_session);
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string CurrentLocationWelcomeMessage
|
||||
{
|
||||
get => _session.CurrentLocationWelcomeMessage;
|
||||
set
|
||||
{
|
||||
if (_session.CurrentLocationWelcomeMessage == value) return;
|
||||
_session.CurrentLocationWelcomeMessage = value;
|
||||
_liteDbService.SaveSession(_session);
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LoggedInViewModel(LiteDbService liteDbService)
|
||||
{
|
||||
_liteDbService = liteDbService;
|
||||
_session = _liteDbService.GetSession();
|
||||
|
||||
// Initialize properties with session data
|
||||
CurrentLocation = _session.CurrentLocation;
|
||||
CurrentLocationWelcomeMessage = _session.CurrentLocationWelcomeMessage;
|
||||
}
|
||||
}
|
||||
@@ -1,333 +1,313 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls.Notifications;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Reactive;
|
||||
using GalaxyViewer.Models;
|
||||
using GalaxyViewer.Services;
|
||||
using ReactiveUI;
|
||||
using Ursa.Controls;
|
||||
using Serilog;
|
||||
using OpenMetaverse;
|
||||
using ReactiveUI;
|
||||
using Serilog;
|
||||
|
||||
namespace GalaxyViewer.ViewModels;
|
||||
|
||||
public class LoginViewModel : ReactiveObject, IRoutableViewModel
|
||||
namespace GalaxyViewer.ViewModels
|
||||
{
|
||||
public string UrlPathSegment => "login";
|
||||
|
||||
public IScreen HostScreen
|
||||
public class LoginViewModel : ViewModelBase
|
||||
{
|
||||
get
|
||||
private readonly IGridService _gridService;
|
||||
private readonly PreferencesViewModel _preferencesViewModel;
|
||||
private readonly SessionManager _sessionManager;
|
||||
private readonly GridClient _client;
|
||||
private string _username;
|
||||
private string _password;
|
||||
private ObservableCollection<GridModel> _grids;
|
||||
private GridModel _selectedGrid;
|
||||
private string _loginStatusMessage;
|
||||
private bool _isLoggedIn;
|
||||
|
||||
public LoginViewModel(IGridService gridService, PreferencesViewModel preferencesViewModel,
|
||||
SessionManager sessionManager)
|
||||
{
|
||||
Debug.Assert(_routableViewModelImplementation?.HostScreen != null,
|
||||
"_routableViewModelImplementation?.HostScreen != null");
|
||||
return _routableViewModelImplementation.HostScreen;
|
||||
_gridService = gridService;
|
||||
_preferencesViewModel = preferencesViewModel;
|
||||
_sessionManager = sessionManager;
|
||||
_client = new GridClient();
|
||||
TryLoginCommand = ReactiveCommand.CreateFromTask(TryLoginAsync);
|
||||
LoadGrids();
|
||||
LoginLocations =
|
||||
new ObservableCollection<string>(_preferencesViewModel.LoginLocationOptions);
|
||||
Grids = new ObservableCollection<GridModel>();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isLoggedIn;
|
||||
|
||||
public bool IsLoggedIn
|
||||
{
|
||||
get => App.IsLoggedIn;
|
||||
set => App.IsLoggedIn = value;
|
||||
}
|
||||
|
||||
private string _loginStatusMessage;
|
||||
|
||||
public string LoginStatusMessage
|
||||
{
|
||||
get => _loginStatusMessage;
|
||||
set => this.RaiseAndSetIfChanged(ref _loginStatusMessage, value);
|
||||
}
|
||||
|
||||
private readonly LiteDbService _liteDbService;
|
||||
private readonly PreferencesViewModel _preferencesViewModel;
|
||||
private string _username;
|
||||
private string _password;
|
||||
private readonly GridClient _client = new();
|
||||
private IRoutableViewModel? _routableViewModelImplementation;
|
||||
private readonly GridService _gridService;
|
||||
private ObservableCollection<GridModel?> _grids;
|
||||
private GridModel _selectedGrid;
|
||||
|
||||
private const string DefaultGridUri =
|
||||
"https://login.agni.lindenlab.com/cgi-bin/login.cgi";
|
||||
|
||||
public WindowToastManager? ToastManager { get; set; }
|
||||
|
||||
public LoginViewModel(LiteDbService liteDbService)
|
||||
{
|
||||
_liteDbService = liteDbService;
|
||||
_preferencesViewModel = new PreferencesViewModel();
|
||||
_username = string.Empty;
|
||||
_password = string.Empty;
|
||||
LoginLocations = _preferencesViewModel.LoginLocationOptions;
|
||||
SelectedLoginLocation = _preferencesViewModel.SelectedLoginLocation;
|
||||
TryLoginCommand = ReactiveCommand.CreateFromTask(TryLoginAsync);
|
||||
_gridService = new GridService();
|
||||
_grids = new ObservableCollection<GridModel?>();
|
||||
|
||||
LoadGrids();
|
||||
|
||||
// Subscribe to the ThrownExceptions property to handle errors
|
||||
TryLoginCommand.ThrownExceptions.Subscribe(ex =>
|
||||
public string Username
|
||||
{
|
||||
Log.Error("An error occurred during login: {Error}", ex.Message);
|
||||
// Handle the error (e.g., show a dialog to the user)
|
||||
_ = ShowLoginErrorAsync("An error occurred during login. Please try again.");
|
||||
});
|
||||
|
||||
// Subscribe to the Network.LoginProgress event
|
||||
_client.Network.LoginProgress += OnLoginProgress;
|
||||
}
|
||||
|
||||
public ObservableCollection<string> LoginLocations { get; }
|
||||
|
||||
public string Username
|
||||
{
|
||||
get => _username;
|
||||
set => this.RaiseAndSetIfChanged(ref _username, value);
|
||||
}
|
||||
|
||||
public string Password
|
||||
{
|
||||
get => _password;
|
||||
set => this.RaiseAndSetIfChanged(ref _password, value);
|
||||
}
|
||||
|
||||
public string SelectedLoginLocation
|
||||
{
|
||||
get => _preferencesViewModel.SelectedLoginLocation;
|
||||
set
|
||||
{
|
||||
_preferencesViewModel.SelectedLoginLocation = value;
|
||||
this.RaisePropertyChanged();
|
||||
get => _username;
|
||||
set => this.RaiseAndSetIfChanged(ref _username, value);
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<GridModel> Grids { get; set; }
|
||||
|
||||
public GridModel? SelectedGrid
|
||||
{
|
||||
get
|
||||
public string Password
|
||||
{
|
||||
var selectedGrid = _grids.FirstOrDefault(g =>
|
||||
g.GridNick == _preferencesViewModel.SelectedGridNick);
|
||||
if (selectedGrid == null)
|
||||
get => _password;
|
||||
set => this.RaiseAndSetIfChanged(ref _password, value);
|
||||
}
|
||||
|
||||
private ObservableCollection<string> _loginLocations;
|
||||
|
||||
public ObservableCollection<string> LoginLocations
|
||||
{
|
||||
get => _loginLocations;
|
||||
set => this.RaiseAndSetIfChanged(ref _loginLocations, value);
|
||||
}
|
||||
|
||||
public string SelectedLoginLocation
|
||||
{
|
||||
get => _preferencesViewModel.SelectedLoginLocation;
|
||||
set
|
||||
{
|
||||
selectedGrid = new GridModel
|
||||
_preferencesViewModel.SelectedLoginLocation = value;
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<GridModel> Grids
|
||||
{
|
||||
get => _grids;
|
||||
set => this.RaiseAndSetIfChanged(ref _grids, value);
|
||||
}
|
||||
|
||||
public GridModel SelectedGrid
|
||||
{
|
||||
get => _selectedGrid;
|
||||
set
|
||||
{
|
||||
if (value == null) return;
|
||||
_preferencesViewModel.SelectedGridNick = value.GridNick;
|
||||
this.RaiseAndSetIfChanged(ref _selectedGrid, value);
|
||||
}
|
||||
}
|
||||
|
||||
public string LoginStatusMessage
|
||||
{
|
||||
get => _loginStatusMessage;
|
||||
set => this.RaiseAndSetIfChanged(ref _loginStatusMessage, value);
|
||||
}
|
||||
|
||||
public bool IsLoggedIn
|
||||
{
|
||||
get => _sessionManager.Session?.IsLoggedIn ?? false;
|
||||
set
|
||||
{
|
||||
var session = _sessionManager.Session;
|
||||
if (session != null)
|
||||
{
|
||||
GridNick = "Second Life",
|
||||
LoginUri = DefaultGridUri
|
||||
};
|
||||
session.IsLoggedIn = value;
|
||||
_sessionManager.Session = session;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> TryLoginCommand { get; }
|
||||
|
||||
private void LoadGrids()
|
||||
{
|
||||
var grids = _gridService.GetAllGrids();
|
||||
Grids = new ObservableCollection<GridModel>(grids);
|
||||
SelectedGrid =
|
||||
Grids.FirstOrDefault(g => g.GridNick == _preferencesViewModel.SelectedGridNick);
|
||||
}
|
||||
|
||||
private async Task ShowLoginErrorAsync(string errorMessage)
|
||||
{
|
||||
// ToastManager?.Show(
|
||||
// new Toast(
|
||||
// errorMessage),
|
||||
// showIcon: true,
|
||||
// showClose: true,
|
||||
// type: NotificationType.Error
|
||||
// );
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task TryLoginAsync()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Username) || string.IsNullOrWhiteSpace(Password))
|
||||
{
|
||||
Log.Warning("Username or password is empty");
|
||||
await ShowLoginErrorAsync("Username or password is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
return selectedGrid;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null) return;
|
||||
_preferencesViewModel.SelectedGridNick = value.GridNick;
|
||||
this.RaiseAndSetIfChanged(ref _selectedGrid, value);
|
||||
}
|
||||
}
|
||||
if (SelectedGrid == null || string.IsNullOrWhiteSpace(SelectedGrid.LoginUri))
|
||||
{
|
||||
Log.Warning("Selected grid or its login URI is empty");
|
||||
await ShowLoginErrorAsync("Selected grid or its login URI is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
private void LoadGrids()
|
||||
{
|
||||
var grids = _gridService.GetAllGrids();
|
||||
_grids = new ObservableCollection<GridModel?>(grids);
|
||||
SelectedGrid = _grids.FirstOrDefault(g =>
|
||||
g.GridNick == _preferencesViewModel.SelectedGridNick);
|
||||
}
|
||||
LoginStatusMessage = "Logging in...";
|
||||
|
||||
public ReactiveCommand<Unit, Unit> TryLoginCommand { get; }
|
||||
var ourPlatform = GetPlatformString();
|
||||
|
||||
private async Task ShowLoginErrorAsync(string errorMessage)
|
||||
{
|
||||
// ToastManager?.Show(
|
||||
// new Toast(
|
||||
// errorMessage),
|
||||
// showIcon: true,
|
||||
// showClose: true,
|
||||
// type: NotificationType.Error
|
||||
// );
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
var loginParams = _client.Network.DefaultLoginParams(
|
||||
Username.Split(' ')[0], // firstName
|
||||
Username.Contains(' ') ? Username.Split(' ')[1] : "Resident", // lastName
|
||||
Password,
|
||||
"GalaxyViewer-test", // ViewerName
|
||||
"0.1.0" // ViewerVersion
|
||||
);
|
||||
|
||||
private async Task TryLoginAsync()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Username) || string.IsNullOrWhiteSpace(Password))
|
||||
{
|
||||
Log.Warning("Username or password is empty");
|
||||
await ShowLoginErrorAsync("Username or password is empty");
|
||||
return;
|
||||
}
|
||||
if (loginParams == null)
|
||||
{
|
||||
Log.Error("Failed to create login parameters");
|
||||
await ShowLoginErrorAsync("Failed to create login parameters");
|
||||
return;
|
||||
}
|
||||
|
||||
LoginStatusMessage = "Logging in...";
|
||||
// Use a specific uriString for the testing phase
|
||||
var isTestingPhase = true; // Set this flag based on your testing condition
|
||||
loginParams.URI = isTestingPhase ? "https://login.agni.lindenlab.com/cgi-bin/login.cgi" : SelectedGrid.LoginUri;
|
||||
loginParams.MfaEnabled = true;
|
||||
loginParams.Platform = ourPlatform;
|
||||
loginParams.PlatformVersion = Environment.OSVersion.VersionString;
|
||||
loginParams.Start = _preferencesViewModel?.SelectedLoginLocation switch
|
||||
{
|
||||
"Home" => "home",
|
||||
"Last Location" => "last",
|
||||
_ => "home"
|
||||
};
|
||||
loginParams.MfaHash = string.Empty;
|
||||
|
||||
var ourPlatform = GetPlatformString();
|
||||
var loginSuccess = await Task.Run(() => _client.Network.Login(loginParams));
|
||||
|
||||
var loginParams = _client?.Network?.DefaultLoginParams(
|
||||
Username.Split(' ')[0], // firstName
|
||||
Username.Contains(' ') ? Username.Split(' ')[1] : "Resident", // lastName
|
||||
Password,
|
||||
"GalaxyViewer-test", // ViewerName
|
||||
"0.1.0" // ViewerVersion
|
||||
);
|
||||
|
||||
if (loginParams == null)
|
||||
{
|
||||
Log.Error("Failed to create login parameters");
|
||||
await ShowLoginErrorAsync("Failed to create login parameters");
|
||||
return;
|
||||
}
|
||||
|
||||
loginParams.URI = SelectedGrid?.LoginUri;
|
||||
loginParams.MfaEnabled = true;
|
||||
loginParams.Platform = ourPlatform;
|
||||
loginParams.PlatformVersion = Environment.OSVersion.VersionString;
|
||||
loginParams.Start = _preferencesViewModel?.SelectedLoginLocation switch
|
||||
{
|
||||
"Home" => "home",
|
||||
"Last Location" => "last",
|
||||
_ => "home"
|
||||
};
|
||||
loginParams.MfaHash = string.Empty;
|
||||
|
||||
var loginSuccess = await Task.Run(() => _client?.Network?.Login(loginParams) ?? false);
|
||||
|
||||
if (loginSuccess)
|
||||
{
|
||||
await HandleSuccessfulLogin();
|
||||
}
|
||||
else if (_client?.Network?.LoginMessage.Contains("MFA required") == true)
|
||||
{
|
||||
await HandleMfaLogin(loginParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("Login failed: {Error}", _client?.Network?.LoginMessage);
|
||||
IsLoggedIn = false;
|
||||
await ShowLoginErrorAsync($"Login failed: {_client?.Network?.LoginMessage}");
|
||||
}
|
||||
}
|
||||
|
||||
private string GetPlatformString()
|
||||
{
|
||||
var platformMap = new Dictionary<OSPlatform, string>
|
||||
{
|
||||
{ OSPlatform.Windows, "Win" },
|
||||
{ OSPlatform.Linux, "Lin" },
|
||||
{ OSPlatform.OSX, "Mac" },
|
||||
{ OSPlatform.Create("ANDROID"), "And" },
|
||||
{ OSPlatform.Create("IOS"), "iOS" }
|
||||
};
|
||||
|
||||
return platformMap.FirstOrDefault(kv => RuntimeInformation.IsOSPlatform(kv.Key)).Value ??
|
||||
"Unk";
|
||||
}
|
||||
|
||||
private async Task HandleSuccessfulLogin()
|
||||
{
|
||||
Log.Information("Login successful as {Name}", _client.Self.Name);
|
||||
IsLoggedIn = true;
|
||||
|
||||
var session = new SessionModel
|
||||
{
|
||||
Id = 1, // Assuming a single session record
|
||||
IsLoggedIn = true,
|
||||
AvatarName = _client.Self.Name,
|
||||
AvatarKey = _client.Self.AgentID,
|
||||
Balance = _client.Self.Balance,
|
||||
CurrentLocation = _client.Network.CurrentSim.Name,
|
||||
CurrentLocationWelcomeMessage = "Welcome to " + _client.Network.CurrentSim.Name
|
||||
};
|
||||
|
||||
_liteDbService.SaveSession(session);
|
||||
Log.Information("Session updated on successful login");
|
||||
|
||||
await ProcessCapabilitiesAsync();
|
||||
}
|
||||
|
||||
private async Task HandleMfaLogin(LoginParams loginParams)
|
||||
{
|
||||
Log.Warning("MFA required");
|
||||
var mfaCode = await ShowMfaInputDialogAsync();
|
||||
if (!string.IsNullOrEmpty(mfaCode))
|
||||
{
|
||||
loginParams.MfaHash = Utils.MD5(mfaCode);
|
||||
var loginSuccess = await Task.Run(() => _client?.Network?.Login(loginParams) ?? false);
|
||||
if (loginSuccess)
|
||||
{
|
||||
await HandleSuccessfulLogin();
|
||||
}
|
||||
else if (_client.Network.LoginMessage.Contains("MFA required"))
|
||||
{
|
||||
await HandleMfaLogin(loginParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("Login failed with MFA: {Error}", _client?.Network?.LoginMessage);
|
||||
await ShowLoginErrorAsync(
|
||||
$"Login failed with MFA: {_client?.Network?.LoginMessage}");
|
||||
Log.Error("Login failed: {Error}", _client.Network.LoginMessage);
|
||||
IsLoggedIn = false;
|
||||
await ShowLoginErrorAsync($"Login failed: {_client.Network.LoginMessage}");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
private string GetPlatformString()
|
||||
{
|
||||
Log.Warning("MFA code entry was canceled");
|
||||
await ShowLoginErrorAsync("MFA code entry was canceled");
|
||||
var platformMap = new Dictionary<OSPlatform, string>
|
||||
{
|
||||
{ OSPlatform.Windows, "Win" },
|
||||
{ OSPlatform.Linux, "Lin" },
|
||||
{ OSPlatform.OSX, "Mac" },
|
||||
{ OSPlatform.Create("ANDROID"), "And" },
|
||||
{ OSPlatform.Create("IOS"), "iOS" }
|
||||
};
|
||||
|
||||
return platformMap.FirstOrDefault(kv => RuntimeInformation.IsOSPlatform(kv.Key))
|
||||
.Value ?? "Unk";
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessCapabilitiesAsync()
|
||||
{
|
||||
var loginMessage = await Task.Run(() => _client?.Network?.LoginMessage);
|
||||
Log.Information("Login message: {Message}", loginMessage);
|
||||
|
||||
var currentSim = await Task.Run(() => _client?.Network?.CurrentSim);
|
||||
Log.Information("Current location: {Sim}", currentSim);
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void OnLoginProgress(object? sender, LoginProgressEventArgs e)
|
||||
{
|
||||
Log.Information("Login progress: {Status}", e.Status);
|
||||
switch (e.Status)
|
||||
private async Task HandleSuccessfulLogin()
|
||||
{
|
||||
case LoginStatus.ConnectingToLogin:
|
||||
LoginStatusMessage = "Connecting to login server...";
|
||||
break;
|
||||
case LoginStatus.ConnectingToSim:
|
||||
LoginStatusMessage = "Connecting to region...";
|
||||
break;
|
||||
case LoginStatus.Redirecting:
|
||||
LoginStatusMessage = "Redirecting...";
|
||||
break;
|
||||
case LoginStatus.ReadingResponse:
|
||||
LoginStatusMessage = "Reading response...";
|
||||
break;
|
||||
case LoginStatus.Success:
|
||||
LoginStatusMessage =
|
||||
$"Logged in as {_client.Self.Name}, welcome to {_client.Network.CurrentSim?.Name}";
|
||||
IsLoggedIn = true;
|
||||
break;
|
||||
case LoginStatus.Failed:
|
||||
LoginStatusMessage = $"Login failed: {e.Message}";
|
||||
break;
|
||||
case LoginStatus.None:
|
||||
default:
|
||||
LoginStatusMessage = $"Unknown login status: {e.Status}";
|
||||
break;
|
||||
}
|
||||
}
|
||||
Log.Information("Login successful as {Name}", _client.Self.Name);
|
||||
IsLoggedIn = true;
|
||||
LoginStatusMessage =
|
||||
$"Logged in as {_client.Self.Name}, welcome to {_client.Network.CurrentSim?.Name}";
|
||||
|
||||
private async Task<string> ShowMfaInputDialogAsync()
|
||||
{
|
||||
// TODO: Implement MFA input dialog
|
||||
return await Task.FromResult(string.Empty);
|
||||
// Update session data
|
||||
var session = _sessionManager.Session;
|
||||
session.IsLoggedIn = true;
|
||||
session.AvatarName = _client.Self.Name;
|
||||
session.AvatarKey = _client.Self.AgentID;
|
||||
session.Balance = _client.Self.Balance;
|
||||
session.CurrentLocation = _client.Network.CurrentSim?.Name;
|
||||
session.CurrentLocationWelcomeMessage =
|
||||
"Welcome to " + _client.Network.CurrentSim?.Name;
|
||||
|
||||
// Save session data to the database once after all properties are updated
|
||||
_sessionManager.Session = session;
|
||||
Log.Information("Session saved after login: {@Session}", session);
|
||||
|
||||
await ProcessCapabilitiesAsync();
|
||||
}
|
||||
|
||||
private async Task HandleMfaLogin(LoginParams loginParams)
|
||||
{
|
||||
Log.Warning("MFA required");
|
||||
var mfaCode = await ShowMfaInputDialogAsync();
|
||||
if (!string.IsNullOrEmpty(mfaCode))
|
||||
{
|
||||
loginParams.MfaHash = Utils.MD5(mfaCode);
|
||||
var loginSuccess = await Task.Run(() => _client.Network.Login(loginParams));
|
||||
if (loginSuccess)
|
||||
{
|
||||
await HandleSuccessfulLogin();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("Login failed with MFA: {Error}", _client.Network.LoginMessage);
|
||||
await ShowLoginErrorAsync(
|
||||
$"Login failed with MFA: {_client.Network.LoginMessage}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning("MFA code entry was canceled");
|
||||
await ShowLoginErrorAsync("MFA code entry was canceled");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessCapabilitiesAsync()
|
||||
{
|
||||
var loginMessage = await Task.Run(() => _client.Network.LoginMessage);
|
||||
Log.Information("Login message: {Message}", loginMessage);
|
||||
|
||||
var currentSim = await Task.Run(() => _client.Network.CurrentSim);
|
||||
Log.Information("Current location: {Sim}", currentSim);
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void OnLoginProgress(object? sender, LoginProgressEventArgs e)
|
||||
{
|
||||
Log.Information("Login progress: {Status}", e.Status);
|
||||
switch (e.Status)
|
||||
{
|
||||
case LoginStatus.ConnectingToLogin:
|
||||
LoginStatusMessage = "Connecting to login server...";
|
||||
break;
|
||||
case LoginStatus.ConnectingToSim:
|
||||
LoginStatusMessage = "Connecting to region...";
|
||||
break;
|
||||
case LoginStatus.Redirecting:
|
||||
LoginStatusMessage = "Redirecting...";
|
||||
break;
|
||||
case LoginStatus.ReadingResponse:
|
||||
LoginStatusMessage = "Reading response...";
|
||||
break;
|
||||
case LoginStatus.Success:
|
||||
LoginStatusMessage =
|
||||
$"Logged in as {_client.Self.Name}, welcome to {_client.Network.CurrentSim?.Name}";
|
||||
IsLoggedIn = true;
|
||||
break;
|
||||
case LoginStatus.Failed:
|
||||
LoginStatusMessage = $"Login failed: {e.Message}";
|
||||
break;
|
||||
case LoginStatus.None:
|
||||
default:
|
||||
LoginStatusMessage = $"Unknown login status: {e.Status}";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> ShowMfaInputDialogAsync()
|
||||
{
|
||||
// TODO: Implement MFA input dialog
|
||||
return await Task.FromResult(string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,121 +1,110 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows.Input;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using GalaxyViewer.Views;
|
||||
using GalaxyViewer.Services;
|
||||
using OpenMetaverse;
|
||||
using GalaxyViewer.Views;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GalaxyViewer.ViewModels;
|
||||
|
||||
public class MainViewModel : ViewModelBase, INotifyPropertyChanged
|
||||
namespace GalaxyViewer.ViewModels
|
||||
{
|
||||
private UserControl _currentView;
|
||||
private readonly GridClient _client = new();
|
||||
private readonly LoginViewModel _loginViewModel;
|
||||
private readonly LoggedInViewModel _loggedInViewModel;
|
||||
private readonly LiteDbService _liteDbService;
|
||||
|
||||
public new event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public object CurrentView
|
||||
public class MainViewModel : ViewModelBase, INotifyPropertyChanged
|
||||
{
|
||||
get => _currentView;
|
||||
set
|
||||
private UserControl _currentView;
|
||||
private readonly LoginViewModel _loginViewModel;
|
||||
private readonly LoggedInViewModel _loggedInViewModel;
|
||||
private readonly SessionManager _sessionManager;
|
||||
|
||||
public SessionManager SessionManager { get; }
|
||||
|
||||
public MainViewModel(LoginViewModel loginViewModel, LoggedInViewModel loggedInViewModel,
|
||||
SessionManager sessionManager)
|
||||
{
|
||||
if (_currentView == value) return;
|
||||
_currentView = (UserControl)value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
SessionManager = sessionManager;
|
||||
_loginViewModel = loginViewModel;
|
||||
_loggedInViewModel = loggedInViewModel;
|
||||
_sessionManager = sessionManager;
|
||||
|
||||
private new void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
// Retrieve the session from the database
|
||||
var session = _sessionManager.Session;
|
||||
|
||||
public bool IsLoggedIn => App.IsLoggedIn;
|
||||
|
||||
public ICommand ExitCommand { get; }
|
||||
public ICommand LogoutCommand { get; }
|
||||
public ICommand NavToLoginViewCommand { get; }
|
||||
public ICommand NavToPreferencesViewCommand { get; }
|
||||
public ICommand NavToDevViewCommand { get; }
|
||||
|
||||
public MainViewModel(LiteDbService liteDbService)
|
||||
{
|
||||
_liteDbService = liteDbService;
|
||||
|
||||
App.StaticPropertyChanged += (sender, args) =>
|
||||
{
|
||||
if (args.PropertyName != nameof(App.IsLoggedIn)) return;
|
||||
OnPropertyChanged(nameof(IsLoggedIn));
|
||||
if (IsLoggedIn)
|
||||
// Check the session's IsLoggedIn property during initialization
|
||||
if (session != null && session.IsLoggedIn)
|
||||
{
|
||||
NavigateToLoggedInView();
|
||||
}
|
||||
};
|
||||
else
|
||||
{
|
||||
_currentView = new LoginView { DataContext = _loginViewModel };
|
||||
}
|
||||
|
||||
_loginViewModel = new LoginViewModel(_liteDbService);
|
||||
_loggedInViewModel = new LoggedInViewModel(_liteDbService);
|
||||
_currentView = new LoginView { DataContext = _loginViewModel };
|
||||
ExitCommand = ReactiveCommand.Create(LogoutAndExit);
|
||||
LogoutCommand = ReactiveCommand.Create(Logout);
|
||||
NavToLoginViewCommand = ReactiveCommand.Create(NavigateToLoginView);
|
||||
NavToPreferencesViewCommand = ReactiveCommand.Create(NavigateToPreferencesView);
|
||||
NavToDevViewCommand = ReactiveCommand.Create(NavigateToDevView);
|
||||
}
|
||||
|
||||
private void LogoutAndExit()
|
||||
{
|
||||
Logout();
|
||||
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime
|
||||
desktop)
|
||||
{
|
||||
desktop.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private void Logout()
|
||||
{
|
||||
if (App.IsLoggedIn)
|
||||
{
|
||||
_client.Network.Logout();
|
||||
_loginViewModel.IsLoggedIn = false;
|
||||
ExitCommand = ReactiveCommand.Create(LogoutAndExit);
|
||||
LogoutCommand = ReactiveCommand.Create(Logout);
|
||||
NavToLoginViewCommand = ReactiveCommand.Create(NavigateToLoginView);
|
||||
NavToPreferencesViewCommand = ReactiveCommand.Create(NavigateToPreferencesView);
|
||||
NavToDevViewCommand = ReactiveCommand.Create(NavigateToDevView);
|
||||
}
|
||||
|
||||
NavigateToLoginView();
|
||||
}
|
||||
public object CurrentView
|
||||
{
|
||||
get => _currentView;
|
||||
set
|
||||
{
|
||||
if (_currentView == value) return;
|
||||
_currentView = (UserControl)value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void NavigateToLoginView()
|
||||
{
|
||||
CurrentView = new LoginView { DataContext = _loginViewModel };
|
||||
}
|
||||
public ICommand ExitCommand { get; }
|
||||
public ICommand LogoutCommand { get; }
|
||||
public ICommand NavToLoginViewCommand { get; }
|
||||
public ICommand NavToPreferencesViewCommand { get; }
|
||||
public ICommand NavToDevViewCommand { get; }
|
||||
|
||||
private void NavigateToLoggedInView()
|
||||
{
|
||||
CurrentView = new LoggedInView { DataContext = _loggedInViewModel };
|
||||
}
|
||||
private void LogoutAndExit()
|
||||
{
|
||||
Logout();
|
||||
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime
|
||||
desktop)
|
||||
{
|
||||
desktop.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private void NavigateToPreferencesView()
|
||||
{
|
||||
#if ANDROID
|
||||
private void Logout()
|
||||
{
|
||||
_sessionManager.Session.IsLoggedIn = false;
|
||||
NavigateToLoginView();
|
||||
}
|
||||
|
||||
private void NavigateToLoginView()
|
||||
{
|
||||
CurrentView = new LoginView { DataContext = _loginViewModel };
|
||||
}
|
||||
|
||||
private void NavigateToLoggedInView()
|
||||
{
|
||||
CurrentView = new LoggedInView(_loggedInViewModel);
|
||||
}
|
||||
|
||||
private void NavigateToPreferencesView()
|
||||
{
|
||||
CurrentView = new PreferencesView { DataContext = new PreferencesViewModel() };
|
||||
#else
|
||||
var preferencesWindow = new PreferencesWindow
|
||||
{
|
||||
DataContext = new PreferencesViewModel()
|
||||
};
|
||||
preferencesWindow.Show();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private void NavigateToDevView()
|
||||
{
|
||||
CurrentView = new DevView { DataContext = new DevViewModel() };
|
||||
private void NavigateToDevView()
|
||||
{
|
||||
CurrentView = new DevView { DataContext = new DevViewModel() };
|
||||
}
|
||||
|
||||
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
public new event PropertyChangedEventHandler PropertyChanged;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
using ReactiveUI;
|
||||
using System.Runtime.CompilerServices;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GalaxyViewer.ViewModels;
|
||||
|
||||
public abstract partial class ViewModelBase : ReactiveObject
|
||||
public partial class ViewModelBase : ReactiveObject
|
||||
{
|
||||
}
|
||||
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
this.RaisePropertyChanged(propertyName);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,20 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using GalaxyViewer.ViewModels;
|
||||
|
||||
namespace GalaxyViewer.Views;
|
||||
|
||||
public partial class LoggedInView : UserControl
|
||||
namespace GalaxyViewer.Views
|
||||
{
|
||||
public LoggedInView()
|
||||
public partial class LoggedInView : UserControl
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
public LoggedInView(LoggedInViewModel viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,19 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using GalaxyViewer.Services;
|
||||
using GalaxyViewer.ViewModels;
|
||||
|
||||
namespace GalaxyViewer.Views;
|
||||
|
||||
public partial class LoginView : UserControl
|
||||
namespace GalaxyViewer.Views
|
||||
{
|
||||
private LiteDbService _liteDbService;
|
||||
|
||||
public LoginView()
|
||||
public partial class LoginView : UserControl
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new LoginViewModel(_liteDbService);
|
||||
}
|
||||
public LoginView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,30 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="clr-namespace:GalaxyViewer.ViewModels"
|
||||
xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls"
|
||||
x:Class="GalaxyViewer.Views.MenuDesktopView"
|
||||
x:DataType="vm:MainViewModel">
|
||||
<Menu DockPanel.Dock="Top">
|
||||
<MenuItem Header="File">
|
||||
<MenuItem.Resources>
|
||||
<converters:CornerRadiusFilterConverter x:Key="InverseBooleanConverter" />
|
||||
</MenuItem.Resources>
|
||||
<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}" />
|
||||
<MenuItem Header="Upload Blinn-Phong Texture" IsEnabled="{Binding SessionManager.Session.IsLoggedIn}" />
|
||||
<MenuItem Header="Upload PBR Material" IsEnabled="{Binding SessionManager.Session.IsLoggedIn}" />
|
||||
<MenuItem Header="Upload Mesh" IsEnabled="{Binding SessionManager.Session.IsLoggedIn}" />
|
||||
<MenuItem Header="Import Object" IsEnabled="{Binding SessionManager.Session.IsLoggedIn}" />
|
||||
<MenuItem Header="Script Editor" IsEnabled="{Binding SessionManager.Session.IsLoggedIn}" />
|
||||
<Separator />
|
||||
<MenuItem Header="Login" Command="{Binding NavToLoginViewCommand}" IsEnabled="{Binding !IsLoggedIn}" />
|
||||
<MenuItem Header="Logout" Command="{Binding LogoutCommand}" IsEnabled="{Binding IsLoggedIn}" />
|
||||
<MenuItem Header="Relog" IsEnabled="{Binding !IsLoggedIn}" />
|
||||
<MenuItem Header="Login" Command="{Binding NavToLoginViewCommand}" IsEnabled="{Binding SessionManager.Session.IsLoggedIn, Converter={StaticResource InverseBooleanConverter}}" />
|
||||
<MenuItem Header="Logout" Command="{Binding LogoutCommand}" IsEnabled="{Binding SessionManager.Session.IsLoggedIn}" />
|
||||
<MenuItem Header="Relog" IsEnabled="{Binding SessionManager.Session.IsLoggedIn, Converter={StaticResource InverseBooleanConverter}}" />
|
||||
<MenuItem Header="Preferences" Command="{Binding NavToPreferencesViewCommand}" />
|
||||
<Separator />
|
||||
<MenuItem Header="Exit" Command="{Binding ExitCommand}" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="World" IsEnabled="{Binding IsLoggedIn}">
|
||||
<MenuItem Header="World" IsEnabled="{Binding SessionManager.Session.IsLoggedIn}">
|
||||
<MenuItem Header="Create new Landmark Here" />
|
||||
<MenuItem Header="Landmarks" />
|
||||
<MenuItem Header="Teleport History" />
|
||||
@@ -35,7 +39,7 @@
|
||||
<MenuItem Header="People Nearby" />
|
||||
<MenuItem Header="Objects Nearby" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="Communicate" IsEnabled="{Binding IsLoggedIn}">
|
||||
<MenuItem Header="Communicate" IsEnabled="{Binding SessionManager.Session.IsLoggedIn}">
|
||||
<MenuItem Header="Chat" />
|
||||
<MenuItem Header="Friends List" />
|
||||
<MenuItem Header="Nearby People" />
|
||||
@@ -43,7 +47,7 @@
|
||||
<MenuItem Header="Nearby Media" />
|
||||
<MenuItem Header="Nearby Objects" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="Community" IsEnabled="{Binding IsLoggedIn}">
|
||||
<MenuItem Header="Community" IsEnabled="{Binding SessionManager.Session.IsLoggedIn}">
|
||||
<MenuItem Header="Friends" />
|
||||
<MenuItem Header="Groups" />
|
||||
<MenuItem Header="Events" />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using GalaxyViewer.Services;
|
||||
using GalaxyViewer.ViewModels;
|
||||
|
||||
namespace GalaxyViewer.Views;
|
||||
@@ -9,6 +10,16 @@ public partial class MenuDesktopView : UserControl
|
||||
public MenuDesktopView()
|
||||
{
|
||||
InitializeComponent();
|
||||
var liteDbService = new LiteDbService();
|
||||
var sessionManager = new SessionManager(liteDbService);
|
||||
var gridService = new GridService(liteDbService);
|
||||
var preferencesViewModel = new PreferencesViewModel();
|
||||
|
||||
DataContext = new MainViewModel(
|
||||
new LoginViewModel(gridService, preferencesViewModel, sessionManager),
|
||||
new LoggedInViewModel(liteDbService),
|
||||
sessionManager
|
||||
);
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
|
||||
@@ -137,6 +137,25 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.NET.Test.Sdk": {
|
||||
"type": "Direct",
|
||||
"requested": "[17.12.0, )",
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeCoverage": "17.12.0",
|
||||
"Microsoft.TestPlatform.TestHost": "17.12.0"
|
||||
}
|
||||
},
|
||||
"Moq": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.20.72, )",
|
||||
"resolved": "4.20.72",
|
||||
"contentHash": "EA55cjyNn8eTNWrgrdZJH5QLFp2L43oxl1tlkoYUKIE9pRwL784OWiTXeCV5ApS+AMYEAlt7Fo03A2XfouvHmQ==",
|
||||
"dependencies": {
|
||||
"Castle.Core": "5.1.1"
|
||||
}
|
||||
},
|
||||
"Semi.Avalonia": {
|
||||
"type": "Direct",
|
||||
"requested": "[11.2.1, )",
|
||||
@@ -164,6 +183,17 @@
|
||||
"Serilog": "4.0.0"
|
||||
}
|
||||
},
|
||||
"xunit": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.9.2, )",
|
||||
"resolved": "2.9.2",
|
||||
"contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
|
||||
"dependencies": {
|
||||
"xunit.analyzers": "1.16.0",
|
||||
"xunit.assert": "2.9.2",
|
||||
"xunit.core": "[2.9.2]"
|
||||
}
|
||||
},
|
||||
"Avalonia.BuildServices": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.0.29",
|
||||
@@ -200,6 +230,14 @@
|
||||
"Avalonia": "11.2.1"
|
||||
}
|
||||
},
|
||||
"Castle.Core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.1.1",
|
||||
"contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==",
|
||||
"dependencies": {
|
||||
"System.Diagnostics.EventLog": "6.0.0"
|
||||
}
|
||||
},
|
||||
"CoreJ2K": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.4.21",
|
||||
@@ -280,6 +318,11 @@
|
||||
"resolved": "0.11.0",
|
||||
"contentHash": "MEnrZ3UIiH40hjzMDsxrTyi8dtqB5ziv3iBeeU4bXsL/7NLSal9F1lZKpK+tfBRnUoDSdtcW3KufE4yhATOMCA=="
|
||||
},
|
||||
"Microsoft.CodeCoverage": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.0",
|
||||
@@ -300,6 +343,28 @@
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
|
||||
},
|
||||
"Microsoft.TestPlatform.ObjectModel": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
|
||||
"dependencies": {
|
||||
"System.Reflection.Metadata": "1.6.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.TestPlatform.TestHost": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.12.0",
|
||||
"contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
|
||||
"dependencies": {
|
||||
"Microsoft.TestPlatform.ObjectModel": "17.12.0",
|
||||
"Newtonsoft.Json": "13.0.1"
|
||||
}
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.1",
|
||||
"contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
|
||||
},
|
||||
"OggVorbisEncoder": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.2.2",
|
||||
@@ -518,6 +583,11 @@
|
||||
"System.Threading": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Diagnostics.EventLog": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw=="
|
||||
},
|
||||
"System.Diagnostics.Tracing": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@@ -696,6 +766,11 @@
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Reflection.Metadata": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.6.0",
|
||||
"contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
|
||||
},
|
||||
"System.Reflection.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
@@ -989,6 +1064,46 @@
|
||||
"resolved": "3.1.3",
|
||||
"contentHash": "SyaOfWFXDoaVazfioH1CLSFHxhbtx4/ckaBtOibiUNhZEK0beZlPD81t339E3VnHwrkYwmqi75HxM/aMAsXWVA=="
|
||||
},
|
||||
"xunit.abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.0.3",
|
||||
"contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
|
||||
},
|
||||
"xunit.analyzers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.16.0",
|
||||
"contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
|
||||
},
|
||||
"xunit.assert": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.2",
|
||||
"contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
|
||||
},
|
||||
"xunit.core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.2",
|
||||
"contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
|
||||
"dependencies": {
|
||||
"xunit.extensibility.core": "[2.9.2]",
|
||||
"xunit.extensibility.execution": "[2.9.2]"
|
||||
}
|
||||
},
|
||||
"xunit.extensibility.core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.2",
|
||||
"contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
|
||||
"dependencies": {
|
||||
"xunit.abstractions": "2.0.3"
|
||||
}
|
||||
},
|
||||
"xunit.extensibility.execution": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.9.2",
|
||||
"contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
|
||||
"dependencies": {
|
||||
"xunit.extensibility.core": "[2.9.2]"
|
||||
}
|
||||
},
|
||||
"zlib.net-mutliplatform": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.8",
|
||||
|
||||
Reference in New Issue
Block a user