Browse Source

rework block editor (#2143)

pull/2145/head
Jason Dove 1 month ago committed by GitHub
parent
commit
448d29546c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 217
      ErsatzTV/Pages/BlockEditor.razor
  2. 4
      ErsatzTV/wwwroot/css/site.css

217
ErsatzTV/Pages/BlockEditor.razor

@ -10,21 +10,51 @@
@inject ISnackbar Snackbar @inject ISnackbar Snackbar
@inject IMediator Mediator @inject IMediator Mediator
<MudForm Style="max-height: 100%">
<MudPaper Square="true" Style="display: flex; height: 64px; min-height: 64px; width: 100%; z-index: 100; align-items: center">
<div style="display: flex; flex-direction: row; margin-bottom: auto; margin-top: auto; width: 100%; align-items: center" class="ml-6 mr-6">
<div class="d-none d-md-flex">
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="SaveChanges" StartIcon="@Icons.Material.Filled.Save">
Save Block
</MudButton>
<MudButton Class="ml-3" Variant="Variant.Filled" Color="Color.Default" OnClick="AddBlockItem" StartIcon="@Icons.Material.Filled.PlaylistAdd">
Add Block Item
</MudButton>
<MudButton Class="ml-3" Variant="Variant.Filled" Color="Color.Secondary" OnClick="PreviewPlayout" StartIcon="@Icons.Material.Filled.Preview">
Preview Block Playout
</MudButton>
</div>
<div style="align-items: center; display: flex; margin-left: auto;" class="d-md-none">
<div class="flex-grow-1"></div>
<MudMenu Icon="@Icons.Material.Filled.MoreVert">
<MudMenuItem Icon="@Icons.Material.Filled.Save" Label="Save Block" OnClick="@SaveChanges"/>
<MudMenuItem Icon="@Icons.Material.Filled.PlaylistAdd" Label="Add Block Item" OnClick="AddBlockItem"/>
<MudMenuItem Icon="@Icons.Material.Filled.Preview" Label="Preview Block Playout" OnClick="PreviewPlayout"/>
</MudMenu>
</div>
</div>
</MudPaper>
<div class="d-flex flex-column" style="height: 100vh; overflow-x: auto">
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8"> <MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8">
<MudText Typo="Typo.h4" Class="mb-4">Edit Block</MudText> <MudText Typo="Typo.h5" Class="mb-2">Block</MudText>
<div style="max-width: 400px"> <MudDivider Class="mb-6"/>
<MudCard> <MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<MudCardContent> <div class="d-flex">
<MudTextField Label="Name" @bind-Value="_block.Name" For="@(() => _block.Name)"/> <MudText>Name</MudText>
<MudGrid Class="mt-3" Style="align-items: center" Justify="Justify.Center"> </div>
<MudItem xs="6"> <MudTextField @bind-Value="_block.Name" For="@(() => _block.Name)"/>
</MudStack>
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Duration</MudText>
</div>
<MudTextField T="int" <MudTextField T="int"
Label="Duration"
@bind-Value="_durationHours" @bind-Value="_durationHours"
Adornment="Adornment.End" Adornment="Adornment.End"
AdornmentText="hours"/> AdornmentText="hours"/>
</MudItem> </MudStack>
<MudItem xs="6"> <MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex"></div>
<MudSelect T="int" @bind-Value="_durationMinutes" Adornment="Adornment.End" AdornmentText="minutes"> <MudSelect T="int" @bind-Value="_durationMinutes" Adornment="Adornment.End" AdornmentText="minutes">
<MudSelectItem Value="0"/> <MudSelectItem Value="0"/>
<MudSelectItem Value="5"/> <MudSelectItem Value="5"/>
@ -39,39 +69,28 @@
<MudSelectItem Value="50"/> <MudSelectItem Value="50"/>
<MudSelectItem Value="55"/> <MudSelectItem Value="55"/>
</MudSelect> </MudSelect>
</MudItem> </MudStack>
</MudGrid> <MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Stop scheduling block items</MudText>
</div>
<MudSelect T="BlockStopScheduling" <MudSelect T="BlockStopScheduling"
Class="mt-3"
@bind-Value="_block.StopScheduling" @bind-Value="_block.StopScheduling"
For="@(() => _block.StopScheduling)" For="@(() => _block.StopScheduling)">
Label="Stop scheduling block items">
<MudSelectItem Value="BlockStopScheduling.BeforeDurationEnd">Before Duration End</MudSelectItem> <MudSelectItem Value="BlockStopScheduling.BeforeDurationEnd">Before Duration End</MudSelectItem>
<MudSelectItem Value="BlockStopScheduling.AfterDurationEnd">After Duration End</MudSelectItem> <MudSelectItem Value="BlockStopScheduling.AfterDurationEnd">After Duration End</MudSelectItem>
</MudSelect> </MudSelect>
</MudCardContent> </MudStack>
</MudCard> <MudText Typo="Typo.h5" Class="mt-10 mb-2">Block Items</MudText>
</div> <MudDivider Class="mb-6"/>
<MudButton Variant="Variant.Filled" Color="Color.Default" OnClick="@(_ => AddBlockItem())" Class="mt-4"> <MudTable Class="mt-6" Hover="true" Items="_block.Items.OrderBy(i => i.Index)" Dense="true" @bind-SelectedItem="_selectedItem" RowClassFunc="@SelectedRowClassFunc">
Add Block Item
</MudButton>
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="@(_ => SaveChanges())" Class="mt-4 ml-4">
Save Changes
</MudButton>
<MudButton Variant="Variant.Filled" Color="Color.Secondary" OnClick="@(_ => PreviewPlayout())" Class="mt-4 ml-4">
Preview Block Playout
</MudButton>
<MudGrid>
<MudItem xs="8">
<MudTable Class="mt-6" Hover="true" Items="_block.Items.OrderBy(i => i.Index)" Dense="true" @bind-SelectedItem="_selectedItem">
<ColGroup> <ColGroup>
<MudHidden Breakpoint="Breakpoint.Xs">
<col/> <col/>
<col/> <col/>
<col/> <col/>
<col style="width: 60px;"/> <col style="width: 240px;"/>
<col style="width: 60px;"/> </MudHidden>
<col style="width: 60px;"/>
<col style="width: 60px;"/>
</ColGroup> </ColGroup>
<HeaderContent> <HeaderContent>
<MudTh>Collection</MudTh> <MudTh>Collection</MudTh>
@ -79,9 +98,6 @@
<MudTh>Show In EPG</MudTh> <MudTh>Show In EPG</MudTh>
<MudTh>Disable Watermarks</MudTh> <MudTh>Disable Watermarks</MudTh>
<MudTh/> <MudTh/>
<MudTh/>
<MudTh/>
<MudTh/>
</HeaderContent> </HeaderContent>
<RowTemplate> <RowTemplate>
<MudTd DataLabel="Collection"> <MudTd DataLabel="Collection">
@ -94,47 +110,41 @@
@context.PlaybackOrder @context.PlaybackOrder
</MudText> </MudText>
</MudTd> </MudTd>
<MudTd> <MudTd DataLabel="Show In EPG">
<MudCheckBox T="bool" Value="@context.IncludeInProgramGuide" ValueChanged="@(e => UpdateEPG(context, e))"/> <MudCheckBox T="bool" Value="@context.IncludeInProgramGuide" ValueChanged="@(e => UpdateEPG(context, e))"/>
</MudTd> </MudTd>
<MudTd> <MudTd DataLabel="Disable Watermarks">
<MudCheckBox T="bool" Value="@context.DisableWatermarks" ValueChanged="@(e => UpdateWatermark(context, e))"/> <MudCheckBox T="bool" Value="@context.DisableWatermarks" ValueChanged="@(e => UpdateWatermark(context, e))"/>
</MudTd> </MudTd>
<MudTd> <MudTd>
<div class="d-flex">
<MudIconButton Icon="@Icons.Material.Filled.ContentCopy" <MudIconButton Icon="@Icons.Material.Filled.ContentCopy"
OnClick="@(_ => CopyItem(context))"> OnClick="@(_ => CopyItem(context))">
</MudIconButton> </MudIconButton>
</MudTd>
<MudTd>
<MudIconButton Icon="@Icons.Material.Filled.ArrowUpward" <MudIconButton Icon="@Icons.Material.Filled.ArrowUpward"
OnClick="@(_ => MoveItemUp(context))" OnClick="@(_ => MoveItemUp(context))"
Disabled="@(_block.Items.All(x => x.Index >= context.Index))"> Disabled="@(_block.Items.All(x => x.Index >= context.Index))">
</MudIconButton> </MudIconButton>
</MudTd>
<MudTd>
<MudIconButton Icon="@Icons.Material.Filled.ArrowDownward" <MudIconButton Icon="@Icons.Material.Filled.ArrowDownward"
OnClick="@(_ => MoveItemDown(context))" OnClick="@(_ => MoveItemDown(context))"
Disabled="@(_block.Items.All(x => x.Index <= context.Index))"> Disabled="@(_block.Items.All(x => x.Index <= context.Index))">
</MudIconButton> </MudIconButton>
</MudTd>
<MudTd>
<MudIconButton Icon="@Icons.Material.Filled.Delete" <MudIconButton Icon="@Icons.Material.Filled.Delete"
OnClick="@(_ => RemoveBlockItem(context))"> OnClick="@(_ => RemoveBlockItem(context))">
</MudIconButton> </MudIconButton>
</div>
</MudTd> </MudTd>
</RowTemplate> </RowTemplate>
</MudTable> </MudTable>
</MudItem>
</MudGrid>
<div class="mt-4">
@if (_selectedItem is not null) @if (_selectedItem is not null)
{ {
<EditForm Model="_selectedItem"> <MudText Typo="Typo.h5" Class="mt-10 mb-2">Block Item</MudText>
<FluentValidationValidator/> <MudDivider Class="mb-6"/>
<div style="max-width: 400px;" class="mr-6"> <MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<MudCard> <div class="d-flex">
<MudCardContent> <MudText>Collection Type</MudText>
<MudSelect Class="mt-3" Label="Collection Type" @bind-Value="_selectedItem.CollectionType" For="@(() => _selectedItem.CollectionType)"> </div>
<MudSelect @bind-Value="_selectedItem.CollectionType" For="@(() => _selectedItem.CollectionType)">
<MudSelectItem Value="ProgramScheduleItemCollectionType.Collection">Collection</MudSelectItem> <MudSelectItem Value="ProgramScheduleItemCollectionType.Collection">Collection</MudSelectItem>
<MudSelectItem Value="ProgramScheduleItemCollectionType.TelevisionShow">Television Show</MudSelectItem> <MudSelectItem Value="ProgramScheduleItemCollectionType.TelevisionShow">Television Show</MudSelectItem>
<MudSelectItem Value="ProgramScheduleItemCollectionType.TelevisionSeason">Television Season</MudSelectItem> <MudSelectItem Value="ProgramScheduleItemCollectionType.TelevisionSeason">Television Season</MudSelectItem>
@ -142,9 +152,14 @@
@* <MudSelectItem Value="ProgramScheduleItemCollectionType.MultiCollection">Multi Collection</MudSelectItem> *@ @* <MudSelectItem Value="ProgramScheduleItemCollectionType.MultiCollection">Multi Collection</MudSelectItem> *@
<MudSelectItem Value="ProgramScheduleItemCollectionType.SmartCollection">Smart Collection</MudSelectItem> <MudSelectItem Value="ProgramScheduleItemCollectionType.SmartCollection">Smart Collection</MudSelectItem>
</MudSelect> </MudSelect>
</MudStack>
@if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Collection) @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Collection)
{ {
<MudAutocomplete Class="mt-3" T="MediaCollectionViewModel" Label="Collection" <MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Collection</MudText>
</div>
<MudAutocomplete Class="mt-3" T="MediaCollectionViewModel"
@bind-Value="_selectedItem.Collection" SearchFunc="@SearchCollections" @bind-Value="_selectedItem.Collection" SearchFunc="@SearchCollections"
ToStringFunc="@(c => c?.Name)" Placeholder="Type to search..."> ToStringFunc="@(c => c?.Name)" Placeholder="Type to search...">
<MoreItemsTemplate> <MoreItemsTemplate>
@ -153,11 +168,15 @@
</MudText> </MudText>
</MoreItemsTemplate> </MoreItemsTemplate>
</MudAutocomplete> </MudAutocomplete>
</MudStack>
} }
@if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.MultiCollection) @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.MultiCollection)
{ {
<MudAutocomplete Class="mt-3" T="MultiCollectionViewModel" Label="Multi Collection" <MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Multi Collection</MudText>
</div>
<MudAutocomplete Class="mt-3" T="MultiCollectionViewModel"
@bind-Value="_selectedItem.MultiCollection" SearchFunc="@SearchMultiCollections" @bind-Value="_selectedItem.MultiCollection" SearchFunc="@SearchMultiCollections"
ToStringFunc="@(c => c?.Name)" Placeholder="Type to search..."> ToStringFunc="@(c => c?.Name)" Placeholder="Type to search...">
<MoreItemsTemplate> <MoreItemsTemplate>
@ -166,11 +185,15 @@
</MudText> </MudText>
</MoreItemsTemplate> </MoreItemsTemplate>
</MudAutocomplete> </MudAutocomplete>
</MudStack>
} }
@if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.SmartCollection) @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.SmartCollection)
{ {
<MudAutocomplete Class="mt-3" T="SmartCollectionViewModel" Label="Smart Collection" <MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Smart Collection</MudText>
</div>
<MudAutocomplete Class="mt-3" T="SmartCollectionViewModel"
@bind-Value="_selectedItem.SmartCollection" SearchFunc="@SearchSmartCollections" @bind-Value="_selectedItem.SmartCollection" SearchFunc="@SearchSmartCollections"
ToStringFunc="@(c => c?.Name)" Placeholder="Type to search..."> ToStringFunc="@(c => c?.Name)" Placeholder="Type to search...">
<MoreItemsTemplate> <MoreItemsTemplate>
@ -179,11 +202,15 @@
</MudText> </MudText>
</MoreItemsTemplate> </MoreItemsTemplate>
</MudAutocomplete> </MudAutocomplete>
</MudStack>
} }
@if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionShow) @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionShow)
{ {
<MudAutocomplete Class="mt-3" T="NamedMediaItemViewModel" Label="Television Show" <MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Television Show</MudText>
</div>
<MudAutocomplete Class="mt-3" T="NamedMediaItemViewModel"
@bind-Value="_selectedItem.MediaItem" SearchFunc="@SearchTelevisionShows" @bind-Value="_selectedItem.MediaItem" SearchFunc="@SearchTelevisionShows"
ToStringFunc="@(c => c?.Name)" Placeholder="Type to search..."> ToStringFunc="@(c => c?.Name)" Placeholder="Type to search...">
<MoreItemsTemplate> <MoreItemsTemplate>
@ -192,11 +219,15 @@
</MudText> </MudText>
</MoreItemsTemplate> </MoreItemsTemplate>
</MudAutocomplete> </MudAutocomplete>
</MudStack>
} }
@if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionSeason)
{ {
<MudAutocomplete Class="mt-3" T="NamedMediaItemViewModel" Label="Television Season" <MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Television Season</MudText>
</div>
<MudAutocomplete Class="mt-3" T="NamedMediaItemViewModel"
@bind-Value="_selectedItem.MediaItem" SearchFunc="@SearchTelevisionSeasons" @bind-Value="_selectedItem.MediaItem" SearchFunc="@SearchTelevisionSeasons"
ToStringFunc="@(c => c?.Name)" Placeholder="Type to search..." ToStringFunc="@(c => c?.Name)" Placeholder="Type to search..."
MaxItems="20"> MaxItems="20">
@ -206,23 +237,13 @@
</MudText> </MudText>
</MoreItemsTemplate> </MoreItemsTemplate>
</MudAutocomplete> </MudAutocomplete>
</MudStack>
} }
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
@if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Artist) <div class="d-flex">
{ <MudText>Playback Order</MudText>
<MudAutocomplete Class="mt-3" T="NamedMediaItemViewModel" Label="Artist" </div>
@bind-Value="_selectedItem.MediaItem" SearchFunc="@SearchArtists" <MudSelect @bind-Value="@_selectedItem.PlaybackOrder" For="@(() => _selectedItem.PlaybackOrder)">
ToStringFunc="@(c => c?.Name)" Placeholder="Type to search..."
MaxItems="10">
<MoreItemsTemplate>
<MudText Align="Align.Center" Class="px-4 py-1">
Only the first 10 items are shown
</MudText>
</MoreItemsTemplate>
</MudAutocomplete>
}
<MudSelect Class="mt-3" Label="Playback Order" @bind-Value="@_selectedItem.PlaybackOrder" For="@(() => _selectedItem.PlaybackOrder)">
@switch (_selectedItem.CollectionType) @switch (_selectedItem.CollectionType)
{ {
case ProgramScheduleItemCollectionType.MultiCollection: case ProgramScheduleItemCollectionType.MultiCollection:
@ -254,21 +275,15 @@
break; break;
} }
</MudSelect> </MudSelect>
</MudCardContent> </MudStack>
</MudCard>
</div>
</EditForm>
} }
</div> else if (_previewItems != null)
@if (_previewItems != null)
{ {
<MudTable Class="mt-8" <MudText Typo="Typo.h5" Class="mt-10 mb-2">Preview</MudText>
Hover="true" <MudDivider Class="mb-6"/>
<MudTable Hover="true"
Dense="true" Dense="true"
Items="_previewItems"> Items="_previewItems">
<ToolBarContent>
<MudText Typo="Typo.h6">Block Playout Preview</MudText>
</ToolBarContent>
<HeaderContent> <HeaderContent>
<MudTh>Start</MudTh> <MudTh>Start</MudTh>
<MudTh>Finish</MudTh> <MudTh>Finish</MudTh>
@ -284,6 +299,8 @@
</MudTable> </MudTable>
} }
</MudContainer> </MudContainer>
</div>
</MudForm>
@code { @code {
private readonly CancellationTokenSource _cts = new(); private readonly CancellationTokenSource _cts = new();
@ -389,15 +406,15 @@
return await Mediator.Send(new SearchTelevisionSeasons(value), _cts.Token); return await Mediator.Send(new SearchTelevisionSeasons(value), _cts.Token);
} }
private async Task<IEnumerable<NamedMediaItemViewModel>> SearchArtists(string value, CancellationToken cancellationToken) // private async Task<IEnumerable<NamedMediaItemViewModel>> SearchArtists(string value, CancellationToken cancellationToken)
{ // {
if (string.IsNullOrWhiteSpace(value)) // if (string.IsNullOrWhiteSpace(value))
{ // {
return new List<NamedMediaItemViewModel>(); // return new List<NamedMediaItemViewModel>();
} // }
//
return await Mediator.Send(new SearchArtists(value), _cts.Token); // return await Mediator.Send(new SearchArtists(value), _cts.Token);
} // }
private static BlockItemEditViewModel ProjectToEditViewModel(BlockItemViewModel item) => private static BlockItemEditViewModel ProjectToEditViewModel(BlockItemViewModel item) =>
new() new()
@ -514,4 +531,6 @@
private static void UpdateWatermark(BlockItemEditViewModel context, bool disableWatermarks) => context.DisableWatermarks = disableWatermarks; private static void UpdateWatermark(BlockItemEditViewModel context, bool disableWatermarks) => context.DisableWatermarks = disableWatermarks;
private string SelectedRowClassFunc(BlockItemEditViewModel element, int rowNumber) => _selectedItem != null && _selectedItem == element ? "selected" : string.Empty;
} }

4
ErsatzTV/wwwroot/css/site.css

@ -145,6 +145,10 @@
font-weight: bold; font-weight: bold;
} }
.form-field-stack .mud-input-adornment-text {
font-weight: unset;
}
.form-field-stack ul.mud-treeview .mud-typography { .form-field-stack ul.mud-treeview .mud-typography {
font-weight: unset; font-weight: unset;
} }

Loading…
Cancel
Save