mirror of https://github.com/ErsatzTV/ErsatzTV.git
29 changed files with 35482 additions and 6 deletions
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
using ErsatzTV.Core; |
||||
|
||||
namespace ErsatzTV.Application.MediaCollections; |
||||
|
||||
public record UpdateTraktList(int Id, bool AutoRefresh) : IRequest<Option<BaseError>>; |
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
using ErsatzTV.Core; |
||||
using ErsatzTV.Infrastructure.Data; |
||||
using Microsoft.EntityFrameworkCore; |
||||
|
||||
namespace ErsatzTV.Application.MediaCollections; |
||||
|
||||
public class UpdateTraktListHandler(IDbContextFactory<TvContext> dbContextFactory) |
||||
: IRequestHandler<UpdateTraktList, Option<BaseError>> |
||||
{ |
||||
public async Task<Option<BaseError>> Handle(UpdateTraktList request, CancellationToken cancellationToken) |
||||
{ |
||||
try |
||||
{ |
||||
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); |
||||
|
||||
await dbContext.TraktLists |
||||
.Where(tl => tl.Id == request.Id) |
||||
.ExecuteUpdateAsync( |
||||
u => u.SetProperty(p => p.AutoRefresh, p => request.AutoRefresh), |
||||
cancellationToken); |
||||
|
||||
return Option<BaseError>.None; |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
return BaseError.New(ex.ToString()); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
namespace ErsatzTV.Application.MediaCollections; |
||||
|
||||
public record GetTraktListById(int Id) : IRequest<Option<TraktListViewModel>>; |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
using ErsatzTV.Infrastructure.Data; |
||||
using ErsatzTV.Infrastructure.Extensions; |
||||
using Microsoft.EntityFrameworkCore; |
||||
|
||||
namespace ErsatzTV.Application.MediaCollections; |
||||
|
||||
public class GetTraktListByIdHandler(IDbContextFactory<TvContext> dbContextFactory) |
||||
: IRequestHandler<GetTraktListById, Option<TraktListViewModel>> |
||||
{ |
||||
public async Task<Option<TraktListViewModel>> Handle(GetTraktListById request, CancellationToken cancellationToken) |
||||
{ |
||||
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); |
||||
return await dbContext.TraktLists |
||||
.Include(tl => tl.Items) |
||||
.SelectOneAsync(tl => tl.Id, tl => tl.Id == request.Id) |
||||
.MapT(Mapper.ProjectToViewModel); |
||||
} |
||||
} |
@ -1,3 +1,10 @@
@@ -1,3 +1,10 @@
|
||||
namespace ErsatzTV.Application.MediaCollections; |
||||
|
||||
public record TraktListViewModel(int Id, int TraktId, string Slug, string Name, int ItemCount, int MatchCount); |
||||
public record TraktListViewModel( |
||||
int Id, |
||||
int TraktId, |
||||
string Slug, |
||||
string Name, |
||||
int ItemCount, |
||||
int MatchCount, |
||||
bool AutoRefresh); |
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
||||
#nullable disable |
||||
|
||||
namespace ErsatzTV.Infrastructure.MySql.Migrations |
||||
{ |
||||
/// <inheritdoc />
|
||||
public partial class Add_TraktListAutoRefresh : Migration |
||||
{ |
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.AddColumn<bool>( |
||||
name: "AutoRefresh", |
||||
table: "TraktList", |
||||
type: "tinyint(1)", |
||||
nullable: false, |
||||
defaultValue: false); |
||||
} |
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.DropColumn( |
||||
name: "AutoRefresh", |
||||
table: "TraktList"); |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
using System; |
||||
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
||||
#nullable disable |
||||
|
||||
namespace ErsatzTV.Infrastructure.MySql.Migrations |
||||
{ |
||||
/// <inheritdoc />
|
||||
public partial class Add_TraktListLastUpdate : Migration |
||||
{ |
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.AddColumn<DateTime>( |
||||
name: "LastUpdate", |
||||
table: "TraktList", |
||||
type: "datetime(6)", |
||||
nullable: true); |
||||
} |
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.DropColumn( |
||||
name: "LastUpdate", |
||||
table: "TraktList"); |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
using System; |
||||
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
||||
#nullable disable |
||||
|
||||
namespace ErsatzTV.Infrastructure.MySql.Migrations |
||||
{ |
||||
/// <inheritdoc />
|
||||
public partial class Add_TraktListLastMatch : Migration |
||||
{ |
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.AddColumn<DateTime>( |
||||
name: "LastMatch", |
||||
table: "TraktList", |
||||
type: "datetime(6)", |
||||
nullable: true); |
||||
} |
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.DropColumn( |
||||
name: "LastMatch", |
||||
table: "TraktList"); |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
||||
#nullable disable |
||||
|
||||
namespace ErsatzTV.Infrastructure.Sqlite.Migrations |
||||
{ |
||||
/// <inheritdoc />
|
||||
public partial class Add_TraktListAutoRefresh : Migration |
||||
{ |
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.AddColumn<bool>( |
||||
name: "AutoRefresh", |
||||
table: "TraktList", |
||||
type: "INTEGER", |
||||
nullable: false, |
||||
defaultValue: false); |
||||
} |
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.DropColumn( |
||||
name: "AutoRefresh", |
||||
table: "TraktList"); |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
using System; |
||||
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
||||
#nullable disable |
||||
|
||||
namespace ErsatzTV.Infrastructure.Sqlite.Migrations |
||||
{ |
||||
/// <inheritdoc />
|
||||
public partial class Add_TraktListLastUpdate : Migration |
||||
{ |
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.AddColumn<DateTime>( |
||||
name: "LastUpdate", |
||||
table: "TraktList", |
||||
type: "TEXT", |
||||
nullable: true); |
||||
} |
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.DropColumn( |
||||
name: "LastUpdate", |
||||
table: "TraktList"); |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
using System; |
||||
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
||||
#nullable disable |
||||
|
||||
namespace ErsatzTV.Infrastructure.Sqlite.Migrations |
||||
{ |
||||
/// <inheritdoc />
|
||||
public partial class Add_TraktListLastMatch : Migration |
||||
{ |
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.AddColumn<DateTime>( |
||||
name: "LastMatch", |
||||
table: "TraktList", |
||||
type: "TEXT", |
||||
nullable: true); |
||||
} |
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.DropColumn( |
||||
name: "LastMatch", |
||||
table: "TraktList"); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,86 @@
@@ -0,0 +1,86 @@
|
||||
@page "/media/trakt/lists/{Id:int}" |
||||
@using ErsatzTV.Application.MediaCollections |
||||
@implements IDisposable |
||||
@inject NavigationManager NavigationManager |
||||
@inject ILogger<ScheduleEditor> Logger |
||||
@inject ISnackbar Snackbar |
||||
@inject IMediator Mediator |
||||
|
||||
<MudForm @ref="_form" @bind-IsValid="@_success" Style="max-height: 100%"> |
||||
<MudPaper Square="true" Style="display: flex; height: 64px; min-height: 64px; width: 100%; z-index: 100; align-items: center"> |
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" Class="ml-6" OnClick="@HandleSubmitAsync" StartIcon="@Icons.Material.Filled.Save"> |
||||
Save Trakt List |
||||
</MudButton> |
||||
</MudPaper> |
||||
<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">Trakt List</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>Id</MudText> |
||||
</div> |
||||
<MudTextField Value="_model.Slug" Disabled="true" /> |
||||
</MudStack> |
||||
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5"> |
||||
<div class="d-flex"> |
||||
<MudText>Automatic Refresh</MudText> |
||||
</div> |
||||
<MudCheckBox @bind-Value="@_model.AutoRefresh" For="@(() => _model.AutoRefresh)" Dense="true"> |
||||
<MudText Typo="Typo.caption" Style="font-weight: normal">Update list from trakt.tv once each day</MudText> |
||||
</MudCheckBox> |
||||
</MudStack> |
||||
</MudContainer> |
||||
</div> |
||||
</MudForm> |
||||
|
||||
@code { |
||||
private readonly CancellationTokenSource _cts = new(); |
||||
|
||||
[Parameter] |
||||
public int Id { get; set; } |
||||
|
||||
private readonly TraktListEditViewModel _model = new(); |
||||
private MudForm _form; |
||||
private bool _success; |
||||
|
||||
public void Dispose() |
||||
{ |
||||
_cts.Cancel(); |
||||
_cts.Dispose(); |
||||
} |
||||
|
||||
protected override async Task OnParametersSetAsync() |
||||
{ |
||||
Option<TraktListViewModel> maybeTraktList = await Mediator.Send(new GetTraktListById(Id), _cts.Token); |
||||
maybeTraktList.Match( |
||||
viewModel => |
||||
{ |
||||
_model.Id = viewModel.Id; |
||||
_model.Slug = viewModel.Slug; |
||||
_model.AutoRefresh = viewModel.AutoRefresh; |
||||
}, |
||||
() => NavigationManager.NavigateTo("404")); |
||||
} |
||||
|
||||
private async Task HandleSubmitAsync() |
||||
{ |
||||
await _form.Validate(); |
||||
if (_success) |
||||
{ |
||||
var request = new UpdateTraktList(_model.Id, _model.AutoRefresh); |
||||
Option<BaseError> result = await Mediator.Send(request, _cts.Token); |
||||
foreach (BaseError error in result) |
||||
{ |
||||
Snackbar.Add(error.Value, Severity.Error); |
||||
Logger.LogError("Unexpected error saving trakt list: {Error}", error.Value); |
||||
} |
||||
|
||||
if (result.IsNone) |
||||
{ |
||||
NavigationManager.NavigateTo("/media/trakt/lists"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue