Merge branch 'main' into user-pfp-discord-rpc

This commit is contained in:
pizzaboxer 2024-09-28 00:33:07 +01:00 committed by GitHub
commit 5d79a95250
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 434 additions and 200 deletions

View File

@ -66,6 +66,15 @@ namespace Bloxstrap
Environment.Exit(exitCodeNum); Environment.Exit(exitCodeNum);
} }
public static void SoftTerminate(ErrorCode exitCode = ErrorCode.ERROR_SUCCESS)
{
int exitCodeNum = (int)exitCode;
Logger.WriteLine("App::SoftTerminate", $"Terminating with exit code {exitCodeNum} ({exitCode})");
Current.Dispatcher.Invoke(() => Current.Shutdown(exitCodeNum));
}
void GlobalExceptionHandler(object sender, DispatcherUnhandledExceptionEventArgs e) void GlobalExceptionHandler(object sender, DispatcherUnhandledExceptionEventArgs e)
{ {
e.Handled = true; e.Handled = true;

View File

@ -34,7 +34,8 @@
{ "ApplicationConfig.zip", @"ApplicationConfig\" }, { "ApplicationConfig.zip", @"ApplicationConfig\" },
{ "Plugins.zip", @"Plugins\" }, { "Plugins.zip", @"Plugins\" },
{ "Qml.zip", @"Qml\" }, { "Qml.zip", @"Qml\" },
{ "StudioFonts.zip", @"StudioFonts\" } { "StudioFonts.zip", @"StudioFonts\" },
{ "RibbonConfig.zip", @"RibbonConfig\" }
}; };
} }
} }

View File

@ -27,6 +27,14 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Resources\Icon2008.ico" />
<EmbeddedResource Include="Resources\Icon2011.ico" />
<EmbeddedResource Include="Resources\Icon2017.ico" />
<EmbeddedResource Include="Resources\Icon2019.ico" />
<EmbeddedResource Include="Resources\Icon2022.ico" />
<EmbeddedResource Include="Resources\IconBloxstrap.ico" />
<EmbeddedResource Include="Resources\IconEarly2015.ico" />
<EmbeddedResource Include="Resources\IconLate2015.ico" />
<EmbeddedResource Include="Resources\Mods\Cursor\From2006\ArrowCursor.png" /> <EmbeddedResource Include="Resources\Mods\Cursor\From2006\ArrowCursor.png" />
<EmbeddedResource Include="Resources\Mods\Cursor\From2006\ArrowFarCursor.png" /> <EmbeddedResource Include="Resources\Mods\Cursor\From2006\ArrowFarCursor.png" />
<EmbeddedResource Include="Resources\Mods\Cursor\From2013\ArrowCursor.png" /> <EmbeddedResource Include="Resources\Mods\Cursor\From2013\ArrowCursor.png" />

View File

@ -252,7 +252,7 @@ namespace Bloxstrap
clientVersion = await RobloxDeployment.GetInfo(channel, AppData.BinaryType); clientVersion = await RobloxDeployment.GetInfo(channel, AppData.BinaryType);
} }
key.SetValue("www.roblox.com", channel); key.SetValueSafe("www.roblox.com", channel);
_latestVersionGuid = clientVersion.VersionGuid; _latestVersionGuid = clientVersion.VersionGuid;
@ -724,7 +724,7 @@ namespace Bloxstrap
using (var uninstallKey = Registry.CurrentUser.CreateSubKey(App.UninstallKey)) using (var uninstallKey = Registry.CurrentUser.CreateSubKey(App.UninstallKey))
{ {
uninstallKey.SetValue("EstimatedSize", totalSize); uninstallKey.SetValueSafe("EstimatedSize", totalSize);
} }
App.Logger.WriteLine(LOG_IDENT, $"Registered as {totalSize} KB"); App.Logger.WriteLine(LOG_IDENT, $"Registered as {totalSize} KB");
@ -950,6 +950,9 @@ namespace Bloxstrap
const int maxTries = 5; const int maxTries = 5;
bool statIsRetrying = false;
bool statIsHttp = false;
App.Logger.WriteLine(LOG_IDENT, "Downloading..."); App.Logger.WriteLine(LOG_IDENT, "Downloading...");
var buffer = new byte[4096]; var buffer = new byte[4096];
@ -1002,8 +1005,12 @@ namespace Bloxstrap
App.Logger.WriteLine(LOG_IDENT, $"An exception occurred after downloading {totalBytesRead} bytes. ({i}/{maxTries})"); App.Logger.WriteLine(LOG_IDENT, $"An exception occurred after downloading {totalBytesRead} bytes. ({i}/{maxTries})");
App.Logger.WriteException(LOG_IDENT, ex); App.Logger.WriteException(LOG_IDENT, ex);
statIsRetrying = true;
if (ex.GetType() == typeof(ChecksumFailedException)) if (ex.GetType() == typeof(ChecksumFailedException))
{ {
_ = App.HttpClient.GetAsync($"http://bloxstraplabs.com/metrics/post?key=packageDownloadState&value=httpFail");
Frontend.ShowConnectivityDialog( Frontend.ShowConnectivityDialog(
Strings.Dialog_Connectivity_UnableToDownload, Strings.Dialog_Connectivity_UnableToDownload,
String.Format(Strings.Dialog_Connectivity_UnableToDownloadReason, "[https://github.com/pizzaboxer/bloxstrap/wiki/Bloxstrap-is-unable-to-download-Roblox](https://github.com/pizzaboxer/bloxstrap/wiki/Bloxstrap-is-unable-to-download-Roblox)"), String.Format(Strings.Dialog_Connectivity_UnableToDownloadReason, "[https://github.com/pizzaboxer/bloxstrap/wiki/Bloxstrap-is-unable-to-download-Roblox](https://github.com/pizzaboxer/bloxstrap/wiki/Bloxstrap-is-unable-to-download-Roblox)"),
@ -1029,9 +1036,16 @@ namespace Bloxstrap
{ {
App.Logger.WriteLine(LOG_IDENT, "Retrying download over HTTP..."); App.Logger.WriteLine(LOG_IDENT, "Retrying download over HTTP...");
packageUrl = packageUrl.Replace("https://", "http://"); packageUrl = packageUrl.Replace("https://", "http://");
statIsHttp = true;
} }
} }
} }
if (statIsRetrying)
{
string stat = statIsHttp ? "httpSuccess" : "retrySuccess";
_ = App.HttpClient.GetAsync($"http://bloxstraplabs.com/metrics/post?key=packageDownloadState&value={stat}");
}
} }
private void ExtractPackage(Package package, List<string>? files = null) private void ExtractPackage(Package package, List<string>? files = null)

View File

@ -0,0 +1,21 @@
using Microsoft.Win32;
namespace Bloxstrap.Extensions
{
public static class RegistryKeyEx
{
public static void SetValueSafe(this RegistryKey registryKey, string? name, object value)
{
try
{
App.Logger.WriteLine("RegistryKeyEx::SetValueSafe", $"Writing '{value}' to {registryKey}\\{name}");
registryKey.SetValue(name, value);
}
catch (UnauthorizedAccessException)
{
Frontend.ShowMessageBox(Strings.Dialog_RegistryWriteError, System.Windows.MessageBoxImage.Error);
App.Terminate(ErrorCode.ERROR_INSTALL_FAILURE);
}
}
}
}

View File

@ -53,23 +53,23 @@ namespace Bloxstrap
// TODO: registry access checks, i'll need to look back on issues to see what the error looks like // TODO: registry access checks, i'll need to look back on issues to see what the error looks like
using (var uninstallKey = Registry.CurrentUser.CreateSubKey(App.UninstallKey)) using (var uninstallKey = Registry.CurrentUser.CreateSubKey(App.UninstallKey))
{ {
uninstallKey.SetValue("DisplayIcon", $"{Paths.Application},0"); uninstallKey.SetValueSafe("DisplayIcon", $"{Paths.Application},0");
uninstallKey.SetValue("DisplayName", App.ProjectName); uninstallKey.SetValueSafe("DisplayName", App.ProjectName);
uninstallKey.SetValue("DisplayVersion", App.Version); uninstallKey.SetValueSafe("DisplayVersion", App.Version);
if (uninstallKey.GetValue("InstallDate") is null) if (uninstallKey.GetValue("InstallDate") is null)
uninstallKey.SetValue("InstallDate", DateTime.Now.ToString("yyyyMMdd")); uninstallKey.SetValueSafe("InstallDate", DateTime.Now.ToString("yyyyMMdd"));
uninstallKey.SetValue("InstallLocation", Paths.Base); uninstallKey.SetValueSafe("InstallLocation", Paths.Base);
uninstallKey.SetValue("NoRepair", 1); uninstallKey.SetValueSafe("NoRepair", 1);
uninstallKey.SetValue("Publisher", App.ProjectOwner); uninstallKey.SetValueSafe("Publisher", App.ProjectOwner);
uninstallKey.SetValue("ModifyPath", $"\"{Paths.Application}\" -settings"); uninstallKey.SetValueSafe("ModifyPath", $"\"{Paths.Application}\" -settings");
uninstallKey.SetValue("QuietUninstallString", $"\"{Paths.Application}\" -uninstall -quiet"); uninstallKey.SetValueSafe("QuietUninstallString", $"\"{Paths.Application}\" -uninstall -quiet");
uninstallKey.SetValue("UninstallString", $"\"{Paths.Application}\" -uninstall"); uninstallKey.SetValueSafe("UninstallString", $"\"{Paths.Application}\" -uninstall");
uninstallKey.SetValue("HelpLink", App.ProjectHelpLink); uninstallKey.SetValueSafe("HelpLink", App.ProjectHelpLink);
uninstallKey.SetValue("URLInfoAbout", App.ProjectSupportLink); uninstallKey.SetValueSafe("URLInfoAbout", App.ProjectSupportLink);
uninstallKey.SetValue("URLUpdateInfo", App.ProjectDownloadLink); uninstallKey.SetValueSafe("URLUpdateInfo", App.ProjectDownloadLink);
} }
// only register player, for the scenario where the user installs bloxstrap, closes it, // only register player, for the scenario where the user installs bloxstrap, closes it,
@ -426,12 +426,12 @@ namespace Bloxstrap
using (var uninstallKey = Registry.CurrentUser.CreateSubKey(App.UninstallKey)) using (var uninstallKey = Registry.CurrentUser.CreateSubKey(App.UninstallKey))
{ {
uninstallKey.SetValue("DisplayVersion", App.Version); uninstallKey.SetValueSafe("DisplayVersion", App.Version);
uninstallKey.SetValue("Publisher", App.ProjectOwner); uninstallKey.SetValueSafe("Publisher", App.ProjectOwner);
uninstallKey.SetValue("HelpLink", App.ProjectHelpLink); uninstallKey.SetValueSafe("HelpLink", App.ProjectHelpLink);
uninstallKey.SetValue("URLInfoAbout", App.ProjectSupportLink); uninstallKey.SetValueSafe("URLInfoAbout", App.ProjectSupportLink);
uninstallKey.SetValue("URLUpdateInfo", App.ProjectDownloadLink); uninstallKey.SetValueSafe("URLUpdateInfo", App.ProjectDownloadLink);
} }
// update migrations // update migrations
@ -459,14 +459,7 @@ namespace Bloxstrap
string configLocation = Path.Combine(Paths.Modifications, "ReShade.ini"); string configLocation = Path.Combine(Paths.Modifications, "ReShade.ini");
if (File.Exists(injectorLocation)) if (File.Exists(injectorLocation))
{
Frontend.ShowMessageBox(
Strings.Bootstrapper_HyperionUpdateInfo,
MessageBoxImage.Warning
);
File.Delete(injectorLocation); File.Delete(injectorLocation);
}
if (File.Exists(configLocation)) if (File.Exists(configLocation))
File.Delete(configLocation); File.Delete(configLocation);

View File

@ -22,6 +22,8 @@ namespace Bloxstrap
public LaunchFlag NoLaunchFlag { get; } = new("nolaunch"); public LaunchFlag NoLaunchFlag { get; } = new("nolaunch");
public LaunchFlag NoGPUFlag { get; } = new("nogpu");
public LaunchFlag UpgradeFlag { get; } = new("upgrade"); public LaunchFlag UpgradeFlag { get; } = new("upgrade");
public LaunchFlag PlayerFlag { get; } = new("player"); public LaunchFlag PlayerFlag { get; } = new("player");

View File

@ -2,10 +2,10 @@
{ {
public class SupporterData public class SupporterData
{ {
[JsonPropertyName("columns")] [JsonPropertyName("monthly")]
public int Columns { get; set; } public SupporterGroup Monthly { get; set; } = new();
[JsonPropertyName("supporters")] [JsonPropertyName("oneoff")]
public List<Supporter> Supporters { get; set; } = null!; public SupporterGroup OneOff { get; set; } = new();
} }
} }

View File

@ -0,0 +1,11 @@
namespace Bloxstrap.Models.APIs.Config
{
public class SupporterGroup
{
[JsonPropertyName("columns")]
public int Columns { get; set; } = 0;
[JsonPropertyName("supporters")]
public List<Supporter> Supporters { get; set; } = Enumerable.Empty<Supporter>().ToList();
}
}

View File

@ -15,6 +15,7 @@ namespace Bloxstrap.Models.Persistable
public string Locale { get; set; } = "nil"; public string Locale { get; set; } = "nil";
public bool ForceRobloxLanguage { get; set; } = false; public bool ForceRobloxLanguage { get; set; } = false;
public bool UseFastFlagManager { get; set; } = true; public bool UseFastFlagManager { get; set; } = true;
public bool WPFSoftwareRender { get; set; } = false;
// integration configuration // integration configuration
public bool EnableActivityTracking { get; set; } = true; public bool EnableActivityTracking { get; set; } = true;

View File

@ -12,7 +12,9 @@ namespace Bloxstrap.Models.SettingTasks.Base
public abstract bool Changed { get; } public abstract bool Changed { get; }
public BaseTask(string prefix, string name) => Name = $"{prefix}.{name}"; public BaseTask(string prefix, string name) : this($"{prefix}.{name}") { }
public BaseTask(string name) => Name = name;
public override string ToString() => Name; public override string ToString() => Name;

View File

@ -41,5 +41,7 @@ namespace Bloxstrap.Models.SettingTasks.Base
public override bool Changed => _newState != OriginalState; public override bool Changed => _newState != OriginalState;
public BoolBaseTask(string prefix, string name) : base(prefix, name) { } public BoolBaseTask(string prefix, string name) : base(prefix, name) { }
public BoolBaseTask(string name) : base(name) { }
} }
} }

View File

@ -0,0 +1,42 @@
using System.Reflection;
using System.Windows.Markup;
namespace Bloxstrap.Models.SettingTasks
{
public class ExtractIconsTask : BoolBaseTask
{
public ExtractIconsTask() : base("ExtractIcons")
{
OriginalState = Directory.Exists(Paths.Icons);
}
public override void Execute()
{
if (NewState)
{
Directory.CreateDirectory(Paths.Icons);
var assembly = Assembly.GetExecutingAssembly();
var resourceNames = assembly.GetManifestResourceNames().Where(x => x.EndsWith(".ico"));
foreach (string name in resourceNames)
{
string path = Path.Combine(Paths.Icons, name.Replace("Bloxstrap.Resources.", ""));
var stream = assembly.GetManifestResourceStream(name)!;
using var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
Filesystem.AssertReadOnly(path);
File.WriteAllBytes(path, memoryStream.ToArray());
}
}
else if (Directory.Exists(Paths.Icons))
{
Directory.Delete(Paths.Icons, true);
}
OriginalState = NewState;
}
}
}

View File

@ -42,7 +42,7 @@ namespace Bloxstrap.Models.SettingTasks
using var resourceStream = data.ResourceStream; using var resourceStream = data.ResourceStream;
using var memoryStream = new MemoryStream(); using var memoryStream = new MemoryStream();
data.ResourceStream.CopyTo(memoryStream); resourceStream.CopyTo(memoryStream);
Filesystem.AssertReadOnly(data.FullFilePath); Filesystem.AssertReadOnly(data.FullFilePath);
File.WriteAllBytes(data.FullFilePath, memoryStream.ToArray()); File.WriteAllBytes(data.FullFilePath, memoryStream.ToArray());

View File

@ -1,6 +1,4 @@
using Bloxstrap.Models.SettingTasks.Base; namespace Bloxstrap.Models.SettingTasks
namespace Bloxstrap.Models.SettingTasks
{ {
public class ShortcutTask : BoolBaseTask public class ShortcutTask : BoolBaseTask
{ {

View File

@ -22,6 +22,7 @@
public static string Integrations { get; private set; } = ""; public static string Integrations { get; private set; } = "";
public static string Modifications { get; private set; } = ""; public static string Modifications { get; private set; } = "";
public static string Roblox { get; private set; } = ""; public static string Roblox { get; private set; } = "";
public static string Icons { get; private set; } = "";
public static string Application { get; private set; } = ""; public static string Application { get; private set; } = "";
@ -37,6 +38,7 @@
Integrations = Path.Combine(Base, "Integrations"); Integrations = Path.Combine(Base, "Integrations");
Modifications = Path.Combine(Base, "Modifications"); Modifications = Path.Combine(Base, "Modifications");
Roblox = Path.Combine(Base, "Roblox"); Roblox = Path.Combine(Base, "Roblox");
Icons = Path.Combine(Base, "Icons");
Application = Path.Combine(Base, $"{App.ProjectName}.exe"); Application = Path.Combine(Base, $"{App.ProjectName}.exe");
} }

View File

@ -78,6 +78,24 @@ namespace Bloxstrap.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Monthly.
/// </summary>
public static string About_Supporters_Monthly {
get {
return ResourceManager.GetString("About.Supporters.Monthly", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to One-off.
/// </summary>
public static string About_Supporters_OneOff {
get {
return ResourceManager.GetString("About.Supporters.OneOff", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Supporters. /// Looks up a localized string similar to Supporters.
/// </summary> /// </summary>
@ -159,17 +177,6 @@ namespace Bloxstrap.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Roblox has now finished rolling out the new game client update, featuring 64-bit support and the Hyperion anticheat. ReShade does not work with this update, and so it has now been disabled and removed from Bloxstrap.
///
///Your ReShade configuration files will still be saved, and you can locate them by opening the folder where Bloxstrap is installed to, and navigating to the Integrations folder. You can choose to delete these if you want..
/// </summary>
public static string Bootstrapper_HyperionUpdateInfo {
get {
return ResourceManager.GetString("Bootstrapper.HyperionUpdateInfo", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Failed to save {0}: {1}. /// Looks up a localized string similar to Failed to save {0}: {1}.
/// </summary> /// </summary>
@ -1032,6 +1039,15 @@ namespace Bloxstrap.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Bloxstrap is unable to write to the Windows Registry. An antivirus is likely interfering and causing issues. Please check to make sure there isn&apos;t anything that would restrict Bloxstrap&apos;s operation..
/// </summary>
public static string Dialog_RegistryWriteError {
get {
return ResourceManager.GetString("Dialog.RegistryWriteError", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Early 2015. /// Looks up a localized string similar to Early 2015.
/// </summary> /// </summary>
@ -3117,6 +3133,24 @@ namespace Bloxstrap.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to To use for your shortcuts, right-click it, open properties, change icon, browse, and pick from the Icons folder..
/// </summary>
public static string Menu_Shortcuts_ExtractIcons_Description {
get {
return ResourceManager.GetString("Menu.Shortcuts.ExtractIcons.Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Extract Roblox icons to folder.
/// </summary>
public static string Menu_Shortcuts_ExtractIcons_Title {
get {
return ResourceManager.GetString("Menu.Shortcuts.ExtractIcons.Title", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Create shortcuts for quick access to specific functions. These will all be placed on the Desktop.. /// Looks up a localized string similar to Create shortcuts for quick access to specific functions. These will all be placed on the Desktop..
/// </summary> /// </summary>

View File

@ -138,11 +138,6 @@
<data name="Bootstrapper.FirstRunUninstall" xml:space="preserve"> <data name="Bootstrapper.FirstRunUninstall" xml:space="preserve">
<value>You must first install Bloxstrap before uninstalling.</value> <value>You must first install Bloxstrap before uninstalling.</value>
</data> </data>
<data name="Bootstrapper.HyperionUpdateInfo" xml:space="preserve">
<value>Roblox has now finished rolling out the new game client update, featuring 64-bit support and the Hyperion anticheat. ReShade does not work with this update, and so it has now been disabled and removed from Bloxstrap.
Your ReShade configuration files will still be saved, and you can locate them by opening the folder where Bloxstrap is installed to, and navigating to the Integrations folder. You can choose to delete these if you want.</value>
</data>
<data name="Bootstrapper.NotEnoughSpace" xml:space="preserve"> <data name="Bootstrapper.NotEnoughSpace" xml:space="preserve">
<value>Bloxstrap does not have enough disk space to download and install Roblox. Please free up some disk space and try again.</value> <value>Bloxstrap does not have enough disk space to download and install Roblox. Please free up some disk space and try again.</value>
</data> </data>
@ -1209,4 +1204,19 @@ Please manually delete Bloxstrap.exe from the install location or try restarting
{0}</value> {0}</value>
</data> </data>
<data name="Menu.Shortcuts.ExtractIcons.Title" xml:space="preserve">
<value>Extract Roblox icons to folder</value>
</data>
<data name="Menu.Shortcuts.ExtractIcons.Description" xml:space="preserve">
<value>To use for your shortcuts, right-click it, open properties, change icon, browse, and pick from the Icons folder.</value>
</data>
<data name="Dialog.RegistryWriteError" xml:space="preserve">
<value>Bloxstrap is unable to write to the Windows Registry. An antivirus is likely interfering and causing issues. Please check to make sure there isn't anything that would restrict Bloxstrap's operation.</value>
</data>
<data name="About.Supporters.Monthly" xml:space="preserve">
<value>Monthly</value>
</data>
<data name="About.Supporters.OneOff" xml:space="preserve">
<value>One-off</value>
</data>
</root> </root>

View File

@ -37,6 +37,7 @@
<ui:NavigationStore x:Name="RootNavigation" Grid.Row="1" Grid.Column="0" Margin="0,0,12,0" Frame="{Binding ElementName=RootFrame}" SelectedPageIndex="0"> <ui:NavigationStore x:Name="RootNavigation" Grid.Row="1" Grid.Column="0" Margin="0,0,12,0" Frame="{Binding ElementName=RootFrame}" SelectedPageIndex="0">
<ui:NavigationStore.Items> <ui:NavigationStore.Items>
<ui:NavigationItem Content="{x:Static resources:Strings.Menu_About_Title}" PageType="{x:Type pages:AboutPage}" Icon="QuestionCircle48" Tag="about" Margin="0,0,0,12" /> <ui:NavigationItem Content="{x:Static resources:Strings.Menu_About_Title}" PageType="{x:Type pages:AboutPage}" Icon="QuestionCircle48" Tag="about" Margin="0,0,0,12" />
<ui:NavigationItem Content="{x:Static resources:Strings.About_Supporters_Title}" PageType="{x:Type pages:SupportersPage}" Icon="Heart24" Tag="translators" Margin="0,0,0,12" />
<ui:NavigationItem Content="{x:Static resources:Strings.About_Translators_Title}" PageType="{x:Type pages:TranslatorsPage}" Icon="Translate24" Tag="translators" Margin="0,0,0,12" /> <ui:NavigationItem Content="{x:Static resources:Strings.About_Translators_Title}" PageType="{x:Type pages:TranslatorsPage}" Icon="Translate24" Tag="translators" Margin="0,0,0,12" />
<ui:NavigationItem Content="{x:Static resources:Strings.About_Licenses_Title}" PageType="{x:Type pages:LicensesPage}" Icon="Code24" Tag="licenses" Margin="0,0,0,12" /> <ui:NavigationItem Content="{x:Static resources:Strings.About_Licenses_Title}" PageType="{x:Type pages:LicensesPage}" Icon="Code24" Tag="licenses" Margin="0,0,0,12" />
</ui:NavigationStore.Items> </ui:NavigationStore.Items>

View File

@ -3,7 +3,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:enums="clr-namespace:Bloxstrap.Enums"
xmlns:models="clr-namespace:Bloxstrap.UI.ViewModels" xmlns:models="clr-namespace:Bloxstrap.UI.ViewModels"
xmlns:dmodels="clr-namespace:Bloxstrap.UI.ViewModels.About" xmlns:dmodels="clr-namespace:Bloxstrap.UI.ViewModels.About"
xmlns:controls="clr-namespace:Bloxstrap.UI.Elements.Controls" xmlns:controls="clr-namespace:Bloxstrap.UI.Elements.Controls"
@ -81,85 +80,6 @@
</Grid> </Grid>
</StackPanel> </StackPanel>
<TextBlock Text="{x:Static resources:Strings.About_Supporters_Title}" FontWeight="Medium" FontSize="20" Margin="0,16,0,0" />
<controls:MarkdownTextBlock MarkdownText="{Binding Source={x:Static resources:Strings.About_Supporters_Description}, Converter={StaticResource StringFormatConverter}, ConverterParameter='https://ko-fi.com/boxerpizza'}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
<Grid Margin="0,8,0,0">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding SupportersLoadedState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Unknown}">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</Grid.Style>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ui:ProgressRing Grid.Column="0" IsIndeterminate="True" />
<TextBlock Grid.Column="1" Margin="16,0,0,0" Text="{x:Static resources:Strings.Common_Loading}" VerticalAlignment="Center" />
</Grid>
<Grid Margin="0,8,0,0">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding SupportersLoadedState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Failed}">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</Grid.Style>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="pack://application:,,,/Resources/MessageBox/Error.png" Width="60" Height="60" RenderOptions.BitmapScalingMode="HighQuality" />
<StackPanel Grid.Column="1" Margin="16,0,0,0" VerticalAlignment="Center">
<TextBlock Text="{x:Static resources:Strings.Common_NetworkError}" />
<TextBlock Text="{Binding SupportersLoadError, Mode=OneWay}" />
</StackPanel>
</Grid>
<ListView ItemsSource="{Binding Supporters, Mode=OneWay}" Margin="0,8,0,0" ScrollViewer.CanContentScroll="False" IsEnabled="False">
<ListView.Style>
<Style TargetType="ListView" BasedOn="{StaticResource {x:Type ListView}}">
<Style.Triggers>
<DataTrigger Binding="{Binding SupportersLoadedState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Successful}">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</ListView.Style>
<ListView.ItemTemplate>
<DataTemplate>
<ui:Card Padding="8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Ellipse Grid.Column="0" Height="32" Width="32" VerticalAlignment="Center">
<Ellipse.Fill>
<ImageBrush ImageSource="{Binding Image, IsAsync=True}" />
</Ellipse.Fill>
</Ellipse>
<TextBlock Grid.Column="1" Margin="8,0,2,0" VerticalAlignment="Center" Text="{Binding Name}" />
</Grid>
</ui:Card>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding SupporterColumns}" Margin="-4" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
<TextBlock Text="{x:Static resources:Strings.Menu_About_Contributors}" FontWeight="Medium" FontSize="20" Margin="0,16,0,0" /> <TextBlock Text="{x:Static resources:Strings.Menu_About_Contributors}" FontWeight="Medium" FontSize="20" Margin="0,16,0,0" />
<TextBlock Text="{x:Static resources:Strings.Menu_About_Contributors_Description}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorTertiaryBrush}" /> <TextBlock Text="{x:Static resources:Strings.Menu_About_Contributors_Description}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
<Grid Margin="0,8,0,0"> <Grid Margin="0,8,0,0">

View File

@ -0,0 +1,131 @@
<ui:UiPage x:Class="Bloxstrap.UI.Elements.About.Pages.SupportersPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:models="clr-namespace:Bloxstrap.UI.ViewModels"
xmlns:controls="clr-namespace:Bloxstrap.UI.Elements.Controls"
xmlns:enums="clr-namespace:Bloxstrap.Enums"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:resources="clr-namespace:Bloxstrap.Resources"
mc:Ignorable="d"
d:DesignHeight="1500" d:DesignWidth="800"
Title="AboutPage"
Scrollable="True">
<StackPanel Margin="0,0,14,14">
<TextBlock Text="{x:Static resources:Strings.About_Supporters_Title}" FontWeight="Medium" FontSize="24" />
<controls:MarkdownTextBlock MarkdownText="{Binding Source={x:Static resources:Strings.About_Supporters_Description}, Converter={StaticResource StringFormatConverter}, ConverterParameter='https://ko-fi.com/boxerpizza'}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
<Grid Margin="0,8,0,0">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding LoadedState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Unknown}">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</Grid.Style>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ui:ProgressRing Grid.Column="0" IsIndeterminate="True" />
<TextBlock Grid.Column="1" Margin="16,0,0,0" Text="{x:Static resources:Strings.Common_Loading}" VerticalAlignment="Center" />
</Grid>
<Grid Margin="0,8,0,0">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding LoadedState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Failed}">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</Grid.Style>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="pack://application:,,,/Resources/MessageBox/Error.png" Width="60" Height="60" RenderOptions.BitmapScalingMode="HighQuality" />
<StackPanel Grid.Column="1" Margin="16,0,0,0" VerticalAlignment="Center">
<TextBlock Text="{x:Static resources:Strings.Common_NetworkError}" />
<TextBlock Text="{Binding LoadError, Mode=OneWay}" />
</StackPanel>
</Grid>
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding LoadedState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Successful}">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</StackPanel.Style>
<TextBlock Text="{x:Static resources:Strings.About_Supporters_Monthly}" FontWeight="Medium" FontSize="20" Margin="0,16,0,0" />
<ListView ItemsSource="{Binding SupporterData.Monthly.Supporters, Mode=OneWay}" Margin="0,8,0,0" ScrollViewer.CanContentScroll="False" IsEnabled="False">
<ListView.ItemTemplate>
<DataTemplate>
<ui:Card Padding="8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Ellipse Grid.Column="0" Height="32" Width="32" VerticalAlignment="Center">
<Ellipse.Fill>
<ImageBrush ImageSource="{Binding Image, IsAsync=True}" />
</Ellipse.Fill>
</Ellipse>
<TextBlock Grid.Column="1" Margin="8,0,2,0" VerticalAlignment="Center" Text="{Binding Name}" />
</Grid>
</ui:Card>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding SupporterData.Monthly.Columns}" Margin="-4" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
<TextBlock Text="{x:Static resources:Strings.About_Supporters_OneOff}" FontWeight="Medium" FontSize="20" Margin="0,16,0,0" />
<ListView ItemsSource="{Binding SupporterData.OneOff.Supporters, Mode=OneWay}" Margin="0,8,0,0" ScrollViewer.CanContentScroll="False" IsEnabled="False">
<ListView.ItemTemplate>
<DataTemplate>
<ui:Card Padding="8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Ellipse Grid.Column="0" Height="32" Width="32" VerticalAlignment="Center">
<Ellipse.Fill>
<ImageBrush ImageSource="{Binding Image, IsAsync=True}" />
</Ellipse.Fill>
</Ellipse>
<TextBlock Grid.Column="1" Margin="8,0,2,0" VerticalAlignment="Center" Text="{Binding Name}" />
</Grid>
</ui:Card>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding SupporterData.OneOff.Columns}" Margin="-4" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</StackPanel>
</StackPanel>
</ui:UiPage>

View File

@ -0,0 +1,16 @@
using Bloxstrap.UI.ViewModels.About;
namespace Bloxstrap.UI.Elements.About.Pages
{
/// <summary>
/// Interaction logic for SupportersPage.xaml
/// </summary>
public partial class SupportersPage
{
public SupportersPage()
{
DataContext = new SupportersViewModel();
InitializeComponent();
}
}
}

View File

@ -1,9 +1,5 @@
using System; using System.Windows;
using System.Collections.Generic; using System.Windows.Interop;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Wpf.Ui.Appearance; using Wpf.Ui.Appearance;
using Wpf.Ui.Controls; using Wpf.Ui.Controls;
using Wpf.Ui.Mvvm.Contracts; using Wpf.Ui.Mvvm.Contracts;
@ -25,5 +21,16 @@ namespace Bloxstrap.UI.Elements.Base
_themeService.SetTheme(App.Settings.Prop.Theme.GetFinal() == Enums.Theme.Dark ? ThemeType.Dark : ThemeType.Light); _themeService.SetTheme(App.Settings.Prop.Theme.GetFinal() == Enums.Theme.Dark ? ThemeType.Dark : ThemeType.Light);
_themeService.SetSystemAccent(); _themeService.SetSystemAccent();
} }
protected override void OnSourceInitialized(EventArgs e)
{
if (App.Settings.Prop.WPFSoftwareRender || App.LaunchSettings.NoGPUFlag.Active)
{
if (PresentationSource.FromVisual(this) is HwndSource hwndSource)
hwndSource.CompositionTarget.RenderMode = RenderMode.SoftwareOnly;
}
base.OnSourceInitialized(e);
}
} }
} }

View File

@ -102,7 +102,7 @@ namespace Bloxstrap.UI.Elements.Settings
App.State.Save(); App.State.Save();
if (!e.Cancel) if (!e.Cancel)
App.Terminate(); App.SoftTerminate();
} }
} }
} }

View File

@ -17,6 +17,13 @@
<StackPanel Margin="0,0,14,14"> <StackPanel Margin="0,0,14,14">
<TextBlock Margin="0,0,0,16" Text="{x:Static resources:Strings.Menu_Shortcuts_Description}" FontSize="14" Foreground="{DynamicResource TextFillColorSecondaryBrush}" /> <TextBlock Margin="0,0,0,16" Text="{x:Static resources:Strings.Menu_Shortcuts_Description}" FontSize="14" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
<controls:OptionControl
Header="{x:Static resources:Strings.Menu_Shortcuts_ExtractIcons_Title}"
Description="{x:Static resources:Strings.Menu_Shortcuts_ExtractIcons_Description}"
Margin="0,0,0,16">
<ui:ToggleSwitch IsChecked="{Binding ExtractIconsTask.NewState, Mode=TwoWay}" />
</controls:OptionControl>
<TextBlock Text="{x:Static resources:Strings.Menu_Shortcuts_General_Title}" FontSize="20" FontWeight="Medium" /> <TextBlock Text="{x:Static resources:Strings.Menu_Shortcuts_General_Title}" FontSize="20" FontWeight="Medium" />
<TextBlock Text="{x:Static resources:Strings.Menu_Shortcuts_General_Description}" Foreground="{DynamicResource TextFillColorSecondaryBrush}" /> <TextBlock Text="{x:Static resources:Strings.Menu_Shortcuts_General_Description}" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />

View File

@ -4,8 +4,6 @@ namespace Bloxstrap.UI.ViewModels.About
{ {
public class AboutViewModel : NotifyPropertyChangedViewModel public class AboutViewModel : NotifyPropertyChangedViewModel
{ {
private SupporterData? _supporterData;
public string Version => string.Format(Strings.Menu_About_Version, App.Version); public string Version => string.Format(Strings.Menu_About_Version, App.Version);
public BuildMetadataAttribute BuildMetadata => App.BuildMetadata; public BuildMetadataAttribute BuildMetadata => App.BuildMetadata;
@ -15,49 +13,5 @@ namespace Bloxstrap.UI.ViewModels.About
public Visibility BuildInformationVisibility => App.IsProductionBuild ? Visibility.Collapsed : Visibility.Visible; public Visibility BuildInformationVisibility => App.IsProductionBuild ? Visibility.Collapsed : Visibility.Visible;
public Visibility BuildCommitVisibility => App.IsActionBuild ? Visibility.Visible : Visibility.Collapsed; public Visibility BuildCommitVisibility => App.IsActionBuild ? Visibility.Visible : Visibility.Collapsed;
public List<Supporter> Supporters => _supporterData?.Supporters ?? Enumerable.Empty<Supporter>().ToList();
public int SupporterColumns => _supporterData?.Columns ?? 0;
public GenericTriState SupportersLoadedState { get; set; } = GenericTriState.Unknown;
public string SupportersLoadError { get; set; } = "";
public AboutViewModel()
{
// this will cause momentary freezes only when ran under the debugger
LoadSupporterData();
}
public async void LoadSupporterData()
{
const string LOG_IDENT = "AboutViewModel::LoadSupporterData";
try
{
_supporterData = await Http.GetJson<SupporterData>("https://raw.githubusercontent.com/bloxstraplabs/config/main/supporters.json");
}
catch (Exception ex)
{
App.Logger.WriteLine(LOG_IDENT, "Could not load supporter data");
App.Logger.WriteException(LOG_IDENT, ex);
SupportersLoadedState = GenericTriState.Failed;
SupportersLoadError = ex.Message;
OnPropertyChanged(nameof(SupportersLoadError));
}
if (_supporterData is not null)
{
SupportersLoadedState = GenericTriState.Successful;
OnPropertyChanged(nameof(Supporters));
OnPropertyChanged(nameof(SupporterColumns));
}
OnPropertyChanged(nameof(SupportersLoadedState));
}
} }
} }

View File

@ -0,0 +1,46 @@
namespace Bloxstrap.UI.ViewModels.About
{
public class SupportersViewModel : NotifyPropertyChangedViewModel
{
public SupporterData? SupporterData { get; private set; }
public GenericTriState LoadedState { get; set; } = GenericTriState.Unknown;
public string LoadError { get; set; } = "";
public SupportersViewModel()
{
// this will cause momentary freezes only when ran under the debugger
LoadSupporterData();
}
public async void LoadSupporterData()
{
const string LOG_IDENT = "AboutViewModel::LoadSupporterData";
try
{
SupporterData = await Http.GetJson<SupporterData>("https://raw.githubusercontent.com/bloxstraplabs/config/main/supporters.json");
}
catch (Exception ex)
{
App.Logger.WriteLine(LOG_IDENT, "Could not load supporter data");
App.Logger.WriteException(LOG_IDENT, ex);
LoadedState = GenericTriState.Failed;
LoadError = ex.Message;
OnPropertyChanged(nameof(LoadError));
}
if (SupporterData is not null)
{
LoadedState = GenericTriState.Successful;
OnPropertyChanged(nameof(SupporterData));
}
OnPropertyChanged(nameof(LoadedState));
}
}
}

View File

@ -12,5 +12,7 @@ namespace Bloxstrap.UI.ViewModels.Settings
public ShortcutTask PlayerIconTask { get; } = new("RobloxPlayer", Paths.Desktop, $"{Strings.LaunchMenu_LaunchRoblox}.lnk", "-player"); public ShortcutTask PlayerIconTask { get; } = new("RobloxPlayer", Paths.Desktop, $"{Strings.LaunchMenu_LaunchRoblox}.lnk", "-player");
public ShortcutTask SettingsIconTask { get; } = new("Settings", Paths.Desktop, $"{Strings.Menu_Title}.lnk", "-settings"); public ShortcutTask SettingsIconTask { get; } = new("Settings", Paths.Desktop, $"{Strings.Menu_Title}.lnk", "-settings");
public ExtractIconsTask ExtractIconsTask { get; } = new();
} }
} }

View File

@ -16,14 +16,14 @@ namespace Bloxstrap.Utility
if (uriKey.GetValue("") is null) if (uriKey.GetValue("") is null)
{ {
uriKey.SetValue("", $"URL: {name} Protocol"); uriKey.SetValueSafe("", $"URL: {name} Protocol");
uriKey.SetValue("URL Protocol", ""); uriKey.SetValueSafe("URL Protocol", "");
} }
if (uriCommandKey.GetValue("") as string != handlerArgs) if (uriCommandKey.GetValue("") as string != handlerArgs)
{ {
uriIconKey.SetValue("", handler); uriIconKey.SetValueSafe("", handler);
uriCommandKey.SetValue("", handlerArgs); uriCommandKey.SetValueSafe("", handlerArgs);
} }
} }
@ -85,16 +85,16 @@ namespace Bloxstrap.Utility
using RegistryKey uriCommandKey = uriOpenKey.CreateSubKey(@"command"); using RegistryKey uriCommandKey = uriOpenKey.CreateSubKey(@"command");
if (uriKey.GetValue("") as string != keyValue) if (uriKey.GetValue("") as string != keyValue)
uriKey.SetValue("", keyValue); uriKey.SetValueSafe("", keyValue);
if (uriCommandKey.GetValue("") as string != handlerArgs) if (uriCommandKey.GetValue("") as string != handlerArgs)
uriCommandKey.SetValue("", handlerArgs); uriCommandKey.SetValueSafe("", handlerArgs);
if (uriOpenKey.GetValue("") as string != "Open") if (uriOpenKey.GetValue("") as string != "Open")
uriOpenKey.SetValue("", "Open"); uriOpenKey.SetValueSafe("", "Open");
if (uriIconKey.GetValue("") as string != iconValue) if (uriIconKey.GetValue("") as string != iconValue)
uriIconKey.SetValue("", iconValue); uriIconKey.SetValueSafe("", iconValue);
} }
public static void RegisterStudioFileType(string key) public static void RegisterStudioFileType(string key)
@ -103,7 +103,7 @@ namespace Bloxstrap.Utility
uriKey.CreateSubKey(RobloxPlaceKey + @"\ShellNew"); uriKey.CreateSubKey(RobloxPlaceKey + @"\ShellNew");
if (uriKey.GetValue("") as string != RobloxPlaceKey) if (uriKey.GetValue("") as string != RobloxPlaceKey)
uriKey.SetValue("", RobloxPlaceKey); uriKey.SetValueSafe("", RobloxPlaceKey);
} }
public static void Unregister(string key) public static void Unregister(string key)