Dashboard Customization Polish (#2295)
This commit is contained in:
parent
25e759d301
commit
25ffb2ffe1
42 changed files with 255 additions and 258 deletions
|
|
@ -55,54 +55,54 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||
<PackageReference Include="CsvHelper" Version="30.0.1" />
|
||||
<PackageReference Include="Docnet.Core" Version="2.4.0-alpha.4" />
|
||||
<PackageReference Include="EasyCaching.InMemory" Version="1.9.0" />
|
||||
<PackageReference Include="ExCSS" Version="4.2.1" />
|
||||
<PackageReference Include="Docnet.Core" Version="2.6.0" />
|
||||
<PackageReference Include="EasyCaching.InMemory" Version="1.9.1" />
|
||||
<PackageReference Include="ExCSS" Version="4.2.2" />
|
||||
<PackageReference Include="Flurl" Version="3.0.7" />
|
||||
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
||||
<PackageReference Include="Hangfire" Version="1.8.4" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.4" />
|
||||
<PackageReference Include="Hangfire" Version="1.8.5" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.5" />
|
||||
<PackageReference Include="Hangfire.InMemory" Version="0.5.1" />
|
||||
<PackageReference Include="Hangfire.MaximumConcurrentExecutions" Version="1.1.0" />
|
||||
<PackageReference Include="Hangfire.MemoryStorage.Core" Version="1.4.0" />
|
||||
<PackageReference Include="Hangfire.Storage.SQLite" Version="0.3.4" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.51" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.53" />
|
||||
<PackageReference Include="MarkdownDeep.NET.Core" Version="1.5.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.11" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.11" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.11" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.11">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.11" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||
<PackageReference Include="MimeTypeMapOfficial" Version="1.0.17" />
|
||||
<PackageReference Include="Nager.ArticleNumber" Version="1.0.7" />
|
||||
<PackageReference Include="NetVips" Version="2.3.1" />
|
||||
<PackageReference Include="NetVips.Native" Version="8.14.3" />
|
||||
<PackageReference Include="NetVips.Native" Version="8.14.5" />
|
||||
<PackageReference Include="NReco.Logging.File" Version="1.1.6" />
|
||||
<PackageReference Include="Serilog" Version="3.0.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.2.0-dev-00752" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.AspNetCore.SignalR" Version="0.4.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.SignalR.Core" Version="0.1.2" />
|
||||
<PackageReference Include="SharpCompress" Version="0.33.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.7.0.75501">
|
||||
<PackageReference Include="SharpCompress" Version="0.34.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.2" />
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.10.0.77988">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.8" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.32.1" />
|
||||
<PackageReference Include="System.IO.Abstractions" Version="19.2.51" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.11" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.0" />
|
||||
<PackageReference Include="System.IO.Abstractions" Version="19.2.69" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
||||
<PackageReference Include="VersOne.Epub" Version="3.3.1" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -193,7 +193,11 @@ public class AccountController : BaseApiController
|
|||
}
|
||||
|
||||
|
||||
if (user == null) return Unauthorized(await _localizationService.Get("en", "bad-credentials"));
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogWarning("Attempted login by {UserName} failed due to unable to find account", loginDto.Username);
|
||||
return Unauthorized(await _localizationService.Get("en", "bad-credentials"));
|
||||
}
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
if (!roles.Contains(PolicyConstants.LoginRole)) return Unauthorized(await _localizationService.Translate(user.Id, "disabled-account"));
|
||||
|
||||
|
|
@ -205,12 +209,19 @@ public class AccountController : BaseApiController
|
|||
if (result.IsLockedOut)
|
||||
{
|
||||
await _userManager.UpdateSecurityStampAsync(user);
|
||||
return Unauthorized(await _localizationService.Translate(user.Id, "locked-out"));
|
||||
var errorStr = await _localizationService.Translate(user.Id, "locked-out");
|
||||
_logger.LogWarning("{UserName} failed to log in at {Time}: {Issue}", user.UserName, user.LastActive,
|
||||
errorStr);
|
||||
return Unauthorized(errorStr);
|
||||
}
|
||||
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return Unauthorized(await _localizationService.Translate(user.Id, result.IsNotAllowed ? "confirm-email" : "bad-credentials"));
|
||||
var errorStr = await _localizationService.Translate(user.Id,
|
||||
result.IsNotAllowed ? "confirm-email" : "bad-credentials");
|
||||
_logger.LogWarning("{UserName} failed to log in at {Time}: {Issue}", user.UserName, user.LastActive,
|
||||
errorStr);
|
||||
return Unauthorized(errorStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -399,7 +410,6 @@ public class AccountController : BaseApiController
|
|||
_logger.LogError(ex, "There was an error during invite user flow, unable to send an email");
|
||||
}
|
||||
|
||||
|
||||
await _eventHub.SendMessageToAsync(MessageFactory.UserUpdate, MessageFactory.UserUpdateEvent(user.Id, user.UserName!), user.Id);
|
||||
|
||||
return Ok();
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ public interface ILibraryRepository
|
|||
Task<bool> DoAnySeriesFoldersMatch(IEnumerable<string> folders);
|
||||
Task<string?> GetLibraryCoverImageAsync(int libraryId);
|
||||
Task<IList<string>> GetAllCoverImagesAsync();
|
||||
Task<IDictionary<int, LibraryType>> GetLibraryTypesForIdsAsync(IEnumerable<int> libraryIds);
|
||||
Task<IList<Library>> GetAllWithCoversInDifferentEncoding(EncodeFormat encodeFormat);
|
||||
Task<bool> GetAllowsScrobblingBySeriesId(int seriesId);
|
||||
}
|
||||
|
|
@ -346,28 +345,6 @@ public class LibraryRepository : ILibraryRepository
|
|||
.ToListAsync())!;
|
||||
}
|
||||
|
||||
public async Task<IDictionary<int, LibraryType>> GetLibraryTypesForIdsAsync(IEnumerable<int> libraryIds)
|
||||
{
|
||||
var types = await _context.Library
|
||||
.Where(l => libraryIds.Contains(l.Id))
|
||||
.AsNoTracking()
|
||||
.Select(l => new
|
||||
{
|
||||
LibraryId = l.Id,
|
||||
LibraryType = l.Type
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var dict = new Dictionary<int, LibraryType>();
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
dict.TryAdd(type.LibraryId, type.LibraryType);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public async Task<IList<Library>> GetAllWithCoversInDifferentEncoding(EncodeFormat encodeFormat)
|
||||
{
|
||||
var extension = encodeFormat.GetExtension();
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ namespace API.Data.Repositories;
|
|||
public interface IMangaFileRepository
|
||||
{
|
||||
void Update(MangaFile file);
|
||||
Task<bool> AnyMissingExtension();
|
||||
Task<IList<MangaFile>> GetAllWithMissingExtension();
|
||||
}
|
||||
|
||||
|
|
@ -27,11 +26,6 @@ public class MangaFileRepository : IMangaFileRepository
|
|||
_context.Entry(file).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
public async Task<bool> AnyMissingExtension()
|
||||
{
|
||||
return (await _context.MangaFile.CountAsync(f => string.IsNullOrEmpty(f.Extension))) > 0;
|
||||
}
|
||||
|
||||
public async Task<IList<MangaFile>> GetAllWithMissingExtension()
|
||||
{
|
||||
return await _context.MangaFile
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ public interface IMediaErrorRepository
|
|||
void Attach(MediaError error);
|
||||
void Remove(MediaError error);
|
||||
Task<MediaError> Find(string filename);
|
||||
Task<PagedList<MediaErrorDto>> GetAllErrorDtosAsync(UserParams userParams);
|
||||
IEnumerable<MediaErrorDto> GetAllErrorDtosAsync();
|
||||
Task<bool> ExistsAsync(MediaError error);
|
||||
Task DeleteAll();
|
||||
|
|
@ -49,15 +48,6 @@ public class MediaErrorRepository : IMediaErrorRepository
|
|||
return _context.MediaError.Where(e => e.FilePath == filename).SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public Task<PagedList<MediaErrorDto>> GetAllErrorDtosAsync(UserParams userParams)
|
||||
{
|
||||
var query = _context.MediaError
|
||||
.OrderByDescending(m => m.Created)
|
||||
.ProjectTo<MediaErrorDto>(_mapper.ConfigurationProvider)
|
||||
.AsNoTracking();
|
||||
return PagedList<MediaErrorDto>.CreateAsync(query, userParams.PageNumber, userParams.PageSize);
|
||||
}
|
||||
|
||||
public IEnumerable<MediaErrorDto> GetAllErrorDtosAsync()
|
||||
{
|
||||
var query = _context.MediaError
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public interface IPersonRepository
|
|||
Task<IList<Person>> GetAllPeople();
|
||||
Task<IList<PersonDto>> GetAllPersonDtosAsync(int userId);
|
||||
Task<IList<PersonDto>> GetAllPersonDtosByRoleAsync(int userId, PersonRole role);
|
||||
Task RemoveAllPeopleNoLongerAssociated(bool removeExternal = false);
|
||||
Task RemoveAllPeopleNoLongerAssociated();
|
||||
Task<IList<PersonDto>> GetAllPeopleDtosForLibrariesAsync(List<int> libraryIds, int userId);
|
||||
Task<int> GetCountAsync();
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ public class PersonRepository : IPersonRepository
|
|||
_context.Person.Remove(person);
|
||||
}
|
||||
|
||||
public async Task RemoveAllPeopleNoLongerAssociated(bool removeExternal = false)
|
||||
public async Task RemoveAllPeopleNoLongerAssociated()
|
||||
{
|
||||
var peopleWithNoConnections = await _context.Person
|
||||
.Include(p => p.SeriesMetadatas)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public interface IScrobbleRepository
|
|||
void Attach(ScrobbleEvent evt);
|
||||
void Attach(ScrobbleError error);
|
||||
void Remove(ScrobbleEvent evt);
|
||||
void Remove(IList<ScrobbleEvent> evts);
|
||||
void Remove(IEnumerable<ScrobbleEvent> events);
|
||||
void Update(ScrobbleEvent evt);
|
||||
Task<IList<ScrobbleEvent>> GetByEvent(ScrobbleEventType type, bool isProcessed = false);
|
||||
Task<IList<ScrobbleEvent>> GetProcessedEvents(int daysAgo);
|
||||
|
|
@ -60,9 +60,9 @@ public class ScrobbleRepository : IScrobbleRepository
|
|||
_context.ScrobbleEvent.Remove(evt);
|
||||
}
|
||||
|
||||
public void Remove(IList<ScrobbleEvent> evts)
|
||||
public void Remove(IEnumerable<ScrobbleEvent> events)
|
||||
{
|
||||
_context.ScrobbleEvent.RemoveRange(evts);
|
||||
_context.ScrobbleEvent.RemoveRange(events);
|
||||
}
|
||||
|
||||
public void Update(ScrobbleEvent evt)
|
||||
|
|
|
|||
|
|
@ -128,8 +128,6 @@ public interface ISeriesRepository
|
|||
Task<Series?> GetSeriesByFolderPath(string folder, SeriesIncludes includes = SeriesIncludes.None);
|
||||
Task<IEnumerable<Series>> GetAllSeriesByNameAsync(IList<string> normalizedNames,
|
||||
int userId, SeriesIncludes includes = SeriesIncludes.None);
|
||||
Task<IEnumerable<SeriesDto>> GetAllSeriesDtosByNameAsync(IEnumerable<string> normalizedNames,
|
||||
int userId, SeriesIncludes includes = SeriesIncludes.None);
|
||||
Task<Series?> GetFullSeriesByAnyName(string seriesName, string localizedName, int libraryId, MangaFormat format, bool withFullIncludes = true);
|
||||
Task<IList<Series>> RemoveSeriesNotInList(IList<ParsedSeries> seenSeries, int libraryId);
|
||||
Task<IDictionary<string, IList<SeriesModified>>> GetFolderPathMap(int libraryId);
|
||||
|
|
@ -1054,7 +1052,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
|
||||
private static IQueryable<Series> BuildFilterGroup(int userId, FilterStatementDto statement, IQueryable<Series> query)
|
||||
{
|
||||
var (value, _) = FilterFieldValueConverter.ConvertValue(statement.Field, statement.Value);
|
||||
var value = FilterFieldValueConverter.ConvertValue(statement.Field, statement.Value);
|
||||
return statement.Field switch
|
||||
{
|
||||
FilterField.Summary => query.HasSummary(true, statement.Comparison, (string) value),
|
||||
|
|
@ -1085,7 +1083,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
FilterField.WantToRead =>
|
||||
// This is handled in the higher level of code as it's more general
|
||||
query,
|
||||
FilterField.ReadProgress => query.HasReadingProgress(true, statement.Comparison, (int) value, userId),
|
||||
FilterField.ReadProgress => query.HasReadingProgress(true, statement.Comparison, (float) value, userId),
|
||||
FilterField.Formats => query.HasFormat(true, statement.Comparison, (IList<MangaFormat>) value),
|
||||
FilterField.ReleaseYear => query.HasReleaseYear(true, statement.Comparison, (int) value),
|
||||
FilterField.ReadTime => query.HasAverageReadTime(true, statement.Comparison, (int) value),
|
||||
|
|
@ -1471,20 +1469,6 @@ public class SeriesRepository : ISeriesRepository
|
|||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SeriesDto>> GetAllSeriesDtosByNameAsync(IEnumerable<string> normalizedNames, int userId,
|
||||
SeriesIncludes includes = SeriesIncludes.None)
|
||||
{
|
||||
var libraryIds = _context.Library.GetUserLibraries(userId);
|
||||
var userRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||
|
||||
return await _context.Series
|
||||
.Where(s => normalizedNames.Contains(s.NormalizedName))
|
||||
.Where(s => libraryIds.Contains(s.LibraryId))
|
||||
.RestrictAgainstAgeRestriction(userRating)
|
||||
.Includes(includes)
|
||||
.ProjectTo<SeriesDto>(_mapper.ConfigurationProvider)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a series by series name or localized name for a given library.
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ public interface ISiteThemeRepository
|
|||
Task<SiteThemeDto?> GetThemeDtoByName(string themeName);
|
||||
Task<SiteTheme> GetDefaultTheme();
|
||||
Task<IEnumerable<SiteTheme>> GetThemes();
|
||||
Task<SiteTheme?> GetThemeById(int themeId);
|
||||
}
|
||||
|
||||
public class SiteThemeRepository : ISiteThemeRepository
|
||||
|
|
@ -89,13 +88,6 @@ public class SiteThemeRepository : ISiteThemeRepository
|
|||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<SiteTheme?> GetThemeById(int themeId)
|
||||
{
|
||||
return await _context.SiteTheme
|
||||
.Where(t => t.Id == themeId)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<SiteThemeDto?> GetThemeDto(int themeId)
|
||||
{
|
||||
return await _context.SiteTheme
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public interface ITagRepository
|
|||
void Remove(Tag tag);
|
||||
Task<IList<Tag>> GetAllTagsAsync();
|
||||
Task<IList<TagDto>> GetAllTagDtosAsync(int userId);
|
||||
Task RemoveAllTagNoLongerAssociated(bool removeExternal = false);
|
||||
Task RemoveAllTagNoLongerAssociated();
|
||||
Task<IList<TagDto>> GetAllTagDtosForLibrariesAsync(IList<int> libraryIds, int userId);
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ public class TagRepository : ITagRepository
|
|||
_context.Tag.Remove(tag);
|
||||
}
|
||||
|
||||
public async Task RemoveAllTagNoLongerAssociated(bool removeExternal = false)
|
||||
public async Task RemoveAllTagNoLongerAssociated()
|
||||
{
|
||||
var tagsWithNoConnections = await _context.Tag
|
||||
.Include(p => p.SeriesMetadatas)
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ public static class SeriesFilter
|
|||
/// <exception cref="KavitaException"></exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public static IQueryable<Series> HasReadingProgress(this IQueryable<Series> queryable, bool condition,
|
||||
FilterComparison comparison, int readProgress, int userId)
|
||||
FilterComparison comparison, float readProgress, int userId)
|
||||
{
|
||||
if (!condition) return queryable;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,72 +8,72 @@ namespace API.Helpers.Converters;
|
|||
|
||||
public static class FilterFieldValueConverter
|
||||
{
|
||||
public static (object Value, Type Type) ConvertValue(FilterField field, string value)
|
||||
public static object ConvertValue(FilterField field, string value)
|
||||
{
|
||||
return field switch
|
||||
{
|
||||
FilterField.SeriesName => (value, typeof(string)),
|
||||
FilterField.Path => (value, typeof(string)),
|
||||
FilterField.FilePath => (value, typeof(string)),
|
||||
FilterField.ReleaseYear => (int.Parse(value), typeof(int)),
|
||||
FilterField.Languages => (value.Split(',').ToList(), typeof(IList<string>)),
|
||||
FilterField.PublicationStatus => (value.Split(',')
|
||||
FilterField.SeriesName => value,
|
||||
FilterField.Path => value,
|
||||
FilterField.FilePath => value,
|
||||
FilterField.ReleaseYear => int.Parse(value),
|
||||
FilterField.Languages => value.Split(',').ToList(),
|
||||
FilterField.PublicationStatus => value.Split(',')
|
||||
.Select(x => (PublicationStatus) Enum.Parse(typeof(PublicationStatus), x))
|
||||
.ToList(), typeof(IList<PublicationStatus>)),
|
||||
FilterField.Summary => (value, typeof(string)),
|
||||
FilterField.AgeRating => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.Summary => value,
|
||||
FilterField.AgeRating => value.Split(',')
|
||||
.Select(x => (AgeRating) Enum.Parse(typeof(AgeRating), x))
|
||||
.ToList(), typeof(IList<AgeRating>)),
|
||||
FilterField.UserRating => (int.Parse(value), typeof(int)),
|
||||
FilterField.Tags => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.UserRating => int.Parse(value),
|
||||
FilterField.Tags => value.Split(',')
|
||||
.Select(int.Parse)
|
||||
.ToList(), typeof(IList<int>)),
|
||||
FilterField.CollectionTags => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.CollectionTags => value.Split(',')
|
||||
.Select(int.Parse)
|
||||
.ToList(), typeof(IList<int>)),
|
||||
FilterField.Translators => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.Translators => value.Split(',')
|
||||
.Select(int.Parse)
|
||||
.ToList(), typeof(IList<int>)),
|
||||
FilterField.Characters => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.Characters => value.Split(',')
|
||||
.Select(int.Parse)
|
||||
.ToList(), typeof(IList<int>)),
|
||||
FilterField.Publisher => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.Publisher => value.Split(',')
|
||||
.Select(int.Parse)
|
||||
.ToList(), typeof(IList<int>)),
|
||||
FilterField.Editor => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.Editor => value.Split(',')
|
||||
.Select(int.Parse)
|
||||
.ToList(), typeof(IList<int>)),
|
||||
FilterField.CoverArtist => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.CoverArtist => value.Split(',')
|
||||
.Select(int.Parse)
|
||||
.ToList(), typeof(IList<int>)),
|
||||
FilterField.Letterer => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.Letterer => value.Split(',')
|
||||
.Select(int.Parse)
|
||||
.ToList(), typeof(IList<int>)),
|
||||
FilterField.Colorist => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.Colorist => value.Split(',')
|
||||
.Select(int.Parse)
|
||||
.ToList(), typeof(IList<int>)),
|
||||
FilterField.Inker => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.Inker => value.Split(',')
|
||||
.Select(int.Parse)
|
||||
.ToList(), typeof(IList<int>)),
|
||||
FilterField.Penciller => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.Penciller => value.Split(',')
|
||||
.Select(int.Parse)
|
||||
.ToList(), typeof(IList<int>)),
|
||||
FilterField.Writers => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.Writers => value.Split(',')
|
||||
.Select(int.Parse)
|
||||
.ToList(), typeof(IList<int>)),
|
||||
FilterField.Genres => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.Genres => value.Split(',')
|
||||
.Select(int.Parse)
|
||||
.ToList(), typeof(IList<int>)),
|
||||
FilterField.Libraries => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.Libraries => value.Split(',')
|
||||
.Select(int.Parse)
|
||||
.ToList(), typeof(IList<int>)),
|
||||
FilterField.WantToRead => (bool.Parse(value), typeof(bool)),
|
||||
FilterField.ReadProgress => (int.Parse(value), typeof(int)),
|
||||
FilterField.ReadingDate => (DateTime.Parse(value), typeof(DateTime?)),
|
||||
FilterField.Formats => (value.Split(',')
|
||||
.ToList(),
|
||||
FilterField.WantToRead => bool.Parse(value),
|
||||
FilterField.ReadProgress => float.Parse(value),
|
||||
FilterField.ReadingDate => DateTime.Parse(value),
|
||||
FilterField.Formats => value.Split(',')
|
||||
.Select(x => (MangaFormat) Enum.Parse(typeof(MangaFormat), x))
|
||||
.ToList(), typeof(IList<MangaFormat>)),
|
||||
FilterField.ReadTime => (int.Parse(value), typeof(int)),
|
||||
.ToList(),
|
||||
FilterField.ReadTime => int.Parse(value),
|
||||
_ => throw new ArgumentException("Invalid field type")
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ public class DirectoryService : IDirectoryService
|
|||
private const RegexOptions MatchOptions = RegexOptions.Compiled | RegexOptions.IgnoreCase;
|
||||
|
||||
private static readonly Regex ExcludeDirectories = new Regex(
|
||||
@"@eaDir|\.DS_Store|\.qpkg|__MACOSX|@Recently-Snapshot|@recycle|\.@__thumb|\.caltrash",
|
||||
@"@eaDir|\.DS_Store|\.qpkg|__MACOSX|@Recently-Snapshot|@recycle|\.@__thumb|\.caltrash|#recycle",
|
||||
MatchOptions,
|
||||
Tasks.Scanner.Parser.Parser.RegexTimeout);
|
||||
private static readonly Regex FileCopyAppend = new Regex(@"\(\d+\)",
|
||||
|
|
|
|||
|
|
@ -550,9 +550,10 @@ public static class Parser
|
|||
new Regex(
|
||||
@"(Глава|глава|Главы|Глава)(\.?)(\s|_)?(?<Chapter>\d+(?:.\d+|-\d+)?)",
|
||||
MatchOptions, RegexTimeout),
|
||||
|
||||
// Hinowa ga CRUSH! 018 (2019) (Digital) (LuCaZ).cbz, Hinowa ga CRUSH! 018.5 (2019) (Digital) (LuCaZ).cbz
|
||||
new Regex(
|
||||
@"^(?!Vol)(?<Series>.+?)(?<!Vol)(?<!Vol.)\s(\d\s)?(?<Chapter>\d+(?:\.\d+|-\d+)?)(?:\s\(\d{4}\))?(\b|_|-)",
|
||||
@"^(?<Series>.+?)(?<!Vol)(?<!Vol.)(?<!Volume)\s(\d\s)?(?<Chapter>\d+(?:\.\d+|-\d+)?)(?:\s\(\d{4}\))?(\b|_|-)",
|
||||
MatchOptions, RegexTimeout),
|
||||
// Tower Of God S01 014 (CBT) (digital).cbz
|
||||
new Regex(
|
||||
|
|
@ -997,6 +998,7 @@ public static class Parser
|
|||
{
|
||||
return path.Contains("__MACOSX") || path.StartsWith("@Recently-Snapshot") || path.StartsWith("@recycle")
|
||||
|| path.StartsWith("._") || Path.GetFileName(path).StartsWith("._") || path.Contains(".qpkg")
|
||||
|| path.StartsWith("#recycle")
|
||||
|| path.Contains(".caltrash");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.SignalR.Presence;
|
||||
|
|
@ -55,8 +56,7 @@ public class EventHub : IEventHub
|
|||
/// <returns></returns>
|
||||
public async Task SendMessageToAsync(string method, SignalRMessage message, int userId)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(userId) ?? throw new InvalidOperationException();
|
||||
await _messageHub.Clients.User(user.UserName!).SendAsync(method, message);
|
||||
await _messageHub.Clients.Users(new List<string>() {userId + string.Empty}).SendAsync(method, message);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ public class MessageHub : Hub
|
|||
|
||||
public override async Task OnConnectedAsync()
|
||||
{
|
||||
await _tracker.UserConnected(Context.User!.GetUserId(), Context.ConnectionId);
|
||||
var userId = Context.User!.GetUserId();
|
||||
await _tracker.UserConnected(userId, Context.ConnectionId);
|
||||
|
||||
var currentUsers = await PresenceTracker.GetOnlineUsers();
|
||||
await Clients.All.SendAsync(MessageFactory.OnlineUsers, currentUsers);
|
||||
|
|
|
|||
|
|
@ -80,7 +80,10 @@ public class PresenceTracker : IPresenceTracker
|
|||
string[] onlineUsers;
|
||||
lock (OnlineUsers)
|
||||
{
|
||||
onlineUsers = OnlineUsers.OrderBy(k => k.Value.UserName).Select(k => k.Value.UserName).ToArray();
|
||||
onlineUsers = OnlineUsers
|
||||
.Select(k => k.Value.UserName)
|
||||
.Order()
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
return Task.FromResult(onlineUsers);
|
||||
|
|
@ -91,7 +94,10 @@ public class PresenceTracker : IPresenceTracker
|
|||
int[] onlineUsers;
|
||||
lock (OnlineUsers)
|
||||
{
|
||||
onlineUsers = OnlineUsers.Where(pair => pair.Value.IsAdmin).OrderBy(k => k.Key).Select(k => k.Key).ToArray();
|
||||
onlineUsers = OnlineUsers.Where(pair => pair.Value.IsAdmin)
|
||||
.Select(k => k.Key)
|
||||
.Order()
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue