Misc Fixes (#1031)

* Changed the default margin for mobile in book reader to 5%

* Fixed a bug where checking for update did no current version validation before sending the update to the UI.

* Added some documentation to the book code

* Changed token expiry to 2 weeks.

* Search bar will by default not have a border outline

* Cleaned up some styles for white mode hovering on search

* Added missing genre search group, reworked some clearing code, fixed click handlers

* Fixed genre property

* Changed the series title to show bookmarks and the edit button will now take you to series

* Fixed up accordion tabpanel color in dark mode

* Fixed a typo of CoverArtist instead of "Cover artist"

* Added some documentation changes

* Fixed a bug where sort options on All-Series wasn't working

* Added a thanks to Palace-Designs who hosts our infrastructure to the readme.

* Fixed a bug where duplicate people for the same role would be returned

* Fixed a bug where when user cleared out input manually, search would retain old search results
This commit is contained in:
Joseph Milazzo 2022-02-04 11:28:58 -08:00 committed by GitHub
parent dc2d5b505f
commit 19e17c85fa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 130 additions and 55 deletions

View file

@ -42,7 +42,7 @@ export class MetadataService {
if (libraries != undefined && libraries.length > 0) {
method += '?libraryIds=' + libraries.join(',');
}
return this.httpClient.get<Array<AgeRatingDto>>(this.baseUrl + method);;
return this.httpClient.get<Array<AgeRatingDto>>(this.baseUrl + method);
}
getAllPublicationStatus(libraries?: Array<number>) {
@ -50,7 +50,7 @@ export class MetadataService {
if (libraries != undefined && libraries.length > 0) {
method += '?libraryIds=' + libraries.join(',');
}
return this.httpClient.get<Array<PublicationStatusDto>>(this.baseUrl + method);;
return this.httpClient.get<Array<PublicationStatusDto>>(this.baseUrl + method);
}
getAllTags(libraries?: Array<number>) {
@ -58,7 +58,7 @@ export class MetadataService {
if (libraries != undefined && libraries.length > 0) {
method += '?libraryIds=' + libraries.join(',');
}
return this.httpClient.get<Array<Tag>>(this.baseUrl + method);;
return this.httpClient.get<Array<Tag>>(this.baseUrl + method);
}
getAllGenres(libraries?: Array<number>) {
@ -66,7 +66,7 @@ export class MetadataService {
if (libraries != undefined && libraries.length > 0) {
method += '?libraryIds=' + libraries.join(',');
}
return this.httpClient.get<Genre[]>(this.baseUrl + method);
return this.httpClient.get<Array<Genre>>(this.baseUrl + method);
}
getAllLanguages(libraries?: Array<number>) {
@ -74,7 +74,7 @@ export class MetadataService {
if (libraries != undefined && libraries.length > 0) {
method += '?libraryIds=' + libraries.join(',');
}
return this.httpClient.get<Language[]>(this.baseUrl + method);
return this.httpClient.get<Array<Language>>(this.baseUrl + method);
}
getAllPeople(libraries?: Array<number>) {
@ -82,6 +82,6 @@ export class MetadataService {
if (libraries != undefined && libraries.length > 0) {
method += '?libraryIds=' + libraries.join(',');
}
return this.httpClient.get<Person[]>(this.baseUrl + method);
return this.httpClient.get<Array<Person>>(this.baseUrl + method);
}
}

View file

@ -615,7 +615,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
let margin = '15%';
if (windowWidth <= 700) {
margin = '0%';
margin = '5%';
}
if (this.user) {
if (windowWidth > 700) {

View file

@ -5,7 +5,6 @@
<span *ngIf="actions.length > 0" class="">
<app-card-actionables (actionHandler)="performAction($event)" [actions]="actions" [labelBy]="header"></app-card-actionables>&nbsp;
</span>{{header}}&nbsp;
<!-- NOTE: On mobile the pill can eat up a lot of space, we can hide it and move to the filter section if user is interested -->
<span class="badge badge-primary badge-pill" attr.aria-label="{{pagination.totalItems}} total items" *ngIf="pagination != undefined">{{pagination.totalItems}}</span>
</h2>
</div>

View file

@ -367,7 +367,7 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
};
this.collectionSettings.compareFn = (options: CollectionTag[], filter: string) => {
const f = filter.toLowerCase();
return options.filter(m => m.title.toLowerCase() === f);
return options.filter(m => m.title.toLowerCase() === f && this.utilityService.filter(m.title, filter));
}
if (this.filterSettings.presets?.collectionTags && this.filterSettings.presets?.collectionTags.length > 0) {

View file

@ -35,6 +35,16 @@
</ul>
</ng-container>
<ng-container *ngIf="genreTemplate !== undefined && grouppedData.genres.length > 0">
<li class="list-group-item section-header"><h5>Genres</h5></li>
<ul class="list-group results">
<li *ngFor="let option of grouppedData.genres; let index = index;" (click)="handleResultlick(option)" tabindex="0"
class="list-group-item" role="option">
<ng-container [ngTemplateOutlet]="genreTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
</li>
</ul>
</ng-container>
<ng-container *ngIf="tagTemplate !== undefined && grouppedData.tags.length > 0">
<li class="list-group-item section-header"><h5>Tags</h5></li>
<ul class="list-group results">
@ -46,7 +56,7 @@
</ng-container>
<ng-container *ngIf="personTemplate !== undefined && grouppedData.persons.length > 0">
<li class="list-group-item section-header"><h5>Tags</h5></li>
<li class="list-group-item section-header"><h5>People</h5></li>
<ul class="list-group results">
<li *ngFor="let option of grouppedData.persons; let index = index;" (click)="handleResultlick(option)" tabindex="0"
class="list-group-item" role="option">

View file

@ -15,8 +15,10 @@ input {
width: 100% !important;
}
.typeahead-input {
border: 1px solid #ccc;
border: 1px solid transparent;
border-radius: 4px;
padding: 0px 6px;
display: inline-block;
overflow: hidden;
@ -24,7 +26,6 @@ input {
z-index: 1;
box-sizing: border-box;
box-shadow: none;
border-radius: 4px;
cursor: text;
background-color: #fff;
min-height: 38px;
@ -32,6 +33,7 @@ input {
transition-duration: 0.3s;
display: block;
.close {
cursor: pointer;
position: absolute;
@ -67,6 +69,7 @@ input {
.typeahead-input.focused {
width: 100%;
border-color: #ccc;
}
/* small devices (phones, 650px and down) */
@ -117,7 +120,6 @@ input {
flex: auto;
max-height: calc(100vh - 58px);
height: fit-content;
//background-color: colors.$dark-bg-color;
}
.list-group.results {
@ -149,13 +151,34 @@ ul ul {
cursor: pointer;
}
.section-header {
background: colors.$dark-item-accent-bg;
cursor: default;
::ng-deep .bg-dark {
& .section-header {
background: colors.$dark-item-accent-bg;
cursor: default;
}
& .section-header:hover {
background-color: colors.$dark-item-accent-bg !important;
}
}
.section-header:hover {
background-color: colors.$dark-item-accent-bg !important;
::ng-deep .bg-light {
& .section-header {
background: colors.$white-item-accent-bg;
cursor: default;
}
& .section-header:hover, .list-group-item.section-header:hover {
background: colors.$white-item-accent-bg !important;
}
& .list-group-item:hover {
background-color: colors.$primary-color !important;
}
}
.spinner-border {

View file

@ -56,6 +56,7 @@ export class GroupedTypeaheadComponent implements OnInit, OnDestroy {
@ContentChild('collectionTemplate') collectionTemplate: TemplateRef<any> | undefined;
@ContentChild('tagTemplate') tagTemplate: TemplateRef<any> | undefined;
@ContentChild('personTemplate') personTemplate: TemplateRef<any> | undefined;
@ContentChild('genreTemplate') genreTemplate!: TemplateRef<any>;
@ContentChild('noResultsTemplate') noResultsTemplate!: TemplateRef<any>;
@ -147,6 +148,7 @@ export class GroupedTypeaheadComponent implements OnInit, OnDestroy {
}
resetField() {
this.prevSearchTerm = '';
this.typeaheadForm.get('typeahead')?.setValue(this.initialValue);
this.clearField.emit();
}
@ -159,6 +161,9 @@ export class GroupedTypeaheadComponent implements OnInit, OnDestroy {
return;
}
}
if (this.searchTerm === '') {
this.resetField();
}
this.hasFocus = false;
this.focusChanged.emit(this.hasFocus);
}
@ -169,7 +174,8 @@ export class GroupedTypeaheadComponent implements OnInit, OnDestroy {
}
public clear() {
this.resetField();
this.prevSearchTerm = '';
this.typeaheadForm.get('typeahead')?.setValue(this.initialValue);
}
}

View file

@ -23,7 +23,7 @@
>
<ng-template #seriesTemplate let-item>
<div style="display: flex;padding: 5px;" (click)="clickSearchResult(item)">
<div style="display: flex;padding: 5px;" (click)="clickSeriesSearchResult(item)">
<div style="width: 24px" class="mr-1">
<app-image class="mr-3 search-result" width="24px" [imageUrl]="imageService.getSeriesCoverImage(item.seriesId)"></app-image>
</div>
@ -39,7 +39,7 @@
</ng-template>
<ng-template #collectionTemplate let-item>
<div style="display: flex;padding: 5px;" (click)="goToPerson(item.role, item.id)">
<div style="display: flex;padding: 5px;" (click)="clickCollectionSearchResult(item)">
<div style="width: 24px" class="mr-1">
<app-image class="mr-3 search-result" width="24px" [imageUrl]="imageService.getCollectionCoverImage(item.id)"></app-image>
</div>
@ -50,7 +50,7 @@
</ng-template>
<ng-template #tagTemplate let-item>
<div style="display: flex;padding: 5px;" (click)="goTo('tag', item.id)">
<div style="display: flex;padding: 5px;" (click)="goTo('tags', item.id)">
<div class="ml-1">
<span *ngIf="item.title.toLowerCase().trim().indexOf(searchTerm) >= 0" [innerHTML]="item.title"></span>
</div>
@ -58,7 +58,7 @@
</ng-template>
<ng-template #personTemplate let-item>
<div style="display: flex;padding: 5px;" class="clickable" (click)="goTo('genres', item.id)">
<div style="display: flex;padding: 5px;" class="clickable" (click)="goToPerson(item.role, item.id)">
<div class="ml-1">
<div [innerHTML]="item.name"></div>
@ -67,6 +67,14 @@
</div>
</ng-template>
<ng-template #genreTemplate let-item>
<div style="display: flex;padding: 5px;" class="clickable" (click)="goTo('genres', item.id)">
<div class="ml-1">
<div [innerHTML]="item.title"></div>
</div>
</div>
</ng-template>
<ng-template #noResultsTemplate let-notFound>
No results found
</ng-template>

View file

@ -3,8 +3,8 @@ import { Component, HostListener, Inject, OnDestroy, OnInit, ViewChild } from '@
import { Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { isTemplateSpan } from 'typescript';
import { ScrollService } from '../scroll.service';
import { CollectionTag } from '../_models/collection-tag';
import { PersonRole } from '../_models/person';
import { SearchResult } from '../_models/search-result';
import { SearchResultGroup } from '../_models/search/search-result-group';
@ -104,11 +104,13 @@ export class NavHeaderComponent implements OnInit, OnDestroy {
let params: any = {};
params[queryParamName] = filter;
params['page'] = 1;
this.clearSearch();
this.router.navigate(['all-series'], {queryParams: params});
}
goToPerson(role: PersonRole, filter: any) {
// TODO: Move this to utility service
this.clearSearch();
switch(role) {
case PersonRole.Artist:
this.goTo('artist', filter);
@ -147,19 +149,24 @@ export class NavHeaderComponent implements OnInit, OnDestroy {
}
clearSearch() {
this.searchViewRef.clear();
this.searchTerm = '';
this.searchResults = new SearchResultGroup();
}
clickSearchResult(item: SearchResult) {
console.log('Click occured');
clickSeriesSearchResult(item: SearchResult) {
this.clearSearch();
const libraryId = item.libraryId;
const seriesId = item.seriesId;
this.searchViewRef.clear();
this.searchResults.reset();
this.searchTerm = '';
this.router.navigate(['library', libraryId, 'series', seriesId]);
}
clickCollectionSearchResult(item: CollectionTag) {
this.clearSearch();
this.router.navigate(['collections', item.id]);
}
scrollToTop() {
window.scroll({
top: 0,
@ -168,7 +175,6 @@ export class NavHeaderComponent implements OnInit, OnDestroy {
}
focusUpdate(searchFocused: boolean) {
console.log('search has focus', searchFocused);
this.searchFocused = searchFocused
return searchFocused;
}

View file

@ -11,7 +11,7 @@ export class PersonRolePipe implements PipeTransform {
case PersonRole.Artist: return 'Artist';
case PersonRole.Character: return 'Character';
case PersonRole.Colorist: return 'Colorist';
case PersonRole.CoverArtist: return 'CoverArtist';
case PersonRole.CoverArtist: return 'Cover Artist';
case PersonRole.Editor: return 'Editor';
case PersonRole.Inker: return 'Inker';
case PersonRole.Letterer: return 'Letterer';

View file

@ -22,7 +22,7 @@ export class TypeaheadSettings<T> {
*/
savedData!: T[] | T;
/**
* Function to compare the elements. Should return all elements that fit the matching criteria.
* Function to compare the elements. Should return all elements that fit the matching criteria. This is only used with non-Observable based fetchFn, but must be defined for all uses of typeahead (TODO)
*/
compareFn!: ((optionList: T[], filter: string) => T[]);
/**

View file

@ -5,7 +5,7 @@
<li *ngFor="let series of series" class="list-group-item">
<div>
<h4>
<a id="series--{{series.name}}" href="/library/{{series.libraryId}}/series/{{series.id}}" >{{series.name | titlecase}}</a>
<a id="series--{{series.name}}" href="javascript:void(0);" (click)="viewBookmarks(series)">{{series.name | titlecase}}</a>
&nbsp;<span class="badge badge-secondary badge-pill">{{getBookmarkPages(series.id)}}</span>
<div class="float-right">
<button attr.aria-labelledby="series--{{series.name}}" class="btn btn-danger mr-2 btn-sm" (click)="clearBookmarks(series)" [disabled]="clearingSeries[series.id]" placement="top" ngbTooltip="Clear Bookmarks" attr.aria-label="Clear Bookmarks">
@ -26,8 +26,8 @@
<i class="fa fa-arrow-alt-circle-down" aria-hidden="true"></i>
</ng-template>
</button>
<button attr.aria-labelledby="series--{{series.name}}" class="btn btn-primary mr-2 btn-sm" (click)="viewBookmarks(series)" placement="top" ngbTooltip="View Bookmarks" attr.aria-label="View Bookmarks">
<i class="fa fa-pen" title="View Bookmarks"></i>
<button attr.aria-labelledby="series--{{series.name}}" class="btn btn-primary mr-2 btn-sm" routerLink="/library/{{series.libraryId}}/series/{{series.id}}" placement="top" ngbTooltip="Open Series" attr.aria-label="Open Series">
<i class="fa fa-eye" title="Open Series"></i>
</button>
</div>
</h4>

View file

@ -29,6 +29,8 @@
color: $dark-primary-color;
}
.accent {
background-color: $dark-form-background !important;
@ -175,6 +177,10 @@
background-color: $dark-card-color;
color: $dark-text-color;
border-color: $dark-form-border;
div[role="tabpanel"] {
background-color: rgba(52, 60, 70, 0.5); // This is a good accent color
}
}
.section-title {

View file

@ -16,6 +16,9 @@ $dark-form-readonly: #434648;
$dark-item-accent-bg: #292d32;
$white-item-accent-bg: rgba(247, 247, 247, 1);
//=========================
// Ratings
//=========================
@ -29,6 +32,7 @@ $rating-empty: #b0c4de;
// --drawer-background-color: #FFF;
// }
$theme-colors: (
"primary": $primary-color,
"danger": $error-color