Side Nav Redesign (#2310)
This commit is contained in:
parent
5c2ebb87cc
commit
00dddaefae
88 changed files with 5971 additions and 572 deletions
|
|
@ -8,7 +8,6 @@ using API.Data;
|
|||
using API.Data.Repositories;
|
||||
using API.DTOs;
|
||||
using API.DTOs.Account;
|
||||
using API.DTOs.Dashboard;
|
||||
using API.DTOs.Email;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
|
|
@ -1046,123 +1045,4 @@ public class AccountController : BaseApiController
|
|||
return Ok(origin + "/" + baseUrl + "api/opds/" + user!.ApiKey);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the layout of the user's dashboard
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("dashboard")]
|
||||
public async Task<ActionResult<IEnumerable<DashboardStreamDto>>> GetDashboardLayout(bool visibleOnly = true)
|
||||
{
|
||||
var streams = await _unitOfWork.UserRepository.GetDashboardStreams(User.GetUserId(), visibleOnly);
|
||||
return Ok(streams);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Dashboard Stream from a SmartFilter and adds it to the user's dashboard as visible
|
||||
/// </summary>
|
||||
/// <param name="smartFilterId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("add-dashboard-stream")]
|
||||
public async Task<ActionResult<DashboardStreamDto>> AddDashboard([FromQuery] int smartFilterId)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(User.GetUserId(), AppUserIncludes.DashboardStreams);
|
||||
if (user == null) return Unauthorized();
|
||||
|
||||
var smartFilter = await _unitOfWork.AppUserSmartFilterRepository.GetById(smartFilterId);
|
||||
if (smartFilter == null) return NoContent();
|
||||
|
||||
var stream = user?.DashboardStreams.FirstOrDefault(d => d.SmartFilter?.Id == smartFilterId);
|
||||
if (stream != null) return BadRequest("There is an existing stream with this Filter");
|
||||
|
||||
var maxOrder = user!.DashboardStreams.Max(d => d.Order);
|
||||
var createdStream = new AppUserDashboardStream()
|
||||
{
|
||||
Name = smartFilter.Name,
|
||||
IsProvided = false,
|
||||
StreamType = DashboardStreamType.SmartFilter,
|
||||
Visible = true,
|
||||
Order = maxOrder + 1,
|
||||
SmartFilter = smartFilter
|
||||
};
|
||||
|
||||
user.DashboardStreams.Add(createdStream);
|
||||
_unitOfWork.UserRepository.Update(user);
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
var ret = new DashboardStreamDto()
|
||||
{
|
||||
Name = createdStream.Name,
|
||||
IsProvided = createdStream.IsProvided,
|
||||
Visible = createdStream.Visible,
|
||||
Order = createdStream.Order,
|
||||
SmartFilterEncoded = smartFilter.Filter,
|
||||
StreamType = createdStream.StreamType
|
||||
};
|
||||
|
||||
|
||||
await _eventHub.SendMessageToAsync(MessageFactory.DashboardUpdate, MessageFactory.DashboardUpdateEvent(user.Id),
|
||||
User.GetUserId());
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the visibility of a dashboard stream
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("update-dashboard-stream")]
|
||||
public async Task<ActionResult> UpdateDashboardStream(DashboardStreamDto dto)
|
||||
{
|
||||
var stream = await _unitOfWork.UserRepository.GetDashboardStream(dto.Id);
|
||||
if (stream == null) return BadRequest();
|
||||
stream.Visible = dto.Visible;
|
||||
|
||||
_unitOfWork.UserRepository.Update(stream);
|
||||
await _unitOfWork.CommitAsync();
|
||||
var userId = User.GetUserId();
|
||||
await _eventHub.SendMessageToAsync(MessageFactory.DashboardUpdate, MessageFactory.DashboardUpdateEvent(userId),
|
||||
userId);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the position of a dashboard stream
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("update-dashboard-position")]
|
||||
public async Task<ActionResult> UpdateDashboardStreamPosition(UpdateDashboardStreamPositionDto dto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(User.GetUserId(),
|
||||
AppUserIncludes.DashboardStreams);
|
||||
var stream = user?.DashboardStreams.FirstOrDefault(d => d.Id == dto.DashboardStreamId);
|
||||
if (stream == null) return BadRequest();
|
||||
if (stream.Order == dto.ToPosition) return Ok();
|
||||
|
||||
var list = user!.DashboardStreams.ToList();
|
||||
ReorderItems(list, stream.Id, dto.ToPosition);
|
||||
user.DashboardStreams = list;
|
||||
|
||||
_unitOfWork.UserRepository.Update(user);
|
||||
await _unitOfWork.CommitAsync();
|
||||
await _eventHub.SendMessageToAsync(MessageFactory.DashboardUpdate, MessageFactory.DashboardUpdateEvent(user.Id),
|
||||
user.Id);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
private static void ReorderItems(List<AppUserDashboardStream> items, int itemId, int toPosition)
|
||||
{
|
||||
var item = items.Find(r => r.Id == itemId);
|
||||
if (item != null)
|
||||
{
|
||||
items.Remove(item);
|
||||
items.Insert(toPosition, item);
|
||||
}
|
||||
|
||||
for (var i = 0; i < items.Count; i++)
|
||||
{
|
||||
items[i].Order = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,10 @@ public class FilterController : BaseApiController
|
|||
// This needs to delete any dashboard filters that have it too
|
||||
var streams = await _unitOfWork.UserRepository.GetDashboardStreamWithFilter(filter.Id);
|
||||
_unitOfWork.UserRepository.Delete(streams);
|
||||
|
||||
var streams2 = await _unitOfWork.UserRepository.GetSideNavStreamWithFilter(filter.Id);
|
||||
_unitOfWork.UserRepository.Delete(streams2);
|
||||
|
||||
_unitOfWork.AppUserSmartFilterRepository.Delete(filter);
|
||||
await _unitOfWork.CommitAsync();
|
||||
return Ok();
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ using AutoMapper;
|
|||
using EasyCaching.Core;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration.UserSecrets;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using TaskScheduler = API.Services.TaskScheduler;
|
||||
|
||||
|
|
@ -97,6 +98,27 @@ public class LibraryController : BaseApiController
|
|||
admin.Libraries.Add(library);
|
||||
}
|
||||
|
||||
var userIds = admins.Select(u => u.Id).Append(User.GetUserId()).ToList();
|
||||
|
||||
var userNeedingNewLibrary = (await _unitOfWork.UserRepository.GetAllUsersAsync(AppUserIncludes.SideNavStreams))
|
||||
.Where(u => userIds.Contains(u.Id))
|
||||
.ToList();
|
||||
|
||||
foreach (var user in userNeedingNewLibrary)
|
||||
{
|
||||
var maxCount = user.SideNavStreams.Select(s => s.Order).Max();
|
||||
user.SideNavStreams.Add(new AppUserSideNavStream()
|
||||
{
|
||||
Name = library.Name,
|
||||
Order = maxCount + 1,
|
||||
IsProvided = false,
|
||||
StreamType = SideNavStreamType.Library,
|
||||
LibraryId = library.Id,
|
||||
Visible = true,
|
||||
});
|
||||
_unitOfWork.UserRepository.Update(user);
|
||||
}
|
||||
|
||||
|
||||
if (!await _unitOfWork.CommitAsync()) return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-library"));
|
||||
|
||||
|
|
@ -105,6 +127,8 @@ public class LibraryController : BaseApiController
|
|||
_taskScheduler.ScanLibrary(library.Id);
|
||||
await _eventHub.SendMessageAsync(MessageFactory.LibraryModified,
|
||||
MessageFactory.LibraryModifiedEvent(library.Id, "create"), false);
|
||||
await _eventHub.SendMessageAsync(MessageFactory.SideNavUpdate,
|
||||
MessageFactory.SideNavUpdateEvent(User.GetUserId()), false);
|
||||
await _libraryCacheProvider.RemoveByPrefixAsync(CacheKey);
|
||||
return Ok();
|
||||
}
|
||||
|
|
@ -329,9 +353,15 @@ public class LibraryController : BaseApiController
|
|||
|
||||
_unitOfWork.LibraryRepository.Delete(library);
|
||||
|
||||
var streams = await _unitOfWork.UserRepository.GetSideNavStreamsByLibraryId(library.Id);
|
||||
_unitOfWork.UserRepository.Delete(streams);
|
||||
|
||||
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
await _libraryCacheProvider.RemoveByPrefixAsync(CacheKey);
|
||||
await _eventHub.SendMessageAsync(MessageFactory.SideNavUpdate,
|
||||
MessageFactory.SideNavUpdateEvent(User.GetUserId()), false);
|
||||
|
||||
if (chapterIds.Any())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -50,4 +50,18 @@ public class PluginController : BaseApiController
|
|||
KavitaVersion = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion)).Value
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the version of the Kavita install
|
||||
/// </summary>
|
||||
/// <param name="apiKey">Required for authenticating to get result</param>
|
||||
/// <returns></returns>
|
||||
[AllowAnonymous]
|
||||
[HttpGet("version")]
|
||||
public async Task<ActionResult<string>> GetVersion([Required] string apiKey)
|
||||
{
|
||||
var userId = await _unitOfWork.UserRepository.GetUserIdByApiKeyAsync(apiKey);
|
||||
if (userId <= 0) return Unauthorized();
|
||||
return Ok((await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion)).Value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
186
API/Controllers/StreamController.cs
Normal file
186
API/Controllers/StreamController.cs
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.DTOs.Dashboard;
|
||||
using API.DTOs.SideNav;
|
||||
using API.Extensions;
|
||||
using API.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Responsible for anything that deals with Streams (SmartFilters, ExternalSource, DashboardStream, SideNavStream)
|
||||
/// </summary>
|
||||
public class StreamController : BaseApiController
|
||||
{
|
||||
private readonly IStreamService _streamService;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly ILogger<StreamController> _logger;
|
||||
|
||||
public StreamController(IStreamService streamService, IUnitOfWork unitOfWork, ILogger<StreamController> logger)
|
||||
{
|
||||
_streamService = streamService;
|
||||
_unitOfWork = unitOfWork;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the layout of the user's dashboard
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("dashboard")]
|
||||
public async Task<ActionResult<IEnumerable<DashboardStreamDto>>> GetDashboardLayout(bool visibleOnly = true)
|
||||
{
|
||||
return Ok(await _streamService.GetDashboardStreams(User.GetUserId(), visibleOnly));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return's the user's side nav
|
||||
/// </summary>
|
||||
[HttpGet("sidenav")]
|
||||
public async Task<ActionResult<IEnumerable<SideNavStreamDto>>> GetSideNav(bool visibleOnly = true)
|
||||
{
|
||||
return Ok(await _streamService.GetSidenavStreams(User.GetUserId(), visibleOnly));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return's the user's external sources
|
||||
/// </summary>
|
||||
[HttpGet("external-sources")]
|
||||
public async Task<ActionResult<IEnumerable<ExternalSourceDto>>> GetExternalSources()
|
||||
{
|
||||
return Ok(await _streamService.GetExternalSources(User.GetUserId()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an external Source
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("create-external-source")]
|
||||
public async Task<ActionResult<ExternalSourceDto>> CreateExternalSource(ExternalSourceDto dto)
|
||||
{
|
||||
// Check if a host and api key exists for the current user
|
||||
return Ok(await _streamService.CreateExternalSource(User.GetUserId(), dto));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing external source
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("update-external-source")]
|
||||
public async Task<ActionResult<ExternalSourceDto>> UpdateExternalSource(ExternalSourceDto dto)
|
||||
{
|
||||
// Check if a host and api key exists for the current user
|
||||
return Ok(await _streamService.UpdateExternalSource(User.GetUserId(), dto));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the external source by host is unique (for this user)
|
||||
/// </summary>
|
||||
/// <param name="host"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("external-source-exists")]
|
||||
public async Task<ActionResult<bool>> ExternalSourceExists(string host, string name, string apiKey)
|
||||
{
|
||||
return Ok(await _unitOfWork.AppUserExternalSourceRepository.ExternalSourceExists(User.GetUserId(), host, name, apiKey));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete's the external source
|
||||
/// </summary>
|
||||
/// <param name="externalSourceId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpDelete("delete-external-source")]
|
||||
public async Task<ActionResult> ExternalSourceExists(int externalSourceId)
|
||||
{
|
||||
await _streamService.DeleteExternalSource(User.GetUserId(), externalSourceId);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Dashboard Stream from a SmartFilter and adds it to the user's dashboard as visible
|
||||
/// </summary>
|
||||
/// <param name="smartFilterId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("add-dashboard-stream")]
|
||||
public async Task<ActionResult<DashboardStreamDto>> AddDashboard([FromQuery] int smartFilterId)
|
||||
{
|
||||
return Ok(await _streamService.CreateDashboardStreamFromSmartFilter(User.GetUserId(), smartFilterId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the visibility of a dashboard stream
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("update-dashboard-stream")]
|
||||
public async Task<ActionResult> UpdateDashboardStream(DashboardStreamDto dto)
|
||||
{
|
||||
await _streamService.UpdateDashboardStream(User.GetUserId(), dto);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the position of a dashboard stream
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("update-dashboard-position")]
|
||||
public async Task<ActionResult> UpdateDashboardStreamPosition(UpdateStreamPositionDto dto)
|
||||
{
|
||||
await _streamService.UpdateDashboardStreamPosition(User.GetUserId(), dto);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a SideNav Stream from a SmartFilter and adds it to the user's sidenav as visible
|
||||
/// </summary>
|
||||
/// <param name="smartFilterId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("add-sidenav-stream")]
|
||||
public async Task<ActionResult<SideNavStreamDto>> AddSideNav([FromQuery] int smartFilterId)
|
||||
{
|
||||
return Ok(await _streamService.CreateSideNavStreamFromSmartFilter(User.GetUserId(), smartFilterId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a SideNav Stream from a SmartFilter and adds it to the user's sidenav as visible
|
||||
/// </summary>
|
||||
/// <param name="externalSourceId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("add-sidenav-stream-from-external-source")]
|
||||
public async Task<ActionResult<SideNavStreamDto>> AddSideNavFromExternalSource([FromQuery] int externalSourceId)
|
||||
{
|
||||
return Ok(await _streamService.CreateSideNavStreamFromExternalSource(User.GetUserId(), externalSourceId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the visibility of a dashboard stream
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("update-sidenav-stream")]
|
||||
public async Task<ActionResult> UpdateSideNavStream(SideNavStreamDto dto)
|
||||
{
|
||||
await _streamService.UpdateSideNavStream(User.GetUserId(), dto);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the position of a dashboard stream
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("update-sidenav-position")]
|
||||
public async Task<ActionResult> UpdateSideNavStreamPosition(UpdateStreamPositionDto dto)
|
||||
{
|
||||
await _streamService.UpdateSideNavStreamPosition(User.GetUserId(), dto);
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue