mirror of https://github.com/ErsatzTV/ErsatzTV.git
Browse Source
* rename yaml validation to sequential schedule validation * some better exception handling * add block playout troubleshooting page * add paged block playout history * add history details * update changelogpull/2513/head
21 changed files with 592 additions and 7 deletions
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
namespace ErsatzTV.Application.Playouts; |
||||
|
||||
public record PagedPlayoutHistoryViewModel(int TotalCount, List<PlayoutHistoryViewModel> Page); |
||||
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
namespace ErsatzTV.Application.Playouts; |
||||
|
||||
public record PlayoutHistoryViewModel(int Id, DateTimeOffset When, DateTimeOffset Finish, string Key, string Details); |
||||
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
namespace ErsatzTV.Application.Playouts; |
||||
|
||||
public record GetAllBlockPlayouts : IRequest<List<PlayoutNameViewModel>>; |
||||
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Infrastructure.Data; |
||||
using Microsoft.EntityFrameworkCore; |
||||
|
||||
namespace ErsatzTV.Application.Playouts; |
||||
|
||||
public class GetAllBlockPlayoutsHandler(IDbContextFactory<TvContext> dbContextFactory) |
||||
: IRequestHandler<GetAllBlockPlayouts, List<PlayoutNameViewModel>> |
||||
{ |
||||
public async Task<List<PlayoutNameViewModel>> Handle( |
||||
GetAllBlockPlayouts request, |
||||
CancellationToken cancellationToken) |
||||
{ |
||||
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); |
||||
|
||||
List<Playout> playouts = await dbContext.Playouts |
||||
.AsNoTracking() |
||||
.Include(p => p.Channel) |
||||
.Include(p => p.ProgramSchedule) |
||||
.Include(p => p.BuildStatus) |
||||
.Where(p => p.Channel != null && p.ScheduleKind == PlayoutScheduleKind.Block) |
||||
.ToListAsync(cancellationToken); |
||||
|
||||
return playouts.Map(Mapper.ProjectToViewModel).ToList(); |
||||
} |
||||
} |
||||
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
namespace ErsatzTV.Application.Playouts; |
||||
|
||||
public record GetBlockPlayoutHistory(int PlayoutId, int BlockId, int PageNum, int PageSize) |
||||
: IRequest<PagedPlayoutHistoryViewModel>; |
||||
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
using ErsatzTV.Core.Domain.Scheduling; |
||||
using ErsatzTV.Infrastructure.Data; |
||||
using Microsoft.EntityFrameworkCore; |
||||
|
||||
namespace ErsatzTV.Application.Playouts; |
||||
|
||||
public class GetBlockPlayoutHistoryHandler(IDbContextFactory<TvContext> dbContextFactory) |
||||
: IRequestHandler<GetBlockPlayoutHistory, PagedPlayoutHistoryViewModel> |
||||
{ |
||||
public async Task<PagedPlayoutHistoryViewModel> Handle( |
||||
GetBlockPlayoutHistory request, |
||||
CancellationToken cancellationToken) |
||||
{ |
||||
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); |
||||
|
||||
IQueryable<PlayoutHistory> query = dbContext.PlayoutHistory |
||||
.AsNoTracking() |
||||
.Where(ph => ph.PlayoutId == request.PlayoutId && ph.BlockId == request.BlockId); |
||||
|
||||
int totalCount = await query.CountAsync(cancellationToken); |
||||
|
||||
List<PlayoutHistory> allHistory = await query |
||||
.OrderBy(ph => ph.Id) |
||||
.Skip(request.PageNum * request.PageSize) |
||||
.Take(request.PageSize) |
||||
.ToListAsync(cancellationToken); |
||||
|
||||
return new PagedPlayoutHistoryViewModel(totalCount, allHistory.Map(Mapper.ProjectToViewModel).ToList()); |
||||
} |
||||
} |
||||
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
namespace ErsatzTV.Application.Scheduling; |
||||
|
||||
public record GetAllBlocksForPlayout(int PlayoutId) : IRequest<List<BlockViewModel>>; |
||||
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
using Dapper; |
||||
using ErsatzTV.Core.Domain.Scheduling; |
||||
using ErsatzTV.Infrastructure.Data; |
||||
using Microsoft.EntityFrameworkCore; |
||||
|
||||
namespace ErsatzTV.Application.Scheduling; |
||||
|
||||
public class GetAllBlocksForPlayoutHandler(IDbContextFactory<TvContext> dbContextFactory) |
||||
: IRequestHandler<GetAllBlocksForPlayout, List<BlockViewModel>> |
||||
{ |
||||
public async Task<List<BlockViewModel>> Handle(GetAllBlocksForPlayout request, CancellationToken cancellationToken) |
||||
{ |
||||
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); |
||||
|
||||
List<int> blockIds = await dbContext.Connection.QueryAsync<int>( |
||||
"""
|
||||
SELECT DISTINCT TI.BlockId |
||||
FROM TemplateItem TI |
||||
INNER JOIN PlayoutTemplate PT ON PT.TemplateId = TI.TemplateId |
||||
WHERE PT.PlayoutId = @PlayoutId |
||||
""",
|
||||
new { request.PlayoutId }) |
||||
.Map(result => result.ToList()); |
||||
|
||||
List<Block> blocks = await dbContext.Blocks |
||||
.AsNoTracking() |
||||
.Where(b => blockIds.Contains(b.Id)) |
||||
.ToListAsync(cancellationToken); |
||||
|
||||
List<BlockGroup> blockGroups = await dbContext.BlockGroups |
||||
.AsNoTracking() |
||||
.ToListAsync(cancellationToken); |
||||
|
||||
// match blocks to block groups
|
||||
foreach (var block in blocks) |
||||
{ |
||||
var maybeBlockGroup = blockGroups.FirstOrDefault(bg => bg.Id == block.BlockGroupId); |
||||
if (maybeBlockGroup != null) |
||||
{ |
||||
block.BlockGroup = maybeBlockGroup; |
||||
} |
||||
} |
||||
|
||||
return blocks.Map(Mapper.ProjectToViewModel) |
||||
.OrderBy(b => b.GroupName) |
||||
.ThenBy(b => b.Name) |
||||
.ToList(); |
||||
} |
||||
} |
||||
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
using ErsatzTV.Core.Domain; |
||||
|
||||
namespace ErsatzTV.Application.Troubleshooting; |
||||
|
||||
public record PlayoutHistoryDetailsViewModel( |
||||
PlaybackOrder PlaybackOrder, |
||||
CollectionType CollectionType, |
||||
string Name, |
||||
string MediaItemType, |
||||
string MediaItemTitle); |
||||
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
using ErsatzTV.Application.Playouts; |
||||
|
||||
namespace ErsatzTV.Application.Troubleshooting.Queries; |
||||
|
||||
public record DecodePlayoutHistory(PlayoutHistoryViewModel PlayoutHistory) : IRequest<PlayoutHistoryDetailsViewModel>; |
||||
@ -0,0 +1,97 @@
@@ -0,0 +1,97 @@
|
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Infrastructure.Data; |
||||
using ErsatzTV.Infrastructure.Extensions; |
||||
using Microsoft.EntityFrameworkCore; |
||||
using Newtonsoft.Json; |
||||
|
||||
namespace ErsatzTV.Application.Troubleshooting.Queries; |
||||
|
||||
public class DecodePlayoutHistoryHandler(IDbContextFactory<TvContext> dbContextFactory) |
||||
: IRequestHandler<DecodePlayoutHistory, PlayoutHistoryDetailsViewModel> |
||||
{ |
||||
public async Task<PlayoutHistoryDetailsViewModel> Handle( |
||||
DecodePlayoutHistory request, |
||||
CancellationToken cancellationToken) |
||||
{ |
||||
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); |
||||
|
||||
var decodedKey = JsonConvert.DeserializeObject<BlockItemHistoryKey>(request.PlayoutHistory.Key); |
||||
|
||||
PlaybackOrder playbackOrder = decodedKey.PlaybackOrder ?? PlaybackOrder.None; |
||||
CollectionType collectionType = decodedKey.CollectionType ?? CollectionType.Collection; |
||||
|
||||
string name = string.Empty; |
||||
|
||||
switch (collectionType) |
||||
{ |
||||
case CollectionType.Collection: |
||||
name = await dbContext.Collections |
||||
.AsNoTracking() |
||||
.Where(c => c.Id == (decodedKey.CollectionId ?? 0)) |
||||
.Map(c => c.Name) |
||||
.FirstOrDefaultAsync(cancellationToken); |
||||
break; |
||||
case CollectionType.SmartCollection: |
||||
name = await dbContext.SmartCollections |
||||
.AsNoTracking() |
||||
.Where(c => c.Id == (decodedKey.SmartCollectionId ?? 0)) |
||||
.Map(c => c.Name) |
||||
.FirstOrDefaultAsync(cancellationToken); |
||||
break; |
||||
} |
||||
|
||||
string mediaItemType = string.Empty; |
||||
string mediaItemTitle = string.Empty; |
||||
|
||||
Details details = JsonConvert.DeserializeObject<Details>(request.PlayoutHistory.Details); |
||||
if (details?.MediaItemId != null) |
||||
{ |
||||
Option<MediaItem> maybeMediaItem = await dbContext.MediaItems |
||||
.AsNoTracking() |
||||
.Include(i => i.LibraryPath) |
||||
.ThenInclude(lp => lp.Library) |
||||
.ThenInclude(l => l.MediaSource) |
||||
.Include(i => (i as Movie).MovieMetadata) |
||||
.Include(i => (i as Episode).EpisodeMetadata) |
||||
.Include(i => (i as Episode).Season) |
||||
.ThenInclude(s => s.Show) |
||||
.ThenInclude(s => s.ShowMetadata) |
||||
.Include(i => (i as OtherVideo).OtherVideoMetadata) |
||||
.Include(i => (i as Image).ImageMetadata) |
||||
.Include(i => (i as RemoteStream).RemoteStreamMetadata) |
||||
.Include(i => (i as Song).SongMetadata) |
||||
.Include(i => (i as MusicVideo).MusicVideoMetadata) |
||||
.Include(i => (i as MusicVideo).Artist) |
||||
.ThenInclude(a => a.ArtistMetadata) |
||||
.SelectOneAsync(i => i.Id, i => i.Id == details.MediaItemId, cancellationToken); |
||||
|
||||
foreach (var mediaItem in maybeMediaItem) |
||||
{ |
||||
mediaItemType = mediaItem switch |
||||
{ |
||||
Episode => "Episode", |
||||
Movie => "Movie", |
||||
MusicVideo => "Music Video", |
||||
OtherVideo => "Other Video", |
||||
Song => "Song", |
||||
Image => "Image", |
||||
RemoteStream => "Remote Stream", |
||||
_ => $"Unknown ({mediaItem.GetType().Name})" |
||||
}; |
||||
|
||||
mediaItemTitle = Playouts.Mapper.GetDisplayTitle(mediaItem, Option<string>.None); |
||||
} |
||||
} |
||||
|
||||
return new PlayoutHistoryDetailsViewModel(playbackOrder, collectionType, name, mediaItemType, mediaItemTitle); |
||||
} |
||||
|
||||
private sealed record BlockItemHistoryKey( |
||||
int? BlockId, |
||||
PlaybackOrder? PlaybackOrder, |
||||
CollectionType? CollectionType, |
||||
int? CollectionId, |
||||
int? SmartCollectionId); |
||||
|
||||
private sealed record Details(int? MediaItemId); |
||||
} |
||||
@ -0,0 +1,159 @@
@@ -0,0 +1,159 @@
|
||||
@page "/system/troubleshooting/block-playout/history" |
||||
@using System.Globalization |
||||
@using ErsatzTV.Application.Configuration |
||||
@using ErsatzTV.Application.Playouts |
||||
@using ErsatzTV.Application.Troubleshooting |
||||
@using ErsatzTV.Application.Troubleshooting.Queries |
||||
@implements IDisposable |
||||
@inject IMediator Mediator |
||||
@inject NavigationManager NavigationManager |
||||
|
||||
<MudForm Style="max-height: 100%"> |
||||
<div class="d-flex flex-column" style="height: 100vh; overflow-x: auto"> |
||||
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8"> |
||||
<MudText Typo="Typo.h5" Class="mb-2">Block Playout History</MudText> |
||||
<MudDivider Class="mb-6"/> |
||||
<MudTable Hover="true" |
||||
Dense="true" |
||||
Class="mt-8" |
||||
SelectedItemChanged="@(async (PlayoutHistoryViewModel x) => await PlayoutHistorySelected(x))" |
||||
@bind-RowsPerPage="@_rowsPerPage" |
||||
ServerData="@(new Func<TableState, CancellationToken, Task<TableData<PlayoutHistoryViewModel>>>(ServerReload))" |
||||
RowClassFunc="@SelectedRowClassFunc"> |
||||
<ColGroup> |
||||
<MudHidden Breakpoint="Breakpoint.Xs"> |
||||
<col/> |
||||
<col/> |
||||
<col/> |
||||
<col/> |
||||
</MudHidden> |
||||
</ColGroup> |
||||
<HeaderContent> |
||||
<MudTh>Start</MudTh> |
||||
<MudTh>Finish</MudTh> |
||||
<MudTh>Key</MudTh> |
||||
<MudTh>Details</MudTh> |
||||
</HeaderContent> |
||||
<RowTemplate> |
||||
<MudTd>@context.When.ToString("G", _dtf)</MudTd> |
||||
<MudTd>@context.Finish.ToString("G", _dtf)</MudTd> |
||||
<MudTd>@context.Key</MudTd> |
||||
<MudTd>@context.Details</MudTd> |
||||
</RowTemplate> |
||||
<PagerContent> |
||||
<MudTablePager/> |
||||
</PagerContent> |
||||
</MudTable> |
||||
<MudText Typo="Typo.h5" Class="mt-10 mb-2">History Details</MudText> |
||||
<MudDivider Class="mb-6"/> |
||||
@if (_decodedHistory != null) |
||||
{ |
||||
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5"> |
||||
<div class="d-flex"> |
||||
<MudText>Playback Order</MudText> |
||||
</div> |
||||
<MudTextField Value="@_decodedHistory.PlaybackOrder.ToString()" ReadOnly="true" /> |
||||
</MudStack> |
||||
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5"> |
||||
<div class="d-flex"> |
||||
<MudText>Collection Type</MudText> |
||||
</div> |
||||
<MudTextField Value="@_decodedHistory.CollectionType.ToString()" ReadOnly="true" /> |
||||
</MudStack> |
||||
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5"> |
||||
<div class="d-flex"> |
||||
<MudText>Collection Name</MudText> |
||||
</div> |
||||
<MudTextField Value="@_decodedHistory.Name" ReadOnly="true" /> |
||||
</MudStack> |
||||
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5"> |
||||
<div class="d-flex"> |
||||
<MudText>Media Item Type</MudText> |
||||
</div> |
||||
<MudTextField Value="@_decodedHistory.MediaItemType" ReadOnly="true" /> |
||||
</MudStack> |
||||
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5"> |
||||
<div class="d-flex"> |
||||
<MudText>Media Item Title</MudText> |
||||
</div> |
||||
<MudTextField Value="@_decodedHistory.MediaItemTitle" ReadOnly="true" /> |
||||
</MudStack> |
||||
} |
||||
</MudContainer> |
||||
</div> |
||||
</MudForm> |
||||
|
||||
@code { |
||||
[SupplyParameterFromQuery(Name = "playoutId")] |
||||
public int? PlayoutId { get; set; } |
||||
|
||||
[SupplyParameterFromQuery(Name = "blockId")] |
||||
public int? BlockId { get; set; } |
||||
|
||||
private CancellationTokenSource _cts; |
||||
private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentUICulture.DateTimeFormat; |
||||
private int _rowsPerPage = 10; |
||||
private int? _selectedPlayoutHistoryId; |
||||
private PlayoutHistoryDetailsViewModel _decodedHistory; |
||||
|
||||
public void Dispose() |
||||
{ |
||||
_cts?.Cancel(); |
||||
_cts?.Dispose(); |
||||
} |
||||
|
||||
protected override async Task OnParametersSetAsync() |
||||
{ |
||||
if (PlayoutId is null || BlockId is null) |
||||
{ |
||||
NavigationManager.NavigateTo("system/troubleshooting"); |
||||
} |
||||
|
||||
_cts?.Cancel(); |
||||
_cts?.Dispose(); |
||||
_cts = new CancellationTokenSource(); |
||||
var token = _cts.Token; |
||||
|
||||
try |
||||
{ |
||||
_rowsPerPage = await Mediator.Send(new GetConfigElementByKey(ConfigElementKey.TroubleshootingBlockPlayoutHistoryPageSize), token) |
||||
.Map(maybeRows => maybeRows.Match(ce => int.TryParse(ce.Value, out int rows) ? rows : 10, () => 10)); |
||||
} |
||||
catch (OperationCanceledException) |
||||
{ |
||||
// do nothing |
||||
} |
||||
} |
||||
|
||||
private async Task<TableData<PlayoutHistoryViewModel>> ServerReload(TableState state, CancellationToken cancellationToken) |
||||
{ |
||||
_selectedPlayoutHistoryId = null; |
||||
_decodedHistory = null; |
||||
await InvokeAsync(StateHasChanged); |
||||
|
||||
await Mediator.Send(new SaveConfigElementByKey(ConfigElementKey.TroubleshootingBlockPlayoutHistoryPageSize, state.PageSize.ToString()), cancellationToken); |
||||
|
||||
PagedPlayoutHistoryViewModel data = await Mediator.Send(new GetBlockPlayoutHistory(PlayoutId!.Value, BlockId!.Value, state.Page, state.PageSize), cancellationToken); |
||||
return new TableData<PlayoutHistoryViewModel> { TotalItems = data.TotalCount, Items = data.Page }; |
||||
} |
||||
|
||||
private async Task PlayoutHistorySelected(PlayoutHistoryViewModel playoutHistory) |
||||
{ |
||||
_selectedPlayoutHistoryId = playoutHistory.Id; |
||||
|
||||
await Task.Delay(100); |
||||
|
||||
_decodedHistory = await Mediator.Send(new DecodePlayoutHistory(playoutHistory), _cts?.Token ?? CancellationToken.None); |
||||
await InvokeAsync(StateHasChanged); |
||||
} |
||||
|
||||
private string SelectedRowClassFunc(PlayoutHistoryViewModel element, int rowNumber) |
||||
{ |
||||
if (_selectedPlayoutHistoryId != null && _selectedPlayoutHistoryId == element.Id) |
||||
{ |
||||
return "selected"; |
||||
} |
||||
|
||||
return string.Empty; |
||||
} |
||||
} |
||||
@ -0,0 +1,162 @@
@@ -0,0 +1,162 @@
|
||||
@page "/system/troubleshooting/block-playout" |
||||
@using ErsatzTV.Application.Playouts |
||||
@using ErsatzTV.Application.Scheduling |
||||
@implements IDisposable |
||||
@inject IMediator Mediator |
||||
|
||||
<MudForm Style="max-height: 100%"> |
||||
<div class="d-flex flex-column" style="height: 100vh; overflow-x: auto"> |
||||
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8"> |
||||
<MudText Typo="Typo.h5" Class="mb-2">Block Playout Troubleshooting</MudText> |
||||
<MudDivider Class="mb-6"/> |
||||
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5"> |
||||
<div class="d-flex"> |
||||
<MudText>Block Playout</MudText> |
||||
</div> |
||||
<MudSelect T="PlayoutNameViewModel" SelectedValuesChanged="OnSelectedPlayoutChanged"> |
||||
@foreach (PlayoutNameViewModel playout in _playouts) |
||||
{ |
||||
<MudSelectItem Value="@playout">@playout.ChannelNumber - @playout.ChannelName</MudSelectItem> |
||||
} |
||||
</MudSelect> |
||||
</MudStack> |
||||
<MudText Typo="Typo.h5" Class="mt-10 mb-2">Blocks</MudText> |
||||
<MudDivider Class="mb-6"/> |
||||
<MudTable Hover="true" |
||||
Dense="true" |
||||
Class="mt-8" |
||||
Items="@_blocks" |
||||
GroupBy="@_groupDefinition" |
||||
GroupHeaderStyle="background-color:var(--mud-palette-appbarbackground)" |
||||
RowStyle="background-color:var(--mud-palette-background-gray)" |
||||
Filter="new Func<BlockViewModel,bool>(FilterBlocks)"> |
||||
<ColGroup> |
||||
<MudHidden Breakpoint="Breakpoint.Xs"> |
||||
<col style="width: 60px;"/> |
||||
<col/> |
||||
<col style="width: 120px;"/> |
||||
</MudHidden> |
||||
</ColGroup> |
||||
<ToolBarContent> |
||||
<MudTextField T="string" |
||||
ValueChanged="@(s => OnSearch(s))" |
||||
Placeholder="Search for blocks" |
||||
Adornment="Adornment.Start" |
||||
AdornmentIcon="@Icons.Material.Filled.FilterList" |
||||
Clearable="true"> |
||||
</MudTextField> |
||||
</ToolBarContent> |
||||
<GroupHeaderTemplate> |
||||
<MudTd Class="mud-table-cell-custom-group"> |
||||
@($"{context.Key}") |
||||
</MudTd> |
||||
<MudTd> |
||||
<div style="align-items: center; display: flex;"> |
||||
<div style="width: 48px;"></div> |
||||
<div style="width: 48px;"></div> |
||||
</div> |
||||
</MudTd> |
||||
</GroupHeaderTemplate> |
||||
<RowTemplate> |
||||
<MudTd>@context.Name</MudTd> |
||||
<MudTd> |
||||
<div class="d-flex"> |
||||
@if (context.Id >= 0) |
||||
{ |
||||
<MudIconButton Icon="@Icons.Material.Filled.Search" |
||||
Href="@($"system/troubleshooting/block-playout/history?playoutId={_selectedPlayout?.PlayoutId}&blockId={context.Id}")"> |
||||
</MudIconButton> |
||||
<div style="width: 48px;"></div> |
||||
} |
||||
else |
||||
{ |
||||
<div style="height: 48px; width: 48px"></div> |
||||
} |
||||
</div> |
||||
</MudTd> |
||||
</RowTemplate> |
||||
</MudTable> |
||||
</MudContainer> |
||||
</div> |
||||
</MudForm> |
||||
|
||||
@code { |
||||
private CancellationTokenSource _cts; |
||||
private readonly List<PlayoutNameViewModel> _playouts = []; |
||||
private PlayoutNameViewModel _selectedPlayout; |
||||
private readonly List<BlockViewModel> _blocks = []; |
||||
private string _searchString; |
||||
|
||||
public void Dispose() |
||||
{ |
||||
_cts?.Cancel(); |
||||
_cts?.Dispose(); |
||||
} |
||||
|
||||
protected override async Task OnParametersSetAsync() |
||||
{ |
||||
_cts?.Cancel(); |
||||
_cts?.Dispose(); |
||||
_cts = new CancellationTokenSource(); |
||||
var token = _cts.Token; |
||||
|
||||
try |
||||
{ |
||||
_playouts.Clear(); |
||||
_playouts.AddRange(await Mediator.Send(new GetAllBlockPlayouts(), token)); |
||||
|
||||
_blocks.Clear(); |
||||
if (_selectedPlayout is not null) |
||||
{ |
||||
_blocks.AddRange(await Mediator.Send(new GetAllBlocksForPlayout(_selectedPlayout.PlayoutId), token)); |
||||
} |
||||
} |
||||
catch (OperationCanceledException) |
||||
{ |
||||
// do nothing |
||||
} |
||||
} |
||||
|
||||
private readonly TableGroupDefinition<BlockViewModel> _groupDefinition = new() |
||||
{ |
||||
GroupName = "Group", |
||||
Indentation = false, |
||||
Expandable = true, |
||||
Selector = (e) => e.GroupName |
||||
}; |
||||
|
||||
private void OnSearch(string query) |
||||
{ |
||||
_searchString = query; |
||||
} |
||||
|
||||
private bool FilterBlocks(BlockViewModel block) => FilterBlocks(block, _searchString); |
||||
|
||||
private bool FilterBlocks(BlockViewModel block, string searchString) |
||||
{ |
||||
if (string.IsNullOrWhiteSpace(searchString)) |
||||
{ |
||||
return true; |
||||
} |
||||
|
||||
if (block.Name.Contains(searchString, StringComparison.OrdinalIgnoreCase)) |
||||
{ |
||||
return true; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
private async Task OnSelectedPlayoutChanged(IEnumerable<PlayoutNameViewModel> selectedPlayouts) |
||||
{ |
||||
_selectedPlayout = null; |
||||
_blocks.Clear(); |
||||
|
||||
foreach (var playout in selectedPlayouts.HeadOrNone()) |
||||
{ |
||||
_selectedPlayout = playout; |
||||
_blocks.AddRange(await Mediator.Send(new GetAllBlocksForPlayout(playout.PlayoutId), _cts?.Token ?? CancellationToken.None)); |
||||
await InvokeAsync(StateHasChanged); |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue