201 lines
6.2 KiB
TypeScript
201 lines
6.2 KiB
TypeScript
import * as mangrove from "mangrove-reviews"
|
|
import { UIEventSource } from "../UIEventSource"
|
|
import { Review } from "./Review"
|
|
import { Utils } from "../../Utils"
|
|
|
|
export class MangroveIdentity {
|
|
public keypair: any = undefined
|
|
public readonly kid: UIEventSource<string> = new UIEventSource<string>(undefined)
|
|
private readonly _mangroveIdentity: UIEventSource<string>
|
|
|
|
constructor(mangroveIdentity: UIEventSource<string>) {
|
|
const self = this
|
|
this._mangroveIdentity = mangroveIdentity
|
|
mangroveIdentity.addCallbackAndRunD((str) => {
|
|
if (str === "") {
|
|
return
|
|
}
|
|
mangrove.jwkToKeypair(JSON.parse(str)).then((keypair) => {
|
|
self.keypair = keypair
|
|
mangrove.publicToPem(keypair.publicKey).then((pem) => {
|
|
console.log("Identity loaded")
|
|
self.kid.setData(pem)
|
|
})
|
|
})
|
|
})
|
|
try {
|
|
if (!Utils.runningFromConsole && (mangroveIdentity.data ?? "") === "") {
|
|
this.CreateIdentity()
|
|
}
|
|
} catch (e) {
|
|
console.error("Could not create identity: ", e)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates an identity if none exists already.
|
|
* Is written into the UIEventsource, which was passed into the constructor
|
|
* @constructor
|
|
*/
|
|
private CreateIdentity() {
|
|
if ("" !== (this._mangroveIdentity.data ?? "")) {
|
|
throw "Identity already defined - not creating a new one"
|
|
}
|
|
const self = this
|
|
mangrove.generateKeypair().then((keypair) => {
|
|
self.keypair = keypair
|
|
mangrove.keypairToJwk(keypair).then((jwk) => {
|
|
self._mangroveIdentity.setData(JSON.stringify(jwk))
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
export default class MangroveReviews {
|
|
private static _reviewsCache = {}
|
|
private static didWarn = false
|
|
private readonly _lon: number
|
|
private readonly _lat: number
|
|
private readonly _name: string
|
|
private readonly _reviews: UIEventSource<Review[]> = new UIEventSource<Review[]>([])
|
|
private _dryRun: boolean
|
|
private _mangroveIdentity: MangroveIdentity
|
|
private _lastUpdate: Date = undefined
|
|
|
|
private constructor(
|
|
lon: number,
|
|
lat: number,
|
|
name: string,
|
|
identity: MangroveIdentity,
|
|
dryRun?: boolean
|
|
) {
|
|
this._lon = lon
|
|
this._lat = lat
|
|
this._name = name
|
|
this._mangroveIdentity = identity
|
|
this._dryRun = dryRun
|
|
if (dryRun && !MangroveReviews.didWarn) {
|
|
MangroveReviews.didWarn = true
|
|
console.warn("Mangrove reviews will _not_ be saved as dryrun is specified")
|
|
}
|
|
}
|
|
|
|
public static Get(
|
|
lon: number,
|
|
lat: number,
|
|
name: string,
|
|
identity: MangroveIdentity,
|
|
dryRun?: boolean
|
|
) {
|
|
const newReviews = new MangroveReviews(lon, lat, name, identity, dryRun)
|
|
|
|
const uri = newReviews.GetSubjectUri()
|
|
const cached = MangroveReviews._reviewsCache[uri]
|
|
if (cached !== undefined) {
|
|
return cached
|
|
}
|
|
MangroveReviews._reviewsCache[uri] = newReviews
|
|
|
|
return newReviews
|
|
}
|
|
|
|
/**
|
|
* Gets an URI which represents the item in a mangrove-compatible way
|
|
* @constructor
|
|
*/
|
|
public GetSubjectUri() {
|
|
let uri = `geo:${this._lat},${this._lon}?u=50`
|
|
if (this._name !== undefined && this._name !== null) {
|
|
uri += "&q=" + this._name
|
|
}
|
|
return uri
|
|
}
|
|
|
|
/**
|
|
* Gives a UIEVentsource with all reviews.
|
|
* Note: rating is between 1 and 100
|
|
*/
|
|
public GetReviews(): UIEventSource<Review[]> {
|
|
if (
|
|
this._lastUpdate !== undefined &&
|
|
this._reviews.data !== undefined &&
|
|
new Date().getTime() - this._lastUpdate.getTime() < 15000
|
|
) {
|
|
// Last update was pretty recent
|
|
return this._reviews
|
|
}
|
|
this._lastUpdate = new Date()
|
|
|
|
const self = this
|
|
mangrove.getReviews({ sub: this.GetSubjectUri() })
|
|
.then((data) => {
|
|
const reviews = []
|
|
const reviewsByUser = []
|
|
for (const review of data.reviews) {
|
|
const r = review.payload
|
|
|
|
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),
|
|
comment: r.opinion,
|
|
author: r.metadata.nickname,
|
|
affiliated: r.metadata.is_affiliated,
|
|
rating: r.rating, // percentage points
|
|
}
|
|
|
|
;(rev.made_by_user ? reviewsByUser : reviews).push(rev)
|
|
}
|
|
self._reviews.setData(reviewsByUser.concat(reviews))
|
|
})
|
|
.catch(e => {
|
|
console.error("Could not download review for ", e);
|
|
})
|
|
return this._reviews
|
|
}
|
|
|
|
AddReview(r: Review, callback?: () => void) {
|
|
callback =
|
|
callback ??
|
|
(() => {
|
|
return undefined
|
|
})
|
|
|
|
const payload = {
|
|
sub: this.GetSubjectUri(),
|
|
rating: r.rating,
|
|
opinion: r.comment,
|
|
metadata: {
|
|
nickname: r.author,
|
|
},
|
|
}
|
|
if (r.affiliated) {
|
|
// @ts-ignore
|
|
payload.metadata.is_affiliated = true
|
|
}
|
|
if (this._dryRun) {
|
|
console.warn("DRYRUNNING mangrove reviews: ", payload)
|
|
if (callback) {
|
|
if (callback) {
|
|
callback()
|
|
}
|
|
this._reviews.data.push(r)
|
|
this._reviews.ping()
|
|
}
|
|
} else {
|
|
mangrove.signAndSubmitReview(this._mangroveIdentity.keypair, payload).then(() => {
|
|
if (callback) {
|
|
callback()
|
|
}
|
|
this._reviews.data.push(r)
|
|
this._reviews.ping()
|
|
})
|
|
}
|
|
}
|
|
}
|