forked from MapComplete/MapComplete
		
	Merge branch 'develop'
This commit is contained in:
		
						commit
						4bd0d2b392
					
				
					 7 changed files with 85 additions and 62 deletions
				
			
		| 
						 | 
				
			
			@ -5,6 +5,7 @@ import {Review} from "./Review";
 | 
			
		|||
export class MangroveIdentity {
 | 
			
		||||
    private readonly _mangroveIdentity: UIEventSource<string>;
 | 
			
		||||
    public keypair: any = undefined;
 | 
			
		||||
    public readonly kid: UIEventSource<string> = new UIEventSource<string>(undefined);
 | 
			
		||||
 | 
			
		||||
    constructor(mangroveIdentity: UIEventSource<string>) {
 | 
			
		||||
        const self = this;
 | 
			
		||||
| 
						 | 
				
			
			@ -13,10 +14,12 @@ export class MangroveIdentity {
 | 
			
		|||
            if (str === undefined || str === "") {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            console.log("JWK ", JSON.parse(str));
 | 
			
		||||
            mangrove.jwkToKeypair(JSON.parse(str)).then(keypair => {
 | 
			
		||||
                self.keypair = keypair;
 | 
			
		||||
                console.log("Identity loaded")
 | 
			
		||||
                mangrove.publicToPem(keypair.publicKey).then(pem => {
 | 
			
		||||
                    console.log("Identity loaded")
 | 
			
		||||
                    self.kid.setData(pem);
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
        if ((mangroveIdentity.data ?? "") === "") {
 | 
			
		||||
| 
						 | 
				
			
			@ -117,9 +120,10 @@ export default class MangroveReviews {
 | 
			
		|||
                const reviewsByUser = [];
 | 
			
		||||
                for (const review of data.reviews) {
 | 
			
		||||
                    const r = review.payload;
 | 
			
		||||
                    console.log("PublicKey is ",self._mangroveIdentity.keypair, "reviews is",review.signature);
 | 
			
		||||
                    const byUser = self._mangroveIdentity.keypair.publicKey === review.signature;
 | 
			
		||||
                    console.log("IS SAME: ", byUser);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    console.log("PublicKey is ", self._mangroveIdentity.kid.data, "reviews.kid is", review.kid);
 | 
			
		||||
                    const byUser = self._mangroveIdentity.kid.map(data => data === review.signature);
 | 
			
		||||
                    const rev: Review = {
 | 
			
		||||
                        made_by_user: byUser,
 | 
			
		||||
                        date: new Date(r.iat * 1000),
 | 
			
		||||
| 
						 | 
				
			
			@ -133,7 +137,7 @@ export default class MangroveReviews {
 | 
			
		|||
 | 
			
		||||
                    (rev.made_by_user ? reviewsByUser : reviews).push(rev);
 | 
			
		||||
                }
 | 
			
		||||
                self._reviews.setData(reviews)
 | 
			
		||||
                self._reviews.setData(reviewsByUser.concat(reviews))
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
        return this._reviews;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
import {UIEventSource} from "../UIEventSource";
 | 
			
		||||
 | 
			
		||||
export interface Review {
 | 
			
		||||
    comment?: string,
 | 
			
		||||
    author: string,
 | 
			
		||||
| 
						 | 
				
			
			@ -7,5 +9,5 @@ export interface Review {
 | 
			
		|||
    /**
 | 
			
		||||
     * True if the current logged in user is the creator of this comment
 | 
			
		||||
     */
 | 
			
		||||
    made_by_user: boolean
 | 
			
		||||
    made_by_user: UIEventSource<boolean>
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,10 +4,9 @@
 | 
			
		|||
import {UIEventSource} from "../../Logic/UIEventSource";
 | 
			
		||||
import {Review} from "../../Logic/Web/Review";
 | 
			
		||||
import {UIElement} from "../UIElement";
 | 
			
		||||
import {Utils} from "../../Utils";
 | 
			
		||||
import Combine from "../Base/Combine";
 | 
			
		||||
import {FixedUiElement} from "../Base/FixedUiElement";
 | 
			
		||||
import Translations from "../i18n/Translations";
 | 
			
		||||
import SingleReview from "./SingleReview";
 | 
			
		||||
 | 
			
		||||
export default class ReviewElement extends UIElement {
 | 
			
		||||
    private readonly _reviews: UIEventSource<Review[]>;
 | 
			
		||||
| 
						 | 
				
			
			@ -17,37 +16,25 @@ export default class ReviewElement extends UIElement {
 | 
			
		|||
    constructor(subject: string, reviews: UIEventSource<Review[]>, middleElement: UIElement) {
 | 
			
		||||
        super(reviews);
 | 
			
		||||
        this._middleElement = middleElement;
 | 
			
		||||
        if(reviews === undefined){
 | 
			
		||||
        if (reviews === undefined) {
 | 
			
		||||
            throw "No reviews UIEVentsource Given!"
 | 
			
		||||
        }
 | 
			
		||||
        this._reviews = reviews;
 | 
			
		||||
        this._subject = subject;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    InnerRender(): string {
 | 
			
		||||
   
 | 
			
		||||
        function genStars(rating: number) {
 | 
			
		||||
            if(rating === undefined){
 | 
			
		||||
                return Translations.t.reviews.no_rating;
 | 
			
		||||
            }
 | 
			
		||||
            if(rating < 10){
 | 
			
		||||
                rating = 10;
 | 
			
		||||
            }
 | 
			
		||||
            const scoreTen = Math.round(rating / 10);
 | 
			
		||||
            return new Combine([
 | 
			
		||||
                "<img src='./assets/svg/star.svg' />".repeat(Math.floor(scoreTen / 2)),
 | 
			
		||||
                scoreTen % 2 == 1 ? "<img src='./assets/svg/star_half.svg' />" : ""
 | 
			
		||||
            ])
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    InnerRender(): string {
 | 
			
		||||
 | 
			
		||||
        const elements = [];
 | 
			
		||||
        const revs = this._reviews.data;
 | 
			
		||||
        revs.sort((a,b) => (b.date.getTime() - a.date.getTime())); // Sort with most recent first
 | 
			
		||||
        revs.sort((a, b) => (b.date.getTime() - a.date.getTime())); // Sort with most recent first
 | 
			
		||||
        const avg = (revs.map(review => review.rating).reduce((a, b) => a + b, 0) / revs.length);
 | 
			
		||||
        elements.push(
 | 
			
		||||
            new Combine([
 | 
			
		||||
                genStars(avg).SetClass("stars"),
 | 
			
		||||
                `<a href='https://mangrove.reviews/search?sub=${this._subject}'>`,
 | 
			
		||||
                SingleReview.GenStars(avg).SetClass("stars"),
 | 
			
		||||
                `<a target="_blank" href='https://mangrove.reviews/search?sub=${encodeURIComponent(this._subject)}'>`,
 | 
			
		||||
                Translations.t.reviews.title
 | 
			
		||||
                    .Subs({count: "" + revs.length}),
 | 
			
		||||
                "</a>"
 | 
			
		||||
| 
						 | 
				
			
			@ -57,29 +44,7 @@ export default class ReviewElement extends UIElement {
 | 
			
		|||
        
 | 
			
		||||
        elements.push(this._middleElement);
 | 
			
		||||
 | 
			
		||||
        elements.push(...revs.map(review => {
 | 
			
		||||
            const d = review.date;
 | 
			
		||||
            return new Combine(
 | 
			
		||||
                [
 | 
			
		||||
                    new Combine([
 | 
			
		||||
                        genStars(review.rating)
 | 
			
		||||
                            .SetClass("review-rating"),
 | 
			
		||||
                        new FixedUiElement(review.comment).SetClass("review-comment")
 | 
			
		||||
                    ]).SetClass("review-stars-comment"),
 | 
			
		||||
 | 
			
		||||
                    new Combine([
 | 
			
		||||
                        new Combine([
 | 
			
		||||
 | 
			
		||||
                            new FixedUiElement(review.author).SetClass("review-author"),
 | 
			
		||||
                            review.affiliated ? Translations.t.reviews.affiliated_reviewer_warning : "",
 | 
			
		||||
                        ]).SetStyle("margin-right: 0.5em"),
 | 
			
		||||
                        new FixedUiElement(`${d.getFullYear()}-${Utils.TwoDigits(d.getMonth() + 1)}-${Utils.TwoDigits(d.getDate())} ${Utils.TwoDigits(d.getHours())}:${Utils.TwoDigits(d.getMinutes())}`)
 | 
			
		||||
                            .SetClass("review-date")
 | 
			
		||||
                    ]).SetClass("review-author-date")
 | 
			
		||||
 | 
			
		||||
                ]
 | 
			
		||||
            ).SetClass("review-element")
 | 
			
		||||
        }));
 | 
			
		||||
        elements.push(...revs.map(review => new SingleReview(review)));
 | 
			
		||||
        elements.push(
 | 
			
		||||
            new Combine([
 | 
			
		||||
                Translations.t.reviews.attribution,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,7 +27,7 @@ export default class ReviewForm extends InputElement<Review> {
 | 
			
		|||
        this.userDetails = userDetails;
 | 
			
		||||
        const t = Translations.t.reviews;
 | 
			
		||||
        this._value  = new UIEventSource({
 | 
			
		||||
            made_by_user: false,
 | 
			
		||||
            made_by_user: new UIEventSource<boolean>(true),
 | 
			
		||||
            rating: undefined,
 | 
			
		||||
            comment: undefined,
 | 
			
		||||
            author: userDetails.data.name,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +0,0 @@
 | 
			
		|||
import {UIElement} from "../UIElement";
 | 
			
		||||
 | 
			
		||||
export default class ReviewPanel extends UIElement {
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    InnerRender(): string {
 | 
			
		||||
        return "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								UI/Reviews/SingleReview.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								UI/Reviews/SingleReview.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,59 @@
 | 
			
		|||
import {UIElement} from "../UIElement";
 | 
			
		||||
import {Review} from "../../Logic/Web/Review";
 | 
			
		||||
import Combine from "../Base/Combine";
 | 
			
		||||
import {FixedUiElement} from "../Base/FixedUiElement";
 | 
			
		||||
import Translations from "../i18n/Translations";
 | 
			
		||||
import {Utils} from "../../Utils";
 | 
			
		||||
import ReviewElement from "./ReviewElement";
 | 
			
		||||
 | 
			
		||||
export default class SingleReview extends UIElement{
 | 
			
		||||
    private _review: Review;
 | 
			
		||||
    constructor(review: Review) {
 | 
			
		||||
        super(review.made_by_user);
 | 
			
		||||
        this._review = review;
 | 
			
		||||
      
 | 
			
		||||
    }
 | 
			
		||||
    public static GenStars(rating: number): UIElement {
 | 
			
		||||
        if (rating === undefined) {
 | 
			
		||||
            return Translations.t.reviews.no_rating;
 | 
			
		||||
        }
 | 
			
		||||
        if (rating < 10) {
 | 
			
		||||
            rating = 10;
 | 
			
		||||
        }
 | 
			
		||||
        const scoreTen = Math.round(rating / 10);
 | 
			
		||||
        return new Combine([
 | 
			
		||||
            "<img src='./assets/svg/star.svg' />".repeat(Math.floor(scoreTen / 2)),
 | 
			
		||||
            scoreTen % 2 == 1 ? "<img src='./assets/svg/star_half.svg' />" : ""
 | 
			
		||||
        ])
 | 
			
		||||
    }
 | 
			
		||||
    InnerRender(): string {
 | 
			
		||||
        const d = this._review.date;
 | 
			
		||||
        let review = this._review;
 | 
			
		||||
        const el=  new Combine(
 | 
			
		||||
            [
 | 
			
		||||
                new Combine([
 | 
			
		||||
                  SingleReview.GenStars(review.rating)
 | 
			
		||||
                        .SetClass("review-rating"),
 | 
			
		||||
                    new FixedUiElement(review.comment).SetClass("review-comment")
 | 
			
		||||
                ]).SetClass("review-stars-comment"),
 | 
			
		||||
 | 
			
		||||
                new Combine([
 | 
			
		||||
                    new Combine([
 | 
			
		||||
 | 
			
		||||
                        new FixedUiElement(review.author).SetClass("review-author"),
 | 
			
		||||
                        review.affiliated ? Translations.t.reviews.affiliated_reviewer_warning : "",
 | 
			
		||||
                    ]).SetStyle("margin-right: 0.5em"),
 | 
			
		||||
                    new FixedUiElement(`${d.getFullYear()}-${Utils.TwoDigits(d.getMonth() + 1)}-${Utils.TwoDigits(d.getDate())} ${Utils.TwoDigits(d.getHours())}:${Utils.TwoDigits(d.getMinutes())}`)
 | 
			
		||||
                        .SetClass("review-date")
 | 
			
		||||
                ]).SetClass("review-author-date")
 | 
			
		||||
 | 
			
		||||
            ]
 | 
			
		||||
        );
 | 
			
		||||
        el.SetClass("review-element");
 | 
			
		||||
        if(review.made_by_user){
 | 
			
		||||
            el.SetClass("review-by-current-user")
 | 
			
		||||
        }
 | 
			
		||||
        return el.Render();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -23,6 +23,10 @@
 | 
			
		|||
    height: 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.review-by-current-user {
 | 
			
		||||
    border: 5px solid var(--catch-detail-color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.review-rating {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue