Add rating to chapter page & volume (if one chapter)
This commit is contained in:
parent
e96cb0fde9
commit
8ccc2b5801
18 changed files with 226 additions and 47 deletions
|
|
@ -4,6 +4,7 @@ import { HttpClient } from "@angular/common/http";
|
|||
import {Chapter} from "../_models/chapter";
|
||||
import {TextResonse} from "../_types/text-response";
|
||||
import {UserReview} from "../_single-module/review-card/user-review";
|
||||
import {Rating} from "../_models/rating";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
|
@ -35,11 +36,19 @@ export class ChapterService {
|
|||
}
|
||||
|
||||
updateChapterReview(seriesId: number, chapterId: number, body: string, rating: number) {
|
||||
return this.httpClient.post<UserReview>(this.baseUrl + 'review/chapter/'+chapterId, {seriesId, body});
|
||||
return this.httpClient.post<UserReview>(this.baseUrl + 'review/chapter/'+chapterId, {seriesId, rating, body});
|
||||
}
|
||||
|
||||
deleteChapterReview(chapterId: number) {
|
||||
return this.httpClient.delete(this.baseUrl + 'review/chapter/'+chapterId);
|
||||
}
|
||||
|
||||
overallRating(chapterId: number) {
|
||||
return this.httpClient.get<Rating>(this.baseUrl + 'rating/overall?chapterId='+chapterId);
|
||||
}
|
||||
|
||||
updateRating(chapterId: number, rating: number) {
|
||||
return this.httpClient.post(this.baseUrl + 'chapter/update-rating', {chapterId, rating});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ export class SeriesService {
|
|||
}
|
||||
updateReview(seriesId: number, body: string, rating: number) {
|
||||
return this.httpClient.post<UserReview>(this.baseUrl + 'review', {
|
||||
seriesId, body, rating
|
||||
seriesId, rating, body
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
|
|||
import {ReviewCardModalComponent} from "../review-card-modal/review-card-modal.component";
|
||||
import {AccountService} from "../../_services/account.service";
|
||||
import {
|
||||
ReviewSeriesModalCloseEvent,
|
||||
ReviewModalCloseEvent,
|
||||
ReviewModalComponent
|
||||
} from "../review-modal/review-modal.component";
|
||||
import {ReadMoreComponent} from "../../shared/read-more/read-more.component";
|
||||
|
|
@ -35,7 +35,7 @@ export class ReviewCardComponent implements OnInit {
|
|||
protected readonly ScrobbleProvider = ScrobbleProvider;
|
||||
|
||||
@Input({required: true}) review!: UserReview;
|
||||
@Output() refresh = new EventEmitter<ReviewSeriesModalCloseEvent>();
|
||||
@Output() refresh = new EventEmitter<ReviewModalCloseEvent>();
|
||||
|
||||
isMyReview: boolean = false;
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ export class ReviewCardComponent implements OnInit {
|
|||
const ref = this.modalService.open(component, {size: 'lg', fullscreen: 'md'});
|
||||
|
||||
ref.componentInstance.review = this.review;
|
||||
ref.closed.subscribe((res: ReviewSeriesModalCloseEvent | undefined) => {
|
||||
ref.closed.subscribe((res: ReviewModalCloseEvent | undefined) => {
|
||||
if (res) {
|
||||
this.refresh.emit(res);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,16 +11,16 @@ import {of} from "rxjs";
|
|||
import {NgxStarsModule} from "ngx-stars";
|
||||
import {ThemeService} from "../../_services/theme.service";
|
||||
|
||||
export enum ReviewSeriesModalCloseAction {
|
||||
export enum ReviewModalCloseAction {
|
||||
Create,
|
||||
Edit,
|
||||
Delete,
|
||||
Close
|
||||
}
|
||||
export interface ReviewSeriesModalCloseEvent {
|
||||
export interface ReviewModalCloseEvent {
|
||||
success: boolean,
|
||||
review: UserReview;
|
||||
action: ReviewSeriesModalCloseAction
|
||||
action: ReviewModalCloseAction
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
|
@ -43,6 +43,7 @@ export class ReviewModalComponent implements OnInit {
|
|||
|
||||
@Input({required: true}) review!: UserReview;
|
||||
reviewGroup!: FormGroup;
|
||||
rating: number = 0;
|
||||
|
||||
starColor = this.themeService.getCssVariable('--rating-star-color');
|
||||
|
||||
|
|
@ -50,15 +51,16 @@ export class ReviewModalComponent implements OnInit {
|
|||
this.reviewGroup = new FormGroup({
|
||||
reviewBody: new FormControl(this.review.body, [Validators.required, Validators.minLength(this.minLength)]),
|
||||
});
|
||||
this.rating = this.review.rating;
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
updateRating($event: number) {
|
||||
this.review.rating = $event;
|
||||
this.rating = $event;
|
||||
}
|
||||
|
||||
close() {
|
||||
this.modal.close({success: false, review: this.review, action: ReviewSeriesModalCloseAction.Close});
|
||||
this.modal.close({success: false, review: this.review, action: ReviewModalCloseAction.Close});
|
||||
}
|
||||
|
||||
async delete() {
|
||||
|
|
@ -73,7 +75,7 @@ export class ReviewModalComponent implements OnInit {
|
|||
|
||||
obs?.subscribe(() => {
|
||||
this.toastr.success(translate('toasts.review-deleted'));
|
||||
this.modal.close({success: true, review: this.review, action: ReviewSeriesModalCloseAction.Delete});
|
||||
this.modal.close({success: true, review: this.review, action: ReviewModalCloseAction.Delete});
|
||||
});
|
||||
|
||||
}
|
||||
|
|
@ -85,13 +87,13 @@ export class ReviewModalComponent implements OnInit {
|
|||
|
||||
let obs;
|
||||
if (!this.review.chapterId) {
|
||||
obs = this.seriesService.updateReview(this.review.seriesId, model.reviewBody, this.review.rating);
|
||||
obs = this.seriesService.updateReview(this.review.seriesId, model.reviewBody, this.rating);
|
||||
} else {
|
||||
obs = this.chapterService.updateChapterReview(this.review.seriesId, this.review.chapterId, model.reviewBody, this.review.rating);
|
||||
obs = this.chapterService.updateChapterReview(this.review.seriesId, this.review.chapterId, model.reviewBody, this.rating);
|
||||
}
|
||||
|
||||
obs?.subscribe(review => {
|
||||
this.modal.close({success: true, review: review, action: ReviewSeriesModalCloseAction.Edit});
|
||||
this.modal.close({success: true, review: review, action: ReviewModalCloseAction.Edit});
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import {UserReview} from "../review-card/user-review";
|
|||
import {User} from "../../_models/user";
|
||||
import {AccountService} from "../../_services/account.service";
|
||||
import {
|
||||
ReviewModalComponent, ReviewSeriesModalCloseAction,
|
||||
ReviewSeriesModalCloseEvent
|
||||
ReviewModalComponent, ReviewModalCloseAction,
|
||||
ReviewModalCloseEvent
|
||||
} from "../review-modal/review-modal.component";
|
||||
import {DefaultModalOptions} from "../../_models/default-modal-options";
|
||||
import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
|
||||
|
|
@ -71,11 +71,11 @@ export class ReviewsComponent {
|
|||
|
||||
}
|
||||
|
||||
updateOrDeleteReview(closeResult: ReviewSeriesModalCloseEvent) {
|
||||
if (closeResult.action === ReviewSeriesModalCloseAction.Close) return;
|
||||
updateOrDeleteReview(closeResult: ReviewModalCloseEvent) {
|
||||
if (closeResult.action === ReviewModalCloseAction.Close) return;
|
||||
|
||||
const index = this.userReviews.findIndex(r => r.username === closeResult.review!.username);
|
||||
if (closeResult.action === ReviewSeriesModalCloseAction.Edit) {
|
||||
if (closeResult.action === ReviewModalCloseAction.Edit) {
|
||||
if (index === -1 ) {
|
||||
this.userReviews = [closeResult.review, ...this.userReviews];
|
||||
this.cdRef.markForCheck();
|
||||
|
|
@ -86,7 +86,7 @@ export class ReviewsComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
if (closeResult.action === ReviewSeriesModalCloseAction.Delete) {
|
||||
if (closeResult.action === ReviewModalCloseAction.Delete) {
|
||||
this.userReviews = [...this.userReviews.filter(r => r.username !== closeResult.review!.username)];
|
||||
this.cdRef.markForCheck();
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -27,15 +27,17 @@
|
|||
|
||||
|
||||
|
||||
<!-- Rating goes here (after I implement support for rating individual issues -->
|
||||
<!-- <div class="mt-2 mb-2">-->
|
||||
<!-- <app-external-rating [seriesId]="series.id"-->
|
||||
<!-- [ratings]="[]"-->
|
||||
<!-- [userRating]="series.userRating"-->
|
||||
<!-- [hasUserRated]="series.hasUserRated"-->
|
||||
<!-- [libraryType]="libraryType!">-->
|
||||
<!-- </app-external-rating>-->
|
||||
<!-- </div>-->
|
||||
<div class="mt-2 mb-2">
|
||||
@let rating = userRating();
|
||||
<app-external-rating [seriesId]="series.id"
|
||||
[ratings]="[]"
|
||||
[userRating]="rating?.rating || 0"
|
||||
[hasUserRated]="rating !== undefined"
|
||||
[libraryType]="libraryType!"
|
||||
[chapterId]="chapterId"
|
||||
>
|
||||
</app-external-rating>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 mb-3">
|
||||
<div class="row g-0">
|
||||
|
|
|
|||
|
|
@ -71,6 +71,8 @@ import {ReviewCardComponent} from "../_single-module/review-card/review-card.com
|
|||
import {User} from "../_models/user";
|
||||
import {ReviewModalComponent} from "../_single-module/review-modal/review-modal.component";
|
||||
import {ReviewsComponent} from "../_single-module/reviews/reviews.component";
|
||||
import {ExternalRatingComponent} from "../series-detail/_components/external-rating/external-rating.component";
|
||||
import {Rating} from "../_models/rating";
|
||||
|
||||
enum TabID {
|
||||
Related = 'related-tab',
|
||||
|
|
@ -111,7 +113,8 @@ enum TabID {
|
|||
CoverImageComponent,
|
||||
CarouselReelComponent,
|
||||
ReviewCardComponent,
|
||||
ReviewsComponent
|
||||
ReviewsComponent,
|
||||
ExternalRatingComponent
|
||||
],
|
||||
templateUrl: './chapter-detail.component.html',
|
||||
styleUrl: './chapter-detail.component.scss',
|
||||
|
|
@ -174,6 +177,7 @@ export class ChapterDetailComponent implements OnInit {
|
|||
mobileSeriesImgBackground: string | undefined;
|
||||
chapterActions: Array<ActionItem<Chapter>> = this.actionFactoryService.getChapterActions(this.handleChapterActionCallback.bind(this));
|
||||
|
||||
user: User | undefined;
|
||||
|
||||
get ScrollingBlockHeight() {
|
||||
if (this.scrollingBlock === undefined) return 'calc(var(--vh)*100)';
|
||||
|
|
@ -188,6 +192,12 @@ export class ChapterDetailComponent implements OnInit {
|
|||
|
||||
|
||||
ngOnInit() {
|
||||
this.accountService.currentUser$.subscribe(user => {
|
||||
if (user) {
|
||||
this.user = user;
|
||||
}
|
||||
});
|
||||
|
||||
const seriesId = this.route.snapshot.paramMap.get('seriesId');
|
||||
const libraryId = this.route.snapshot.paramMap.get('libraryId');
|
||||
const chapterId = this.route.snapshot.paramMap.get('chapterId');
|
||||
|
|
@ -376,6 +386,10 @@ export class ChapterDetailComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
userRating() {
|
||||
return this.userReviews.find(r => r.username == this.user?.username && !r.isExternal)
|
||||
}
|
||||
|
||||
protected readonly LibraryType = LibraryType;
|
||||
protected readonly encodeURIComponent = encodeURIComponent;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {ImageService} from "../../../_services/image.service";
|
|||
import {AsyncPipe, NgOptimizedImage, NgTemplateOutlet} from "@angular/common";
|
||||
import {RatingModalComponent} from "../rating-modal/rating-modal.component";
|
||||
import {ScrobbleProviderNamePipe} from "../../../_pipes/scrobble-provider-name.pipe";
|
||||
import {ChapterService} from "../../../_services/chapter.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-external-rating',
|
||||
|
|
@ -38,6 +39,7 @@ export class ExternalRatingComponent implements OnInit {
|
|||
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
private readonly seriesService = inject(SeriesService);
|
||||
private readonly chapterService = inject(ChapterService);
|
||||
private readonly themeService = inject(ThemeService);
|
||||
public readonly utilityService = inject(UtilityService);
|
||||
public readonly destroyRef = inject(DestroyRef);
|
||||
|
|
@ -47,6 +49,7 @@ export class ExternalRatingComponent implements OnInit {
|
|||
protected readonly Breakpoint = Breakpoint;
|
||||
|
||||
@Input({required: true}) seriesId!: number;
|
||||
@Input() chapterId: number | undefined;
|
||||
@Input({required: true}) userRating!: number;
|
||||
@Input({required: true}) hasUserRated!: boolean;
|
||||
@Input({required: true}) libraryType!: LibraryType;
|
||||
|
|
@ -58,11 +61,24 @@ export class ExternalRatingComponent implements OnInit {
|
|||
starColor = this.themeService.getCssVariable('--rating-star-color');
|
||||
|
||||
ngOnInit() {
|
||||
this.seriesService.getOverallRating(this.seriesId).subscribe(r => this.overallRating = r.averageScore);
|
||||
let obs;
|
||||
if (this.chapterId) {
|
||||
obs = this.chapterService.overallRating(this.chapterId);
|
||||
} else {
|
||||
obs = this.seriesService.getOverallRating(this.seriesId);
|
||||
}
|
||||
obs?.subscribe(r => this.overallRating = r.averageScore);
|
||||
}
|
||||
|
||||
updateRating(rating: number) {
|
||||
this.seriesService.updateRating(this.seriesId, rating).subscribe(() => {
|
||||
let obs;
|
||||
if (this.chapterId) {
|
||||
obs = this.chapterService.updateRating(this.chapterId, rating);
|
||||
} else {
|
||||
obs = this.seriesService.updateRating(this.seriesId, rating);
|
||||
}
|
||||
|
||||
obs?.subscribe(() => {
|
||||
this.userRating = rating;
|
||||
this.hasUserRated = true;
|
||||
this.cdRef.markForCheck();
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ import {ReadingListService} from 'src/app/_services/reading-list.service';
|
|||
import {ScrollService} from 'src/app/_services/scroll.service';
|
||||
import {SeriesService} from 'src/app/_services/series.service';
|
||||
import {
|
||||
ReviewSeriesModalCloseAction,
|
||||
ReviewSeriesModalCloseEvent,
|
||||
ReviewModalCloseAction,
|
||||
ReviewModalCloseEvent,
|
||||
ReviewModalComponent
|
||||
} from '../../../_single-module/review-modal/review-modal.component';
|
||||
import {PageLayoutMode} from 'src/app/_models/page-layout-mode';
|
||||
|
|
|
|||
|
|
@ -29,17 +29,18 @@
|
|||
[mangaFormat]="series.format">
|
||||
</app-metadata-detail-row>
|
||||
|
||||
<!-- Rating goes here (after I implement support for rating individual issues -->
|
||||
<!-- @if (libraryType !== null && series) {-->
|
||||
<!-- <div class="mt-2 mb-2">-->
|
||||
<!-- <app-external-rating [seriesId]="series.id"-->
|
||||
<!-- [ratings]="[]"-->
|
||||
<!-- [userRating]="series.userRating"-->
|
||||
<!-- [hasUserRated]="series.hasUserRated"-->
|
||||
<!-- [libraryType]="libraryType">-->
|
||||
<!-- </app-external-rating>-->
|
||||
<!-- </div>-->
|
||||
<!-- }-->
|
||||
@if (libraryType !== null && series && volume.chapters.length === 1) {
|
||||
<div class="mt-2 mb-2">
|
||||
@let rating = userRating();
|
||||
<app-external-rating [seriesId]="series.id"
|
||||
[ratings]="[]"
|
||||
[userRating]="rating?.rating || 0"
|
||||
[hasUserRated]="rating !== undefined"
|
||||
[libraryType]="libraryType"
|
||||
[chapterId]="volume.chapters[0].id"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="mt-2 mb-3">
|
||||
<div class="row g-0">
|
||||
|
|
|
|||
|
|
@ -80,6 +80,8 @@ import {CoverImageComponent} from "../_single-module/cover-image/cover-image.com
|
|||
import {DefaultModalOptions} from "../_models/default-modal-options";
|
||||
import {UserReview} from "../_single-module/review-card/user-review";
|
||||
import {ReviewsComponent} from "../_single-module/reviews/reviews.component";
|
||||
import {ExternalRatingComponent} from "../series-detail/_components/external-rating/external-rating.component";
|
||||
import {User} from "../_models/user";
|
||||
|
||||
enum TabID {
|
||||
|
||||
|
|
@ -150,7 +152,8 @@ interface VolumeCast extends IHasCast {
|
|||
CardActionablesComponent,
|
||||
BulkOperationsComponent,
|
||||
CoverImageComponent,
|
||||
ReviewsComponent
|
||||
ReviewsComponent,
|
||||
ExternalRatingComponent
|
||||
],
|
||||
templateUrl: './volume-detail.component.html',
|
||||
styleUrl: './volume-detail.component.scss',
|
||||
|
|
@ -204,6 +207,8 @@ export class VolumeDetailComponent implements OnInit {
|
|||
mobileSeriesImgBackground: string | undefined;
|
||||
downloadInProgress: boolean = false;
|
||||
|
||||
user: User | undefined;
|
||||
|
||||
volumeActions: Array<ActionItem<Volume>> = this.actionFactoryService.getVolumeActions(this.handleVolumeAction.bind(this));
|
||||
chapterActions: Array<ActionItem<Chapter>> = this.actionFactoryService.getChapterActions(this.handleChapterActionCallback.bind(this));
|
||||
|
||||
|
|
@ -337,6 +342,12 @@ export class VolumeDetailComponent implements OnInit {
|
|||
|
||||
|
||||
ngOnInit() {
|
||||
this.accountService.currentUser$.subscribe(user => {
|
||||
if (user) {
|
||||
this.user = user;
|
||||
}
|
||||
})
|
||||
|
||||
const seriesId = this.route.snapshot.paramMap.get('seriesId');
|
||||
const libraryId = this.route.snapshot.paramMap.get('libraryId');
|
||||
const volumeId = this.route.snapshot.paramMap.get('volumeId');
|
||||
|
|
@ -675,5 +686,9 @@ export class VolumeDetailComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
userRating() {
|
||||
return this.userReviews.find(r => r.username === this.user?.username && !r.isExternal);
|
||||
}
|
||||
|
||||
protected readonly encodeURIComponent = encodeURIComponent;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue