diff --git a/src/Abstractions/NexusMods.Abstractions.Games.Diagnostics/DiagnosticSettings.cs b/src/Abstractions/NexusMods.Abstractions.Games.Diagnostics/DiagnosticSettings.cs index 3de677e4b6..cdcf75c0e8 100644 --- a/src/Abstractions/NexusMods.Abstractions.Games.Diagnostics/DiagnosticSettings.cs +++ b/src/Abstractions/NexusMods.Abstractions.Games.Diagnostics/DiagnosticSettings.cs @@ -17,8 +17,7 @@ public class DiagnosticSettings : ISettings /// public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder) { - // TODO: put in some section - var sectionId = SectionId.DefaultValue; + var sectionId = Sections.General; return settingsBuilder.AddToUI(builder => builder .AddPropertyToUI(x => x.MinimumSeverity, propertyBuilder => propertyBuilder diff --git a/src/Abstractions/NexusMods.Abstractions.Settings/Sections.cs b/src/Abstractions/NexusMods.Abstractions.Settings/Sections.cs new file mode 100644 index 0000000000..1feb4dfddb --- /dev/null +++ b/src/Abstractions/NexusMods.Abstractions.Settings/Sections.cs @@ -0,0 +1,20 @@ +using JetBrains.Annotations; + +namespace NexusMods.Abstractions.Settings; + +/// +/// A collection of sections to use. +/// +[PublicAPI] +public static class Sections +{ + public static readonly SectionId General = SectionId.From(Guid.Parse("3106487b-db84-4caa-acdd-9428506fbf6d")); + + public static readonly SectionId Privacy = SectionId.From(Guid.Parse("4ef7b142-8f0e-4cae-867f-a58d985241c0")); + + public static readonly SectionId Advanced = SectionId.From(Guid.Parse("1531efa7-cb0a-4463-8a04-a865d848ca06")); + + public static readonly SectionId DeveloperTools = SectionId.From(Guid.Parse("c33fb41c-7dc5-4911-b48c-3a8c822083d9")); + + public static readonly SectionId Experimental = SectionId.From(Guid.Parse("864495d4-aa30-48a7-b292-d23adce391f1")); +} diff --git a/src/Abstractions/NexusMods.Abstractions.Telemetry/TelemetrySettings.cs b/src/Abstractions/NexusMods.Abstractions.Telemetry/TelemetrySettings.cs index 0178d2fc5d..80eb5df67a 100644 --- a/src/Abstractions/NexusMods.Abstractions.Telemetry/TelemetrySettings.cs +++ b/src/Abstractions/NexusMods.Abstractions.Telemetry/TelemetrySettings.cs @@ -12,16 +12,13 @@ public record TelemetrySettings : ISettings public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder) { - // TODO: put in some section - var sectionId = SectionId.DefaultValue; - return settingsBuilder .ConfigureStorageBackend(backend => backend.UseJson()) .AddToUI(builder => builder .AddPropertyToUI(x => x.IsEnabled, propertyBuilder => propertyBuilder - .AddToSection(sectionId) - .WithDisplayName("Enable Telemetry") - .WithDescription("Send anonymous analytics information and usage data to Nexus Mods.") + .AddToSection(Sections.Privacy) + .WithDisplayName("Send usage data") + .WithDescription("Help us improve the App by sending usage data to Nexus Mods.") .UseBooleanContainer() .RequiresRestart() ) diff --git a/src/NexusMods.App.UI/ExperimentalSettings.cs b/src/NexusMods.App.UI/ExperimentalSettings.cs index 69781d229b..3c8910b955 100644 --- a/src/NexusMods.App.UI/ExperimentalSettings.cs +++ b/src/NexusMods.App.UI/ExperimentalSettings.cs @@ -21,23 +21,20 @@ public record ExperimentalSettings : ISettings public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder) { - // TODO: put in some section - var sectionId = SectionId.DefaultValue; - return settingsBuilder .ConfigureStorageBackend(builder => builder.UseJson()) .AddToUI(builder => builder .AddPropertyToUI(x => x.EnableAllGames, propertyBuilder => propertyBuilder - .AddToSection(sectionId) - .WithDisplayName("[Unsupported] Enable Unsupported Games") - .WithDescription("When set, 'work-in-progress' games that are not yet fully supported will be enabled in the UI.") + .AddToSection(Sections.Experimental) + .WithDisplayName("Enable unsupported games") + .WithDescription("Allows you to manage unsupported games.") .UseBooleanContainer() .RequiresRestart() ) .AddPropertyToUI(x => x.EnableMultipleLoadouts, propertyBuilder => propertyBuilder - .AddToSection(sectionId) - .WithDisplayName("(Experimental) Enable Multiple Loadouts") - .WithDescription("When set, you will be able to create multiple loadouts for a game.") + .AddToSection(Sections.Experimental) + .WithDisplayName("Enable multiple loadouts") + .WithDescription("Allows you to create multiple Loadouts.") .UseBooleanContainer() ) ); diff --git a/src/NexusMods.App.UI/Overlays/MetricsOptIn/MetricsOptInViewModel.cs b/src/NexusMods.App.UI/Overlays/MetricsOptIn/MetricsOptInViewModel.cs index b70828084f..44766c103c 100644 --- a/src/NexusMods.App.UI/Overlays/MetricsOptIn/MetricsOptInViewModel.cs +++ b/src/NexusMods.App.UI/Overlays/MetricsOptIn/MetricsOptInViewModel.cs @@ -29,13 +29,11 @@ public MetricsOptInViewModel(ISettingsManager settingsManager, IMarkdownRenderer MarkdownRendererViewModel = markdownRendererViewModel; MarkdownRendererViewModel.Contents = """ -## Telemetry and usage data +## Help us improve Help us provide you with the best modding experience. -With your permission, we will collect analytics information and send it to our team to help us improve quality and performance. - -This information is sent anonymously and will never be shared with a 3rd party. +With your permission, we will collect anonymous analytics information and send it to our team to help us improve quality and performance. [More information about the data we track](https://help.nexusmods.com/article/132-diagnostics-and-usage-nexus-mods-app) """; diff --git a/src/NexusMods.App.UI/Pages/Settings/SettingsView.axaml.cs b/src/NexusMods.App.UI/Pages/Settings/SettingsView.axaml.cs index ebdcf3d460..e8cbd19a95 100644 --- a/src/NexusMods.App.UI/Pages/Settings/SettingsView.axaml.cs +++ b/src/NexusMods.App.UI/Pages/Settings/SettingsView.axaml.cs @@ -37,7 +37,7 @@ public SettingsView() foreach (var group in grouped) { res.Add(dict[group.Key]); - res.AddRange(group); + res.AddRange(group.OrderBy(x => x.PropertyUIDescriptor.DisplayName, StringComparer.OrdinalIgnoreCase)); } return res; diff --git a/src/NexusMods.App.UI/Settings/LanguageSettings.cs b/src/NexusMods.App.UI/Settings/LanguageSettings.cs index 3bea6051c1..42277255d3 100644 --- a/src/NexusMods.App.UI/Settings/LanguageSettings.cs +++ b/src/NexusMods.App.UI/Settings/LanguageSettings.cs @@ -30,12 +30,9 @@ static LanguageSettings() public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder) { - // TODO: put in some section - var sectionId = SectionId.DefaultValue; - return settingsBuilder.AddToUI(builder => builder .AddPropertyToUI(x => x.UICulture, propertyBuilder => propertyBuilder - .AddToSection(sectionId) + .AddToSection(Sections.General) .WithDisplayName("Language") .WithDescription("Set the language for the application.") .UseSingleValueMultipleChoiceContainer( diff --git a/src/NexusMods.App.UI/Settings/LoadoutGridSettings.cs b/src/NexusMods.App.UI/Settings/LoadoutGridSettings.cs index 76df40a1f8..b6944d8341 100644 --- a/src/NexusMods.App.UI/Settings/LoadoutGridSettings.cs +++ b/src/NexusMods.App.UI/Settings/LoadoutGridSettings.cs @@ -11,20 +11,17 @@ public record LoadoutGridSettings : ISettings public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder) { - // TODO: put in some section - var sectionId = SectionId.DefaultValue; - return settingsBuilder.AddToUI(builder => builder .AddPropertyToUI(x => x.ShowGameFiles, propertyBuilder => propertyBuilder - .AddToSection(sectionId) - .WithDisplayName("Show Game Files") - .WithDescription("Shows the Game Files in the Mods page.") + .AddToSection(Sections.Advanced) + .WithDisplayName("Show game files") + .WithDescription("Show game files as a mod alongside your added mods.") .UseBooleanContainer() ) .AddPropertyToUI(x => x.ShowOverride, propertyBuilder => propertyBuilder - .AddToSection(sectionId) - .WithDisplayName("Show Override") - .WithDescription("Shows Override in the Mods page.") + .AddToSection(Sections.Advanced) + .WithDisplayName("Show Override mod") + .WithDescription("Shows the Override mod, which contains files generated or modified during gameplay that aren't part of any specific mod.") .UseBooleanContainer() ) ); diff --git a/src/NexusMods.App.UI/Settings/TextEditorSettings.cs b/src/NexusMods.App.UI/Settings/TextEditorSettings.cs index 7c6b7f8062..26ee88fa95 100644 --- a/src/NexusMods.App.UI/Settings/TextEditorSettings.cs +++ b/src/NexusMods.App.UI/Settings/TextEditorSettings.cs @@ -12,22 +12,6 @@ public record TextEditorSettings : ISettings public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder) { - // TODO: put in some section - var sectionId = SectionId.DefaultValue; - - // TODO: add font size to UI (requires new component) - - return settingsBuilder.AddToUI(builder => builder - .AddPropertyToUI(x => x.ThemeName, propertyBuilder => propertyBuilder - .AddToSection(sectionId) - .WithDisplayName("Text Editor Theme") - .WithDescription("Set the desired theme in the text editor.") - .UseSingleValueMultipleChoiceContainer( - valueComparer: EqualityComparer.Default, - allowedValues: Enum.GetValues(), - valueToDisplayString: x => x.ToString() - ) - ) - ); + return settingsBuilder; } } diff --git a/src/NexusMods.CrossPlatform/LoggingSettings.cs b/src/NexusMods.CrossPlatform/LoggingSettings.cs index 12f59aeee2..95da734a91 100644 --- a/src/NexusMods.CrossPlatform/LoggingSettings.cs +++ b/src/NexusMods.CrossPlatform/LoggingSettings.cs @@ -58,17 +58,14 @@ public record LoggingSettings : ISettings /// public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder) { - // TODO: put in some section - var sectionId = SectionId.DefaultValue; - return settingsBuilder .ConfigureDefault(serviceProvider => CreateDefault(serviceProvider.GetRequiredService().OS)) .ConfigureStorageBackend(builder => builder.UseJson()) .AddToUI(builder => builder .AddPropertyToUI(x => x.MinimumLevel, propertyBuilder => propertyBuilder - .AddToSection(sectionId) - .WithDisplayName("Minimum Logging Level") - .WithDescription("Set the minimum logging level. Recommended: Debug") + .AddToSection(Sections.General) + .WithDisplayName("Minimum logging level") + .WithDescription("Sets the minimum logging level. Recommended: Debug") .UseSingleValueMultipleChoiceContainer( valueComparer: EqualityComparer.Default, allowedValues: [ @@ -81,9 +78,9 @@ public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder) .RequiresRestart() ) .AddPropertyToUI(x => x.LogToConsole, propertyBuilder => propertyBuilder - .AddToSection(sectionId) - .WithDisplayName("Log to Console") - .WithDescription("When enabled, logs will be written to the console as well as the log file.") + .AddToSection(Sections.DeveloperTools) + .WithDisplayName("Log to stdout") + .WithDescription("Enables the ConsoleTarget for all loggers.") .UseBooleanContainer() .RequiresRestart() ) diff --git a/src/NexusMods.Icons/IconValues.cs b/src/NexusMods.Icons/IconValues.cs index 0afd8ec667..e7e4d51531 100644 --- a/src/NexusMods.Icons/IconValues.cs +++ b/src/NexusMods.Icons/IconValues.cs @@ -136,6 +136,9 @@ public static class IconValues #region Editor + // https://pictogrammers.com/library/mdi/icon/poll + public static readonly IconValue BarChart = new ProjektankerIcon("mdi-poll"); + // https://pictogrammers.com/library/mdi/icon/drag-horizontal-variant/ public static readonly IconValue DragHandleHorizontal = new ProjektankerIcon("mdi-drag-horizontal-variant"); @@ -177,6 +180,9 @@ public static class IconValues #region Hardware + // https://pictogrammers.com/library/mdi/icon/monitor/ + public static readonly IconValue Desktop = new ProjektankerIcon("mdi-monitor"); + // https://pictogrammers.com/library/mdi/icon/gamepad-square-outline/ public static readonly IconValue Game = new ProjektankerIcon("mdi-gamepad-square-outline"); @@ -227,6 +233,13 @@ public static class IconValues #endregion +#region Social + + // https://pictogrammers.com/library/mdi/icon/school + public static readonly IconValue School = new ProjektankerIcon("mdi-school"); + +#endregion + #region Toggle // https://pictogrammers.com/library/mdi/icon/star/ diff --git a/src/NexusMods.Settings/Services.cs b/src/NexusMods.Settings/Services.cs index f147bbff56..e6082f3bf5 100644 --- a/src/NexusMods.Settings/Services.cs +++ b/src/NexusMods.Settings/Services.cs @@ -12,9 +12,33 @@ public static IServiceCollection AddSettingsManager(this IServiceCollection serv .AddSingleton() .AddSettingsSection(new SettingsSectionSetup { - Id = SectionId.DefaultValue, - IconFunc = static () => IconValues.Error, - Name = "No Section", + Id = Sections.General, + IconFunc = static () => IconValues.Desktop, + Name = "General", + }) + .AddSettingsSection(new SettingsSectionSetup + { + Id = Sections.Privacy, + IconFunc = static () => IconValues.BarChart, + Name = "Privacy", + }) + .AddSettingsSection(new SettingsSectionSetup + { + Id = Sections.Advanced, + IconFunc = static () => IconValues.School, + Name = "Advanced", + }) + .AddSettingsSection(new SettingsSectionSetup + { + Id = Sections.DeveloperTools, + IconFunc = static () => IconValues.Code, + Name = "Developer tools", + }) + .AddSettingsSection(new SettingsSectionSetup + { + Id = Sections.Experimental, + IconFunc = static () => IconValues.WarningAmber, + Name = "Experimental - Not currently supported", }); } } diff --git a/src/NexusMods.Settings/SettingsManager.cs b/src/NexusMods.Settings/SettingsManager.cs index 428c0388cf..462be2b5ab 100644 --- a/src/NexusMods.Settings/SettingsManager.cs +++ b/src/NexusMods.Settings/SettingsManager.cs @@ -41,7 +41,7 @@ public SettingsManager(IServiceProvider serviceProvider) var settingsSectionSetups = serviceProvider.GetServices().ToArray(); - // NOTE(erri120): This has to be Lazy because the Icon isn't available until Avalonia starts up. + // NOTE(erri120): This has to be Lazy because icons aren't available until Avalonia starts up. _sectionDescriptors = new Lazy(() => settingsSectionSetups .Select(descriptor => (ISettingsSectionDescriptor)new SettingsSectionDescriptor { @@ -53,16 +53,6 @@ public SettingsManager(IServiceProvider serviceProvider) mode: LazyThreadSafetyMode.ExecutionAndPublication ); - if (CompileConstants.IsDebug) - { - var ids = new HashSet(); - foreach (var sectionDescriptor in settingsSectionSetups) - { - var id = sectionDescriptor.Id; - Debug.Assert(ids.Add(id), $"duplicate section ID: {id}"); - } - } - var baseStorageBackendArray = serviceProvider.GetServices().ToArray(); var settingsTypeInformationArray = serviceProvider.GetServices().ToArray(); var defaultBaseStorageBackend = serviceProvider.GetService()?.Backend; @@ -72,6 +62,21 @@ public SettingsManager(IServiceProvider serviceProvider) _storageBackendMappings = builderOutput.StorageBackendMappings; _asyncStorageBackendMappings = builderOutput.AsyncStorageBackendMappings; _propertyBuilderOutputs = builderOutput.PropertyBuilderOutputs; + + if (CompileConstants.IsDebug) + { + var ids = new HashSet(); + foreach (var sectionDescriptor in settingsSectionSetups) + { + var id = sectionDescriptor.Id; + Debug.Assert(ids.Add(id), $"duplicate section ID: {id}"); + } + + foreach (var propertyBuilderOutput in _propertyBuilderOutputs) + { + Debug.Assert(settingsSectionSetups.Any(x => x.Id == propertyBuilderOutput.SectionId), $"section not registered: {propertyBuilderOutput.SectionId} (for setting {propertyBuilderOutput.DisplayName})"); + } + } } private void CoreSet(T value, bool notify) where T : class, ISettings, new() diff --git a/src/NexusMods.SingleProcess/CliSettings.cs b/src/NexusMods.SingleProcess/CliSettings.cs index cea626de7b..fecc9890d1 100644 --- a/src/NexusMods.SingleProcess/CliSettings.cs +++ b/src/NexusMods.SingleProcess/CliSettings.cs @@ -12,7 +12,7 @@ public class CliSettings() : ISettings /// public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder) { - var sectionId = SectionId.DefaultValue; + var sectionId = Sections.DeveloperTools; return settingsBuilder .ConfigureDefault(CreateDefault) .ConfigureStorageBackend(builder => builder.UseJson()) diff --git a/src/NexusMods.StandardGameLocators/GameLocatorSettings.cs b/src/NexusMods.StandardGameLocators/GameLocatorSettings.cs index 8b4891be8b..2bf42fe476 100644 --- a/src/NexusMods.StandardGameLocators/GameLocatorSettings.cs +++ b/src/NexusMods.StandardGameLocators/GameLocatorSettings.cs @@ -10,16 +10,13 @@ public record GameLocatorSettings : ISettings public static ISettingsBuilder Configure(ISettingsBuilder settingsBuilder) { - // TODO: put in some section - var sectionId = SectionId.DefaultValue; - return settingsBuilder .ConfigureStorageBackend(builder => builder.UseJson()) .AddToUI(builder => builder .AddPropertyToUI(x => x.EnableXboxGamePass, propertyBuilder => propertyBuilder - .AddToSection(sectionId) - .WithDisplayName("(Experimental) Enable Xbox Game Pass support") - .WithDescription("For testing the Xbox Game Pass detection") + .AddToSection(Sections.Experimental) + .WithDisplayName("Enable Xbox Game Pass support") + .WithDescription("Allows you to manage games installed with Xbox Game Pass.") .UseBooleanContainer() ) );