diff --git a/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml.cs b/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml.cs index 6b7fd2a02..c6e491438 100644 --- a/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml.cs +++ b/src/Artemis.UI.Shared/Controls/ProfileConfigurationIcon.axaml.cs @@ -6,7 +6,6 @@ using Avalonia.Controls; using Avalonia.Controls.Documents; using Avalonia.Layout; using Avalonia.LogicalTree; -using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Threading; @@ -43,7 +42,7 @@ public partial class ProfileConfigurationIcon : UserControl, IDisposable if (ConfigurationIcon.IconType == ProfileConfigurationIconType.MaterialIcon) { Content = Enum.TryParse(ConfigurationIcon.IconName, true, out MaterialIconKind parsedIcon) - ? new MaterialIcon {Kind = parsedIcon!} + ? new MaterialIcon {Kind = parsedIcon} : new MaterialIcon {Kind = MaterialIconKind.QuestionMark}; } else if (ConfigurationIcon.IconBytes != null) @@ -65,19 +64,28 @@ public partial class ProfileConfigurationIcon : UserControl, IDisposable return; _stream = new MemoryStream(ConfigurationIcon.IconBytes); - if (!ConfigurationIcon.Fill) + Border border = new() { - Content = new Image {Source = new Bitmap(_stream)}; - return; + CornerRadius = CornerRadius, + ClipToBounds = true, + VerticalAlignment = VerticalAlignment.Stretch, + HorizontalAlignment = HorizontalAlignment.Stretch + + }; + + if (ConfigurationIcon.Fill) + { + // Fill mode: use Foreground as Background and the bitmap as opacity mask + border.Background = TextElement.GetForeground(this); + border.OpacityMask = new ImageBrush(new Bitmap(_stream)); + } + else + { + // Non-fill mode: place the image inside the rounded border + border.Child = new Image { Source = new Bitmap(_stream) }; } - Content = new Border - { - Background = TextElement.GetForeground(this), - VerticalAlignment = VerticalAlignment.Stretch, - HorizontalAlignment = HorizontalAlignment.Stretch, - OpacityMask = new ImageBrush(new Bitmap(_stream)) - }; + Content = border; } catch (Exception) { diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml index 361ca360d..6708c965b 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml @@ -89,7 +89,7 @@ Background="{DynamicResource ControlFillColorDefaultBrush}" IsVisible="{CompiledBinding ProfileConfiguration, Converter={x:Static ObjectConverters.IsNotNull}}"> - + diff --git a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml.cs b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml.cs index f7e4491a5..648c815ab 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Panels/VisualEditor/VisualEditorView.axaml.cs @@ -27,8 +27,9 @@ public partial class VisualEditorView : ReactiveUserControl { - ViewModel!.AutoFitRequested += ViewModelOnAutoFitRequested; - Disposable.Create(() => ViewModel.AutoFitRequested -= ViewModelOnAutoFitRequested).DisposeWith(d); + VisualEditorViewModel? viewModel = ViewModel; + viewModel!.AutoFitRequested += ViewModelOnAutoFitRequested; + Disposable.Create(() => viewModel.AutoFitRequested -= ViewModelOnAutoFitRequested).DisposeWith(d); }); this.WhenAnyValue(v => v.Bounds).Where(_ => !_movedByUser).Subscribe(_ => AutoFit(true)); diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationView.axaml b/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationView.axaml index cd4223392..5e7884967 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationView.axaml +++ b/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationView.axaml @@ -72,15 +72,20 @@ Background="Transparent" ContextFlyout="{StaticResource ProfileMenuFlyout}" Classes.flyout-open="{CompiledBinding IsOpen, Source={StaticResource ProfileMenuFlyout}}"> - - - - - - - - - + + + + + + + c.ProfileConfigurations).FirstOrDefault(c => c.ProfileId == profileId); if (profile == null) return; - + ProfileCategory category = _profileService.ProfileCategories.FirstOrDefault(c => c.Name == "General") ?? _profileService.CreateProfileCategory("General", true); if (category.ProfileConfigurations.Contains(profile)) return; - + + + // Add the profile to the category category.AddProfileConfiguration(profile, null); + + // Suspend all but the first profile in the category + profile.IsSuspended = category.ProfileConfigurations.Count > 1; + _profileService.SaveProfileCategory(category); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileSelectionStepView.axaml b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileSelectionStepView.axaml index 8fd0eb216..7f5aee8f8 100644 --- a/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileSelectionStepView.axaml +++ b/src/Artemis.UI/Screens/Workshop/SubmissionWizard/Steps/Profile/ProfileSelectionStepView.axaml @@ -38,6 +38,7 @@ Grid.Column="0" ConfigurationIcon="{CompiledBinding Icon}" VerticalAlignment="Center" + CornerRadius="4" Width="22" Height="22" Margin="0 0 10 0" /> diff --git a/src/Artemis.WebClient.Workshop/Services/AuthenticationService.cs b/src/Artemis.WebClient.Workshop/Services/AuthenticationService.cs index 7e7e76f47..3f7e65735 100644 --- a/src/Artemis.WebClient.Workshop/Services/AuthenticationService.cs +++ b/src/Artemis.WebClient.Workshop/Services/AuthenticationService.cs @@ -180,13 +180,15 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi { await _authLock.WaitAsync(cancellationToken); + // Start a HTTP listener, this port could be in use but chances are very slim + // IdentityServer only accepts these two redirect URLs + string redirectUri = Constants.StartupArguments.Contains("--alt-login-callback") ? "http://localhost:56789" : "http://localhost:57461"; + try { if (_isLoggedInSubject.Value) return; - - // Start a HTTP listener, this port could be in use but chances are very slim - string redirectUri = "http://localhost:57461"; + using HttpListener listener = new(); listener.Prefixes.Add(redirectUri + "/"); listener.Start(); @@ -249,7 +251,11 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi } catch (HttpListenerException e) { - throw new ArtemisWebClientException($"HTTP listener for login callback failed with error code {e.ErrorCode}", e); + // I've seen the Nvidia app do this after a login. What are the odds... + if (e.ErrorCode == 32) + throw new ArtemisWebClientException($"HTTP listener for login callback failed because another application is already listening on '{redirectUri}', please close that application and try again", e); + else + throw new ArtemisWebClientException($"HTTP listener for login callback failed with error code {e.ErrorCode}", e); } finally {