Merge branch 'develop' into feature/cascade-visibility-skip-deleted
This commit is contained in:
@@ -1,37 +0,0 @@
|
|||||||
@* Generic collapsible section with clickable header and chevron toggle *@
|
|
||||||
<div class="@Class">
|
|
||||||
<div class="d-flex align-items-center gap-2 cursor-pointer" @onclick="Toggle" role="button">
|
|
||||||
@if (TitleContent is not null) {
|
|
||||||
@TitleContent
|
|
||||||
} else if (!string.IsNullOrEmpty(Title)) {
|
|
||||||
<span>@Title</span>
|
|
||||||
}
|
|
||||||
<span class="ms-auto small @(Expanded ? "bi-chevron-up" : "bi-chevron-down")"></span>
|
|
||||||
</div>
|
|
||||||
@if (Expanded) {
|
|
||||||
<div class="@ContentClass">
|
|
||||||
@ChildContent
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
[Parameter] public string? Title { get; set; }
|
|
||||||
[Parameter] public RenderFragment? TitleContent { get; set; }
|
|
||||||
[Parameter] public RenderFragment? ChildContent { get; set; }
|
|
||||||
[Parameter] public bool InitiallyExpanded { get; set; }
|
|
||||||
[Parameter] public string? Class { get; set; }
|
|
||||||
[Parameter] public string? ContentClass { get; set; }
|
|
||||||
[Parameter] public EventCallback<bool> ExpandedChanged { get; set; }
|
|
||||||
|
|
||||||
bool Expanded { get; set; }
|
|
||||||
|
|
||||||
protected override void OnInitialized() {
|
|
||||||
Expanded = InitiallyExpanded;
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task Toggle() {
|
|
||||||
Expanded = !Expanded;
|
|
||||||
await ExpandedChanged.InvokeAsync(Expanded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -113,8 +113,6 @@
|
|||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public FolderFullDto Folder { get; set; } = new FolderFullDto();
|
public FolderFullDto Folder { get; set; } = new FolderFullDto();
|
||||||
[Parameter]
|
|
||||||
public EventCallback<Guid> OnDeleted { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<Guid> OnDeleteRequested { get; set; }
|
public EventCallback<Guid> OnDeleteRequested { get; set; }
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
@* Section wrapper for a group of jobs with title and count *@
|
|
||||||
<div class="mb-3">
|
|
||||||
<div class="d-flex align-items-center mb-1">
|
|
||||||
<h6 class="mb-0 text-muted text-uppercase small">@Title @(Count is not null ? $"({Count})" : "")</h6>
|
|
||||||
@if (Extra is not null) {
|
|
||||||
<span class="ms-auto">@Extra</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div class="border rounded">
|
|
||||||
@ChildContent
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
[Parameter] public required string Title { get; set; }
|
|
||||||
[Parameter] public int? Count { get; set; }
|
|
||||||
[Parameter] public RenderFragment? Extra { get; set; }
|
|
||||||
[Parameter] public required RenderFragment ChildContent { get; set; }
|
|
||||||
}
|
|
||||||
@@ -2,14 +2,13 @@
|
|||||||
@using Butter.Dtos.Album
|
@using Butter.Dtos.Album
|
||||||
@using Butter.Dtos.Asset
|
@using Butter.Dtos.Asset
|
||||||
@using Butter.Types
|
@using Butter.Types
|
||||||
@using Microsoft.Extensions.Options
|
|
||||||
@implements IAsyncDisposable
|
@implements IAsyncDisposable
|
||||||
|
|
||||||
@inject AlbumService albumService
|
@inject AlbumService albumService
|
||||||
@inject AssetService assetService
|
@inject AssetService assetService
|
||||||
@inject LoginService loginService
|
@inject LoginService loginService
|
||||||
@inject IJSRuntime jsRuntime
|
@inject IJSRuntime jsRuntime
|
||||||
@inject IOptions<ServiceOptions> serviceOptions
|
@inject MediaService mediaService
|
||||||
@inject NavigationManager navigationManager
|
@inject NavigationManager navigationManager
|
||||||
|
|
||||||
<PageTitle>@(album?.Name ?? "Album")</PageTitle>
|
<PageTitle>@(album?.Name ?? "Album")</PageTitle>
|
||||||
@@ -157,7 +156,7 @@
|
|||||||
style="aspect-ratio:@GetAspectRatio(preview)"
|
style="aspect-ratio:@GetAspectRatio(preview)"
|
||||||
@onclick="() => HandleTileClick(index, assetId)">
|
@onclick="() => HandleTileClick(index, assetId)">
|
||||||
@if (preview == null || preview.HasThumbnail) {
|
@if (preview == null || preview.HasThumbnail) {
|
||||||
<img src="@ThumbUrl(assetId)"
|
<img src="@mediaService.ThumbUrl(assetId)"
|
||||||
loading="lazy" alt=""
|
loading="lazy" alt=""
|
||||||
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
||||||
<div class="tile-fallback" style="display:none">
|
<div class="tile-fallback" style="display:none">
|
||||||
@@ -249,10 +248,6 @@
|
|||||||
HashSet<Guid> selectedImageIds = [];
|
HashSet<Guid> selectedImageIds = [];
|
||||||
Guid? selectedAssetId;
|
Guid? selectedAssetId;
|
||||||
int selectedIndex = -1;
|
int selectedIndex = -1;
|
||||||
string token = string.Empty;
|
|
||||||
string apiBase => serviceOptions.Value.BaseUrl.TrimEnd('/');
|
|
||||||
|
|
||||||
string TokenParam => string.IsNullOrEmpty(token) ? "" : $"?token={token}";
|
|
||||||
string viewMode = "masonry";
|
string viewMode = "masonry";
|
||||||
bool _masonrySetup;
|
bool _masonrySetup;
|
||||||
|
|
||||||
@@ -293,7 +288,6 @@
|
|||||||
|
|
||||||
protected override async Task OnParametersSetAsync() {
|
protected override async Task OnParametersSetAsync() {
|
||||||
await jsRuntime.InvokeVoidAsync("masonryObserver.unlockBodyScroll");
|
await jsRuntime.InvokeVoidAsync("masonryObserver.unlockBodyScroll");
|
||||||
token = loginService.AuthInfo?.Token ?? string.Empty;
|
|
||||||
await LoadAlbum();
|
await LoadAlbum();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,9 +303,6 @@
|
|||||||
|
|
||||||
protected override void OnInitialized() {
|
protected override void OnInitialized() {
|
||||||
loginService.LoggedUserChanged += (_, _) => StateHasChanged();
|
loginService.LoggedUserChanged += (_, _) => StateHasChanged();
|
||||||
loginService.AuthInfoChanged += (_, info) => {
|
|
||||||
token = info?.Token ?? string.Empty;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task LoadAlbum() {
|
async Task LoadAlbum() {
|
||||||
@@ -504,8 +495,6 @@
|
|||||||
return "4 / 3";
|
return "4 / 3";
|
||||||
}
|
}
|
||||||
|
|
||||||
string ThumbUrl(Guid id) => $"{apiBase}/api/media/thumb/{id}{TokenParam}";
|
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
await jsRuntime.InvokeVoidAsync("masonryLayout.teardown", ".album-assets-grid.masonry");
|
await jsRuntime.InvokeVoidAsync("masonryLayout.teardown", ".album-assets-grid.masonry");
|
||||||
|
|||||||
@@ -3,13 +3,12 @@
|
|||||||
@using Butter.Dtos.Asset
|
@using Butter.Dtos.Asset
|
||||||
@using Butter.Dtos.Person
|
@using Butter.Dtos.Person
|
||||||
@using Butter.Types
|
@using Butter.Types
|
||||||
@using Microsoft.Extensions.Options
|
|
||||||
@using System.Globalization
|
@using System.Globalization
|
||||||
@inject PersonService personService
|
@inject PersonService personService
|
||||||
@inject AlbumService albumService
|
@inject AlbumService albumService
|
||||||
@inject AssetService assetService
|
@inject AssetService assetService
|
||||||
@inject LoginService loginService
|
@inject LoginService loginService
|
||||||
@inject IOptions<ServiceOptions> serviceOptions
|
@inject MediaService mediaService
|
||||||
@inject NavigationManager navigationManager
|
@inject NavigationManager navigationManager
|
||||||
|
|
||||||
<PageTitle>@(person?.Name ?? "Cosplayer")</PageTitle>
|
<PageTitle>@(person?.Name ?? "Cosplayer")</PageTitle>
|
||||||
@@ -40,7 +39,7 @@
|
|||||||
var cy = person.ProfileCropY ?? 50;
|
var cy = person.ProfileCropY ?? 50;
|
||||||
var z = person.ProfileCropZoom ?? 0.6f;
|
var z = person.ProfileCropZoom ?? 0.6f;
|
||||||
<div class="cosplayer-avatar-clip">
|
<div class="cosplayer-avatar-clip">
|
||||||
<img src="@ThumbUrl(person.ProfileAssetId.Value)" alt=""
|
<img src="@mediaService.ThumbUrl(person.ProfileAssetId.Value)" alt=""
|
||||||
class="cosplayer-avatar-img"
|
class="cosplayer-avatar-img"
|
||||||
style="width: calc(100% / @z.ToString(CultureInfo.InvariantCulture)); transform: translate(calc(-@cx.ToString(CultureInfo.InvariantCulture) * 1%), calc(-@cy.ToString(CultureInfo.InvariantCulture) * 1%))" />
|
style="width: calc(100% / @z.ToString(CultureInfo.InvariantCulture)); transform: translate(calc(-@cx.ToString(CultureInfo.InvariantCulture) * 1%), calc(-@cy.ToString(CultureInfo.InvariantCulture) * 1%))" />
|
||||||
</div>
|
</div>
|
||||||
@@ -317,8 +316,6 @@
|
|||||||
Func<Task>? _pendingDeleteAction;
|
Func<Task>? _pendingDeleteAction;
|
||||||
bool cascadeToAssets;
|
bool cascadeToAssets;
|
||||||
EVisibility selectedAlbumVisibility = EVisibility.Public;
|
EVisibility selectedAlbumVisibility = EVisibility.Public;
|
||||||
string token = string.Empty;
|
|
||||||
string apiBase => serviceOptions.Value.BaseUrl.TrimEnd('/');
|
|
||||||
string viewMode = "masonry";
|
string viewMode = "masonry";
|
||||||
bool showAlbumAssigner;
|
bool showAlbumAssigner;
|
||||||
List<AlbumPreviewDto>? unassignedAlbums;
|
List<AlbumPreviewDto>? unassignedAlbums;
|
||||||
@@ -341,8 +338,6 @@
|
|||||||
|
|
||||||
int albumLoadVersion;
|
int albumLoadVersion;
|
||||||
|
|
||||||
string TokenParam => string.IsNullOrEmpty(token) ? "" : $"?token={token}";
|
|
||||||
|
|
||||||
List<Guid>? allAssetIds => person?.Albums?
|
List<Guid>? allAssetIds => person?.Albums?
|
||||||
.SelectMany(a => Enumerable.Repeat(a.CoverAssetId ?? Guid.Empty, 1))
|
.SelectMany(a => Enumerable.Repeat(a.CoverAssetId ?? Guid.Empty, 1))
|
||||||
.Where(id => id != Guid.Empty)
|
.Where(id => id != Guid.Empty)
|
||||||
@@ -350,15 +345,11 @@
|
|||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync() {
|
protected override async Task OnParametersSetAsync() {
|
||||||
token = loginService.AuthInfo?.Token ?? string.Empty;
|
|
||||||
await LoadPerson();
|
await LoadPerson();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnInitialized() {
|
protected override void OnInitialized() {
|
||||||
loginService.LoggedUserChanged += (_, _) => StateHasChanged();
|
loginService.LoggedUserChanged += (_, _) => StateHasChanged();
|
||||||
loginService.AuthInfoChanged += (_, info) => {
|
|
||||||
token = info?.Token ?? string.Empty;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task LoadPerson() {
|
async Task LoadPerson() {
|
||||||
@@ -372,7 +363,7 @@
|
|||||||
.Where(a => a.CoverAssetId.HasValue)
|
.Where(a => a.CoverAssetId.HasValue)
|
||||||
.OrderBy(_ => Random.Shared.Next())
|
.OrderBy(_ => Random.Shared.Next())
|
||||||
.Take(3)
|
.Take(3)
|
||||||
.Select(a => ThumbUrl(a.CoverAssetId!.Value))
|
.Select(a => mediaService.ThumbUrl(a.CoverAssetId!.Value))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,6 +513,4 @@
|
|||||||
if (_pendingDeleteAction != null)
|
if (_pendingDeleteAction != null)
|
||||||
await _pendingDeleteAction();
|
await _pendingDeleteAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
string ThumbUrl(Guid id) => $"{apiBase}/api/media/thumb/{id}{TokenParam}";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
@page "/"
|
@page "/"
|
||||||
@using Butter.Dtos.Asset
|
@using Butter.Dtos.Asset
|
||||||
@using Butter.Types
|
@using Butter.Types
|
||||||
@using Microsoft.Extensions.Options
|
|
||||||
@implements IAsyncDisposable
|
@implements IAsyncDisposable
|
||||||
|
|
||||||
@inject AssetService assetService
|
@inject AssetService assetService
|
||||||
@inject LoginService loginService
|
@inject LoginService loginService
|
||||||
@inject IJSRuntime jsRuntime
|
@inject IJSRuntime jsRuntime
|
||||||
@inject IOptions<ServiceOptions> serviceOptions
|
@inject MediaService mediaService
|
||||||
|
|
||||||
<PageTitle>Home</PageTitle>
|
<PageTitle>Home</PageTitle>
|
||||||
|
|
||||||
@@ -36,7 +35,7 @@
|
|||||||
style="aspect-ratio:@GetAspectRatio(asset)"
|
style="aspect-ratio:@GetAspectRatio(asset)"
|
||||||
@onclick="() => OpenPreview(asset, capturedIndex)">
|
@onclick="() => OpenPreview(asset, capturedIndex)">
|
||||||
@if (asset.HasThumbnail) {
|
@if (asset.HasThumbnail) {
|
||||||
<img src="@ThumbUrl(asset.Id)"
|
<img src="@mediaService.ThumbUrl(asset.Id)"
|
||||||
loading="lazy" alt=""
|
loading="lazy" alt=""
|
||||||
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
||||||
<div class="tile-fallback" style="display:none">
|
<div class="tile-fallback" style="display:none">
|
||||||
@@ -136,18 +135,10 @@
|
|||||||
ElementReference topSentinelRef;
|
ElementReference topSentinelRef;
|
||||||
DotNetObjectReference<Home>? dotNetRef;
|
DotNetObjectReference<Home>? dotNetRef;
|
||||||
Guid _randomSeed;
|
Guid _randomSeed;
|
||||||
string token = string.Empty;
|
|
||||||
string apiBase => serviceOptions.Value.BaseUrl.TrimEnd('/');
|
|
||||||
|
|
||||||
string TokenParam => string.IsNullOrEmpty(token) ? "" : $"?token={token}";
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync() {
|
protected override async Task OnInitializedAsync() {
|
||||||
loginService.LoggedUserChanged += (_, _) => StateHasChanged();
|
loginService.LoggedUserChanged += (_, _) => StateHasChanged();
|
||||||
loginService.AuthInfoChanged += (_, info) => {
|
loginService.AuthInfoChanged += (_, _) => StateHasChanged();
|
||||||
token = info?.Token ?? string.Empty;
|
|
||||||
StateHasChanged();
|
|
||||||
};
|
|
||||||
token = loginService.AuthInfo?.Token ?? string.Empty;
|
|
||||||
await LoadFirstPage();
|
await LoadFirstPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,8 +268,6 @@
|
|||||||
return "4 / 3";
|
return "4 / 3";
|
||||||
}
|
}
|
||||||
|
|
||||||
string ThumbUrl(Guid id) => $"{apiBase}/api/media/thumb/{id}{TokenParam}";
|
|
||||||
|
|
||||||
public async ValueTask DisposeAsync() {
|
public async ValueTask DisposeAsync() {
|
||||||
await jsRuntime.InvokeVoidAsync("masonryObserver.unlockBodyScroll");
|
await jsRuntime.InvokeVoidAsync("masonryObserver.unlockBodyScroll");
|
||||||
if (dotNetRef != null) {
|
if (dotNetRef != null) {
|
||||||
|
|||||||
@@ -10,16 +10,13 @@
|
|||||||
</div>
|
</div>
|
||||||
} else {
|
} else {
|
||||||
<div class="progress" role="progressbar" style="height: @Height">
|
<div class="progress" role="progressbar" style="height: @Height">
|
||||||
<div class="progress-bar @BgClass" style="width: @((Value * 100).ToString(CultureInfo.InvariantCulture))%">
|
<div class="progress-bar @BgClass" style="width: @((Value * 100).ToString(CultureInfo.InvariantCulture))%"></div>
|
||||||
@if (ShowPercent) { @($"{Value * 100:F0}%") }
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public float Value { get; set; }
|
[Parameter] public float Value { get; set; }
|
||||||
[Parameter] public string? Color { get; set; }
|
[Parameter] public string? Color { get; set; }
|
||||||
[Parameter] public bool ShowPercent { get; set; }
|
|
||||||
[Parameter] public string Size { get; set; } = "sm";
|
[Parameter] public string Size { get; set; } = "sm";
|
||||||
[Parameter] public IReadOnlyList<(float Width, string Color)>? Segments { get; set; }
|
[Parameter] public IReadOnlyList<(float Width, string Color)>? Segments { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -124,13 +124,6 @@ public static class RegexHighlighter
|
|||||||
return new MarkupString(sb.ToString());
|
return new MarkupString(sb.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the set of CSS class names used by the highlighter for embedding in isolated CSS.
|
|
||||||
/// </summary>
|
|
||||||
public static string CssClasses => string.Join(" ",
|
|
||||||
ClassAnchor, ClassQuantifier, ClassCharClass, ClassEscape,
|
|
||||||
ClassNamedGroup, ClassGroupRef, ClassOperator, ClassLiteral);
|
|
||||||
|
|
||||||
private static int FindNamedGroupEnd(ReadOnlySpan<char> span, int start) =>
|
private static int FindNamedGroupEnd(ReadOnlySpan<char> span, int start) =>
|
||||||
FindMatchingParen(span, start);
|
FindMatchingParen(span, start);
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,13 @@
|
|||||||
@using Butter.Dtos.Asset
|
@using Butter.Dtos.Asset
|
||||||
@using Butter.Dtos.Person
|
@using Butter.Dtos.Person
|
||||||
@using Butter.Types
|
@using Butter.Types
|
||||||
@using Microsoft.Extensions.Options
|
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
|
|
||||||
@inject AssetService assetService
|
@inject AssetService assetService
|
||||||
@inject AlbumService albumService
|
@inject AlbumService albumService
|
||||||
@inject PersonService personService
|
@inject PersonService personService
|
||||||
@inject LoginService loginService
|
@inject LoginService loginService
|
||||||
@inject IOptions<ServiceOptions> serviceOptions
|
@inject MediaService mediaService
|
||||||
|
|
||||||
@if (Embedded) {
|
@if (Embedded) {
|
||||||
<div class="picker-embedded">
|
<div class="picker-embedded">
|
||||||
@@ -86,7 +85,7 @@
|
|||||||
<div class="picker-tile @(selectedIds.Contains(asset.Id) ? "selected" : "")"
|
<div class="picker-tile @(selectedIds.Contains(asset.Id) ? "selected" : "")"
|
||||||
@key="asset.Id" @onclick="() => ToggleSelection(asset.Id)">
|
@key="asset.Id" @onclick="() => ToggleSelection(asset.Id)">
|
||||||
@if (asset.HasThumbnail) {
|
@if (asset.HasThumbnail) {
|
||||||
<img src="@ThumbUrl(asset.Id)" loading="lazy" alt=""
|
<img src="@mediaService.ThumbUrl(asset.Id)" loading="lazy" alt=""
|
||||||
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
||||||
<div class="tile-fallback" style="display:none"><i class="bi bi-image"></i></div>
|
<div class="tile-fallback" style="display:none"><i class="bi bi-image"></i></div>
|
||||||
} else {
|
} else {
|
||||||
@@ -137,7 +136,7 @@
|
|||||||
<div class="picker-tile @(selectedIds.Contains(asset.Id) ? "selected" : "")"
|
<div class="picker-tile @(selectedIds.Contains(asset.Id) ? "selected" : "")"
|
||||||
@key="asset.Id" @onclick="() => ToggleSelection(asset.Id)">
|
@key="asset.Id" @onclick="() => ToggleSelection(asset.Id)">
|
||||||
@if (asset.HasThumbnail) {
|
@if (asset.HasThumbnail) {
|
||||||
<img src="@ThumbUrl(asset.Id)" loading="lazy" alt=""
|
<img src="@mediaService.ThumbUrl(asset.Id)" loading="lazy" alt=""
|
||||||
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
||||||
<div class="tile-fallback" style="display:none"><i class="bi bi-image"></i></div>
|
<div class="tile-fallback" style="display:none"><i class="bi bi-image"></i></div>
|
||||||
} else {
|
} else {
|
||||||
@@ -254,7 +253,7 @@
|
|||||||
<div class="picker-tile @(selectedIds.Contains(asset.Id) ? "selected" : "")"
|
<div class="picker-tile @(selectedIds.Contains(asset.Id) ? "selected" : "")"
|
||||||
@key="asset.Id" @onclick="() => ToggleSelection(asset.Id)">
|
@key="asset.Id" @onclick="() => ToggleSelection(asset.Id)">
|
||||||
@if (asset.HasThumbnail) {
|
@if (asset.HasThumbnail) {
|
||||||
<img src="@ThumbUrl(asset.Id)" loading="lazy" alt=""
|
<img src="@mediaService.ThumbUrl(asset.Id)" loading="lazy" alt=""
|
||||||
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
||||||
<div class="tile-fallback" style="display:none"><i class="bi bi-image"></i></div>
|
<div class="tile-fallback" style="display:none"><i class="bi bi-image"></i></div>
|
||||||
} else {
|
} else {
|
||||||
@@ -305,7 +304,7 @@
|
|||||||
<div class="picker-tile @(selectedIds.Contains(asset.Id) ? "selected" : "")"
|
<div class="picker-tile @(selectedIds.Contains(asset.Id) ? "selected" : "")"
|
||||||
@key="asset.Id" @onclick="() => ToggleSelection(asset.Id)">
|
@key="asset.Id" @onclick="() => ToggleSelection(asset.Id)">
|
||||||
@if (asset.HasThumbnail) {
|
@if (asset.HasThumbnail) {
|
||||||
<img src="@ThumbUrl(asset.Id)" loading="lazy" alt=""
|
<img src="@mediaService.ThumbUrl(asset.Id)" loading="lazy" alt=""
|
||||||
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
||||||
<div class="tile-fallback" style="display:none"><i class="bi bi-image"></i></div>
|
<div class="tile-fallback" style="display:none"><i class="bi bi-image"></i></div>
|
||||||
} else {
|
} else {
|
||||||
@@ -429,7 +428,6 @@
|
|||||||
string currentPath = "";
|
string currentPath = "";
|
||||||
HashSet<Guid> selectedIds = [];
|
HashSet<Guid> selectedIds = [];
|
||||||
string searchQuery = string.Empty;
|
string searchQuery = string.Empty;
|
||||||
string token = string.Empty;
|
|
||||||
int currentPage;
|
int currentPage;
|
||||||
bool isLoading;
|
bool isLoading;
|
||||||
bool hasMore = true;
|
bool hasMore = true;
|
||||||
@@ -455,15 +453,12 @@
|
|||||||
const int PageSize = 150;
|
const int PageSize = 150;
|
||||||
|
|
||||||
bool isAdminOrCurator => loginService.IsAdminOrCurator;
|
bool isAdminOrCurator => loginService.IsAdminOrCurator;
|
||||||
string apiBase => serviceOptions.Value.BaseUrl.TrimEnd('/');
|
|
||||||
string TokenParam => string.IsNullOrEmpty(token) ? "" : $"?token={token}";
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync() {
|
protected override async Task OnInitializedAsync() {
|
||||||
if (Embedded) await InitializeAsync();
|
if (Embedded) await InitializeAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync() {
|
protected override async Task OnParametersSetAsync() {
|
||||||
token = loginService.AuthInfo?.Token ?? string.Empty;
|
|
||||||
if (!Embedded && Show && !_initializing) {
|
if (!Embedded && Show && !_initializing) {
|
||||||
_initializing = true;
|
_initializing = true;
|
||||||
await InitializeAsync();
|
await InitializeAsync();
|
||||||
@@ -613,8 +608,6 @@
|
|||||||
_personSearchTimer?.Dispose();
|
_personSearchTimer?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
string ThumbUrl(Guid id) => $"{apiBase}/api/media/thumb/{id}{TokenParam}";
|
|
||||||
|
|
||||||
static string TruncateName(string name, int maxLen = 40) {
|
static string TruncateName(string name, int maxLen = 40) {
|
||||||
if (name.Length <= maxLen) return name;
|
if (name.Length <= maxLen) return name;
|
||||||
var lastSlash = name.LastIndexOf('/');
|
var lastSlash = name.LastIndexOf('/');
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
@using Butter.Dtos.Album
|
@using Butter.Dtos.Album
|
||||||
@using Butter.Types
|
@using Butter.Types
|
||||||
@using Microsoft.Extensions.Options
|
|
||||||
|
|
||||||
@inject IOptions<ServiceOptions> serviceOptions
|
|
||||||
@inject LoginService loginService
|
@inject LoginService loginService
|
||||||
|
@inject MediaService mediaService
|
||||||
|
|
||||||
<div class="@GetCardClass()" @key="Album.Id" style="@GetCardStyle()">
|
<div class="@GetCardClass()" @key="Album.Id" style="@GetCardStyle()">
|
||||||
<a href="/albums/@Album.Id" class="album-card-overlay" aria-label="View album @Album.Name"
|
<a href="/albums/@Album.Id" class="album-card-overlay" aria-label="View album @Album.Name"
|
||||||
@onclick="HandleOverlayClick" @onclick:preventDefault="SelectionMode"></a>
|
@onclick="HandleOverlayClick" @onclick:preventDefault="SelectionMode"></a>
|
||||||
@if (Album.CoverAssetId.HasValue) {
|
@if (Album.CoverAssetId.HasValue) {
|
||||||
<img src="@ThumbUrl(Album.CoverAssetId.Value)"
|
<img src="@mediaService.ThumbUrl(Album.CoverAssetId.Value)"
|
||||||
loading="lazy" alt=""
|
loading="lazy" alt=""
|
||||||
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
||||||
<div class="album-card-fallback" style="display:none">
|
<div class="album-card-fallback" style="display:none">
|
||||||
@@ -54,12 +53,6 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string ViewMode { get; set; } = "masonry";
|
public string ViewMode { get; set; } = "masonry";
|
||||||
|
|
||||||
string apiBase => serviceOptions.Value.BaseUrl.TrimEnd('/');
|
|
||||||
string token => loginService.AuthInfo?.Token ?? string.Empty;
|
|
||||||
string TokenParam => string.IsNullOrEmpty(token) ? "" : $"?token={token}";
|
|
||||||
|
|
||||||
string ThumbUrl(Guid id) => $"{apiBase}/api/media/thumb/{id}{TokenParam}";
|
|
||||||
|
|
||||||
string GetCardClass() {
|
string GetCardClass() {
|
||||||
var classes = new List<string> { "album-card" };
|
var classes = new List<string> { "album-card" };
|
||||||
if (SelectionMode) {
|
if (SelectionMode) {
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
@using Microsoft.Extensions.Options
|
|
||||||
@using MilkStream.Client.Services
|
|
||||||
@using System.Linq
|
@using System.Linq
|
||||||
|
|
||||||
@inject LoginService loginService
|
@inject MediaService mediaService
|
||||||
@inject IOptions<ServiceOptions> serviceOptions
|
|
||||||
|
|
||||||
<ModalFrame Title="Select Cover Image" Show="Show" Size="lg" OnClose="OnCancel">
|
<ModalFrame Title="Select Cover Image" Show="Show" Size="lg" OnClose="OnCancel">
|
||||||
<Body>
|
<Body>
|
||||||
@@ -15,7 +12,7 @@
|
|||||||
<div class="picker-tile @(SelectedAssetId == id ? "selected" : "")"
|
<div class="picker-tile @(SelectedAssetId == id ? "selected" : "")"
|
||||||
@key="id"
|
@key="id"
|
||||||
@onclick="() => SelectAsset(id)">
|
@onclick="() => SelectAsset(id)">
|
||||||
<img src="@ThumbUrl(id)"
|
<img src="@mediaService.ThumbUrl(id)"
|
||||||
loading="lazy" alt=""
|
loading="lazy" alt=""
|
||||||
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
||||||
<div class="tile-fallback" style="display:none">
|
<div class="tile-fallback" style="display:none">
|
||||||
@@ -47,15 +44,6 @@
|
|||||||
[Parameter] public EventCallback<Guid?> OnSelected { get; set; }
|
[Parameter] public EventCallback<Guid?> OnSelected { get; set; }
|
||||||
[Parameter] public EventCallback OnClosed { get; set; }
|
[Parameter] public EventCallback OnClosed { get; set; }
|
||||||
|
|
||||||
string token = string.Empty;
|
|
||||||
string apiBase => serviceOptions.Value.BaseUrl.TrimEnd('/');
|
|
||||||
|
|
||||||
string TokenParam => string.IsNullOrEmpty(token) ? "" : $"?token={token}";
|
|
||||||
|
|
||||||
protected override void OnParametersSet() {
|
|
||||||
token = loginService.AuthInfo?.Token ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SelectAsset(Guid id) {
|
void SelectAsset(Guid id) {
|
||||||
SelectedAssetId = id;
|
SelectedAssetId = id;
|
||||||
}
|
}
|
||||||
@@ -68,6 +56,4 @@
|
|||||||
async Task OnCancel() {
|
async Task OnCancel() {
|
||||||
await OnClosed.InvokeAsync();
|
await OnClosed.InvokeAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
string ThumbUrl(Guid id) => $"{apiBase}/api/media/thumb/{id}{TokenParam}";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
@using Butter.Dtos.Person
|
@using Butter.Dtos.Person
|
||||||
@using Butter.Types
|
@using Butter.Types
|
||||||
@using Microsoft.Extensions.Options
|
|
||||||
@using System.Globalization
|
@using System.Globalization
|
||||||
@implements IAsyncDisposable
|
@implements IAsyncDisposable
|
||||||
|
|
||||||
@inject PersonService personService
|
@inject PersonService personService
|
||||||
@inject LoginService loginService
|
@inject LoginService loginService
|
||||||
@inject IOptions<ServiceOptions> serviceOptions
|
@inject MediaService mediaService
|
||||||
@inject IJSRuntime jsRuntime
|
@inject IJSRuntime jsRuntime
|
||||||
|
|
||||||
@if (isLoading) {
|
@if (isLoading) {
|
||||||
@@ -34,7 +33,7 @@
|
|||||||
var cy1 = person.ProfileCropY ?? 50;
|
var cy1 = person.ProfileCropY ?? 50;
|
||||||
var zoom1 = person.ProfileCropZoom ?? 0.6f;
|
var zoom1 = person.ProfileCropZoom ?? 0.6f;
|
||||||
<div class="cosplayer-card-img-wrap">
|
<div class="cosplayer-card-img-wrap">
|
||||||
<img src="@ThumbUrl(person.ProfileAssetId.Value)" alt=""
|
<img src="@mediaService.ThumbUrl(person.ProfileAssetId.Value)" alt=""
|
||||||
class="cosplayer-card-img"
|
class="cosplayer-card-img"
|
||||||
style="width: calc(100% / @zoom1.ToString(CultureInfo.InvariantCulture)); transform: translate(calc(-@cx1.ToString(CultureInfo.InvariantCulture) * 1%), calc(-@cy1.ToString(CultureInfo.InvariantCulture) * 1%))"
|
style="width: calc(100% / @zoom1.ToString(CultureInfo.InvariantCulture)); transform: translate(calc(-@cx1.ToString(CultureInfo.InvariantCulture) * 1%), calc(-@cy1.ToString(CultureInfo.InvariantCulture) * 1%))"
|
||||||
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
onerror="this.style.display='none';this.nextElementSibling.style.display='flex'" />
|
||||||
@@ -92,14 +91,6 @@
|
|||||||
string? _prevSearch;
|
string? _prevSearch;
|
||||||
string? _prevSortBy;
|
string? _prevSortBy;
|
||||||
bool _prevSortAsc;
|
bool _prevSortAsc;
|
||||||
string _token = string.Empty;
|
|
||||||
string _apiBase => serviceOptions.Value.BaseUrl.TrimEnd('/');
|
|
||||||
string TokenParam => string.IsNullOrEmpty(_token) ? "" : $"?token={_token}";
|
|
||||||
|
|
||||||
protected override void OnInitialized() {
|
|
||||||
loginService.AuthInfoChanged += OnAuthInfoChanged;
|
|
||||||
_token = loginService.AuthInfo?.Token ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync() {
|
protected override async Task OnParametersSetAsync() {
|
||||||
bool filterChanged = _prevLoadVersion != LoadVersion
|
bool filterChanged = _prevLoadVersion != LoadVersion
|
||||||
@@ -183,8 +174,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string ThumbUrl(Guid id) => $"{_apiBase}/api/media/thumb/{id}{TokenParam}";
|
|
||||||
|
|
||||||
string GetCardClass(PersonPreviewDto person) {
|
string GetCardClass(PersonPreviewDto person) {
|
||||||
var classes = new List<string> { "cosplayer-card" };
|
var classes = new List<string> { "cosplayer-card" };
|
||||||
if (SelectionMode) {
|
if (SelectionMode) {
|
||||||
@@ -198,10 +187,6 @@
|
|||||||
return string.Join(" ", classes);
|
return string.Join(" ", classes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnAuthInfoChanged(object? _, AuthInfo? info) {
|
|
||||||
_token = info?.Token ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task DisposeObserver() {
|
async Task DisposeObserver() {
|
||||||
if (dotNetRef != null) {
|
if (dotNetRef != null) {
|
||||||
await jsRuntime.InvokeVoidAsync("cosplayerObserver.dispose");
|
await jsRuntime.InvokeVoidAsync("cosplayerObserver.dispose");
|
||||||
@@ -211,7 +196,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask DisposeAsync() {
|
public async ValueTask DisposeAsync() {
|
||||||
loginService.AuthInfoChanged -= OnAuthInfoChanged;
|
|
||||||
await DisposeObserver();
|
await DisposeObserver();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
@using Microsoft.Extensions.Options
|
|
||||||
|
|
||||||
@inject IOptions<ServiceOptions> Options
|
|
||||||
@inject LoginService LoginService
|
|
||||||
@inject IJSRuntime JSRuntime
|
@inject IJSRuntime JSRuntime
|
||||||
|
@inject MediaService mediaService
|
||||||
|
|
||||||
@if (Show && AssetId != Guid.Empty) {
|
@if (Show && AssetId != Guid.Empty) {
|
||||||
<div class="preview-overlay" @onkeydown="OnKeyDown" tabindex="0" @onclick="OnCloseCallback">
|
<div class="preview-overlay" @onkeydown="OnKeyDown" tabindex="0" @onclick="OnCloseCallback">
|
||||||
@@ -18,10 +15,10 @@
|
|||||||
|
|
||||||
<div class="preview-image-wrapper">
|
<div class="preview-image-wrapper">
|
||||||
@if (HasPreview) {
|
@if (HasPreview) {
|
||||||
<img class="preview-img" src="@PreviewUrl" alt=""
|
<img class="preview-img" src="@mediaService.PreviewUrl(AssetId)" alt=""
|
||||||
onerror="this.onerror=null;this.src='@ThumbUrl'" />
|
onerror="this.onerror=null;this.src='@mediaService.ThumbUrl(AssetId)'" />
|
||||||
} else {
|
} else {
|
||||||
<img class="preview-img" src="@ThumbUrl" alt="" />
|
<img class="preview-img" src="@mediaService.ThumbUrl(AssetId)" alt="" />
|
||||||
}
|
}
|
||||||
<div class="preview-info">
|
<div class="preview-info">
|
||||||
<div class="preview-info-text">
|
<div class="preview-info-text">
|
||||||
@@ -32,7 +29,7 @@
|
|||||||
<span class="preview-info-filename">@Filename</span>
|
<span class="preview-info-filename">@Filename</span>
|
||||||
}
|
}
|
||||||
<div class="preview-info-actions">
|
<div class="preview-info-actions">
|
||||||
<a class="preview-action-btn" href="@OriginalUrl" target="_blank"
|
<a class="preview-action-btn" href="@mediaService.OriginalUrl(AssetId)" target="_blank"
|
||||||
title="View full size" @onclick:stopPropagation="true">
|
title="View full size" @onclick:stopPropagation="true">
|
||||||
<i class="bi bi-arrows-fullscreen"></i>
|
<i class="bi bi-arrows-fullscreen"></i>
|
||||||
</a>
|
</a>
|
||||||
@@ -59,18 +56,6 @@
|
|||||||
[Parameter] public EventCallback OnClose { get; set; }
|
[Parameter] public EventCallback OnClose { get; set; }
|
||||||
[Parameter] public RenderFragment? ChildContent { get; set; }
|
[Parameter] public RenderFragment? ChildContent { get; set; }
|
||||||
|
|
||||||
string token = string.Empty;
|
|
||||||
string apiBase => Options.Value.BaseUrl.TrimEnd('/');
|
|
||||||
string TokenParam => string.IsNullOrEmpty(token) ? "" : $"?token={token}";
|
|
||||||
string ThumbUrl => $"{apiBase}/api/media/thumb/{AssetId}{TokenParam}";
|
|
||||||
string PreviewUrl => $"{apiBase}/api/media/preview/{AssetId}{TokenParam}";
|
|
||||||
string OriginalUrl => $"{apiBase}/api/media/original/{AssetId}{TokenParam}";
|
|
||||||
|
|
||||||
protected override void OnInitialized() {
|
|
||||||
LoginService.AuthInfoChanged += (_, info) => { token = info?.Token ?? string.Empty; };
|
|
||||||
token = LoginService.AuthInfo?.Token ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task OnCloseCallback() {
|
async Task OnCloseCallback() {
|
||||||
await OnClose.InvokeAsync();
|
await OnClose.InvokeAsync();
|
||||||
}
|
}
|
||||||
@@ -90,6 +75,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async Task Download() {
|
async Task Download() {
|
||||||
await JSRuntime.InvokeVoidAsync("masonryObserver.downloadFile", OriginalUrl);
|
await JSRuntime.InvokeVoidAsync("masonryObserver.downloadFile", mediaService.OriginalUrl(AssetId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
@using Microsoft.Extensions.Options
|
|
||||||
@inject IJSRuntime jsRuntime
|
@inject IJSRuntime jsRuntime
|
||||||
@inject IOptions<ServiceOptions> serviceOptions
|
@inject MediaService mediaService
|
||||||
@inject LoginService loginService
|
|
||||||
|
|
||||||
@if (Show && AssetId.HasValue) {
|
@if (Show && AssetId.HasValue) {
|
||||||
<div class="modal-overlay" style="background: rgba(0, 0, 0, 0.85)" @onclick="OnCancel">
|
<div class="modal-overlay" style="background: rgba(0, 0, 0, 0.85)" @onclick="OnCancel">
|
||||||
@@ -72,11 +70,6 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter] public EventCallback OnClosed { get; set; }
|
[Parameter] public EventCallback OnClosed { get; set; }
|
||||||
|
|
||||||
string apiBase => serviceOptions.Value.BaseUrl.TrimEnd('/');
|
|
||||||
string token => loginService.AuthInfo?.Token ?? string.Empty;
|
|
||||||
string TokenParam => string.IsNullOrEmpty(token) ? "" : $"?token={token}";
|
|
||||||
string ImageUrl => $"{apiBase}/api/media/thumb/{AssetId!.Value}{TokenParam}";
|
|
||||||
|
|
||||||
protected override void OnInitialized() {
|
protected override void OnInitialized() {
|
||||||
_instanceId = ++_counter;
|
_instanceId = ++_counter;
|
||||||
}
|
}
|
||||||
@@ -93,7 +86,7 @@
|
|||||||
if (_pendingInit) {
|
if (_pendingInit) {
|
||||||
_pendingInit = false;
|
_pendingInit = false;
|
||||||
await jsRuntime.InvokeVoidAsync("profileCropper.initialize",
|
await jsRuntime.InvokeVoidAsync("profileCropper.initialize",
|
||||||
ContainerId, ImageUrl, CropX ?? 50, CropY ?? 50, CropZoom ?? 0.6f);
|
ContainerId, mediaService.ThumbUrl(AssetId!.Value), CropX ?? 50, CropY ?? 50, CropZoom ?? 0.6f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
@using Butter.Dtos.Album
|
@using Butter.Dtos.Album
|
||||||
@using Butter.Dtos.Person
|
@using Butter.Dtos.Person
|
||||||
@using Microsoft.Extensions.Options
|
|
||||||
@using MilkStream.Client.Services
|
@using MilkStream.Client.Services
|
||||||
|
|
||||||
@namespace MilkStream.Client.Components.Shared
|
@namespace MilkStream.Client.Components.Shared
|
||||||
@@ -8,8 +7,7 @@
|
|||||||
@inject PersonService PersonSvc
|
@inject PersonService PersonSvc
|
||||||
@inject AlbumService AlbumSvc
|
@inject AlbumService AlbumSvc
|
||||||
@inject NavigationManager Nav
|
@inject NavigationManager Nav
|
||||||
@inject IOptions<ServiceOptions> ServiceOptions
|
@inject MediaService mediaService
|
||||||
@inject LoginService LoginSvc
|
|
||||||
|
|
||||||
<div class="search-dropdown-container position-relative">
|
<div class="search-dropdown-container position-relative">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
@@ -67,7 +65,7 @@
|
|||||||
@onmouseenter="() => OnItemHover(idx)">
|
@onmouseenter="() => OnItemHover(idx)">
|
||||||
@if (item.ImageId.HasValue)
|
@if (item.ImageId.HasValue)
|
||||||
{
|
{
|
||||||
<img class="search-dropdown-thumb" src="@ThumbUrl(item.ImageId.Value)" alt="" loading="lazy" />
|
<img class="search-dropdown-thumb" src="@mediaService.ThumbUrl(item.ImageId.Value)" alt="" loading="lazy" />
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -85,7 +83,7 @@
|
|||||||
@onmouseenter="() => OnItemHover(idx)">
|
@onmouseenter="() => OnItemHover(idx)">
|
||||||
@if (item.ImageId.HasValue)
|
@if (item.ImageId.HasValue)
|
||||||
{
|
{
|
||||||
<img class="search-dropdown-thumb-rounded" src="@ThumbUrl(item.ImageId.Value)" alt="" loading="lazy" />
|
<img class="search-dropdown-thumb-rounded" src="@mediaService.ThumbUrl(item.ImageId.Value)" alt="" loading="lazy" />
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -117,12 +115,6 @@
|
|||||||
int _selectedIndex = -1;
|
int _selectedIndex = -1;
|
||||||
CancellationTokenSource? _blurCts;
|
CancellationTokenSource? _blurCts;
|
||||||
|
|
||||||
string ApiBase => ServiceOptions.Value.BaseUrl.TrimEnd('/');
|
|
||||||
string Token => LoginSvc.AuthInfo?.Token ?? string.Empty;
|
|
||||||
string TokenParam => string.IsNullOrEmpty(Token) ? "" : $"?token={Token}";
|
|
||||||
|
|
||||||
string ThumbUrl(Guid id) => $"{ApiBase}/api/media/thumb/{id}{TokenParam}";
|
|
||||||
|
|
||||||
async Task OnInput(ChangeEventArgs e)
|
async Task OnInput(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_query = e.Value?.ToString() ?? string.Empty;
|
_query = e.Value?.ToString() ?? string.Empty;
|
||||||
|
|||||||
@@ -12,15 +12,6 @@ public sealed class FoldersService(
|
|||||||
LoginService loginService,
|
LoginService loginService,
|
||||||
ILogger<FoldersService> logger
|
ILogger<FoldersService> logger
|
||||||
) : AuthServiceBase(options, httpClientFactory, loginService, logger) {
|
) : AuthServiceBase(options, httpClientFactory, loginService, logger) {
|
||||||
/// <summary>
|
|
||||||
/// Fired when folders are saved.
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler? SaveFolders;
|
|
||||||
/// <summary>
|
|
||||||
/// Invokes the <see cref="SaveFolders"/> event.
|
|
||||||
/// </summary>
|
|
||||||
public void OnSaveFolders() => SaveFolders?.Invoke(this, EventArgs.Empty);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all tracked folders.
|
/// Gets all tracked folders.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -33,19 +24,6 @@ public sealed class FoldersService(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a folder by its ID.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">The folder ID.</param>
|
|
||||||
/// <returns>The folder details, or null if not found.</returns>
|
|
||||||
public async Task<FolderFullDto?> GetFolderById(Guid id) {
|
|
||||||
var response = await Client.GetAsync($"/api/folder/{id}");
|
|
||||||
|
|
||||||
if (response.IsSuccessStatusCode) return await response.Content.ReadFromJsonAsync<FolderFullDto>();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new folder.
|
/// Creates a new folder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -3,28 +3,37 @@ using Microsoft.Extensions.Options;
|
|||||||
namespace MilkStream.Client.Services;
|
namespace MilkStream.Client.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides access to media files (original, thumbnails, previews) via the API.
|
/// Builds media URLs (original, thumbnail, preview) authenticated via a token query parameter.
|
||||||
|
/// Media is served by <c>MediaController</c> through <c><img></c>-accessible URLs, so the
|
||||||
|
/// JWT is passed as a <c>token</c> query parameter rather than an Authorization header.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class MediaService : AuthServiceBase {
|
/// <param name="options">Service configuration options.</param>
|
||||||
/// <inheritdoc />
|
/// <param name="loginService">The login service providing the current auth token.</param>
|
||||||
protected override HttpClient Client { get; init; }
|
public sealed class MediaService(IOptions<ServiceOptions> options, LoginService loginService) {
|
||||||
/// <inheritdoc />
|
string BaseUrl => options.Value.BaseUrl.TrimEnd('/');
|
||||||
protected override ILogger Logger { get; init; }
|
|
||||||
|
string TokenParam {
|
||||||
|
get {
|
||||||
|
var token = loginService.AuthInfo?.Token;
|
||||||
|
return string.IsNullOrEmpty(token) ? "" : $"?token={token}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialises a new instance of the <see cref="MediaService"/> class.
|
/// Gets the thumbnail URL for an asset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="options">Service configuration options.</param>
|
/// <param name="id">The asset ID.</param>
|
||||||
/// <param name="httpClientFactory">Factory for creating HTTP clients.</param>
|
public string ThumbUrl(Guid id) => $"{BaseUrl}/api/media/thumb/{id}{TokenParam}";
|
||||||
/// <param name="loginService">The login service for authentication.</param>
|
|
||||||
/// <param name="logger">Logger instance.</param>
|
/// <summary>
|
||||||
public MediaService(
|
/// Gets the preview URL for an asset.
|
||||||
IOptions<ServiceOptions> options,
|
/// </summary>
|
||||||
IHttpClientFactory httpClientFactory,
|
/// <param name="id">The asset ID.</param>
|
||||||
LoginService loginService,
|
public string PreviewUrl(Guid id) => $"{BaseUrl}/api/media/preview/{id}{TokenParam}";
|
||||||
ILogger<MediaService> logger
|
|
||||||
) : base(options, httpClientFactory, loginService, logger) {
|
/// <summary>
|
||||||
Client = base.Client;
|
/// Gets the original image URL for an asset.
|
||||||
Logger = logger;
|
/// </summary>
|
||||||
}
|
/// <param name="id">The asset ID.</param>
|
||||||
|
public string OriginalUrl(Guid id) => $"{BaseUrl}/api/media/original/{id}{TokenParam}";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace MilkStream.Client;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides extension methods for string operations.
|
|
||||||
/// </summary>
|
|
||||||
public class StringExtensions {
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user