mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 13:28:33 +00:00
Workshop - Add ability to mark entry as default (admin only)
Workshop - Allow editing other user's entries (admin only) Workshop - Show last update date in entry list instead of creation date
This commit is contained in:
parent
10a10b9149
commit
3caf782d8e
@ -15,7 +15,7 @@
|
||||
</UserControl.Resources>
|
||||
<Panel>
|
||||
<StackPanel IsVisible="{CompiledBinding Entry, Converter={x:Static ObjectConverters.IsNull}}">
|
||||
<Border Classes="skeleton-text"
|
||||
<Border Classes="skeleton-text"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="30 30 30 0"
|
||||
Width="80"
|
||||
@ -43,13 +43,20 @@
|
||||
ClipToBounds="True">
|
||||
<Image Stretch="UniformToFill" il:ImageLoader.Source="{CompiledBinding Entry.Id, Converter={StaticResource EntryIconUriConverter}, Mode=OneWay}" />
|
||||
</Border>
|
||||
<Button Classes="icon-button"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{CompiledBinding CopyShareLink}"
|
||||
ToolTip.Tip="Copy share link">
|
||||
<avalonia:MaterialIcon Kind="ShareVariant" />
|
||||
</Button>
|
||||
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Right" Orientation="Horizontal" Spacing="5">
|
||||
<Button IsVisible="{CompiledBinding IsAdministrator}"
|
||||
Classes="icon-button"
|
||||
Command="{CompiledBinding GoToEdit}"
|
||||
ToolTip.Tip="Edit submission">
|
||||
<avalonia:MaterialIcon Kind="Edit" />
|
||||
</Button>
|
||||
<Button Classes="icon-button"
|
||||
Command="{CompiledBinding CopyShareLink}"
|
||||
ToolTip.Tip="Copy share link">
|
||||
<avalonia:MaterialIcon Kind="ShareVariant" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
</Panel>
|
||||
|
||||
<TextBlock Theme="{StaticResource TitleTextBlockStyle}"
|
||||
@ -62,15 +69,15 @@
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Classes="subtitle" TextTrimming="CharacterEllipsis" Text="{CompiledBinding Entry.Author, FallbackValue=Author}" />
|
||||
<avalonia:MaterialIcon IsVisible="{CompiledBinding Entry.IsOfficial}"
|
||||
Kind="ShieldStar"
|
||||
Kind="ShieldStar"
|
||||
Foreground="{DynamicResource SystemAccentColorLight1}"
|
||||
Margin="2 0 0 0"
|
||||
Width="18"
|
||||
Height="18"
|
||||
Height="18"
|
||||
HorizontalAlignment="Left"
|
||||
ToolTip.Tip="Official entry by the Artemis team" />
|
||||
</StackPanel>
|
||||
|
||||
|
||||
<TextBlock Margin="0 8" TextWrapping="Wrap" Text="{CompiledBinding Entry.Summary, FallbackValue=Summary}" />
|
||||
|
||||
<!-- Categories -->
|
||||
|
||||
@ -25,7 +25,7 @@ public partial class EntryInfoViewModel : ActivatableViewModelBase
|
||||
[Notify] private DateTimeOffset? _updatedAt;
|
||||
[Notify] private bool _canBeManaged;
|
||||
|
||||
public EntryInfoViewModel(IRouter router, INotificationService notificationService, IWorkshopService workshopService)
|
||||
public EntryInfoViewModel(IRouter router, INotificationService notificationService, IWorkshopService workshopService, IAuthenticationService authenticationService)
|
||||
{
|
||||
_router = router;
|
||||
_notificationService = notificationService;
|
||||
@ -38,8 +38,12 @@ public partial class EntryInfoViewModel : ActivatableViewModelBase
|
||||
.Subscribe(_ => CanBeManaged = Entry != null && Entry.EntryType != EntryType.Profile && workshopService.GetInstalledEntry(Entry.Id) != null)
|
||||
.DisposeWith(d);
|
||||
});
|
||||
|
||||
IsAdministrator = authenticationService.GetRoles().Contains("Administrator");
|
||||
}
|
||||
|
||||
|
||||
public bool IsAdministrator { get; }
|
||||
|
||||
public void SetEntry(IEntryDetails? entry)
|
||||
{
|
||||
Entry = entry;
|
||||
@ -55,6 +59,14 @@ public partial class EntryInfoViewModel : ActivatableViewModelBase
|
||||
await Shared.UI.Clipboard.SetTextAsync($"{WorkshopConstants.WORKSHOP_URL}/entries/{Entry.Id}/{StringUtilities.UrlFriendly(Entry.Name)}");
|
||||
_notificationService.CreateNotification().WithTitle("Copied share link to clipboard.").Show();
|
||||
}
|
||||
|
||||
public async Task GoToEdit()
|
||||
{
|
||||
if (Entry == null)
|
||||
return;
|
||||
|
||||
await _router.Navigate($"workshop/library/submissions/{Entry.Id}");
|
||||
}
|
||||
|
||||
public async Task GoToManage()
|
||||
{
|
||||
|
||||
@ -63,6 +63,8 @@
|
||||
|
||||
<Label Target="Summary" Margin="0 5 0 0">Summary</Label>
|
||||
<TextBox Name="Summary" Text="{CompiledBinding Summary}"></TextBox>
|
||||
|
||||
<CheckBox IsVisible="{CompiledBinding IsAdministrator}" IsChecked="{CompiledBinding IsDefault}">Download by default (admin only)</CheckBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ using Artemis.UI.Screens.Workshop.Categories;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.WebClient.Workshop;
|
||||
using Artemis.WebClient.Workshop.Services;
|
||||
using Avalonia.Media.Imaging;
|
||||
using AvaloniaEdit.Document;
|
||||
using DynamicData;
|
||||
@ -34,10 +35,11 @@ public partial class EntrySpecificationsViewModel : ValidatableViewModelBase
|
||||
[Notify] private string _name = string.Empty;
|
||||
[Notify] private string _summary = string.Empty;
|
||||
[Notify] private string _description = string.Empty;
|
||||
[Notify] private bool _isDefault;
|
||||
[Notify] private Bitmap? _iconBitmap;
|
||||
[Notify(Setter.Private)] private bool _iconChanged;
|
||||
|
||||
public EntrySpecificationsViewModel(IWorkshopClient workshopClient, IWindowService windowService)
|
||||
public EntrySpecificationsViewModel(IWorkshopClient workshopClient, IWindowService windowService, IAuthenticationService authenticationService)
|
||||
{
|
||||
_workshopClient = workshopClient;
|
||||
_windowService = windowService;
|
||||
@ -65,8 +67,9 @@ public partial class EntrySpecificationsViewModel : ValidatableViewModelBase
|
||||
_descriptionValid = descriptionRule.ValidationChanged.Select(c => c.IsValid).ToProperty(this, vm => vm.DescriptionValid);
|
||||
|
||||
this.WhenActivatedAsync(async _ => await PopulateCategories());
|
||||
IsAdministrator = authenticationService.GetRoles().Contains("Administrator");
|
||||
}
|
||||
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SelectIcon { get; }
|
||||
|
||||
public ObservableCollection<CategoryViewModel> Categories { get; } = new();
|
||||
@ -76,7 +79,8 @@ public partial class EntrySpecificationsViewModel : ValidatableViewModelBase
|
||||
public bool CategoriesValid => _categoriesValid.Value ;
|
||||
public bool IconValid => _iconValid.Value;
|
||||
public bool DescriptionValid => _descriptionValid.Value;
|
||||
|
||||
public bool IsAdministrator { get; }
|
||||
|
||||
public List<long> PreselectedCategories { get; set; } = new();
|
||||
|
||||
private async Task ExecuteSelectIcon()
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
|
||||
<!-- Info -->
|
||||
<StackPanel Grid.Column="2" Grid.Row="0" Margin="0 0 4 0" HorizontalAlignment="Right">
|
||||
<TextBlock TextAlignment="Right" Text="{CompiledBinding Entry.CreatedAt, FallbackValue=01-01-1337, Converter={StaticResource DateTimeConverter}}" />
|
||||
<TextBlock TextAlignment="Right" Text="{CompiledBinding Entry.LatestRelease.CreatedAt, FallbackValue=01-01-1337, Converter={StaticResource DateTimeConverter}}" />
|
||||
<TextBlock TextAlignment="Right">
|
||||
<avalonia:MaterialIcon Kind="Downloads" />
|
||||
<Run Classes="h5" Text="{CompiledBinding Entry.Downloads, FallbackValue=0}" />
|
||||
|
||||
@ -59,7 +59,7 @@ public partial class SubmissionDetailsViewModel : RoutableScreen
|
||||
DiscardChanges = ReactiveCommand.CreateFromTask(ExecuteDiscardChanges, this.WhenAnyValue(vm => vm.HasChanges));
|
||||
SaveChanges = ReactiveCommand.CreateFromTask(ExecuteSaveChanges, this.WhenAnyValue(vm => vm.HasChanges));
|
||||
}
|
||||
|
||||
|
||||
public ObservableCollection<ImageSubmissionViewModel> Images { get; } = new();
|
||||
public ReactiveCommand<Unit, Unit> AddImage { get; }
|
||||
public ReactiveCommand<Unit, Unit> SaveChanges { get; }
|
||||
@ -105,6 +105,7 @@ public partial class SubmissionDetailsViewModel : RoutableScreen
|
||||
specificationsViewModel.Name = Entry.Name;
|
||||
specificationsViewModel.Summary = Entry.Summary;
|
||||
specificationsViewModel.Description = Entry.Description;
|
||||
specificationsViewModel.IsDefault = Entry.IsDefault;
|
||||
specificationsViewModel.PreselectedCategories = Entry.Categories.Select(c => c.Id).ToList();
|
||||
|
||||
specificationsViewModel.Tags.Clear();
|
||||
@ -169,6 +170,7 @@ public partial class SubmissionDetailsViewModel : RoutableScreen
|
||||
HasChanges = EntrySpecificationsViewModel.Name != Entry.Name ||
|
||||
EntrySpecificationsViewModel.Description != Entry.Description ||
|
||||
EntrySpecificationsViewModel.Summary != Entry.Summary ||
|
||||
EntrySpecificationsViewModel.IsDefault != Entry.IsDefault ||
|
||||
EntrySpecificationsViewModel.IconChanged ||
|
||||
!tags.SequenceEqual(Entry.Tags.Select(t => t.Name).OrderBy(t => t)) ||
|
||||
!categories.SequenceEqual(Entry.Categories.Select(c => c.Id).OrderBy(c => c)) ||
|
||||
@ -192,6 +194,7 @@ public partial class SubmissionDetailsViewModel : RoutableScreen
|
||||
Name = EntrySpecificationsViewModel.Name,
|
||||
Summary = EntrySpecificationsViewModel.Summary,
|
||||
Description = EntrySpecificationsViewModel.Description,
|
||||
IsDefault = EntrySpecificationsViewModel.IsDefault,
|
||||
Categories = EntrySpecificationsViewModel.SelectedCategories,
|
||||
Tags = EntrySpecificationsViewModel.Tags
|
||||
};
|
||||
@ -233,7 +236,7 @@ public partial class SubmissionDetailsViewModel : RoutableScreen
|
||||
HasChanges = false;
|
||||
await _router.Reload();
|
||||
}
|
||||
|
||||
|
||||
private async Task ExecuteAddImage(CancellationToken arg)
|
||||
{
|
||||
string[]? result = await _windowService.CreateOpenFileDialog().WithAllowMultiple().HavingFilter(f => f.WithBitmaps()).ShowAsync();
|
||||
|
||||
@ -67,7 +67,7 @@
|
||||
|
||||
<!-- Info -->
|
||||
<StackPanel Grid.Column="2" Grid.Row="0" Margin="0 0 4 0" HorizontalAlignment="Right">
|
||||
<TextBlock TextAlignment="Right" Text="{CompiledBinding Entry.CreatedAt, FallbackValue=01-01-1337, Converter={StaticResource DateTimeConverter}}" />
|
||||
<TextBlock TextAlignment="Right" Text="{CompiledBinding Entry.LatestRelease.CreatedAt, FallbackValue=01-01-1337, Converter={StaticResource DateTimeConverter}}" />
|
||||
<TextBlock TextAlignment="Right">
|
||||
<avalonia:MaterialIcon Kind="Downloads" />
|
||||
<Run Classes="h5" Text="{CompiledBinding Entry.Downloads, FallbackValue=0}" />
|
||||
|
||||
@ -32,6 +32,7 @@ public class SubmissionWizardState : IDisposable
|
||||
public Stream? Icon { get; set; }
|
||||
public string Summary { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public bool IsDefault { get; set; }
|
||||
|
||||
public List<long> Categories { get; set; } = new();
|
||||
public List<string> Tags { get; set; } = new();
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Reactive.Disposables;
|
||||
using Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Layout;
|
||||
using Artemis.UI.Screens.Workshop.SubmissionWizard.Steps.Plugin;
|
||||
@ -10,7 +11,7 @@ namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
||||
|
||||
public partial class ChangelogStepViewModel : SubmissionViewModel
|
||||
{
|
||||
[Notify] private string? _changelog;
|
||||
[Notify] private string _changelog = string.Empty;
|
||||
|
||||
public ChangelogStepViewModel()
|
||||
{
|
||||
@ -18,7 +19,7 @@ public partial class ChangelogStepViewModel : SubmissionViewModel
|
||||
Continue = ReactiveCommand.Create(ExecuteContinue);
|
||||
ContinueText = "Submit";
|
||||
|
||||
this.WhenActivated((CompositeDisposable _) => Changelog = State.Changelog);
|
||||
this.WhenActivated((CompositeDisposable _) => Changelog = State.Changelog ?? string.Empty);
|
||||
}
|
||||
|
||||
private void ExecuteContinue()
|
||||
@ -29,7 +30,7 @@ public partial class ChangelogStepViewModel : SubmissionViewModel
|
||||
|
||||
private void ExecuteGoBack()
|
||||
{
|
||||
State.Changelog = Changelog;
|
||||
State.Changelog = string.IsNullOrWhiteSpace(Changelog) ? null : Changelog;
|
||||
if (State.EntryType == EntryType.Layout)
|
||||
State.ChangeScreen<LayoutInfoStepViewModel>();
|
||||
else if (State.EntryType == EntryType.Plugin)
|
||||
|
||||
@ -65,6 +65,7 @@ public partial class SpecificationsStepViewModel : SubmissionViewModel
|
||||
viewModel.Name = State.Name;
|
||||
viewModel.Summary = State.Summary;
|
||||
viewModel.Description = State.Description;
|
||||
viewModel.IsDefault = State.IsDefault;
|
||||
|
||||
// Tags
|
||||
viewModel.Tags.Clear();
|
||||
@ -93,6 +94,7 @@ public partial class SpecificationsStepViewModel : SubmissionViewModel
|
||||
State.Name = EntrySpecificationsViewModel.Name;
|
||||
State.Summary = EntrySpecificationsViewModel.Summary;
|
||||
State.Description = EntrySpecificationsViewModel.Description;
|
||||
State.IsDefault = EntrySpecificationsViewModel.IsDefault;
|
||||
|
||||
// Categories and tasks
|
||||
State.Categories = EntrySpecificationsViewModel.Categories.Where(c => c.IsSelected).Select(c => c.Id).ToList();
|
||||
|
||||
@ -26,6 +26,10 @@ fragment submittedEntry on Entry {
|
||||
entryType
|
||||
downloads
|
||||
createdAt
|
||||
isDefault
|
||||
latestRelease {
|
||||
...release
|
||||
}
|
||||
}
|
||||
|
||||
fragment entrySummary on Entry {
|
||||
@ -38,6 +42,9 @@ fragment entrySummary on Entry {
|
||||
downloads
|
||||
createdAt
|
||||
latestReleaseId
|
||||
latestRelease {
|
||||
...release
|
||||
}
|
||||
categories {
|
||||
...category
|
||||
}
|
||||
|
||||
@ -283,6 +283,12 @@ internal class AuthenticationService : CorePropertyChanged, IAuthenticationServi
|
||||
return emailVerified?.Value.ToLower() == "true";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<string> GetRoles()
|
||||
{
|
||||
return Claims.Where(c => c.Type == JwtClaimTypes.Role).Select(c => c.Value).ToList();
|
||||
}
|
||||
|
||||
private async Task<bool> InternalAutoLogin(bool force = false)
|
||||
{
|
||||
if (!force && _isLoggedInSubject.Value)
|
||||
|
||||
@ -15,4 +15,5 @@ public interface IAuthenticationService : IProtectedArtemisService
|
||||
Task Login(CancellationToken cancellationToken);
|
||||
Task Logout();
|
||||
bool GetIsEmailVerified();
|
||||
List<string> GetRoles();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user