diff --git a/API/Controllers/OPDSController.cs b/API/Controllers/OPDSController.cs index c9f460590..49a70d90d 100644 --- a/API/Controllers/OPDSController.cs +++ b/API/Controllers/OPDSController.cs @@ -65,15 +65,15 @@ namespace API.Controllers SetFeedId(feed, "root"); feed.Entries.Add(new FeedEntry() { - Id = "inProgress", - Title = "In Progress", + Id = "onDeck", + Title = "On Deck", Content = new FeedEntryContent() { - Text = "Browse by In Progress" + Text = "Browse by On Deck" }, Links = new List() { - CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation, Prefix + $"{apiKey}/in-progress"), + CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation, Prefix + $"{apiKey}/on-deck"), } }); feed.Entries.Add(new FeedEntry() @@ -374,9 +374,9 @@ namespace API.Controllers return CreateXmlResult(SerializeXml(feed)); } - [HttpGet("{apiKey}/in-progress")] + [HttpGet("{apiKey}/on-deck")] [Produces("application/xml")] - public async Task GetInProgress(string apiKey, [FromQuery] int pageNumber = 1) + public async Task GetOnDeck(string apiKey, [FromQuery] int pageNumber = 1) { if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds) return BadRequest("OPDS is not enabled on this server"); @@ -386,16 +386,16 @@ namespace API.Controllers PageNumber = pageNumber, PageSize = 20 }; - var results = await _unitOfWork.SeriesRepository.GetInProgress(userId, 0, userParams, _filterDto); + var results = await _unitOfWork.SeriesRepository.GetOnDeck(userId, 0, userParams, _filterDto); var listResults = results.DistinctBy(s => s.Name).Skip((userParams.PageNumber - 1) * userParams.PageSize) .Take(userParams.PageSize).ToList(); var pagedList = new PagedList(listResults, listResults.Count, userParams.PageNumber, userParams.PageSize); Response.AddPaginationHeader(pagedList.CurrentPage, pagedList.PageSize, pagedList.TotalCount, pagedList.TotalPages); - var feed = CreateFeed("In Progress", $"{apiKey}/in-progress", apiKey); - SetFeedId(feed, "in-progress"); - AddPagination(feed, pagedList, $"{Prefix}{apiKey}/in-progress"); + var feed = CreateFeed("On Deck", $"{apiKey}/on-deck", apiKey); + SetFeedId(feed, "on-deck"); + AddPagination(feed, pagedList, $"{Prefix}{apiKey}/on-deck"); foreach (var seriesDto in pagedList) { diff --git a/API/Controllers/SeriesController.cs b/API/Controllers/SeriesController.cs index e2072955b..04e33a07a 100644 --- a/API/Controllers/SeriesController.cs +++ b/API/Controllers/SeriesController.cs @@ -230,12 +230,19 @@ namespace API.Controllers return Ok(series); } - [HttpPost("in-progress")] - public async Task>> GetInProgress(FilterDto filterDto, [FromQuery] UserParams userParams, [FromQuery] int libraryId = 0) + /// + /// Fetches series that are on deck aka have progress on them. + /// + /// + /// + /// Default of 0 meaning all libraries + /// + [HttpPost("on-deck")] + public async Task>> GetOnDeck(FilterDto filterDto, [FromQuery] UserParams userParams, [FromQuery] int libraryId = 0) { // NOTE: This has to be done manually like this due to the DistinctBy requirement var userId = await _unitOfWork.UserRepository.GetUserIdByUsernameAsync(User.GetUsername()); - var results = await _unitOfWork.SeriesRepository.GetInProgress(userId, libraryId, userParams, filterDto); + var results = await _unitOfWork.SeriesRepository.GetOnDeck(userId, libraryId, userParams, filterDto); var listResults = results.DistinctBy(s => s.Name).Skip((userParams.PageNumber - 1) * userParams.PageSize) .Take(userParams.PageSize).ToList(); diff --git a/API/Data/Repositories/SeriesRepository.cs b/API/Data/Repositories/SeriesRepository.cs index bed73e2f5..8e94ff9b6 100644 --- a/API/Data/Repositories/SeriesRepository.cs +++ b/API/Data/Repositories/SeriesRepository.cs @@ -312,14 +312,15 @@ namespace API.Data.Repositories } /// - /// Returns Series that the user has some partial progress on + /// Returns Series that the user has some partial progress on. Sorts based on activity. Sort first by User progress, but if a series + /// has been updated recently, bump it to the front. /// /// /// Library to restrict to, if 0, will apply to all libraries /// Pagination information /// Optional (default null) filter on query /// - public async Task> GetInProgress(int userId, int libraryId, UserParams userParams, FilterDto filter) + public async Task> GetOnDeck(int userId, int libraryId, UserParams userParams, FilterDto filter) { var formats = filter.GetSqlFilter(); IList userLibraries; @@ -352,6 +353,7 @@ namespace API.Data.Repositories && s.PagesRead > 0 && s.PagesRead < s.Series.Pages) .OrderByDescending(s => s.LastModified) + .ThenByDescending(s => s.Series.LastModified) .Select(s => s.Series) .ProjectTo(_mapper.ConfigurationProvider) .AsSplitQuery() diff --git a/API/Interfaces/Repositories/ISeriesRepository.cs b/API/Interfaces/Repositories/ISeriesRepository.cs index 2129c894b..70b5aa672 100644 --- a/API/Interfaces/Repositories/ISeriesRepository.cs +++ b/API/Interfaces/Repositories/ISeriesRepository.cs @@ -45,7 +45,7 @@ namespace API.Interfaces.Repositories /// Task AddSeriesModifiers(int userId, List series); Task GetSeriesCoverImageAsync(int seriesId); - Task> GetInProgress(int userId, int libraryId, UserParams userParams, FilterDto filter); + Task> GetOnDeck(int userId, int libraryId, UserParams userParams, FilterDto filter); Task> GetRecentlyAdded(int libraryId, int userId, UserParams userParams, FilterDto filter); // NOTE: Probably put this in LibraryRepo Task GetSeriesMetadata(int seriesId); Task> GetSeriesDtoForCollectionAsync(int collectionId, int userId, UserParams userParams); diff --git a/UI/Web/src/app/_services/series.service.ts b/UI/Web/src/app/_services/series.service.ts index 6d3693558..8ed258c45 100644 --- a/UI/Web/src/app/_services/series.service.ts +++ b/UI/Web/src/app/_services/series.service.ts @@ -6,7 +6,6 @@ import { environment } from 'src/environments/environment'; import { Chapter } from '../_models/chapter'; import { CollectionTag } from '../_models/collection-tag'; import { InProgressChapter } from '../_models/in-progress-chapter'; -import { MangaFormat } from '../_models/manga-format'; import { PaginatedResult } from '../_models/pagination'; import { Series } from '../_models/series'; import { SeriesFilter } from '../_models/series-filter'; @@ -112,13 +111,13 @@ export class SeriesService { ); } - getInProgress(libraryId: number = 0, pageNum?: number, itemsPerPage?: number, filter?: SeriesFilter) { + getOnDeck(libraryId: number = 0, pageNum?: number, itemsPerPage?: number, filter?: SeriesFilter) { const data = this.createSeriesFilter(filter); let params = new HttpParams(); params = this._addPaginationIfExists(params, pageNum, itemsPerPage); - return this.httpClient.post(this.baseUrl + 'series/in-progress?libraryId=' + libraryId, data, {observe: 'response', params}).pipe( + return this.httpClient.post(this.baseUrl + 'series/on-deck?libraryId=' + libraryId, data, {observe: 'response', params}).pipe( map(response => { return this._cachePaginatedResults(response, new PaginatedResult()); })); diff --git a/UI/Web/src/app/app-routing.module.ts b/UI/Web/src/app/app-routing.module.ts index 5ddee3f2b..39800d8cd 100644 --- a/UI/Web/src/app/app-routing.module.ts +++ b/UI/Web/src/app/app-routing.module.ts @@ -7,7 +7,7 @@ import { RecentlyAddedComponent } from './recently-added/recently-added.componen import { UserLoginComponent } from './user-login/user-login.component'; import { AuthGuard } from './_guards/auth.guard'; import { LibraryAccessGuard } from './_guards/library-access.guard'; -import { InProgressComponent } from './in-progress/in-progress.component'; +import { OnDeckComponent } from './on-deck/on-deck.component'; import { DashboardComponent } from './dashboard/dashboard.component'; // TODO: Once we modularize the components, use this and measure performance impact: https://angular.io/guide/lazy-loading-ngmodules#preloading-modules @@ -54,7 +54,7 @@ const routes: Routes = [ children: [ {path: 'library', component: DashboardComponent}, {path: 'recently-added', component: RecentlyAddedComponent}, - {path: 'in-progress', component: InProgressComponent}, + {path: 'on-deck', component: OnDeckComponent}, ] }, {path: 'login', component: UserLoginComponent}, diff --git a/UI/Web/src/app/app.module.ts b/UI/Web/src/app/app.module.ts index ed8074d08..2dacb097b 100644 --- a/UI/Web/src/app/app.module.ts +++ b/UI/Web/src/app/app.module.ts @@ -1,12 +1,12 @@ import { BrowserModule, Title } from '@angular/platform-browser'; -import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { APP_BASE_HREF } from '@angular/common'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { HttpClient, HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; +import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { NgbCollapseModule, NgbDropdownModule, NgbNavModule, NgbPaginationModule, NgbRatingModule } from '@ng-bootstrap/ng-bootstrap'; import { NavHeaderComponent } from './nav-header/nav-header.component'; import { JwtInterceptor } from './_interceptors/jwt.interceptor'; @@ -25,7 +25,7 @@ import { CarouselModule } from './carousel/carousel.module'; import { PersonBadgeComponent } from './person-badge/person-badge.component'; import { TypeaheadModule } from './typeahead/typeahead.module'; import { RecentlyAddedComponent } from './recently-added/recently-added.component'; -import { InProgressComponent } from './in-progress/in-progress.component'; +import { OnDeckComponent } from './on-deck/on-deck.component'; import { DashboardComponent } from './dashboard/dashboard.component'; import { CardsModule } from './cards/cards.module'; import { CollectionsModule } from './collections/collections.module'; @@ -46,7 +46,7 @@ import { ConfigData } from './_models/config-data'; ReviewSeriesModalComponent, PersonBadgeComponent, RecentlyAddedComponent, - InProgressComponent, + OnDeckComponent, DashboardComponent, ], imports: [ diff --git a/UI/Web/src/app/library/library.component.html b/UI/Web/src/app/library/library.component.html index ba2003226..3157a7d80 100644 --- a/UI/Web/src/app/library/library.component.html +++ b/UI/Web/src/app/library/library.component.html @@ -5,7 +5,7 @@

You haven't been granted access to any libraries.

- + diff --git a/UI/Web/src/app/library/library.component.ts b/UI/Web/src/app/library/library.component.ts index fab0d4222..f8a7649d3 100644 --- a/UI/Web/src/app/library/library.component.ts +++ b/UI/Web/src/app/library/library.component.ts @@ -92,7 +92,7 @@ export class LibraryComponent implements OnInit, OnDestroy { } loadInProgress() { - this.seriesService.getInProgress().pipe(takeUntil(this.onDestroy)).subscribe((updatedSeries) => { + this.seriesService.getOnDeck().pipe(takeUntil(this.onDestroy)).subscribe((updatedSeries) => { this.inProgress = updatedSeries.result; }); } @@ -108,8 +108,8 @@ export class LibraryComponent implements OnInit, OnDestroy { this.router.navigate(['collections']); } else if (sectionTitle.toLowerCase() === 'recently added') { this.router.navigate(['recently-added']); - } else if (sectionTitle.toLowerCase() === 'in progress') { - this.router.navigate(['in-progress']); + } else if (sectionTitle.toLowerCase() === 'on deck') { + this.router.navigate(['on-deck']); } } diff --git a/UI/Web/src/app/not-connected/not-connected.component.ts b/UI/Web/src/app/not-connected/not-connected.component.ts index 70b019fa2..d49575a55 100644 --- a/UI/Web/src/app/not-connected/not-connected.component.ts +++ b/UI/Web/src/app/not-connected/not-connected.component.ts @@ -11,6 +11,7 @@ export class NotConnectedComponent implements OnInit { constructor(private memberService: MemberService, private router: Router) { } ngOnInit(): void { + // BUG: TODO: This causes an infinite reload loop on the UI when the API on backend doesn't exist // We make a call to backend on refresh so that if it's up, we can redirect to /home this.memberService.adminExists().subscribe((exists) => { const pageResume = localStorage.getItem('kavita--no-connection-url'); diff --git a/UI/Web/src/app/in-progress/in-progress.component.html b/UI/Web/src/app/on-deck/on-deck.component.html similarity index 93% rename from UI/Web/src/app/in-progress/in-progress.component.html rename to UI/Web/src/app/on-deck/on-deck.component.html index f4ca791f0..2d8eded75 100644 --- a/UI/Web/src/app/in-progress/in-progress.component.html +++ b/UI/Web/src/app/on-deck/on-deck.component.html @@ -1,5 +1,5 @@ - false; - this.titleService.setTitle('Kavita - In Progress'); + this.titleService.setTitle('Kavita - On Deck'); if (this.pagination === undefined || this.pagination === null) { this.pagination = {currentPage: 0, itemsPerPage: 30, totalItems: 0, totalPages: 1}; } @@ -79,7 +79,7 @@ export class InProgressComponent implements OnInit { this.pagination.currentPage = parseInt(page, 10); } this.isLoading = true; - this.seriesService.getInProgress(this.libraryId, this.pagination?.currentPage, this.pagination?.itemsPerPage, this.filter).pipe(take(1)).subscribe(series => { + this.seriesService.getOnDeck(this.libraryId, this.pagination?.currentPage, this.pagination?.itemsPerPage, this.filter).pipe(take(1)).subscribe(series => { this.series = series.result; this.pagination = series.pagination; this.isLoading = false; diff --git a/UI/Web/src/app/recently-added/recently-added.component.ts b/UI/Web/src/app/recently-added/recently-added.component.ts index 97640118d..5f3164cf4 100644 --- a/UI/Web/src/app/recently-added/recently-added.component.ts +++ b/UI/Web/src/app/recently-added/recently-added.component.ts @@ -10,13 +10,13 @@ import { SeriesAddedEvent } from '../_models/events/series-added-event'; import { Pagination } from '../_models/pagination'; import { Series } from '../_models/series'; import { FilterItem, mangaFormatFilters, SeriesFilter } from '../_models/series-filter'; -import { Action, ActionFactoryService } from '../_services/action-factory.service'; +import { Action } from '../_services/action-factory.service'; import { ActionService } from '../_services/action.service'; import { MessageHubService } from '../_services/message-hub.service'; import { SeriesService } from '../_services/series.service'; /** - * This component is used as a standard layout for any card detail. ie) series, in-progress, collections, etc. + * This component is used as a standard layout for any card detail. ie) series, on-deck, collections, etc. */ @Component({ selector: 'app-recently-added',