From 5e2606b0dec407b85e9f06b27cc422635c04f547 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Sun, 26 Sep 2021 05:40:52 -0700 Subject: [PATCH] Feature/release cleanup (#597) * Deselect all after bulk operations complete. * Implemented a way to trigger selection code on mobile. * When selection mode is active, make the clickable area the whole image. * Series detail shouldn't use gutters for card layout as it can cause skewing on mobile * Long press on card items to trigger the selection on mobile * Code cleanup * One more * Misread the code issue --- API/Startup.cs | 13 +++++++++++- .../cards/card-item/card-item.component.scss | 2 ++ .../cards/card-item/card-item.component.ts | 20 ++++++++++++++++++- .../collection-detail.component.ts | 6 +++++- .../library-detail.component.ts | 14 +++++-------- .../series-detail.component.html | 4 ++-- .../series-detail/series-detail.component.ts | 20 +++++++++---------- 7 files changed, 54 insertions(+), 25 deletions(-) diff --git a/API/Startup.cs b/API/Startup.cs index ddbf25105..ee26e2d2b 100644 --- a/API/Startup.cs +++ b/API/Startup.cs @@ -2,6 +2,8 @@ using System; using System.IO; using System.IO.Compression; using System.Linq; +using System.Net; +using System.Net.Sockets; using API.Extensions; using API.Middleware; using API.Services; @@ -9,6 +11,7 @@ using API.Services.HostedServices; using API.SignalR; using Hangfire; using Hangfire.MemoryStorage; +using Kavita.Common; using Kavita.Common.EnvironmentInfo; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -109,7 +112,7 @@ namespace API .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials() // For SignalR token query param - .WithOrigins("http://localhost:4200") + .WithOrigins("http://localhost:4200", $"http://{GetLocalIpAddress()}:4200") .WithExposedHeaders("Content-Disposition", "Pagination")); } @@ -164,6 +167,14 @@ namespace API Console.WriteLine("You may now close the application window."); } + private static string GetLocalIpAddress() + { + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0); + socket.Connect("8.8.8.8", 65530); + if (socket.LocalEndPoint is IPEndPoint endPoint) return endPoint.Address.ToString(); + throw new KavitaException("No network adapters with an IPv4 address in the system!"); + } + } } diff --git a/UI/Web/src/app/cards/card-item/card-item.component.scss b/UI/Web/src/app/cards/card-item/card-item.component.scss index 1a5fef4d5..0aa5c8ef6 100644 --- a/UI/Web/src/app/cards/card-item/card-item.component.scss +++ b/UI/Web/src/app/cards/card-item/card-item.component.scss @@ -82,6 +82,8 @@ $image-width: 160px; &.always-show { visibility: visible !important; + width: $image-width; + height: $image-height; } input[type="checkbox"] { diff --git a/UI/Web/src/app/cards/card-item/card-item.component.ts b/UI/Web/src/app/cards/card-item/card-item.component.ts index 38c580379..740cf6043 100644 --- a/UI/Web/src/app/cards/card-item/card-item.component.ts +++ b/UI/Web/src/app/cards/card-item/card-item.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { ToastrService } from 'ngx-toastr'; import { Observable, Subject } from 'rxjs'; import { finalize, take, takeUntil, takeWhile } from 'rxjs/operators'; @@ -118,6 +118,24 @@ export class CardItemComponent implements OnInit, OnDestroy { this.onDestroy.complete(); } + + prevTouchTime: number = 0; + @HostListener('touchstart', ['$event']) + onTouchStart(event: TouchEvent) { + this.prevTouchTime = event.timeStamp; + } + + @HostListener('touchend', ['$event']) + onTouchEnd(event: TouchEvent) { + if (event.timeStamp - this.prevTouchTime >= 200) { + this.handleSelection(); + event.stopPropagation(); + event.preventDefault(); + } + this.prevTouchTime = 0; + } + + handleClick(event?: any) { this.clicked.emit(this.title); } diff --git a/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts b/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts index b592098d8..5f3fa3145 100644 --- a/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts +++ b/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts @@ -46,16 +46,20 @@ export class CollectionDetailComponent implements OnInit { switch (action) { case Action.AddToReadingList: - this.actionService.addMultipleSeriesToReadingList(selectedSeries); + this.actionService.addMultipleSeriesToReadingList(selectedSeries, () => { + this.bulkSelectionService.deselectAll(); + }); break; case Action.MarkAsRead: this.actionService.markMultipleSeriesAsRead(selectedSeries, () => { this.loadPage(); + this.bulkSelectionService.deselectAll(); }); break; case Action.MarkAsUnread: this.actionService.markMultipleSeriesAsUnread(selectedSeries, () => { this.loadPage(); + this.bulkSelectionService.deselectAll(); }); break; } diff --git a/UI/Web/src/app/library-detail/library-detail.component.ts b/UI/Web/src/app/library-detail/library-detail.component.ts index 133f6e0cb..fdd58fb33 100644 --- a/UI/Web/src/app/library-detail/library-detail.component.ts +++ b/UI/Web/src/app/library-detail/library-detail.component.ts @@ -33,30 +33,26 @@ export class LibraryDetailComponent implements OnInit { }; bulkActionCallback = (action: Action, data: any) => { - console.log('handling bulk action callback'); - // we need to figure out what is actually selected now const selectedSeriesIndexies = this.bulkSelectionService.getSelectedCardsForSource('series'); - const selectedSeries = this.series.filter((series, index: number) => selectedSeriesIndexies.includes(index + '')); switch (action) { case Action.AddToReadingList: - this.actionService.addMultipleSeriesToReadingList(selectedSeries); + this.actionService.addMultipleSeriesToReadingList(selectedSeries, () => { + this.bulkSelectionService.deselectAll(); + }); break; case Action.MarkAsRead: - console.log('marking series as read: ', selectedSeries) - this.actionService.markMultipleSeriesAsRead(selectedSeries, () => { this.loadPage(); + this.bulkSelectionService.deselectAll(); }); break; case Action.MarkAsUnread: - //console.log('marking volumes as unread: ', selectedVolumeIds) - //console.log('marking chapters as unread: ', chapters) - this.actionService.markMultipleSeriesAsUnread(selectedSeries, () => { this.loadPage(); + this.bulkSelectionService.deselectAll(); }); break; } diff --git a/UI/Web/src/app/series-detail/series-detail.component.html b/UI/Web/src/app/series-detail/series-detail.component.html index 9a6edebab..541ff5b88 100644 --- a/UI/Web/src/app/series-detail/series-detail.component.html +++ b/UI/Web/src/app/series-detail/series-detail.component.html @@ -102,7 +102,7 @@
  • Specials -
    +
    Volumes/Chapters -
    +
    `${item.title}_${item.number}_${item.pagesRead}`; bulkActionCallback = (action: Action, data: any) => { - console.log('handling bulk action callback'); if (this.series === undefined) { return; } @@ -101,32 +100,31 @@ export class SeriesDetailComponent implements OnInit, OnDestroy { const selectedChapterIndexes = this.bulkSelectionService.getSelectedCardsForSource('chapter'); const selectedSpecialIndexes = this.bulkSelectionService.getSelectedCardsForSource('special'); - const selectedChapterIds = this.chapters.filter((chapter, index: number) => selectedChapterIndexes.includes(index + '')); - const selectedVolumeIds = this.volumes.filter((volume, index: number) => selectedVolumeIndexes.includes(index + '')); - const selectedSpecials = this.specials.filter((chapter, index: number) => selectedSpecialIndexes.includes(index + '')); + const selectedChapterIds = this.chapters.filter((_chapter, index: number) => selectedChapterIndexes.includes(index + '')); + const selectedVolumeIds = this.volumes.filter((_volume, index: number) => selectedVolumeIndexes.includes(index + '')); + const selectedSpecials = this.specials.filter((_chapter, index: number) => selectedSpecialIndexes.includes(index + '')); const chapters = [...selectedChapterIds, ...selectedSpecials]; switch (action) { case Action.AddToReadingList: - this.actionService.addMultipleToReadingList(seriesId, selectedVolumeIds, chapters, () => this.actionInProgress = false); + this.actionService.addMultipleToReadingList(seriesId, selectedVolumeIds, chapters, () => { + this.actionInProgress = false; + this.bulkSelectionService.deselectAll(); + }); break; case Action.MarkAsRead: - console.log('marking volumes as read: ', selectedVolumeIds) - console.log('marking chapters as read: ', chapters) - this.actionService.markMultipleAsRead(seriesId, selectedVolumeIds, chapters, () => { this.setContinuePoint(); this.actionInProgress = false; + this.bulkSelectionService.deselectAll(); }); break; case Action.MarkAsUnread: - console.log('marking volumes as unread: ', selectedVolumeIds) - console.log('marking chapters as unread: ', chapters) - this.actionService.markMultipleAsUnread(seriesId, selectedVolumeIds, chapters, () => { this.setContinuePoint(); this.actionInProgress = false; + this.bulkSelectionService.deselectAll(); }); break; }