Skip to content

Commit

Permalink
Merge pull request #1145 from VocaDB/react-album-edit
Browse files Browse the repository at this point in the history
Convert album edit to React
  • Loading branch information
ycanardeau authored Jul 29, 2022
2 parents 2646a9b + 039131b commit 6bed7ad
Show file tree
Hide file tree
Showing 43 changed files with 2,720 additions and 259 deletions.
20 changes: 10 additions & 10 deletions Tests/Web/Controllers/DataAccess/AlbumQueriesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,14 @@ private ArtistForAlbumContract CreateArtistForAlbumContract(int artistId = 0, st
return new ArtistForAlbumContract { Name = customArtistName, Roles = roles };
}

private Task<AlbumForEditContract> CallUpdate(AlbumForEditContract contract)
private Task<AlbumForEditForApiContract> CallUpdate(AlbumForEditForApiContract contract)
{
return _queries.UpdateBasicProperties(contract, null);
}

private async Task<AlbumForEditContract> CallUpdate(Stream image)
private async Task<AlbumForEditForApiContract> CallUpdate(Stream image)
{
var contract = new AlbumForEditContract(_album, ContentLanguagePreference.English, new InMemoryImagePersister());
var contract = new AlbumForEditForApiContract(_album, ContentLanguagePreference.English, new InMemoryImagePersister(), _permissionContext);
using (var stream = image)
{
return await _queries.UpdateBasicProperties(contract, new EntryPictureFileContract(stream, MediaTypeNames.Image.Jpeg, purpose: ImagePurpose.Main));
Expand Down Expand Up @@ -378,7 +378,7 @@ public void Revert()
// Arrange
_album.Description.English = "Original";
var oldVer = _repository.HandleTransaction(ctx => _queries.Archive(ctx, _album, AlbumArchiveReason.PropertiesUpdated));
var contract = new AlbumForEditContract(_album, ContentLanguagePreference.English, new InMemoryImagePersister());
var contract = new AlbumForEditForApiContract(_album, ContentLanguagePreference.English, new InMemoryImagePersister(), _permissionContext);
contract.Description.English = "Updated";
CallUpdate(contract);

Expand Down Expand Up @@ -425,7 +425,7 @@ public async Task Revert_RemoveImage()
[TestMethod]
public async Task Update_Names()
{
var contract = new AlbumForEditContract(_album, ContentLanguagePreference.English, new InMemoryImagePersister());
var contract = new AlbumForEditForApiContract(_album, ContentLanguagePreference.English, new InMemoryImagePersister(), _permissionContext);

contract.Names.First().Value = "Replaced name";
contract.UpdateNotes = "Updated album";
Expand Down Expand Up @@ -476,7 +476,7 @@ public void MoveToTrash()
[TestMethod]
public async Task Update_Tracks()
{
var contract = new AlbumForEditContract(_album, ContentLanguagePreference.English, new InMemoryImagePersister());
var contract = new AlbumForEditForApiContract(_album, ContentLanguagePreference.English, new InMemoryImagePersister(), _permissionContext);
var existingSong = CreateEntry.Song(name: "Nebula");
_repository.Save(existingSong);

Expand Down Expand Up @@ -506,7 +506,7 @@ public async Task Update_Tracks()
[TestMethod]
public async Task Update_Discs()
{
var contract = new AlbumForEditContract(_album, ContentLanguagePreference.English, new InMemoryImagePersister());
var contract = new AlbumForEditForApiContract(_album, ContentLanguagePreference.English, new InMemoryImagePersister(), _permissionContext);
_repository.Save(CreateEntry.AlbumDisc(_album));

contract.Discs = new[] {
Expand Down Expand Up @@ -557,7 +557,7 @@ public async Task Update_CoverPicture()
[TestMethod]
public async Task Update_Artists()
{
var contract = new AlbumForEditContract(_album, ContentLanguagePreference.English, new InMemoryImagePersister());
var contract = new AlbumForEditForApiContract(_album, ContentLanguagePreference.English, new InMemoryImagePersister(), _permissionContext);
contract.ArtistLinks = new[] {
CreateArtistForAlbumContract(artistId: _producer.Id),
CreateArtistForAlbumContract(artistId: _vocalist.Id)
Expand All @@ -582,7 +582,7 @@ public async Task Update_Artists()
[TestMethod]
public async Task Update_Artists_CustomArtist()
{
var contract = new AlbumForEditContract(_album, ContentLanguagePreference.English, new InMemoryImagePersister());
var contract = new AlbumForEditForApiContract(_album, ContentLanguagePreference.English, new InMemoryImagePersister(), _permissionContext);
contract.ArtistLinks = new[] {
CreateArtistForAlbumContract(customArtistName: "Custom artist", roles: ArtistRoles.Composer)
};
Expand All @@ -606,7 +606,7 @@ public async Task Update_Artists_Notify()
{
Save(_user2.AddArtist(_vocalist));

var contract = new AlbumForEditContract(_album, ContentLanguagePreference.Default, new InMemoryImagePersister());
var contract = new AlbumForEditForApiContract(_album, ContentLanguagePreference.Default, new InMemoryImagePersister(), _permissionContext);
contract.ArtistLinks = contract.ArtistLinks.Concat(new[] { CreateArtistForAlbumContract(_vocalist.Id) }).ToArray();

await _queries.UpdateBasicProperties(contract, null);
Expand Down
141 changes: 141 additions & 0 deletions VocaDbModel/DataContracts/Albums/AlbumForEditForApiContract.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using VocaDb.Model.DataContracts.Globalization;
using VocaDb.Model.DataContracts.PVs;
using VocaDb.Model.DataContracts.Songs;
using VocaDb.Model.Domain;
using VocaDb.Model.Domain.Albums;
using VocaDb.Model.Domain.Globalization;
using VocaDb.Model.Domain.Images;
using VocaDb.Model.Domain.Security;

namespace VocaDb.Model.DataContracts.Albums;

[DataContract(Namespace = Schemas.VocaDb)]
public sealed record AlbumForEditForApiContract
{
[DataMember]
public ArtistForAlbumContract[] ArtistLinks { get; set; }

[DataMember]
public bool CanDelete { get; init; }

[DataMember]
public string? CoverPictureMime { get; init; }

[DataMember]
public ContentLanguageSelection DefaultNameLanguage { get; init; }

[DataMember]
public bool Deleted { get; init; }

[DataMember]
public EnglishTranslatedStringContract Description { get; init; }

[DataMember]
public AlbumDiscPropertiesContract[] Discs { get; set; }

[DataMember]
[JsonConverter(typeof(StringEnumConverter))]
public DiscType DiscType { get; init; }

[DataMember]
public int Id { get; set; }

[DataMember]
public string[] Identifiers { get; init; }

[DataMember]
public string Name { get; init; }

[DataMember]
public LocalizedStringWithIdContract[] Names { get; init; }

[DataMember]
public AlbumReleaseContract OriginalRelease { get; init; }

[DataMember]
public IList<EntryPictureFileContract> Pictures { get; init; }

[DataMember(Name = "pvs")]
public PVContract[] PVs { get; init; }

[DataMember]
public SongInAlbumEditContract[] Songs { get; set; }

[DataMember]
public EntryStatus Status { get; init; }

[DataMember]
public string UpdateNotes { get; set; }

[DataMember]
public WebLinkForApiContract[] WebLinks { get; init; }

public AlbumForEditForApiContract()
{
ArtistLinks = Array.Empty<ArtistForAlbumContract>();
Description = new EnglishTranslatedStringContract();
Discs = Array.Empty<AlbumDiscPropertiesContract>();
Identifiers = Array.Empty<string>();
Name = string.Empty;
Names = Array.Empty<LocalizedStringWithIdContract>();
OriginalRelease = new();
Pictures = Array.Empty<EntryPictureFileContract>();
PVs = Array.Empty<PVContract>();
Songs = Array.Empty<SongInAlbumEditContract>();
UpdateNotes = string.Empty;
WebLinks = Array.Empty<WebLinkForApiContract>();
}

public AlbumForEditForApiContract(
Album album,
ContentLanguagePreference languagePreference,
IAggregatedEntryImageUrlFactory imageStore,
IUserPermissionContext permissionContext
)
{
ArtistLinks = album.Artists
.Select(a => new ArtistForAlbumContract(a, languagePreference))
.OrderBy(a => a.Name)
.ToArray();
CanDelete = EntryPermissionManager.CanDelete(permissionContext, album);
CoverPictureMime = album.CoverPictureMime;
DefaultNameLanguage = album.TranslatedName.DefaultLanguage;
Deleted = album.Deleted;
Description = new EnglishTranslatedStringContract(album.Description);
Discs = album.Discs
.Select(d => new AlbumDiscPropertiesContract(d))
.ToArray();
DiscType = album.DiscType;
Id = album.Id;
Identifiers = album.Identifiers
.Select(i => i.Value)
.ToArray();
Name = album.TranslatedName[languagePreference];
Names = album.Names
.Select(n => new LocalizedStringWithIdContract(n))
.ToArray();
OriginalRelease = album.OriginalRelease is not null
? new(album.OriginalRelease, languagePreference)
: new();
Pictures = album.Pictures
.Select(p => new EntryPictureFileContract(p, imageStore))
.ToList();
PVs = album.PVs
.Select(p => new PVContract(p))
.ToArray();
Songs = album.Songs
.OrderBy(s => s.DiscNumber)
.ThenBy(s => s.TrackNumber)
.Select(s => new SongInAlbumEditContract(s, languagePreference))
.ToArray();
Status = album.Status;
UpdateNotes = string.Empty;
WebLinks = album.WebLinks
.Select(w => new WebLinkForApiContract(w))
.OrderBy(w => w.DescriptionOrUrl)
.ToArray();
}
}
1 change: 1 addition & 0 deletions VocaDbModel/DataContracts/UseCases/AlbumForEditContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace VocaDb.Model.DataContracts.UseCases
{
[Obsolete]
[DataContract(Namespace = Schemas.VocaDb)]
public class AlbumForEditContract : AlbumContract
{
Expand Down
17 changes: 11 additions & 6 deletions VocaDbModel/Database/Queries/AlbumQueries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -598,11 +598,16 @@ public EntryForPictureDisplayContract GetCoverPictureThumb(int albumId)
});
}

public AlbumForEditContract GetForEdit(int id)
public AlbumForEditForApiContract GetForEdit(int id)
{
return
HandleQuery(session =>
new AlbumForEditContract(session.Load<Album>(id), PermissionContext.LanguagePreference, _imageUrlFactory));
return HandleQuery(session =>
new AlbumForEditForApiContract(
album: session.Load<Album>(id),
languagePreference: PermissionContext.LanguagePreference,
imageStore: _imageUrlFactory,
permissionContext: PermissionContext
)
);
}

public RelatedAlbumsContract GetRelatedAlbums(int albumId)
Expand Down Expand Up @@ -947,7 +952,7 @@ public EntryRevertedContract RevertToVersion(int archivedAlbumVersionId)
}

#nullable enable
public async Task<AlbumForEditContract> UpdateBasicProperties(AlbumForEditContract properties, EntryPictureFileContract? pictureData)
public async Task<AlbumForEditForApiContract> UpdateBasicProperties(AlbumForEditForApiContract properties, EntryPictureFileContract? pictureData)
{
ParamIs.NotNull(() => properties);

Expand Down Expand Up @@ -1140,7 +1145,7 @@ public async Task<AlbumForEditContract> UpdateBasicProperties(AlbumForEditContra
}
}
return new AlbumForEditContract(album, PermissionContext.LanguagePreference, _imageUrlFactory);
return new AlbumForEditForApiContract(album, PermissionContext.LanguagePreference, _imageUrlFactory, PermissionContext);
});
}
#nullable disable
Expand Down
73 changes: 2 additions & 71 deletions VocaDbWeb/Controllers/AlbumController.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#nullable disable

using System.Globalization;
using System.Net;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
Expand All @@ -11,16 +10,13 @@
using VocaDb.Model.DataContracts.UseCases;
using VocaDb.Model.Domain;
using VocaDb.Model.Domain.Albums;
using VocaDb.Model.Domain.Images;
using VocaDb.Model.Domain.Security;
using VocaDb.Model.Helpers;
using VocaDb.Model.Resources;
using VocaDb.Model.Service;
using VocaDb.Model.Service.ExtSites;
using VocaDb.Model.Service.TagFormatting;
using VocaDb.Model.Utils.Search;
using VocaDb.Web.Code;
using VocaDb.Web.Code.Exceptions;
using VocaDb.Web.Code.Markdown;
using VocaDb.Web.Code.WebApi;
using VocaDb.Web.Helpers;
Expand Down Expand Up @@ -244,78 +240,13 @@ public async Task<ActionResult> Create(Create model)
return RedirectToAction("Edit", new { id = album.Id });
}

#nullable enable
//
// GET: /Album/Edit/5
[Authorize]
public ActionResult Edit(int id)
{
CheckConcurrentEdit(EntryType.Album, id);

return View(CreateAlbumEditViewModel(id, null));
}

#nullable enable
//
// POST: /Album/Edit/5

[HttpPost]
[Authorize]
public async Task<ActionResult> Edit(AlbumEditViewModel viewModel)
{
// Unable to continue if viewmodel is null because we need the ID at least
if (viewModel is null || viewModel.EditedAlbum is null)
{
s_log.Warn("Viewmodel was null");
return HttpStatusCodeResult(HttpStatusCode.BadRequest, "Viewmodel was null - probably JavaScript is disabled");
}

try
{
viewModel.CheckModel();
}
catch (InvalidFormException x)
{
AddFormSubmissionError(x.Message);
}

var model = viewModel.EditedAlbum;

// Note: name is allowed to be whitespace, but not empty.
if (model.Names is not null && model.Names.All(n => n is null || string.IsNullOrEmpty(n.Value)))
{
ModelState.AddModelError("Names", AlbumValidationErrors.UnspecifiedNames);
}

if (model.OriginalRelease is not null && model.OriginalRelease.ReleaseDate is not null && !OptionalDateTime.IsValid(model.OriginalRelease.ReleaseDate.Year, model.OriginalRelease.ReleaseDate.Day, model.OriginalRelease.ReleaseDate.Month))
ModelState.AddModelError("ReleaseYear", "Invalid date");

var coverPicUpload = Request.Form.Files["coverPicUpload"];
var pictureData = ParsePicture(coverPicUpload, "CoverPicture", ImagePurpose.Main);

if (model.Pictures is null)
{
AddFormSubmissionError("List of pictures was null");
}

if (model.Pictures is not null)
ParseAdditionalPictures(coverPicUpload, model.Pictures);

if (!ModelState.IsValid)
{
return View(CreateAlbumEditViewModel(model.Id, model));
}

try
{
await _queries.UpdateBasicProperties(model, pictureData);
}
catch (InvalidPictureException)
{
ModelState.AddModelError("ImageError", "The uploaded image could not processed, it might be broken. Please check the file and try again.");
return View(CreateAlbumEditViewModel(model.Id, model));
}

return RedirectToAction("Details", new { id = model.Id });
return View("React/Index");
}
#nullable disable

Expand Down
Loading

0 comments on commit 6bed7ad

Please sign in to comment.