mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Release Polish 2 (#2332)
This commit is contained in:
parent
1d784c6442
commit
d286d531ad
112
API.Tests/Helpers/OrderableHelperTests.cs
Normal file
112
API.Tests/Helpers/OrderableHelperTests.cs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using API.Entities;
|
||||||
|
using API.Helpers;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace API.Tests.Helpers;
|
||||||
|
|
||||||
|
public class OrderableHelperTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ReorderItems_ItemExists_SuccessfullyReorders()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var items = new List<AppUserSideNavStream>
|
||||||
|
{
|
||||||
|
new AppUserSideNavStream { Id = 1, Order = 0, Name = "A" },
|
||||||
|
new AppUserSideNavStream { Id = 2, Order = 1, Name = "A" },
|
||||||
|
new AppUserSideNavStream { Id = 3, Order = 2, Name = "A" },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
OrderableHelper.ReorderItems(items, 2, 0);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(2, items[0].Id); // Item 2 should be at position 0
|
||||||
|
Assert.Equal(1, items[1].Id); // Item 1 should be at position 1
|
||||||
|
Assert.Equal(3, items[2].Id); // Item 3 should remain at position 2
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReorderItems_ItemNotFound_NoChange()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var items = new List<AppUserSideNavStream>
|
||||||
|
{
|
||||||
|
new AppUserSideNavStream { Id = 1, Order = 0, Name = "A" },
|
||||||
|
new AppUserSideNavStream { Id = 2, Order = 1, Name = "A" },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
OrderableHelper.ReorderItems(items, 3, 0); // Item with Id 3 doesn't exist
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(1, items[0].Id); // Item 1 should remain at position 0
|
||||||
|
Assert.Equal(2, items[1].Id); // Item 2 should remain at position 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReorderItems_InvalidPosition_NoChange()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var items = new List<AppUserSideNavStream>
|
||||||
|
{
|
||||||
|
new AppUserSideNavStream { Id = 1, Order = 0, Name = "A" },
|
||||||
|
new AppUserSideNavStream { Id = 2, Order = 1, Name = "A" },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
OrderableHelper.ReorderItems(items, 2, 3); // Position 3 is out of range
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(1, items[0].Id); // Item 1 should remain at position 0
|
||||||
|
Assert.Equal(2, items[1].Id); // Item 2 should remain at position 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReorderItems_EmptyList_NoChange()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var items = new List<AppUserSideNavStream>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
OrderableHelper.ReorderItems(items, 2, 1); // List is empty
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(items); // The list should remain empty
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReorderItems_DoubleMove()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var items = new List<AppUserSideNavStream>
|
||||||
|
{
|
||||||
|
new AppUserSideNavStream { Id = 1, Order = 0, Name = "0" },
|
||||||
|
new AppUserSideNavStream { Id = 2, Order = 1, Name = "1" },
|
||||||
|
new AppUserSideNavStream { Id = 3, Order = 2, Name = "2" },
|
||||||
|
new AppUserSideNavStream { Id = 4, Order = 3, Name = "3" },
|
||||||
|
new AppUserSideNavStream { Id = 5, Order = 4, Name = "4" },
|
||||||
|
new AppUserSideNavStream { Id = 6, Order = 5, Name = "5" },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Move 4 -> 1
|
||||||
|
OrderableHelper.ReorderItems(items, 5, 1);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(1, items[0].Id);
|
||||||
|
Assert.Equal(0, items[0].Order);
|
||||||
|
Assert.Equal(5, items[1].Id);
|
||||||
|
Assert.Equal(1, items[1].Order);
|
||||||
|
Assert.Equal(2, items[2].Id);
|
||||||
|
Assert.Equal(2, items[2].Order);
|
||||||
|
|
||||||
|
// Ensure the items are in the correct order
|
||||||
|
Assert.Equal("041235", string.Join("", items.Select(s => s.Name)));
|
||||||
|
|
||||||
|
OrderableHelper.ReorderItems(items, items[4].Id, 1); // 3 -> 1
|
||||||
|
|
||||||
|
Assert.Equal("034125", string.Join("", items.Select(s => s.Name)));
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ public static class OrderableHelper
|
|||||||
public static void ReorderItems(List<AppUserSideNavStream> items, int itemId, int toPosition)
|
public static void ReorderItems(List<AppUserSideNavStream> items, int itemId, int toPosition)
|
||||||
{
|
{
|
||||||
var item = items.Find(r => r.Id == itemId);
|
var item = items.Find(r => r.Id == itemId);
|
||||||
if (item != null)
|
if (item != null && toPosition < items.Count)
|
||||||
{
|
{
|
||||||
items.Remove(item);
|
items.Remove(item);
|
||||||
items.Insert(toPosition, item);
|
items.Insert(toPosition, item);
|
||||||
|
@ -247,6 +247,7 @@ public class StreamService : IStreamService
|
|||||||
var stream = await _unitOfWork.UserRepository.GetSideNavStream(dto.Id);
|
var stream = await _unitOfWork.UserRepository.GetSideNavStream(dto.Id);
|
||||||
if (stream == null)
|
if (stream == null)
|
||||||
throw new KavitaException(await _localizationService.Translate(userId, "sidenav-stream-doesnt-exist"));
|
throw new KavitaException(await _localizationService.Translate(userId, "sidenav-stream-doesnt-exist"));
|
||||||
|
|
||||||
stream.Visible = dto.Visible;
|
stream.Visible = dto.Visible;
|
||||||
|
|
||||||
_unitOfWork.UserRepository.Update(stream);
|
_unitOfWork.UserRepository.Update(stream);
|
||||||
@ -266,8 +267,8 @@ public class StreamService : IStreamService
|
|||||||
var list = user!.SideNavStreams.ToList();
|
var list = user!.SideNavStreams.ToList();
|
||||||
OrderableHelper.ReorderItems(list, stream.Id, dto.ToPosition);
|
OrderableHelper.ReorderItems(list, stream.Id, dto.ToPosition);
|
||||||
user.SideNavStreams = list;
|
user.SideNavStreams = list;
|
||||||
_unitOfWork.UserRepository.Update(user);
|
|
||||||
|
|
||||||
|
_unitOfWork.UserRepository.Update(user);
|
||||||
await _unitOfWork.CommitAsync();
|
await _unitOfWork.CommitAsync();
|
||||||
if (!stream.Visible) return;
|
if (!stream.Visible) return;
|
||||||
await _eventHub.SendMessageToAsync(MessageFactory.SideNavUpdate, MessageFactory.SideNavUpdateEvent(userId),
|
await _eventHub.SendMessageToAsync(MessageFactory.SideNavUpdate, MessageFactory.SideNavUpdateEvent(userId),
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
inject,
|
inject,
|
||||||
Input,
|
Input,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output, ViewChild,
|
Output,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
||||||
import {FilterStatement} from '../../../_models/metadata/v2/filter-statement';
|
import {FilterStatement} from '../../../_models/metadata/v2/filter-statement';
|
||||||
@ -29,7 +29,6 @@ import {
|
|||||||
NgbDate,
|
NgbDate,
|
||||||
NgbDateParserFormatter,
|
NgbDateParserFormatter,
|
||||||
NgbDatepicker,
|
NgbDatepicker,
|
||||||
NgbDateStruct,
|
|
||||||
NgbInputDatepicker,
|
NgbInputDatepicker,
|
||||||
NgbTooltip
|
NgbTooltip
|
||||||
} from "@ng-bootstrap/ng-bootstrap";
|
} from "@ng-bootstrap/ng-bootstrap";
|
||||||
@ -79,20 +78,26 @@ const NumberFieldsThatIncludeDateComparisons = [
|
|||||||
FilterField.ReleaseYear
|
FilterField.ReleaseYear
|
||||||
];
|
];
|
||||||
|
|
||||||
const StringComparisons = [FilterComparison.Equal,
|
const StringComparisons = [
|
||||||
|
FilterComparison.Equal,
|
||||||
FilterComparison.NotEqual,
|
FilterComparison.NotEqual,
|
||||||
FilterComparison.BeginsWith,
|
FilterComparison.BeginsWith,
|
||||||
FilterComparison.EndsWith,
|
FilterComparison.EndsWith,
|
||||||
FilterComparison.Matches];
|
FilterComparison.Matches];
|
||||||
const DateComparisons = [FilterComparison.IsBefore, FilterComparison.IsAfter, FilterComparison.Equal,
|
const DateComparisons = [
|
||||||
|
FilterComparison.IsBefore,
|
||||||
|
FilterComparison.IsAfter,
|
||||||
|
FilterComparison.Equal,
|
||||||
FilterComparison.NotEqual,];
|
FilterComparison.NotEqual,];
|
||||||
const NumberComparisons = [FilterComparison.Equal,
|
const NumberComparisons = [
|
||||||
|
FilterComparison.Equal,
|
||||||
FilterComparison.NotEqual,
|
FilterComparison.NotEqual,
|
||||||
FilterComparison.LessThan,
|
FilterComparison.LessThan,
|
||||||
FilterComparison.LessThanEqual,
|
FilterComparison.LessThanEqual,
|
||||||
FilterComparison.GreaterThan,
|
FilterComparison.GreaterThan,
|
||||||
FilterComparison.GreaterThanEqual];
|
FilterComparison.GreaterThanEqual];
|
||||||
const DropdownComparisons = [FilterComparison.Equal,
|
const DropdownComparisons = [
|
||||||
|
FilterComparison.Equal,
|
||||||
FilterComparison.NotEqual,
|
FilterComparison.NotEqual,
|
||||||
FilterComparison.Contains,
|
FilterComparison.Contains,
|
||||||
FilterComparison.NotContains,
|
FilterComparison.NotContains,
|
||||||
|
@ -81,9 +81,6 @@ export class DraggableOrderedListComponent {
|
|||||||
return Math.min(this.items.length / 20, 20);
|
return Math.min(this.items.length / 20, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
log(a: any, b: any) {console.log('item: ', a, 'index', b)}
|
|
||||||
|
|
||||||
|
|
||||||
constructor(private readonly cdRef: ChangeDetectorRef) {
|
constructor(private readonly cdRef: ChangeDetectorRef) {
|
||||||
this.bulkSelectionService.selections$.pipe(
|
this.bulkSelectionService.selections$.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
@ -108,12 +105,12 @@ export class DraggableOrderedListComponent {
|
|||||||
// get the new value of the input
|
// get the new value of the input
|
||||||
const inputElem = <HTMLInputElement>document.querySelector('#reorder-' + previousIndex);
|
const inputElem = <HTMLInputElement>document.querySelector('#reorder-' + previousIndex);
|
||||||
const newIndex = parseInt(inputElem.value, 10);
|
const newIndex = parseInt(inputElem.value, 10);
|
||||||
if (previousIndex === newIndex) return;
|
if (item.order === newIndex) return;
|
||||||
moveItemInArray(this.items, previousIndex, newIndex);
|
moveItemInArray(this.items, item.order, newIndex);
|
||||||
this.orderUpdated.emit({
|
this.orderUpdated.emit({
|
||||||
fromPosition: previousIndex,
|
fromPosition: item.order,
|
||||||
toPosition: newIndex,
|
toPosition: newIndex,
|
||||||
item: this.items[newIndex],
|
item: item,
|
||||||
fromAccessibilityMode: true
|
fromAccessibilityMode: true
|
||||||
});
|
});
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
@ -121,7 +118,7 @@ export class DraggableOrderedListComponent {
|
|||||||
|
|
||||||
removeItem(item: any, position: number) {
|
removeItem(item: any, position: number) {
|
||||||
this.itemRemove.emit({
|
this.itemRemove.emit({
|
||||||
position,
|
position: item!.order,
|
||||||
item
|
item
|
||||||
});
|
});
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
|
@ -67,17 +67,13 @@ export class ReadingListDetailComponent implements OnInit {
|
|||||||
characters$!: Observable<Person[]>;
|
characters$!: Observable<Person[]>;
|
||||||
|
|
||||||
private translocoService = inject(TranslocoService);
|
private translocoService = inject(TranslocoService);
|
||||||
|
protected readonly MangaFormat = MangaFormat;
|
||||||
get MangaFormat(): typeof MangaFormat {
|
|
||||||
return MangaFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private router: Router, private readingListService: ReadingListService,
|
constructor(private route: ActivatedRoute, private router: Router, private readingListService: ReadingListService,
|
||||||
private actionService: ActionService, private actionFactoryService: ActionFactoryService, public utilityService: UtilityService,
|
private actionService: ActionService, private actionFactoryService: ActionFactoryService, public utilityService: UtilityService,
|
||||||
public imageService: ImageService, private accountService: AccountService, private toastr: ToastrService,
|
public imageService: ImageService, private accountService: AccountService, private toastr: ToastrService,
|
||||||
private confirmService: ConfirmService, private libraryService: LibraryService, private readerService: ReaderService,
|
private confirmService: ConfirmService, private libraryService: LibraryService, private readerService: ReaderService,
|
||||||
private readonly cdRef: ChangeDetectorRef, private filterUtilityService: FilterUtilitiesService, private titleService: Title) {
|
private readonly cdRef: ChangeDetectorRef, private filterUtilityService: FilterUtilitiesService, private titleService: Title) {
|
||||||
this.titleService.setTitle('Kavita - ' + translate('side-nav.reading-lists'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -87,6 +83,7 @@ export class ReadingListDetailComponent implements OnInit {
|
|||||||
this.router.navigateByUrl('/libraries');
|
this.router.navigateByUrl('/libraries');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.titleService.setTitle('Kavita - ' + translate('side-nav.reading-lists'));
|
||||||
this.listId = parseInt(listId, 10);
|
this.listId = parseInt(listId, 10);
|
||||||
this.characters$ = this.readingListService.getCharacters(this.listId);
|
this.characters$ = this.readingListService.getCharacters(this.listId);
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
.modal-body {
|
.modal-body {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 1rem 0 1rem 1rem;
|
|
||||||
|
|
||||||
.tab-content {
|
.tab-content {
|
||||||
max-height: calc(var(--vh) * 100 - 235px);
|
max-height: calc(var(--vh) * 100 - 235px);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"name": "GPL-3.0",
|
"name": "GPL-3.0",
|
||||||
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
||||||
},
|
},
|
||||||
"version": "0.7.8.13"
|
"version": "0.7.8.14"
|
||||||
},
|
},
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user