@ -43,7 +43,7 @@ namespace StabilityMatrix.Avalonia.ViewModels;
public partial class SettingsViewModel : PageViewModelBase
public partial class SettingsViewModel : PageViewModelBase
{
{
private static readonly Logger Logger = LogManager . GetCurrentClassLogger ( ) ;
private static readonly Logger Logger = LogManager . GetCurrentClassLogger ( ) ;
private readonly INotificationService notificationService ;
private readonly INotificationService notificationService ;
private readonly ISettingsManager settingsManager ;
private readonly ISettingsManager settingsManager ;
private readonly IPrerequisiteHelper prerequisiteHelper ;
private readonly IPrerequisiteHelper prerequisiteHelper ;
@ -51,72 +51,74 @@ public partial class SettingsViewModel : PageViewModelBase
private readonly ServiceManager < ViewModelBase > dialogFactory ;
private readonly ServiceManager < ViewModelBase > dialogFactory ;
private readonly ITrackedDownloadService trackedDownloadService ;
private readonly ITrackedDownloadService trackedDownloadService ;
private readonly IModelIndexService modelIndexService ;
private readonly IModelIndexService modelIndexService ;
public SharedState SharedState { get ; }
public SharedState SharedState { get ; }
public override string Title = > "Settings" ;
public override string Title = > "Settings" ;
public override IconSource IconSource = > new SymbolIconSource { Symbol = Symbol . Settings , IsFilled = true } ;
public override IconSource IconSource = >
new SymbolIconSource { Symbol = Symbol . Settings , IsFilled = true } ;
// ReSharper disable once MemberCanBeMadeStatic.Global
// ReSharper disable once MemberCanBeMadeStatic.Global
public string AppVersion = > $"Version {Compat.AppVersion}" +
public string AppVersion = >
( Program . IsDebugBuild ? " (Debug)" : "" ) ;
$"Version {Compat.AppVersion}" + ( Program . IsDebugBuild ? " (Debug)" : "" ) ;
// Theme section
// Theme section
[ObservableProperty] private string? selectedTheme ;
[ObservableProperty]
private string? selectedTheme ;
public IReadOnlyList < string > AvailableThemes { get ; } = new [ ]
{
public IReadOnlyList < string > AvailableThemes { get ; } = new [ ] { "Light" , "Dark" , "System" , } ;
"Light" ,
"Dark" ,
"System" ,
} ;
[ObservableProperty] private CultureInfo selectedLanguage ;
[ObservableProperty]
private CultureInfo selectedLanguage ;
// ReSharper disable once MemberCanBeMadeStatic.Global
// ReSharper disable once MemberCanBeMadeStatic.Global
public IReadOnlyList < CultureInfo > AvailableLanguages = > Cultures . SupportedCultures ;
public IReadOnlyList < CultureInfo > AvailableLanguages = > Cultures . SupportedCultures ;
public IReadOnlyList < float > AnimationScaleOptions { get ; } = new [ ]
public IReadOnlyList < float > AnimationScaleOptions { get ; } =
{
new [ ] { 0f , 0.25f , 0.5f , 0.75f , 1f , 1.25f , 1.5f , 1.75f , 2f , } ;
0f ,
0.25f ,
[ObservableProperty]
0.5f ,
private float selectedAnimationScale ;
0.75f ,
1f ,
1.25f ,
1.5f ,
1.75f ,
2f ,
} ;
[ObservableProperty] private float selectedAnimationScale ;
// Shared folder options
// Shared folder options
[ObservableProperty] private bool removeSymlinksOnShutdown ;
[ObservableProperty]
private bool removeSymlinksOnShutdown ;
// Integrations section
// Integrations section
[ObservableProperty] private bool isDiscordRichPresenceEnabled ;
[ObservableProperty]
private bool isDiscordRichPresenceEnabled ;
// Debug section
// Debug section
[ObservableProperty] private string? debugPaths ;
[ObservableProperty]
[ObservableProperty] private string? debugCompatInfo ;
private string? debugPaths ;
[ObservableProperty] private string? debugGpuInfo ;
[ObservableProperty]
private string? debugCompatInfo ;
[ObservableProperty]
private string? debugGpuInfo ;
// Info section
// Info section
private const int VersionTapCountThreshold = 7 ;
private const int VersionTapCountThreshold = 7 ;
[ObservableProperty, NotifyPropertyChangedFor(nameof(VersionFlyoutText))] private int versionTapCount ;
[ObservableProperty] private bool isVersionTapTeachingTipOpen ;
[ObservableProperty, NotifyPropertyChangedFor(nameof(VersionFlyoutText))]
public string VersionFlyoutText = > $"You are {VersionTapCountThreshold - VersionTapCount} clicks away from enabling Debug options." ;
private int versionTapCount ;
[ObservableProperty]
private bool isVersionTapTeachingTipOpen ;
public string VersionFlyoutText = >
$"You are {VersionTapCountThreshold - VersionTapCount} clicks away from enabling Debug options." ;
public SettingsViewModel (
public SettingsViewModel (
INotificationService notificationService ,
INotificationService notificationService ,
ISettingsManager settingsManager ,
ISettingsManager settingsManager ,
IPrerequisiteHelper prerequisiteHelper ,
IPrerequisiteHelper prerequisiteHelper ,
IPyRunner pyRunner ,
IPyRunner pyRunner ,
ServiceManager < ViewModelBase > dialogFactory ,
ServiceManager < ViewModelBase > dialogFactory ,
SharedState sharedState ,
SharedState sharedState ,
ITrackedDownloadService trackedDownloadService ,
ITrackedDownloadService trackedDownloadService ,
IModelIndexService modelIndexService )
IModelIndexService modelIndexService
)
{
{
this . notificationService = notificationService ;
this . notificationService = notificationService ;
this . settingsManager = settingsManager ;
this . settingsManager = settingsManager ;
@ -127,29 +129,32 @@ public partial class SettingsViewModel : PageViewModelBase
this . modelIndexService = modelIndexService ;
this . modelIndexService = modelIndexService ;
SharedState = sharedState ;
SharedState = sharedState ;
SelectedTheme = settingsManager . Settings . Theme ? ? AvailableThemes [ 1 ] ;
SelectedTheme = settingsManager . Settings . Theme ? ? AvailableThemes [ 1 ] ;
SelectedLanguage = Cultures . GetSupportedCultureOrDefault ( settingsManager . Settings . Language ) ;
SelectedLanguage = Cultures . GetSupportedCultureOrDefault ( settingsManager . Settings . Language ) ;
RemoveSymlinksOnShutdown = settingsManager . Settings . RemoveFolderLinksOnShutdown ;
RemoveSymlinksOnShutdown = settingsManager . Settings . RemoveFolderLinksOnShutdown ;
SelectedAnimationScale = settingsManager . Settings . AnimationScale ;
SelectedAnimationScale = settingsManager . Settings . AnimationScale ;
settingsManager . RelayPropertyFor ( this ,
settingsManager . RelayPropertyFor ( this , vm = > vm . SelectedTheme , settings = > settings . Theme ) ;
vm = > vm . SelectedTheme ,
settings = > settings . Theme ) ;
settingsManager . RelayPropertyFor (
this ,
settingsManager . RelayPropertyFor ( this ,
vm = > vm . IsDiscordRichPresenceEnabled ,
vm = > vm . IsDiscordRichPresenceEnabled ,
settings = > settings . IsDiscordRichPresenceEnabled ) ;
settings = > settings . IsDiscordRichPresenceEnabled
) ;
settingsManager . RelayPropertyFor ( this ,
settingsManager . RelayPropertyFor (
this ,
vm = > vm . SelectedAnimationScale ,
vm = > vm . SelectedAnimationScale ,
settings = > settings . AnimationScale ) ;
settings = > settings . AnimationScale
) ;
}
}
partial void OnSelectedThemeChanged ( string? value )
partial void OnSelectedThemeChanged ( string? value )
{
{
// In case design / tests
// In case design / tests
if ( Application . Current is null ) return ;
if ( Application . Current is null )
return ;
// Change theme
// Change theme
Application . Current . RequestedThemeVariant = value switch
Application . Current . RequestedThemeVariant = value switch
{
{
@ -161,16 +166,16 @@ public partial class SettingsViewModel : PageViewModelBase
partial void OnSelectedLanguageChanged ( CultureInfo ? oldValue , CultureInfo newValue )
partial void OnSelectedLanguageChanged ( CultureInfo ? oldValue , CultureInfo newValue )
{
{
if ( oldValue is null | | newValue . Name = = Cultures . Current . Name ) return ;
if ( oldValue is null | | newValue . Name = = Cultures . Current . Name )
return ;
// Set locale
// Set locale
if ( AvailableLanguages . Contains ( newValue ) )
if ( AvailableLanguages . Contains ( newValue ) )
{
{
Logger . Info ( "Changing language from {Old} to {New}" ,
Logger . Info ( "Changing language from {Old} to {New}" , oldValue , newValue ) ;
oldValue , newValue ) ;
Cultures . TrySetSupportedCulture ( newValue ) ;
Cultures . TrySetSupportedCulture ( newValue ) ;
settingsManager . Transaction ( s = > s . Language = newValue . Name ) ;
settingsManager . Transaction ( s = > s . Language = newValue . Name ) ;
var dialog = new BetterContentDialog
var dialog = new BetterContentDialog
{
{
Title = Resources . Label_RelaunchRequired ,
Title = Resources . Label_RelaunchRequired ,
@ -191,11 +196,14 @@ public partial class SettingsViewModel : PageViewModelBase
}
}
else
else
{
{
Logger . Info ( "Requested invalid language change from {Old} to {New}" ,
Logger . Info (
oldValue , newValue ) ;
"Requested invalid language change from {Old} to {New}" ,
oldValue ,
newValue
) ;
}
}
}
}
partial void OnRemoveSymlinksOnShutdownChanged ( bool value )
partial void OnRemoveSymlinksOnShutdownChanged ( bool value )
{
{
settingsManager . Transaction ( s = > s . RemoveFolderLinksOnShutdown = value ) ;
settingsManager . Transaction ( s = > s . RemoveFolderLinksOnShutdown = value ) ;
@ -205,29 +213,30 @@ public partial class SettingsViewModel : PageViewModelBase
{
{
settingsManager . Transaction ( s = > s . InstalledModelHashes = new HashSet < string > ( ) ) ;
settingsManager . Transaction ( s = > s . InstalledModelHashes = new HashSet < string > ( ) ) ;
await Task . Run ( ( ) = > settingsManager . IndexCheckpoints ( ) ) ;
await Task . Run ( ( ) = > settingsManager . IndexCheckpoints ( ) ) ;
notificationService . Show ( "Checkpoint cache reset" , "The checkpoint cache has been reset." ,
notificationService . Show (
NotificationType . Success ) ;
"Checkpoint cache reset" ,
"The checkpoint cache has been reset." ,
NotificationType . Success
) ;
}
}
#region Package Environment
#region Package Environment
[RelayCommand]
[RelayCommand]
private async Task OpenEnvVarsDialog ( )
private async Task OpenEnvVarsDialog ( )
{
{
var viewModel = dialogFactory . Get < EnvVarsViewModel > ( ) ;
var viewModel = dialogFactory . Get < EnvVarsViewModel > ( ) ;
// Load current settings
// Load current settings
var current = settingsManager . Settings . EnvironmentVariables
var current =
? ? new Dictionary < string , string > ( ) ;
settingsManager . Settings . EnvironmentVariables ? ? new Dictionary < string , string > ( ) ;
viewModel . EnvVars = new ObservableCollection < EnvVarKeyPair > (
viewModel . EnvVars = new ObservableCollection < EnvVarKeyPair > (
current . Select ( kvp = > new EnvVarKeyPair ( kvp . Key , kvp . Value ) ) ) ;
current . Select ( kvp = > new EnvVarKeyPair ( kvp . Key , kvp . Value ) )
) ;
var dialog = new BetterContentDialog
var dialog = new BetterContentDialog
{
{
Content = new EnvVarsDialog
Content = new EnvVarsDialog { DataContext = viewModel } ,
{
DataContext = viewModel
} ,
PrimaryButtonText = "Save" ,
PrimaryButtonText = "Save" ,
IsPrimaryButtonEnabled = true ,
IsPrimaryButtonEnabled = true ,
CloseButtonText = "Cancel" ,
CloseButtonText = "Cancel" ,
@ -275,7 +284,7 @@ public partial class SettingsViewModel : PageViewModelBase
dialog . PrimaryButtonText = "Ok" ;
dialog . PrimaryButtonText = "Ok" ;
await dialog . ShowAsync ( ) ;
await dialog . ShowAsync ( ) ;
}
}
# endregion
# endregion
#region System
#region System
@ -288,27 +297,29 @@ public partial class SettingsViewModel : PageViewModelBase
{
{
if ( ! Compat . IsWindows )
if ( ! Compat . IsWindows )
{
{
notificationService . Show (
notificationService . Show ( "Not supported" , "This feature is only supported on Windows." ) ;
"Not supported" , "This feature is only supported on Windows." ) ;
return ;
return ;
}
}
await using var _ = new MinimumDelay ( 2 0 0 , 3 0 0 ) ;
await using var _ = new MinimumDelay ( 2 0 0 , 3 0 0 ) ;
var shortcutDir = new DirectoryPath (
var shortcutDir = new DirectoryPath (
Environment . GetFolderPath ( Environment . SpecialFolder . StartMenu ) ,
Environment . GetFolderPath ( Environment . SpecialFolder . StartMenu ) ,
"Programs" ) ;
"Programs"
) ;
var shortcutLink = shortcutDir . JoinFile ( "Stability Matrix.lnk" ) ;
var shortcutLink = shortcutDir . JoinFile ( "Stability Matrix.lnk" ) ;
var appPath = Compat . AppCurrentPath ;
var appPath = Compat . AppCurrentPath ;
var iconPath = shortcutDir . JoinFile ( "Stability Matrix.ico" ) ;
var iconPath = shortcutDir . JoinFile ( "Stability Matrix.ico" ) ;
await Assets . AppIcon . ExtractTo ( iconPath ) ;
await Assets . AppIcon . ExtractTo ( iconPath ) ;
WindowsShortcuts . CreateShortcut (
WindowsShortcuts . CreateShortcut ( shortcutLink , appPath , iconPath , "Stability Matrix" ) ;
shortcutLink , appPath , iconPath , "Stability Matrix" ) ;
notificationService . Show (
notificationService . Show ( "Added to Start Menu" ,
"Added to Start Menu" ,
"Stability Matrix has been added to the Start Menu." , NotificationType . Success ) ;
"Stability Matrix has been added to the Start Menu." ,
NotificationType . Success
) ;
}
}
/// <summary>
/// <summary>
@ -320,54 +331,62 @@ public partial class SettingsViewModel : PageViewModelBase
{
{
if ( ! Compat . IsWindows )
if ( ! Compat . IsWindows )
{
{
notificationService . Show (
notificationService . Show ( "Not supported" , "This feature is only supported on Windows." ) ;
"Not supported" , "This feature is only supported on Windows." ) ;
return ;
return ;
}
}
// Confirmation dialog
// Confirmation dialog
var dialog = new BetterContentDialog
var dialog = new BetterContentDialog
{
{
Title = "This will create a shortcut for Stability Matrix in the Start Menu for all users" ,
Title =
"This will create a shortcut for Stability Matrix in the Start Menu for all users" ,
Content = "You will be prompted for administrator privileges. Continue?" ,
Content = "You will be prompted for administrator privileges. Continue?" ,
PrimaryButtonText = "Yes" ,
PrimaryButtonText = "Yes" ,
CloseButtonText = "Cancel" ,
CloseButtonText = "Cancel" ,
DefaultButton = ContentDialogButton . Primary
DefaultButton = ContentDialogButton . Primary
} ;
} ;
if ( await dialog . ShowAsync ( ) ! = ContentDialogResult . Primary )
if ( await dialog . ShowAsync ( ) ! = ContentDialogResult . Primary )
{
{
return ;
return ;
}
}
await using var _ = new MinimumDelay ( 2 0 0 , 3 0 0 ) ;
await using var _ = new MinimumDelay ( 2 0 0 , 3 0 0 ) ;
var shortcutDir = new DirectoryPath (
var shortcutDir = new DirectoryPath (
Environment . GetFolderPath ( Environment . SpecialFolder . CommonStartMenu ) ,
Environment . GetFolderPath ( Environment . SpecialFolder . CommonStartMenu ) ,
"Programs" ) ;
"Programs"
) ;
var shortcutLink = shortcutDir . JoinFile ( "Stability Matrix.lnk" ) ;
var shortcutLink = shortcutDir . JoinFile ( "Stability Matrix.lnk" ) ;
var appPath = Compat . AppCurrentPath ;
var appPath = Compat . AppCurrentPath ;
var iconPath = shortcutDir . JoinFile ( "Stability Matrix.ico" ) ;
var iconPath = shortcutDir . JoinFile ( "Stability Matrix.ico" ) ;
// We can't directly write to the targets, so extract to temporary directory first
// We can't directly write to the targets, so extract to temporary directory first
using var tempDir = new TempDirectoryPath ( ) ;
using var tempDir = new TempDirectoryPath ( ) ;
await Assets . AppIcon . ExtractTo ( tempDir . JoinFile ( "Stability Matrix.ico" ) ) ;
await Assets . AppIcon . ExtractTo ( tempDir . JoinFile ( "Stability Matrix.ico" ) ) ;
WindowsShortcuts . CreateShortcut (
WindowsShortcuts . CreateShortcut (
tempDir . JoinFile ( "Stability Matrix.lnk" ) , appPath , iconPath ,
tempDir . JoinFile ( "Stability Matrix.lnk" ) ,
"Stability Matrix" ) ;
appPath ,
iconPath ,
"Stability Matrix"
) ;
// Move to target
// Move to target
try
try
{
{
var moveLinkResult = await WindowsElevated . MoveFiles (
var moveLinkResult = await WindowsElevated . MoveFiles (
( tempDir . JoinFile ( "Stability Matrix.lnk" ) , shortcutLink ) ,
( tempDir . JoinFile ( "Stability Matrix.lnk" ) , shortcutLink ) ,
( tempDir . JoinFile ( "Stability Matrix.ico" ) , iconPath ) ) ;
( tempDir . JoinFile ( "Stability Matrix.ico" ) , iconPath )
) ;
if ( moveLinkResult ! = 0 )
if ( moveLinkResult ! = 0 )
{
{
notificationService . ShowPersistent ( "Failed to create shortcut" , $"Could not copy shortcut" ,
notificationService . ShowPersistent (
NotificationType . Error ) ;
"Failed to create shortcut" ,
$"Could not copy shortcut" ,
NotificationType . Error
) ;
}
}
}
}
catch ( Win32Exception e )
catch ( Win32Exception e )
@ -377,9 +396,12 @@ public partial class SettingsViewModel : PageViewModelBase
notificationService . Show ( "Could not create shortcut" , "" , NotificationType . Warning ) ;
notificationService . Show ( "Could not create shortcut" , "" , NotificationType . Warning ) ;
return ;
return ;
}
}
notificationService . Show ( "Added to Start Menu" ,
notificationService . Show (
"Stability Matrix has been added to the Start Menu for all users." , NotificationType . Success ) ;
"Added to Start Menu" ,
"Stability Matrix has been added to the Start Menu for all users." ,
NotificationType . Success
) ;
}
}
public async Task PickNewDataDirectory ( )
public async Task PickNewDataDirectory ( )
@ -390,10 +412,7 @@ public partial class SettingsViewModel : PageViewModelBase
IsPrimaryButtonEnabled = false ,
IsPrimaryButtonEnabled = false ,
IsSecondaryButtonEnabled = false ,
IsSecondaryButtonEnabled = false ,
IsFooterVisible = false ,
IsFooterVisible = false ,
Content = new SelectDataDirectoryDialog
Content = new SelectDataDirectoryDialog { DataContext = viewModel }
{
DataContext = viewModel
}
} ;
} ;
var result = await dialog . ShowAsync ( ) ;
var result = await dialog . ShowAsync ( ) ;
@ -409,7 +428,7 @@ public partial class SettingsViewModel : PageViewModelBase
{
{
settingsManager . SetLibraryPath ( viewModel . DataDirectory ) ;
settingsManager . SetLibraryPath ( viewModel . DataDirectory ) ;
}
}
// Restart
// Restart
var restartDialog = new BetterContentDialog
var restartDialog = new BetterContentDialog
{
{
@ -420,14 +439,14 @@ public partial class SettingsViewModel : PageViewModelBase
IsSecondaryButtonEnabled = false ,
IsSecondaryButtonEnabled = false ,
} ;
} ;
await restartDialog . ShowAsync ( ) ;
await restartDialog . ShowAsync ( ) ;
Process . Start ( Compat . AppCurrentPath ) ;
Process . Start ( Compat . AppCurrentPath ) ;
App . Shutdown ( ) ;
App . Shutdown ( ) ;
}
}
}
}
# endregion
# endregion
#region Debug Section
#region Debug Section
public void LoadDebugInfo ( )
public void LoadDebugInfo ( )
{
{
@ -443,12 +462,12 @@ public partial class SettingsViewModel : PageViewModelBase
AppData Directory [ SpecialFolder . ApplicationData ]
AppData Directory [ SpecialFolder . ApplicationData ]
"{appData}"
"{appData}"
"" ";
"" ";
// 1. Check portable mode
// 1. Check portable mode
var appDir = Compat . AppCurrentDir ;
var appDir = Compat . AppCurrentDir ;
var expectedPortableFile = Path . Combine ( appDir , "Data" , ".sm-portable" ) ;
var expectedPortableFile = Path . Combine ( appDir , "Data" , ".sm-portable" ) ;
var isPortableMode = File . Exists ( expectedPortableFile ) ;
var isPortableMode = File . Exists ( expectedPortableFile ) ;
DebugCompatInfo = $"" "
DebugCompatInfo = $"" "
Platform : { Compat . Platform }
Platform : { Compat . Platform }
AppData : { Compat . AppData }
AppData : { Compat . AppData }
@ -461,24 +480,27 @@ public partial class SettingsViewModel : PageViewModelBase
IsLibraryDirSet = { settingsManager . IsLibraryDirSet }
IsLibraryDirSet = { settingsManager . IsLibraryDirSet }
IsPortableMode = { settingsManager . IsPortableMode }
IsPortableMode = { settingsManager . IsPortableMode }
"" ";
"" ";
// Get Gpu info
// Get Gpu info
var gpuInfo = "" ;
var gpuInfo = "" ;
foreach ( var ( i , gpu ) in HardwareHelper . IterGpuInfo ( ) . Enumerate ( ) )
foreach ( var ( i , gpu ) in HardwareHelper . IterGpuInfo ( ) . Enumerate ( ) )
{
{
gpuInfo + = $"[{i+1}] {gpu}\n" ;
gpuInfo + = $"[{i + 1}] {gpu}\n" ;
}
}
DebugGpuInfo = gpuInfo ;
DebugGpuInfo = gpuInfo ;
}
}
// Debug buttons
// Debug buttons
[RelayCommand]
[RelayCommand]
private void DebugNotification ( )
private void DebugNotification ( )
{
{
notificationService . Show ( new Notification (
notificationService . Show (
title : "Test Notification" ,
new Notification (
message : "Here is some message" ,
title : "Test Notification" ,
type : NotificationType . Information ) ) ;
message : "Here is some message" ,
type : NotificationType . Information
)
) ;
}
}
[RelayCommand]
[RelayCommand]
@ -493,8 +515,7 @@ public partial class SettingsViewModel : PageViewModelBase
} ;
} ;
var result = await dialog . ShowAsync ( ) ;
var result = await dialog . ShowAsync ( ) ;
notificationService . Show ( new Notification ( "Content dialog closed" ,
notificationService . Show ( new Notification ( "Content dialog closed" , $"Result: {result}" ) ) ;
$"Result: {result}" ) ) ;
}
}
[RelayCommand]
[RelayCommand]
@ -509,20 +530,14 @@ public partial class SettingsViewModel : PageViewModelBase
{
{
await modelIndexService . RefreshIndex ( ) ;
await modelIndexService . RefreshIndex ( ) ;
}
}
[RelayCommand]
[RelayCommand]
private async Task DebugTrackedDownload ( )
private async Task DebugTrackedDownload ( )
{
{
var textFields = new TextBoxField [ ]
var textFields = new TextBoxField [ ]
{
{
new ( )
new ( ) { Label = "Url" , } ,
{
new ( ) { Label = "File path" }
Label = "Url" ,
} ,
new ( )
{
Label = "File path"
}
} ;
} ;
var dialog = DialogHelper . CreateTextEntryDialog ( "Add download" , "" , textFields ) ;
var dialog = DialogHelper . CreateTextEntryDialog ( "Add download" , "" , textFields ) ;
@ -542,10 +557,11 @@ public partial class SettingsViewModel : PageViewModelBase
public void OnVersionClick ( )
public void OnVersionClick ( )
{
{
// Ignore if already enabled
// Ignore if already enabled
if ( SharedState . IsDebugMode ) return ;
if ( SharedState . IsDebugMode )
return ;
VersionTapCount + + ;
VersionTapCount + + ;
switch ( VersionTapCount )
switch ( VersionTapCount )
{
{
// Reached required threshold
// Reached required threshold
@ -555,7 +571,9 @@ public partial class SettingsViewModel : PageViewModelBase
// Enable debug options
// Enable debug options
SharedState . IsDebugMode = true ;
SharedState . IsDebugMode = true ;
notificationService . Show (
notificationService . Show (
"Debug options enabled" , "Warning: Improper use may corrupt application state or cause loss of data." ) ;
"Debug options enabled" ,
"Warning: Improper use may corrupt application state or cause loss of data."
) ;
VersionTapCount = 0 ;
VersionTapCount = 0 ;
break ;
break ;
}
}
@ -579,8 +597,11 @@ public partial class SettingsViewModel : PageViewModelBase
}
}
catch ( Exception e )
catch ( Exception e )
{
{
notificationService . Show ( "Failed to read licenses information" ,
notificationService . Show (
$"{e}" , NotificationType . Error ) ;
"Failed to read licenses information" ,
$"{e}" ,
NotificationType . Error
) ;
}
}
}
}
@ -588,15 +609,17 @@ public partial class SettingsViewModel : PageViewModelBase
{
{
// Read licenses.json
// Read licenses.json
using var reader = new StreamReader ( Assets . LicensesJson . Open ( ) ) ;
using var reader = new StreamReader ( Assets . LicensesJson . Open ( ) ) ;
var licenses = JsonSerializer
var licenses =
. Deserialize < IReadOnlyList < LicenseInfo > > ( reader . ReadToEnd ( ) ) ? ?
JsonSerializer . Deserialize < IReadOnlyList < LicenseInfo > > ( reader . ReadToEnd ( ) )
throw new InvalidOperationException ( "Failed to read licenses.json" ) ;
? ? throw new InvalidOperationException ( "Failed to read licenses.json" ) ;
// Generate markdown
// Generate markdown
var builder = new StringBuilder ( ) ;
var builder = new StringBuilder ( ) ;
foreach ( var license in licenses )
foreach ( var license in licenses )
{
{
builder . AppendLine ( $"## [{license.PackageName}]({license.PackageUrl}) by {string.Join(" , ", license.Authors)}" ) ;
builder . AppendLine (
$"## [{license.PackageName}]({license.PackageUrl}) by {string.Join(" , ", license.Authors)}"
) ;
builder . AppendLine ( ) ;
builder . AppendLine ( ) ;
builder . AppendLine ( license . Description ) ;
builder . AppendLine ( license . Description ) ;
builder . AppendLine ( ) ;
builder . AppendLine ( ) ;
@ -608,5 +631,4 @@ public partial class SettingsViewModel : PageViewModelBase
}
}
# endregion
# endregion
}
}