forked from MapComplete/MapComplete
Feature(privacy): allow to disable reviews, disable reviews in sensitive themes, fix https://github.com/pietervdvn/MapComplete/issues/2282
This commit is contained in:
parent
1facbaa7d1
commit
b4a7391725
10 changed files with 1137 additions and 291 deletions
assets/layers
langs/layers
src
Logic/Web
UI
BigComponents
Reviews
SpecialVisualisations
File diff suppressed because it is too large
Load diff
|
@ -1150,6 +1150,39 @@
|
|||
],
|
||||
"metacondition": "_uid~*"
|
||||
},
|
||||
{
|
||||
"id": "mangrove-reviews-allowed",
|
||||
"question": {
|
||||
"en": "Should reviews be loaded when browsing an item?"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "mapcomplete-reviews-allowed=always",
|
||||
"then": {
|
||||
"en": "Show reviews by default, also in sensitive themes"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "mapcomplete-reviews-allowed=yes",
|
||||
"alsoShowIf": "mapcomplete-reviews-allowed=",
|
||||
"then": {
|
||||
"en": "Show reviews by default, except in sensitive themes (where we'll ask per feature)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "mapcomplete-reviews-allowed=ask",
|
||||
"then": {
|
||||
"en": "Always ask before loading reviews"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "mapcomplete-reviews-allowed=hidden",
|
||||
"then": {
|
||||
"en": "Never show reviews at all"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "title-id",
|
||||
"render": {
|
||||
|
|
|
@ -10023,6 +10023,33 @@
|
|||
}
|
||||
},
|
||||
"question": "What type of special needs are given here?"
|
||||
},
|
||||
"uniform": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Students must wear a uniform, which is extensively described"
|
||||
},
|
||||
"1": {
|
||||
"then": "Students must wear clothes in a specific colour scheme"
|
||||
},
|
||||
"2": {
|
||||
"then": "There is no formal dress code, students are allowed to come in casual wear such as t-shirt, jeans, ..."
|
||||
},
|
||||
"3": {
|
||||
"then": "Arms must be covered by the clothes"
|
||||
},
|
||||
"4": {
|
||||
"then": "Knees must be covered by the clothes"
|
||||
},
|
||||
"5": {
|
||||
"then": "Legs must be covered by the clothes"
|
||||
},
|
||||
"6": {
|
||||
"then": "The belly must be covered by the clothes"
|
||||
}
|
||||
},
|
||||
"question": "Do pupils have to wear a uniform or obey a dresscode?",
|
||||
"render": "{dress_code}"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
|
@ -13110,6 +13137,23 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"mangrove-reviews-allowed": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Show reviews by default, also in sensitive themes"
|
||||
},
|
||||
"1": {
|
||||
"then": "Show reviews by default, except in sensitive themes (where we'll ask per feature)"
|
||||
},
|
||||
"2": {
|
||||
"then": "Always ask before loading"
|
||||
},
|
||||
"3": {
|
||||
"then": "Never show reviews at all"
|
||||
}
|
||||
},
|
||||
"question": "Should reviews be loaded when browsing an item?"
|
||||
},
|
||||
"more_privacy": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
|
|
|
@ -8492,6 +8492,32 @@
|
|||
}
|
||||
},
|
||||
"question": "Welke soorten zorg voor buitengewone leerlingen is hier beschikbaar?"
|
||||
},
|
||||
"uniform": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Studenten moeten een specifiek uniform dragen"
|
||||
},
|
||||
"1": {
|
||||
"then": "Leerlingen moeten kleren van een bepaalde kleur dragen"
|
||||
},
|
||||
"2": {
|
||||
"then": "Er is geen uniformverplichting, leerlingen mogen kledij zoals t-shirts, jeans, ... dragen"
|
||||
},
|
||||
"3": {
|
||||
"then": "De armen moeten volledig bedekt zijn"
|
||||
},
|
||||
"4": {
|
||||
"then": "De knieën moeten volledig bedekt zijn"
|
||||
},
|
||||
"5": {
|
||||
"then": "De benen moeten volledig bedekt zijn"
|
||||
},
|
||||
"6": {
|
||||
"then": "De buik mag niet zichtbaar zijn"
|
||||
}
|
||||
},
|
||||
"question": "Moeten leerlingen een uniform dragen of specifieke kledingsvoorschriften volgen?"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
|
|
|
@ -3,6 +3,7 @@ import { MangroveReviews, Review } from "mangrove-reviews-typescript"
|
|||
import { Utils } from "../../Utils"
|
||||
import { Feature, Position } from "geojson"
|
||||
import { GeoOperations } from "../GeoOperations"
|
||||
import { SpecialVisualizationState } from "../../UI/SpecialVisualization"
|
||||
|
||||
export class MangroveIdentity {
|
||||
private readonly keypair: UIEventSource<CryptoKeyPair> = new UIEventSource<CryptoKeyPair>(
|
||||
|
@ -116,12 +117,12 @@ export class MangroveIdentity {
|
|||
return []
|
||||
}
|
||||
const allReviews = await MangroveReviews.getReviews({
|
||||
kid: pem,
|
||||
kid: pem
|
||||
})
|
||||
this.allReviewsById.setData(
|
||||
allReviews.reviews.map((r) => ({
|
||||
...r,
|
||||
...r.payload,
|
||||
...r.payload
|
||||
}))
|
||||
)
|
||||
})
|
||||
|
@ -157,6 +158,7 @@ export default class FeatureReviews {
|
|||
private readonly _name: Store<string>
|
||||
private readonly _identity: MangroveIdentity
|
||||
private readonly _testmode: Store<boolean>
|
||||
public loadingAllowed: UIEventSource<boolean | null>
|
||||
|
||||
private constructor(
|
||||
feature: Feature,
|
||||
|
@ -167,8 +169,10 @@ export default class FeatureReviews {
|
|||
fallbackName?: string
|
||||
uncertaintyRadius?: number
|
||||
},
|
||||
testmode?: Store<boolean>
|
||||
testmode?: Store<boolean>,
|
||||
loadingAllowed?: UIEventSource<boolean | null>
|
||||
) {
|
||||
this.loadingAllowed = loadingAllowed
|
||||
const centerLonLat = GeoOperations.centerpointCoordinates(feature)
|
||||
;[this._lon, this._lat] = centerLonLat
|
||||
this._identity = mangroveIdentity
|
||||
|
@ -222,6 +226,9 @@ export default class FeatureReviews {
|
|||
*/
|
||||
this.ConstructSubjectUri(true).mapD(
|
||||
async (sub) => {
|
||||
if (!loadingAllowed.data) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const reviews = await MangroveReviews.getReviews({ sub })
|
||||
console.debug("Got reviews (no-encode) for", feature, reviews, sub)
|
||||
|
@ -230,7 +237,7 @@ export default class FeatureReviews {
|
|||
console.log("Could not fetch reviews for partially incorrect query ", sub)
|
||||
}
|
||||
},
|
||||
[this._name]
|
||||
[this._name, loadingAllowed]
|
||||
)
|
||||
this.average = this._reviews.map((reviews) => {
|
||||
if (!reviews) {
|
||||
|
@ -268,19 +275,39 @@ export default class FeatureReviews {
|
|||
fallbackName?: string
|
||||
uncertaintyRadius?: number
|
||||
},
|
||||
testmode: Store<boolean>
|
||||
state: SpecialVisualizationState
|
||||
): FeatureReviews {
|
||||
const key = feature.properties.id
|
||||
const cached = FeatureReviews._featureReviewsCache[key]
|
||||
if (cached !== undefined) {
|
||||
return cached
|
||||
}
|
||||
const themeIsSensitive = state.theme?.enableMorePrivacy
|
||||
const settings = state.osmConnection.getPreference<"always" | "yes" | "ask" | "hidden">("reviews-allowed")
|
||||
const loadingAllowed = new UIEventSource(false)
|
||||
settings.addCallbackAndRun((s) => {
|
||||
console.log("Reviews allowed is", s)
|
||||
if (s === "hidden") {
|
||||
loadingAllowed.set(null)
|
||||
return
|
||||
}
|
||||
if (s === "always") {
|
||||
loadingAllowed.set(true)
|
||||
return
|
||||
}
|
||||
if (themeIsSensitive || s === "ask") {
|
||||
loadingAllowed.set(false)
|
||||
return
|
||||
}
|
||||
loadingAllowed.set(true)
|
||||
})
|
||||
const featureReviews = new FeatureReviews(
|
||||
feature,
|
||||
tagsSource,
|
||||
mangroveIdentity,
|
||||
options,
|
||||
testmode
|
||||
state.featureSwitchIsTesting,
|
||||
loadingAllowed
|
||||
)
|
||||
FeatureReviews._featureReviewsCache[key] = featureReviews
|
||||
return featureReviews
|
||||
|
@ -302,7 +329,7 @@ export default class FeatureReviews {
|
|||
}
|
||||
const r: Review = {
|
||||
sub: this.subjectUri.data,
|
||||
...review,
|
||||
...review
|
||||
}
|
||||
const keypair: CryptoKeyPair = await this._identity.getKeypair()
|
||||
const jwt = await MangroveReviews.signReview(keypair, r)
|
||||
|
@ -317,7 +344,7 @@ export default class FeatureReviews {
|
|||
...r,
|
||||
kid,
|
||||
signature: jwt,
|
||||
madeByLoggedInUser: new ImmutableStore(true),
|
||||
madeByLoggedInUser: new ImmutableStore(true)
|
||||
}
|
||||
this._reviews.data.push(reviewWithKid)
|
||||
this._reviews.ping()
|
||||
|
@ -375,7 +402,7 @@ export default class FeatureReviews {
|
|||
signature: reviewData.signature,
|
||||
madeByLoggedInUser: this._identity.getKeyId().map((user_key_id) => {
|
||||
return reviewData.kid === user_key_id
|
||||
}),
|
||||
})
|
||||
})
|
||||
hasNew = true
|
||||
}
|
||||
|
@ -401,8 +428,8 @@ export default class FeatureReviews {
|
|||
} else if (this._uncertainty > 1000) {
|
||||
console.error(
|
||||
"Not fetching reviews. Only got a point and a very big uncertainty range (" +
|
||||
this._uncertainty +
|
||||
"), so you'd probably only get garbage. Specify a name"
|
||||
this._uncertainty +
|
||||
"), so you'd probably only get garbage. Specify a name"
|
||||
)
|
||||
return undefined
|
||||
}
|
||||
|
|
|
@ -4,15 +4,22 @@
|
|||
import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import UserRelatedState from "../../Logic/State/UserRelatedState"
|
||||
import type { Feature } from "geojson"
|
||||
|
||||
const t = Translations.t.privacy
|
||||
export let state: SpecialVisualizationState
|
||||
const usersettings = UserRelatedState.usersettingsConfig
|
||||
const editPrivacy = usersettings.tagRenderings.find((tr) => tr.id === "more_privacy")
|
||||
const editThemeHistory = usersettings.tagRenderings.find((tr) => tr.id === "sync-visited-themes")
|
||||
const editReviews = usersettings.tagRenderings.find((tr) => tr.id === "mangrove-reviews-allowed")
|
||||
|
||||
const editLocationHistory = usersettings.tagRenderings.find((tr) => tr.id === "sync-visited-locations")
|
||||
|
||||
|
||||
const selectedElement: Feature = {
|
||||
type: "Feature",
|
||||
properties: { id: "settings" },
|
||||
geometry: { type: "Point", coordinates: [0, 0] }
|
||||
}
|
||||
const isLoggedIn = state.osmConnection.isLoggedIn
|
||||
</script>
|
||||
|
||||
|
@ -80,11 +87,7 @@
|
|||
<li>
|
||||
<TagRenderingEditable
|
||||
config={editLocationHistory}
|
||||
selectedElement={{
|
||||
type: "Feature",
|
||||
properties: { id: "settings" },
|
||||
geometry: { type: "Point", coordinates: [0, 0] },
|
||||
}}
|
||||
{selectedElement}
|
||||
{state}
|
||||
tags={state.userRelatedState.preferencesAsTags}
|
||||
/>
|
||||
|
@ -93,11 +96,7 @@
|
|||
|
||||
<TagRenderingEditable
|
||||
config={editThemeHistory}
|
||||
selectedElement={{
|
||||
type: "Feature",
|
||||
properties: { id: "settings" },
|
||||
geometry: { type: "Point", coordinates: [0, 0] },
|
||||
}}
|
||||
{selectedElement}
|
||||
{state}
|
||||
tags={state.userRelatedState.preferencesAsTags}
|
||||
/>
|
||||
|
@ -114,6 +113,14 @@
|
|||
</h3>
|
||||
<Tr t={t.miscCookies} />
|
||||
|
||||
<TagRenderingEditable
|
||||
config={editReviews}
|
||||
{selectedElement}
|
||||
{state}
|
||||
tags={state.userRelatedState.preferencesAsTags}
|
||||
/>
|
||||
|
||||
|
||||
<h3>
|
||||
<Tr t={t.whileYoureHere} />
|
||||
</h3>
|
||||
|
|
|
@ -5,20 +5,15 @@
|
|||
import StarsBar from "./StarsBar.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import type { Feature } from "geojson"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import Mangrove_logo from "../../assets/svg/Mangrove_logo.svelte"
|
||||
import ReviewPrivacyShield from "./ReviewPrivacyShield.svelte"
|
||||
import ThemeViewState from "../../Models/ThemeViewState"
|
||||
|
||||
/**
|
||||
* An element showing all reviews
|
||||
*/
|
||||
export let reviews: FeatureReviews
|
||||
export let state: SpecialVisualizationState
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
export let feature: Feature
|
||||
export let layer: LayerConfig
|
||||
export let state: ThemeViewState
|
||||
|
||||
let average = reviews.average
|
||||
let _reviews = []
|
||||
reviews.reviews.addCallbackAndRunD((r) => {
|
||||
|
@ -26,20 +21,22 @@
|
|||
})
|
||||
</script>
|
||||
|
||||
<div class="border-2 border-dashed border-gray-300 p-2">
|
||||
{#if _reviews.length > 1}
|
||||
<StarsBar score={$average} />
|
||||
{/if}
|
||||
{#if _reviews.length > 0}
|
||||
{#each _reviews as review}
|
||||
<SingleReview {review} />
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="subtle m-2 italic">
|
||||
<Tr t={Translations.t.reviews.no_reviews_yet} />
|
||||
<ReviewPrivacyShield {reviews} guistate={state.guistate}>
|
||||
<div class="border-2 border-dashed border-gray-300 p-2">
|
||||
{#if _reviews.length > 1}
|
||||
<StarsBar score={$average} />
|
||||
{/if}
|
||||
{#if _reviews.length > 0}
|
||||
{#each _reviews as review}
|
||||
<SingleReview {review} />
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="subtle m-2 italic">
|
||||
<Tr t={Translations.t.reviews.no_reviews_yet} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex justify-end">
|
||||
<Tr cls="text-sm subtle" t={Translations.t.reviews.attribution} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex justify-end">
|
||||
<Tr cls="text-sm subtle" t={Translations.t.reviews.attribution} />
|
||||
</div>
|
||||
</div>
|
||||
</ReviewPrivacyShield>
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import FeatureReviews from "../../Logic/Web/MangroveReviews"
|
||||
import StarsBar from "./StarsBar.svelte"
|
||||
import SpecialTranslation from "../Popup/TagRendering/SpecialTranslation.svelte"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import type { Feature } from "geojson"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
|
@ -12,11 +11,12 @@
|
|||
import If from "../Base/If.svelte"
|
||||
import Loading from "../Base/Loading.svelte"
|
||||
import { Review } from "mangrove-reviews-typescript"
|
||||
import { Utils } from "../../Utils"
|
||||
import { placeholder } from "../../Utils/placeholder"
|
||||
import { ExclamationTriangle } from "@babeard/svelte-heroicons/solid/ExclamationTriangle"
|
||||
import ReviewPrivacyShield from "./ReviewPrivacyShield.svelte"
|
||||
import ThemeViewState from "../../Models/ThemeViewState"
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
export let state: ThemeViewState
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
export let feature: Feature
|
||||
export let layer: LayerConfig
|
||||
|
@ -60,7 +60,7 @@
|
|||
const review: Omit<Review, "sub"> = {
|
||||
rating: confirmedScore,
|
||||
opinion: opinion.data,
|
||||
metadata: { nickname, is_affiliated: isAffiliated.data },
|
||||
metadata: { nickname, is_affiliated: isAffiliated.data }
|
||||
}
|
||||
try {
|
||||
await reviews.createReview(review)
|
||||
|
@ -72,84 +72,87 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
{#if uploadFailed}
|
||||
<div class="alert flex">
|
||||
<ExclamationTriangle class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.error} />
|
||||
{uploadFailed}
|
||||
</div>
|
||||
{:else if _state === "done"}
|
||||
<Tr cls="thanks w-full" t={t.saved} />
|
||||
{:else if _state === "saving"}
|
||||
<Loading>
|
||||
<Tr t={t.saving_review} />
|
||||
</Loading>
|
||||
{:else}
|
||||
<div class="interactive border-interactive p-1">
|
||||
<div class="font-bold">
|
||||
{#if question}
|
||||
{question}
|
||||
{:else}
|
||||
<SpecialTranslation {feature} {layer} {state} t={Translations.t.reviews.question} {tags} />
|
||||
{/if}
|
||||
<ReviewPrivacyShield hiddenIfNotAllowed {reviews} guistate={state.guistate}>
|
||||
|
||||
{#if uploadFailed}
|
||||
<div class="alert flex">
|
||||
<ExclamationTriangle class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.error} />
|
||||
{uploadFailed}
|
||||
</div>
|
||||
<StarsBar
|
||||
on:click={(e) => {
|
||||
{:else if _state === "done"}
|
||||
<Tr cls="thanks w-full" t={t.saved} />
|
||||
{:else if _state === "saving"}
|
||||
<Loading>
|
||||
<Tr t={t.saving_review} />
|
||||
</Loading>
|
||||
{:else}
|
||||
<div class="interactive border-interactive p-1">
|
||||
<div class="font-bold">
|
||||
{#if question}
|
||||
{question}
|
||||
{:else}
|
||||
<SpecialTranslation {feature} {layer} {state} t={Translations.t.reviews.question} {tags} />
|
||||
{/if}
|
||||
</div>
|
||||
<StarsBar
|
||||
on:click={(e) => {
|
||||
confirmedScore = e.detail.score
|
||||
score = confirmedScore
|
||||
console.log("Confirmed score is:", confirmedScore)
|
||||
}}
|
||||
on:hover={(e) => {
|
||||
on:hover={(e) => {
|
||||
score = e.detail.score
|
||||
}}
|
||||
on:mouseout={() => {
|
||||
on:mouseout={() => {
|
||||
score = null
|
||||
}}
|
||||
score={score ?? confirmedScore ?? 0}
|
||||
starSize="w-8 h-8"
|
||||
/>
|
||||
score={score ?? confirmedScore ?? 0}
|
||||
starSize="w-8 h-8"
|
||||
/>
|
||||
|
||||
{#if confirmedScore !== undefined}
|
||||
<label class="neutral-label">
|
||||
<Tr cls="font-bold mt-2" t={t.question_opinion} />
|
||||
<textarea
|
||||
autofocus
|
||||
bind:value={$opinion}
|
||||
inputmode="text"
|
||||
rows="3"
|
||||
class="mb-1 w-full"
|
||||
use:placeholder={t.reviewPlaceholder}
|
||||
/>
|
||||
{#if $hasError === "too_long"}
|
||||
<div class="alert flex items-center px-2">
|
||||
<ExclamationTriangle class="h-12 w-12" />
|
||||
<Tr
|
||||
t={t.too_long.Subs({
|
||||
{#if confirmedScore !== undefined}
|
||||
<label class="neutral-label">
|
||||
<Tr cls="font-bold mt-2" t={t.question_opinion} />
|
||||
<textarea
|
||||
autofocus
|
||||
bind:value={$opinion}
|
||||
inputmode="text"
|
||||
rows="3"
|
||||
class="mb-1 w-full"
|
||||
use:placeholder={t.reviewPlaceholder}
|
||||
/>
|
||||
{#if $hasError === "too_long"}
|
||||
<div class="alert flex items-center px-2">
|
||||
<ExclamationTriangle class="h-12 w-12" />
|
||||
<Tr
|
||||
t={t.too_long.Subs({
|
||||
max: FeatureReviews.REVIEW_OPINION_MAX_LENGTH,
|
||||
amount: $opinion?.length ?? 0,
|
||||
})}
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<Checkbox selected={isAffiliated}>
|
||||
<div class="flex flex-col">
|
||||
<Tr t={t.i_am_affiliated} />
|
||||
<Tr cls="subtle" t={t.i_am_affiliated_explanation} />
|
||||
</div>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<Checkbox selected={isAffiliated}>
|
||||
<div class="flex flex-col">
|
||||
<Tr t={t.i_am_affiliated} />
|
||||
<Tr cls="subtle" t={t.i_am_affiliated_explanation} />
|
||||
</Checkbox>
|
||||
<div class="flex w-full flex-wrap items-center justify-between">
|
||||
<If condition={state.osmConnection.isLoggedIn}>
|
||||
<Tr t={t.reviewing_as.Subs({ nickname: state.osmConnection.userDetails.data.name })} />
|
||||
<Tr slot="else" t={t.reviewing_as_anonymous} />
|
||||
</If>
|
||||
<button class="primary" class:disabled={$hasError !== undefined} on:click={save}>
|
||||
<Tr t={t.save} />
|
||||
</button>
|
||||
</div>
|
||||
</Checkbox>
|
||||
<div class="flex w-full flex-wrap items-center justify-between">
|
||||
<If condition={state.osmConnection.isLoggedIn}>
|
||||
<Tr t={t.reviewing_as.Subs({ nickname: state.osmConnection.userDetails.data.name })} />
|
||||
<Tr slot="else" t={t.reviewing_as_anonymous} />
|
||||
</If>
|
||||
<button class="primary" class:disabled={$hasError !== undefined} on:click={save}>
|
||||
<Tr t={t.save} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Tr cls="subtle mt-4" t={t.tos} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<Tr cls="subtle mt-4" t={t.tos} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</ReviewPrivacyShield>
|
||||
|
|
27
src/UI/Reviews/ReviewPrivacyShield.svelte
Normal file
27
src/UI/Reviews/ReviewPrivacyShield.svelte
Normal file
|
@ -0,0 +1,27 @@
|
|||
<script lang="ts">/**
|
||||
* Due to privacy, we cannot load reviews unless allowed in the privacy policy
|
||||
*/
|
||||
import FeatureReviews from "../../Logic/Web/MangroveReviews"
|
||||
import { MenuState } from "../../Models/MenuState"
|
||||
|
||||
export let guistate: MenuState
|
||||
export let reviews: FeatureReviews
|
||||
export let hiddenIfNotAllowed: boolean = false
|
||||
let allowed = reviews.loadingAllowed
|
||||
</script>
|
||||
|
||||
{#if $allowed}
|
||||
<slot />
|
||||
{:else if !hiddenIfNotAllowed && $allowed !== null }
|
||||
<div class="low-interaction flex flex-col rounded mx-1">
|
||||
|
||||
Reviews are disabled due to your privacy settings.
|
||||
<button on:click={() => reviews.loadingAllowed.set(true)} class="primary">
|
||||
Load reviews once
|
||||
</button>
|
||||
<button class="as-link self-end" on:click={() => guistate.openUsersettings("mangrove-reviews-allowed")}>
|
||||
Edit your privacy settings
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -47,7 +47,7 @@ export class ReviewSpecialVisualisations {
|
|||
nameKey: nameKey,
|
||||
fallbackName,
|
||||
},
|
||||
state.featureSwitchIsTesting
|
||||
state
|
||||
)
|
||||
return new SvelteUIElement(ReviewForm, {
|
||||
reviews,
|
||||
|
@ -76,7 +76,7 @@ export class ReviewSpecialVisualisations {
|
|||
doc: "The identifier to use, if <i>tags[subjectKey]</i> as specified above is not available. This is effectively a fallback value",
|
||||
},
|
||||
],
|
||||
constr: (state, tags, args, feature, layer) => {
|
||||
constr: (state, tags, args, feature) => {
|
||||
const nameKey = args[0] ?? "name"
|
||||
const fallbackName = args[1]
|
||||
const reviews = FeatureReviews.construct(
|
||||
|
@ -87,9 +87,9 @@ export class ReviewSpecialVisualisations {
|
|||
nameKey: nameKey,
|
||||
fallbackName,
|
||||
},
|
||||
state.featureSwitchIsTesting
|
||||
state
|
||||
)
|
||||
return new SvelteUIElement(AllReviews, { reviews, state, tags, feature, layer })
|
||||
return new SvelteUIElement(AllReviews, { reviews, state })
|
||||
},
|
||||
}
|
||||
return [
|
||||
|
@ -120,7 +120,7 @@ export class ReviewSpecialVisualisations {
|
|||
nameKey: nameKey,
|
||||
fallbackName,
|
||||
},
|
||||
state.featureSwitchIsTesting
|
||||
state
|
||||
)
|
||||
return new SvelteUIElement(StarsBarIcon, {
|
||||
score: reviews.average,
|
||||
|
@ -181,7 +181,6 @@ export class ReviewSpecialVisualisations {
|
|||
): BaseUIElement {
|
||||
return new Combine([
|
||||
createReview.constr(state, tagSource, args, feature, layer),
|
||||
|
||||
listReviews.constr(state, tagSource, args, feature, layer),
|
||||
])
|
||||
},
|
||||
|
|
Loading…
Add table
Reference in a new issue