UX Pass 6 (#3131)
Co-authored-by: Joseph Milazzo <joseph.v.milazzo@gmail.com>
This commit is contained in:
parent
fc644985be
commit
62383042b0
70 changed files with 741 additions and 399 deletions
|
@ -11,7 +11,7 @@ import {
|
|||
import {NgbDropdown, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle} from '@ng-bootstrap/ng-bootstrap';
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
import { Action, ActionItem } from 'src/app/_services/action-factory.service';
|
||||
import {AsyncPipe, CommonModule, NgTemplateOutlet} from "@angular/common";
|
||||
import {AsyncPipe, NgTemplateOutlet} from "@angular/common";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {DynamicListPipe} from "./_pipes/dynamic-list.pipe";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<ng-container *transloco="let t; read: 'series-detail'">
|
||||
@if(mobileSeriesImgBackground === 'true') {
|
||||
<app-image [styles]="{'background': 'none'}" [imageUrl]="coverImage"></app-image>
|
||||
} @else {
|
||||
<app-image [styles]="{'object-fit': 'contain', 'background': 'none', 'max-height': '400px'}" [imageUrl]="coverImage"></app-image>
|
||||
}
|
||||
<div class="card-overlay"></div>
|
||||
<div class="overlay-information">
|
||||
<div class="overlay-information--centered">
|
||||
<span class="card-title library mx-auto" style="width: auto;" (click)="read.emit()">
|
||||
<!-- Card Image -->
|
||||
<div>
|
||||
<i class="fa-solid fa-book text-center" aria-hidden="true"></i>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (entity.pagesRead < entity.pages && entity.pagesRead > 0) {
|
||||
<div class="progress-banner series" ngbTooltip="{{(entity.pagesRead / entity.pages) * 100 | number:'1.0-1'}}%">
|
||||
<ngb-progressbar type="primary" [value]="entity.pagesRead" [max]="entity.pages" [showValue]="true"></ngb-progressbar>
|
||||
</div>
|
||||
@if (continueTitle !== '') {
|
||||
<div class="under-image">
|
||||
<div class="continue-from">
|
||||
{{t('continue-from', {title: continueTitle})}}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</ng-container>
|
|
@ -0,0 +1,139 @@
|
|||
.overlay-information {
|
||||
position: relative;
|
||||
top: -364px;
|
||||
height: 364px;
|
||||
transition: all 0.2s;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: var(--card-overlay-hover-bg-color) !important;
|
||||
|
||||
.overlay-information--centered {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.overlay-information--centered {
|
||||
position: absolute;
|
||||
border-radius: 15px;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 50px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 115;
|
||||
visibility: hidden;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-color) !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
i {
|
||||
font-size: 1.6rem;
|
||||
line-height: 60px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.overlay-information {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 12px;
|
||||
width: calc(100% - 24px);
|
||||
height: 100%;
|
||||
transition: all 0.2s;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--card-overlay-hover-bg-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.overlay-information--centered {
|
||||
position: absolute;
|
||||
border-radius: 15px;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 50px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 115;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-color) !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.series {
|
||||
.overlay-information--centered {
|
||||
div {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
i {
|
||||
font-size: 1.4rem;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .image-container app-image img {
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
.progress {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.progress-banner.series {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
::ng-deep .progress-banner.series span {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: white;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.under-image {
|
||||
position: relative;
|
||||
|
||||
.continue-from {
|
||||
background-color: var(--breadcrumb-bg-color);
|
||||
color: white;
|
||||
border-bottom-left-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 991px) {
|
||||
.overlay-information {
|
||||
visibility: hidden;
|
||||
.overlay-information--centered {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
}
|
||||
.progress-banner {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.under-image {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import {ChangeDetectionStrategy, Component, EventEmitter, Input, Output} from '@angular/core';
|
||||
import {DecimalPipe, NgClass} from "@angular/common";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {ImageComponent} from "../../shared/image/image.component";
|
||||
import {NgbProgressbar, NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {IHasProgress} from "../../_models/common/i-has-progress";
|
||||
|
||||
/**
|
||||
* Used for the Series/Volume/Chapter Detail pages
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-cover-image',
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgClass,
|
||||
TranslocoDirective,
|
||||
ImageComponent,
|
||||
NgbProgressbar,
|
||||
DecimalPipe,
|
||||
NgbTooltip
|
||||
],
|
||||
templateUrl: './cover-image.component.html',
|
||||
styleUrl: './cover-image.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class CoverImageComponent {
|
||||
|
||||
@Input({required: true}) coverImage!: string;
|
||||
@Input({required: true}) entity!: IHasProgress;
|
||||
@Input() continueTitle: string = '';
|
||||
@Output() read = new EventEmitter();
|
||||
|
||||
mobileSeriesImgBackground = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue('--mobile-series-img-background').trim();
|
||||
|
||||
}
|
|
@ -1,23 +1,21 @@
|
|||
<ng-container *transloco="let t; read: 'details-tab'">
|
||||
<div class="details pb-3">
|
||||
<div class="mb-3">
|
||||
<app-carousel-reel [items]="genres" [title]="t('genres-title')">
|
||||
<ng-template #carouselItem let-item>
|
||||
<app-tag-badge (click)="openGeneric(FilterField.Genres, item.id)" [selectionMode]="TagBadgeCursor.Clickable">
|
||||
{{item.title}}
|
||||
</app-tag-badge>
|
||||
<h4 class="header">{{t('genres-title')}}</h4>
|
||||
<app-badge-expander [includeComma]="true" [items]="genres">
|
||||
<ng-template #badgeExpanderItem let-item let-position="idx" let-last="last">
|
||||
<a href="javascript:void(0)" class="dark-exempt btn-icon" (click)="openGeneric(FilterField.Genres, item.id)">{{item.title}}</a>
|
||||
</ng-template>
|
||||
</app-carousel-reel>
|
||||
</app-badge-expander>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<app-carousel-reel [items]="tags" [title]="t('tags-title')">
|
||||
<ng-template #carouselItem let-item>
|
||||
<app-tag-badge (click)="openGeneric(FilterField.Tags, item.id)" [selectionMode]="TagBadgeCursor.Clickable">
|
||||
{{item.title}}
|
||||
</app-tag-badge>
|
||||
<h4 class="header">{{t('tags-title')}}</h4>
|
||||
<app-badge-expander [includeComma]="true" [items]="tags">
|
||||
<ng-template #badgeExpanderItem let-item let-position="idx" let-last="last">
|
||||
<a href="javascript:void(0)" class="dark-exempt btn-icon" (click)="openGeneric(FilterField.Tags, item.id)">{{item.title}}</a>
|
||||
</ng-template>
|
||||
</app-carousel-reel>
|
||||
</app-badge-expander>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
|
|
|
@ -14,18 +14,20 @@ import {TagBadgeComponent, TagBadgeCursor} from "../../shared/tag-badge/tag-badg
|
|||
import {ImageComponent} from "../../shared/image/image.component";
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
import {ImageService} from "../../_services/image.service";
|
||||
import {BadgeExpanderComponent} from "../../shared/badge-expander/badge-expander.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-details-tab',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CarouselReelComponent,
|
||||
PersonBadgeComponent,
|
||||
TranslocoDirective,
|
||||
TagBadgeComponent,
|
||||
ImageComponent,
|
||||
SafeHtmlPipe
|
||||
],
|
||||
imports: [
|
||||
CarouselReelComponent,
|
||||
PersonBadgeComponent,
|
||||
TranslocoDirective,
|
||||
TagBadgeComponent,
|
||||
ImageComponent,
|
||||
SafeHtmlPipe,
|
||||
BadgeExpanderComponent
|
||||
],
|
||||
templateUrl: './details-tab.component.html',
|
||||
styleUrl: './details-tab.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
|
|
@ -553,15 +553,17 @@
|
|||
</div>
|
||||
}
|
||||
|
||||
<app-setting-item [title]="t('files-label')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
@for (file of chapter.files; track file.id) {
|
||||
<div>
|
||||
<span>{{file.filePath}}</span><span class="ms-2 me-2">•</span><span>{{file.bytes | bytes}}</span>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
@if (accountService.isAdmin$ | async) {
|
||||
<app-setting-item [title]="t('files-label')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
@for (file of chapter.files; track file.id) {
|
||||
<div>
|
||||
<span>{{file.filePath}}</span><span class="ms-2 me-2">•</span><span>{{file.bytes | bytes}}</span>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
}
|
||||
|
||||
</ng-template>
|
||||
</li>
|
||||
|
|
|
@ -28,7 +28,6 @@ import {Language} from "../../_models/metadata/language";
|
|||
import {Person, PersonRole} from "../../_models/metadata/person";
|
||||
import {Genre} from "../../_models/metadata/genre";
|
||||
import {AgeRatingDto} from "../../_models/metadata/age-rating-dto";
|
||||
import {SeriesService} from "../../_services/series.service";
|
||||
import {ImageService} from "../../_services/image.service";
|
||||
import {UploadService} from "../../_services/upload.service";
|
||||
import {MetadataService} from "../../_services/metadata.service";
|
||||
|
|
|
@ -88,16 +88,17 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<app-setting-item [title]="t('files-label')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
@for (file of files; track file.id) {
|
||||
<div>
|
||||
<span>{{file.filePath}}</span><span class="ms-2 me-2">•</span><span>{{file.bytes | bytes}}</span>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
@if (accountService.isAdmin$ | async) {
|
||||
<app-setting-item [title]="t('files-label')" [toggleOnViewClick]="false" [showEdit]="false">
|
||||
<ng-template #view>
|
||||
@for (file of files; track file.id) {
|
||||
<div>
|
||||
<span>{{file.filePath}}</span><span class="ms-2 me-2">•</span><span>{{file.bytes | bytes}}</span>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
</app-setting-item>
|
||||
}
|
||||
|
||||
</ng-template>
|
||||
</li>
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
ViewContainerRef,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import {CommonModule, DOCUMENT, NgOptimizedImage} from '@angular/common';
|
||||
import {DOCUMENT, NgOptimizedImage} from '@angular/common';
|
||||
import {NgbActiveModal} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {ReactiveFormsModule} from "@angular/forms";
|
||||
import {UserReview} from "../review-card/user-review";
|
||||
|
@ -20,7 +20,7 @@ import {ProviderImagePipe} from "../../_pipes/provider-image.pipe";
|
|||
@Component({
|
||||
selector: 'app-review-card-modal',
|
||||
standalone: true,
|
||||
imports: [CommonModule, ReactiveFormsModule, SpoilerComponent, SafeHtmlPipe, TranslocoDirective, DefaultValuePipe, NgOptimizedImage, ProviderImagePipe],
|
||||
imports: [ReactiveFormsModule, SpoilerComponent, SafeHtmlPipe, TranslocoDirective, DefaultValuePipe, NgOptimizedImage, ProviderImagePipe],
|
||||
templateUrl: './review-card-modal.component.html',
|
||||
styleUrls: ['./review-card-modal.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
|
|
|
@ -8,13 +8,12 @@ import {
|
|||
OnInit,
|
||||
Output
|
||||
} from '@angular/core';
|
||||
import {CommonModule, NgOptimizedImage} from '@angular/common';
|
||||
import {NgOptimizedImage} from '@angular/common';
|
||||
import {UserReview} from "./user-review";
|
||||
import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {ReviewCardModalComponent} from "../review-card-modal/review-card-modal.component";
|
||||
import {AccountService} from "../../_services/account.service";
|
||||
import {
|
||||
ReviewSeriesModalCloseAction,
|
||||
ReviewSeriesModalCloseEvent,
|
||||
ReviewSeriesModalComponent
|
||||
} from "../review-series-modal/review-series-modal.component";
|
||||
|
|
|
@ -13,14 +13,16 @@
|
|||
<textarea id="review" class="form-control" formControlName="reviewBody" rows="3" [minlength]="minLength"
|
||||
[class.is-invalid]="reviewGroup.get('reviewBody')?.invalid && reviewGroup.get('reviewBody')?.touched" aria-describedby="body-validations"
|
||||
></textarea>
|
||||
<div id="body-validations" class="invalid-feedback" *ngIf="reviewGroup.dirty || reviewGroup.touched">
|
||||
@if (reviewGroup.get('reviewBody')?.errors?.required) {
|
||||
<div>{{t('required')}}</div>
|
||||
}
|
||||
@if (reviewGroup.get('reviewBody')?.errors?.minlength) {
|
||||
<div>{{t('min-length', {count: minLength})}}</div>
|
||||
}
|
||||
</div>
|
||||
@if (reviewGroup.dirty || reviewGroup.touched) {
|
||||
<div id="body-validations" class="invalid-feedback">
|
||||
@if (reviewGroup.get('reviewBody')?.errors?.required) {
|
||||
<div>{{t('required')}}</div>
|
||||
}
|
||||
@if (reviewGroup.get('reviewBody')?.errors?.minlength) {
|
||||
<div>{{t('min-length', {count: minLength})}}</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import {
|
|||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
EventEmitter,
|
||||
inject,
|
||||
Input,
|
||||
OnInit
|
||||
|
@ -11,7 +10,6 @@ import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/
|
|||
import {NgbActiveModal, NgbRating} from '@ng-bootstrap/ng-bootstrap';
|
||||
import { SeriesService } from 'src/app/_services/series.service';
|
||||
import {UserReview} from "../review-card/user-review";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||
import {ConfirmService} from "../../shared/confirm.service";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
|
@ -31,7 +29,7 @@ export interface ReviewSeriesModalCloseEvent {
|
|||
@Component({
|
||||
selector: 'app-review-series-modal',
|
||||
standalone: true,
|
||||
imports: [CommonModule, NgbRating, ReactiveFormsModule, TranslocoDirective],
|
||||
imports: [NgbRating, ReactiveFormsModule, TranslocoDirective],
|
||||
templateUrl: './review-series-modal.component.html',
|
||||
styleUrls: ['./review-series-modal.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
|
|
@ -22,6 +22,6 @@ a.read-more-link {
|
|||
}
|
||||
|
||||
.offcanvas-body {
|
||||
mask-image: linear-gradient(to bottom, transparent, black 0%, black 95%, transparent 100%);
|
||||
-webkit-mask-image: linear-gradient(to bottom, transparent, black 0%, black 95%, transparent 100%);
|
||||
mask-image: linear-gradient(to bottom, transparent, black 0%, black 97%, transparent 100%);
|
||||
-webkit-mask-image: linear-gradient(to bottom, transparent, black 0%, black 97%, transparent 100%);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<ng-container *transloco="let t; read:'spoiler'">
|
||||
<div (click)="toggle()" [attr.aria-expanded]="!isCollapsed" class="btn spoiler" tabindex="0">
|
||||
<span *ngIf="isCollapsed; else show">{{t('click-to-show')}}</span>
|
||||
<ng-template #show>
|
||||
@if (isCollapsed) {
|
||||
<span>{{t('click-to-show')}}</span>
|
||||
} @else {
|
||||
<div [innerHTML]="html | safeHtml"></div>
|
||||
</ng-template>
|
||||
}
|
||||
</div>
|
||||
</ng-container>
|
||||
|
|
|
@ -7,14 +7,13 @@ import {
|
|||
OnInit,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
|
||||
@Component({
|
||||
selector: 'app-spoiler',
|
||||
standalone: true,
|
||||
imports: [CommonModule, SafeHtmlPipe, TranslocoDirective],
|
||||
imports: [SafeHtmlPipe, TranslocoDirective],
|
||||
templateUrl: './spoiler.component.html',
|
||||
styleUrls: ['./spoiler.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, OnInit} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {ScrobbleProvider, ScrobblingService} from "../../_services/scrobbling.service";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
|
@ -21,7 +20,7 @@ import {LooseLeafOrDefaultNumber, SpecialVolumeNumber} from "../../_models/chapt
|
|||
@Component({
|
||||
selector: 'app-user-scrobble-history',
|
||||
standalone: true,
|
||||
imports: [CommonModule, ScrobbleEventTypePipe, NgbPagination, ReactiveFormsModule, SortableHeader, TranslocoModule,
|
||||
imports: [ScrobbleEventTypePipe, NgbPagination, ReactiveFormsModule, SortableHeader, TranslocoModule,
|
||||
DefaultValuePipe, TranslocoLocaleModule, UtcToLocalTimePipe, NgbTooltip],
|
||||
templateUrl: './user-scrobble-history.component.html',
|
||||
styleUrls: ['./user-scrobble-history.component.scss'],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue