diff --git a/.changeset/fair-colts-explain.md b/.changeset/fair-colts-explain.md
new file mode 100644
index 00000000..0ffdc2dc
--- /dev/null
+++ b/.changeset/fair-colts-explain.md
@@ -0,0 +1,5 @@
+---
+'@openstapps/app': patch
+---
+
+Use observable chains instead of change detection in the rating component
diff --git a/frontend/app/src/app/modules/data/elements/rating.component.ts b/frontend/app/src/app/modules/data/elements/rating.component.ts
index 03f423cb..75eabf98 100644
--- a/frontend/app/src/app/modules/data/elements/rating.component.ts
+++ b/frontend/app/src/app/modules/data/elements/rating.component.ts
@@ -1,3 +1,4 @@
+/* eslint-disable unicorn/no-useless-undefined */
/*
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
@@ -12,60 +13,51 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see .
*/
-import {Component, ElementRef, HostListener, Input} from '@angular/core';
-import {SCDish, SCRatingRequest, SCUuid} from '@openstapps/core';
+import {ChangeDetectionStrategy, Component, ElementRef, HostListener, Input} from '@angular/core';
+import {SCDish, SCRatingRequest} from '@openstapps/core';
import {RatingProvider} from '../rating.provider';
import {ratingAnimation} from './rating.animation';
+import {BehaviorSubject, filter, merge, mergeMap, of, ReplaySubject, withLatestFrom} from 'rxjs';
+import {catchError, map} from 'rxjs/operators';
@Component({
selector: 'stapps-rating',
templateUrl: 'rating.html',
styleUrls: ['rating.scss'],
animations: [ratingAnimation],
+ changeDetection: ChangeDetectionStrategy.OnPush,
})
export class StappsRatingComponent {
- rate = false;
+ performRating = new BehaviorSubject(false);
- rated = false;
+ userRating = new BehaviorSubject(undefined);
- canBeRated = false;
+ dish = new ReplaySubject(1);
- uid: SCUuid;
+ wasAlreadyRated = merge(
+ this.dish.pipe(mergeMap(({uid}) => this.ratingProvider.hasRated(uid))),
+ this.userRating.pipe(
+ filter(it => it !== undefined),
+ withLatestFrom(this.dish),
+ mergeMap(([rating, {uid}]) => this.ratingProvider.rate(uid, rating as SCRatingRequest['rating'])),
+ map(() => true),
+ catchError(() => of(false)),
+ ),
+ );
- rating?: number;
+ canBeRated = this.dish.pipe(mergeMap(dish => this.ratingProvider.canRate(dish)));
- @Input() set item(value: SCDish) {
- this.uid = value.uid;
-
- Promise.all([this.ratingProvider.canRate(value), this.ratingProvider.hasRated(this.uid)] as const).then(
- ([canRate, hasRated]) => {
- this.canBeRated = canRate;
- this.rated = hasRated;
- },
- );
+ @Input({required: true}) set item(value: SCDish) {
+ this.dish.next(value);
}
constructor(readonly elementRef: ElementRef, readonly ratingProvider: RatingProvider) {}
- async submitRating(rating: number) {
- this.rating = rating;
- try {
- await this.ratingProvider.rate(this.uid, rating as SCRatingRequest['rating']);
- this.rated = true;
- } catch {
- this.rating = undefined;
- // allow change detection to catch up first
- setTimeout(() => {
- this.rate = false;
- });
- }
- }
-
@HostListener('document:mousedown', ['$event'])
clickOutside(event: MouseEvent) {
- if (this.rating) return;
+ if (this.userRating.value) return;
if (!this.elementRef.nativeElement.contains(event.target)) {
- this.rate = false;
+ this.performRating.next(false);
}
}
}
diff --git a/frontend/app/src/app/modules/data/elements/rating.html b/frontend/app/src/app/modules/data/elements/rating.html
index 2248eb4b..05cec835 100644
--- a/frontend/app/src/app/modules/data/elements/rating.html
+++ b/frontend/app/src/app/modules/data/elements/rating.html
@@ -14,19 +14,23 @@
-->
-