UI(Reviews): move 'loading allowed' to FeatureReviews

This commit is contained in:
Pieter Vander Vennet 2025-07-25 15:39:59 +02:00
parent ce3a049d9d
commit d662bc2707
10 changed files with 105 additions and 63 deletions

View file

@ -810,9 +810,12 @@
"deleteConfirm": "Permantently delete this review", "deleteConfirm": "Permantently delete this review",
"deleteText": "This cannot be undone", "deleteText": "This cannot be undone",
"deleteTitle": "Delete this review?", "deleteTitle": "Delete this review?",
"disabledForPrivacy": "Reviews are disabled due to your privacy settings.",
"edit": "Edit review", "edit": "Edit review",
"editPrivacySettings": "Edit your privacy settings",
"i_am_affiliated": "I am affiliated with this object", "i_am_affiliated": "I am affiliated with this object",
"i_am_affiliated_explanation": "Check if you are an owner, creator, employee, …", "i_am_affiliated_explanation": "Check if you are an owner, creator, employee, …",
"loadOnce": "Load reviews once",
"no_reviews_yet": "There are no reviews yet. Be the first one!", "no_reviews_yet": "There are no reviews yet. Be the first one!",
"non_place_review": "One review is not about a place and is not shown here.", "non_place_review": "One review is not about a place and is not shown here.",
"non_place_reviews": "{n} reviews are not about a place and are not shown here.", "non_place_reviews": "{n} reviews are not about a place and are not shown here.",

View file

@ -369,7 +369,7 @@ export default class UserRelatedState {
private static initUserSettingsState(): LayerConfig { private static initUserSettingsState(): LayerConfig {
try { try {
return new LayerConfig(<LayerConfigJson>usersettings, "userinformationpanel") return new LayerConfig(<LayerConfigJson><any>usersettings, "userinformationpanel")
} catch (e) { } catch (e) {
return undefined return undefined
} }
@ -655,7 +655,7 @@ export default class UserRelatedState {
for (const key in featureSwitches) { for (const key in featureSwitches) {
if (featureSwitches[key].addCallbackAndRun) { if (featureSwitches[key].addCallbackAndRun) {
featureSwitches[key].addCallbackAndRun((v) => { featureSwitches[key].addCallbackAndRun((v: string) => {
const oldV = amendedPrefs.data["__" + key] const oldV = amendedPrefs.data["__" + key]
if (oldV === v) { if (oldV === v) {
return return

View file

@ -4,6 +4,17 @@ import { Utils } from "../../Utils"
import { Feature, Position } from "geojson" import { Feature, Position } from "geojson"
import { GeoOperations } from "../GeoOperations" import { GeoOperations } from "../GeoOperations"
import { SpecialVisualizationState } from "../../UI/SpecialVisualization" import { SpecialVisualizationState } from "../../UI/SpecialVisualization"
import { WithUserRelatedState } from "../../Models/ThemeViewState/WithUserRelatedState"
export interface ReviewCollection {
readonly subjectUri?: Store<string>
readonly loadingAllowed: UIEventSource<boolean>
removeReviewLocally(review: Review): void
deleteReview(review: Review & { signature: string }): Promise<void>
createReview(review: Omit<Review, "sub">): Promise<void>
}
export class MangroveIdentity { export class MangroveIdentity {
private readonly keypair: UIEventSource<CryptoKeyPair> = new UIEventSource<CryptoKeyPair>( private readonly keypair: UIEventSource<CryptoKeyPair> = new UIEventSource<CryptoKeyPair>(
@ -134,12 +145,13 @@ export class MangroveIdentity {
} }
} }
/** /**
* Tracks all reviews of a given feature, allows to create a new review (and inserts this into the list) * Tracks all reviews of a given feature, allows to create a new review (and inserts this into the list)
* *
* This object will start fetching the reviews as soon as it is constructed * This object will start fetching the reviews as soon as it is constructed
*/ */
export default class FeatureReviews { export default class FeatureReviews implements ReviewCollection {
/** /**
* See https://gitlab.com/open-reviews/mangrove/-/blob/master/servers/reviewer/src/review.rs#L269 and https://source.mapcomplete.org/MapComplete/MapComplete/issues/1775 * See https://gitlab.com/open-reviews/mangrove/-/blob/master/servers/reviewer/src/review.rs#L269 and https://source.mapcomplete.org/MapComplete/MapComplete/issues/1775
*/ */
@ -147,10 +159,11 @@ export default class FeatureReviews {
private static readonly _featureReviewsCache: Record<string, FeatureReviews> = {} private static readonly _featureReviewsCache: Record<string, FeatureReviews> = {}
public readonly subjectUri: Store<string> public readonly subjectUri: Store<string>
public readonly average: Store<number | null> public readonly average: Store<number | null>
public readonly loadingAllowed: UIEventSource<boolean>
private readonly _reviews: UIEventSource< private readonly _reviews: UIEventSource<
(Review & { kid: string; signature: string; madeByLoggedInUser: Store<boolean> })[] (Review & { kid: string; signature: string; madeByLoggedInUser: Store<boolean> })[]
> = new UIEventSource(undefined) > = new UIEventSource(undefined)
public readonly reviews: Store<(Review & { signature: string, madeByLoggedInUser: Store<boolean> })[]> = public readonly reviews: Store<(Review & { kid: string, signature: string, madeByLoggedInUser: Store<boolean> })[]> =
this._reviews this._reviews
private readonly _lat: number private readonly _lat: number
private readonly _lon: number private readonly _lon: number
@ -158,23 +171,21 @@ export default class FeatureReviews {
private readonly _name: Store<string> private readonly _name: Store<string>
private readonly _identity: MangroveIdentity private readonly _identity: MangroveIdentity
private readonly _testmode: Store<boolean> private readonly _testmode: Store<boolean>
public readonly loadingAllowed: UIEventSource<boolean | null>
private readonly _reportError: (msg: string, extra: string) => Promise<void> private readonly _reportError: (msg: string, extra: string) => Promise<void>
private constructor( private constructor(
feature: Feature, feature: Feature,
tagsSource: UIEventSource<Record<string, string>>, tagsSource: UIEventSource<Record<string, string>>,
mangroveIdentity: MangroveIdentity, mangroveIdentity: MangroveIdentity,
loadingAllowed?: UIEventSource<boolean>,
options?: Readonly<{ options?: Readonly<{
nameKey?: "name" | string nameKey?: "name" | string
fallbackName?: string fallbackName?: string
uncertaintyRadius?: number uncertaintyRadius?: number,
}>, }>,
testmode?: Store<boolean>, testmode?: Store<boolean>,
loadingAllowed?: UIEventSource<boolean | null>,
reportError?: (msg: string, extra: string) => Promise<void> reportError?: (msg: string, extra: string) => Promise<void>
) { ) {
this.loadingAllowed = loadingAllowed
this._reportError = reportError this._reportError = reportError
const centerLonLat = GeoOperations.centerpointCoordinates(feature) const centerLonLat = GeoOperations.centerpointCoordinates(feature)
;[this._lon, this._lat] = centerLonLat ;[this._lon, this._lat] = centerLonLat
@ -221,14 +232,17 @@ export default class FeatureReviews {
}) })
this.subjectUri = this.ConstructSubjectUri() this.subjectUri = this.ConstructSubjectUri()
this.loadingAllowed = loadingAllowed
this.subjectUri.mapD( this.subjectUri.mapD(
async (sub) => { async (sub) => {
if (!loadingAllowed.data) {
return
}
const reviews = await MangroveReviews.getReviews({ sub }) const reviews = await MangroveReviews.getReviews({ sub })
console.debug("Got reviews for", feature, reviews, sub) console.debug("Got reviews for", feature, reviews, sub)
this.addReviews(reviews.reviews, this._name.data) this.addReviews(reviews.reviews, this._name.data)
}, },
[this._name] [this._name, loadingAllowed]
) )
/* We also construct all subject queries _without_ encoding the name to work around a previous bug /* We also construct all subject queries _without_ encoding the name to work around a previous bug
* See https://github.com/giggls/opencampsitemap/issues/30 * See https://github.com/giggls/opencampsitemap/issues/30
@ -267,6 +281,7 @@ export default class FeatureReviews {
}) })
} }
/** /**
* Construct a featureReviewsFor or fetches it from the cache * Construct a featureReviewsFor or fetches it from the cache
* *
@ -288,7 +303,7 @@ export default class FeatureReviews {
tagsSource: UIEventSource<Record<string, string>>, tagsSource: UIEventSource<Record<string, string>>,
mangroveIdentity: MangroveIdentity, mangroveIdentity: MangroveIdentity,
options: { nameKey: string; fallbackName: string }, options: { nameKey: string; fallbackName: string },
state?: SpecialVisualizationState state?: SpecialVisualizationState & WithUserRelatedState,
): FeatureReviews { ): FeatureReviews {
const key = const key =
feature.properties.id + feature.properties.id +
@ -300,34 +315,14 @@ export default class FeatureReviews {
if (cached !== undefined) { if (cached !== undefined) {
return cached return cached
} }
const themeIsSensitive = state?.theme?.enableMorePrivacy ?? false
const settings =
state?.osmConnection?.getPreference<"always" | "yes" | "ask" | "hidden">(
"reviews-allowed"
) ?? new ImmutableStore("yes")
const loadingAllowed = new UIEventSource(false)
settings.addCallbackAndRun((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( const featureReviews = new FeatureReviews(
feature, feature,
tagsSource, tagsSource,
mangroveIdentity, mangroveIdentity,
new UIEventSource(state?.loadReviews?.data ?? true),
options, options,
state?.featureSwitchIsTesting, state?.featureSwitchIsTesting,
loadingAllowed,
(msg, extra) => state?.reportError(msg, extra) (msg, extra) => state?.reportError(msg, extra)
) )
FeatureReviews._featureReviewsCache[key] = featureReviews FeatureReviews._featureReviewsCache[key] = featureReviews
@ -458,7 +453,7 @@ export default class FeatureReviews {
this.removeReviewLocally(review) this.removeReviewLocally(review)
} }
public removeReviewLocally(review: Review){ public removeReviewLocally(review: Review): void {
this._reviews.set( this._reviews.set(
this._reviews.data?.filter(r => r !== review) this._reviews.data?.filter(r => r !== review)
) )

View file

@ -4,7 +4,7 @@ import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import UserRelatedState from "../../Logic/State/UserRelatedState" import UserRelatedState from "../../Logic/State/UserRelatedState"
import { QueryParameters } from "../../Logic/Web/QueryParameters" import { QueryParameters } from "../../Logic/Web/QueryParameters"
import FeatureSwitchState from "../../Logic/State/FeatureSwitchState" import FeatureSwitchState from "../../Logic/State/FeatureSwitchState"
import { Store, UIEventSource } from "../../Logic/UIEventSource" import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
import LayerConfig from "../ThemeConfig/LayerConfig" import LayerConfig from "../ThemeConfig/LayerConfig"
import { LastClickFeatureSource } from "../../Logic/FeatureSource/Sources/LastClickFeatureSource" import { LastClickFeatureSource } from "../../Logic/FeatureSource/Sources/LastClickFeatureSource"
import { GeocodingUtils } from "../../Logic/Search/GeocodingProvider" import { GeocodingUtils } from "../../Logic/Search/GeocodingProvider"
@ -18,6 +18,9 @@ export class WithUserRelatedState {
readonly osmConnection: OsmConnection readonly osmConnection: OsmConnection
readonly userRelatedState: UserRelatedState readonly userRelatedState: UserRelatedState
readonly loadReviews: Store<boolean>
readonly overlayLayerStates: ReadonlyMap< readonly overlayLayerStates: ReadonlyMap<
string, string,
{ readonly isDisplayed: UIEventSource<boolean> } { readonly isDisplayed: UIEventSource<boolean> }
@ -62,6 +65,7 @@ export class WithUserRelatedState {
this.featureSwitches, this.featureSwitches,
rasterLayer rasterLayer
) )
this.loadReviews = this.constructIsLoadingReviewsAllowed()
if (!this.theme.official) { if (!this.theme.official) {
// Add custom themes to the "visited custom themes" // Add custom themes to the "visited custom themes"
@ -113,4 +117,31 @@ export class WithUserRelatedState {
} }
return this.theme.getMatchingLayer(properties) return this.theme.getMatchingLayer(properties)
} }
private constructIsLoadingReviewsAllowed() : UIEventSource<boolean>{
const loadingAllowed = new UIEventSource(false)
const themeIsSensitive = this.theme?.enableMorePrivacy ?? false
const settings =
this?.osmConnection?.getPreference<"always" | "yes" | "ask" | "hidden">(
"reviews-allowed",
) ?? new ImmutableStore("yes")
settings.addCallbackAndRun((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)
})
return loadingAllowed
}
} }

View file

@ -23,6 +23,7 @@
let average = reviews.average let average = reviews.average
let allReviews: Store< let allReviews: Store<
(Review & { (Review & {
kid: string,
signature: string, signature: string,
madeByLoggedInUser: Store<boolean> madeByLoggedInUser: Store<boolean>
})[] })[]
@ -32,7 +33,7 @@
let subject: Store<string> = reviews.subjectUri let subject: Store<string> = reviews.subjectUri
</script> </script>
<ReviewPrivacyShield {reviews} guistate={state.guistate}> <ReviewPrivacyShield loadingAllowed={reviews.loadingAllowed} guistate={state.guistate}>
<div class="border-2 border-dashed border-gray-300 p-2 flex flex-col gap-y-2"> <div class="border-2 border-dashed border-gray-300 p-2 flex flex-col gap-y-2">
{#if $allReviews?.length > 1} {#if $allReviews?.length > 1}
<StarsBar score={$average} /> <StarsBar score={$average} />
@ -43,7 +44,7 @@
</div> </div>
{:else if $allReviews.length > 0} {:else if $allReviews.length > 0}
{#each $allReviews as review} {#each $allReviews as review}
<SingleReview {review} {state} {tags} {feature} {layer} {reviews}/> <SingleReview {review} {state} {tags} {feature} {layer} {reviews} showMenu={false}/>
{/each} {/each}
{:else} {:else}
<div class="subtle m-2 italic flex justify-center"> <div class="subtle m-2 italic flex justify-center">

View file

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import FeatureReviews from "../../Logic/Web/MangroveReviews" import FeatureReviews from "../../Logic/Web/MangroveReviews"
import type { ReviewCollection } from "../../Logic/Web/MangroveReviews"
import StarsBar from "./StarsBar.svelte" import StarsBar from "./StarsBar.svelte"
import SpecialTranslation from "../Popup/TagRendering/SpecialTranslation.svelte" import SpecialTranslation from "../Popup/TagRendering/SpecialTranslation.svelte"
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource" import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
@ -10,7 +11,7 @@
import Tr from "../Base/Tr.svelte" import Tr from "../Base/Tr.svelte"
import If from "../Base/If.svelte" import If from "../Base/If.svelte"
import Loading from "../Base/Loading.svelte" import Loading from "../Base/Loading.svelte"
import { MangroveReviews, Review } from "mangrove-reviews-typescript" import { Review } from "mangrove-reviews-typescript"
import { placeholder } from "../../Utils/placeholder" import { placeholder } from "../../Utils/placeholder"
import { ExclamationTriangle } from "@babeard/svelte-heroicons/solid/ExclamationTriangle" import { ExclamationTriangle } from "@babeard/svelte-heroicons/solid/ExclamationTriangle"
import ReviewPrivacyShield from "./ReviewPrivacyShield.svelte" import ReviewPrivacyShield from "./ReviewPrivacyShield.svelte"
@ -25,9 +26,9 @@
* The form to create a new review. * The form to create a new review.
* This is multi-stepped. * This is multi-stepped.
*/ */
export let reviews: FeatureReviews export let reviews: ReviewCollection
export let editReview: Review & { signature: string } = undefined export let editReview: Review & { signature: string } = undefined
let subject: Store<string> = editReview !== undefined ? new ImmutableStore(editReview.sub) : reviews.subjectUri export let subject: Store<string> = editReview !== undefined ? new ImmutableStore(editReview.sub) : reviews.subjectUri
let score = editReview?.rating ?? 0 let score = editReview?.rating ?? 0
let confirmedScore = editReview?.rating let confirmedScore = editReview?.rating
let isAffiliated = new UIEventSource(editReview?.metadata?.is_affiliated ?? false) let isAffiliated = new UIEventSource(editReview?.metadata?.is_affiliated ?? false)
@ -49,7 +50,11 @@
let uploadFailed: string = undefined let uploadFailed: string = undefined
let isTesting = state?.featureSwitchIsTesting let isTesting = state?.featureSwitchIsTesting
let debug = state.featureSwitches.featureSwitchIsDebugging
/**
* only for this feature, copy upstream value to start with
*/
async function save() { async function save() {
if (hasError.data) { if (hasError.data) {
return return
@ -98,11 +103,9 @@
_state = "done" _state = "done"
} }
let test = state.featureSwitches.featureSwitchIsTesting
let debug = state.featureSwitches.featureSwitchIsDebugging
</script> </script>
<ReviewPrivacyShield hiddenIfNotAllowed {reviews} guistate={state.guistate}> <ReviewPrivacyShield hiddenIfNotAllowed loadingAllowed={reviews.loadingAllowed} guistate={state.guistate}>
{#if uploadFailed} {#if uploadFailed}
<div class="alert flex"> <div class="alert flex">
<ExclamationTriangle class="h-6 w-6" /> <ExclamationTriangle class="h-6 w-6" />
@ -188,7 +191,7 @@
<Tr cls="subtle mt-4" t={t.tos} /> <Tr cls="subtle mt-4" t={t.tos} />
{/if} {/if}
{#if $debug || $test} {#if $debug || $isTesting}
<span class="subtle self-end">{$subject}</span> <span class="subtle self-end">{$subject}</span>
{/if} {/if}
</div> </div>

View file

@ -2,29 +2,31 @@
/** /**
* Due to privacy, we cannot load reviews unless allowed in the privacy policy * Due to privacy, we cannot load reviews unless allowed in the privacy policy
*/ */
import FeatureReviews from "../../Logic/Web/MangroveReviews"
import { MenuState } from "../../Models/MenuState" import { MenuState } from "../../Models/MenuState"
import { ImmutableStore } from "../../Logic/UIEventSource" import { UIEventSource } from "../../Logic/UIEventSource"
import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte"
export let guistate: MenuState export let guistate: MenuState
export let reviews: FeatureReviews export let loadingAllowed : UIEventSource<boolean>
export let hiddenIfNotAllowed: boolean = false export let hiddenIfNotAllowed: boolean = false
let allowed = reviews?.loadingAllowed ?? new ImmutableStore(true) let t = Translations.t.reviews
</script> </script>
{#if $allowed} {#if $loadingAllowed}
<slot /> <slot />
{:else if !hiddenIfNotAllowed && $allowed !== null} {:else if !hiddenIfNotAllowed && $loadingAllowed !== null}
<div class="low-interaction mx-1 flex flex-col rounded"> <div class="low-interaction mx-1 flex flex-col rounded">
Reviews are disabled due to your privacy settings. <Tr t={t.disabledForPrivacy}/>
<button on:click={() => reviews.loadingAllowed.set(true)} class="primary"> <button on:click={() => {loadingAllowed.set(true)}} class="primary">
Load reviews once <Tr t={t.loadOnce}/>
</button> </button>
<button <button
class="as-link self-end" class="as-link self-end"
on:click={() => guistate.openUsersettings("mangrove-reviews-allowed")} on:click={() => guistate.openUsersettings("mangrove-reviews-allowed")}
> >
Edit your privacy settings <Tr t={t.editPrivacySettings}/>
</button> </button>
</div> </div>
{/if} {/if}

View file

@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import type { SpecialVisualizationState } from "../SpecialVisualization"
import Translations from "../i18n/Translations" import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte" import Tr from "../Base/Tr.svelte"
import LoginToggle from "../Base/LoginToggle.svelte" import LoginToggle from "../Base/LoginToggle.svelte"
@ -7,11 +6,14 @@
import SingleReview from "./SingleReview.svelte" import SingleReview from "./SingleReview.svelte"
import Mangrove_logo from "../../assets/svg/Mangrove_logo.svelte" import Mangrove_logo from "../../assets/svg/Mangrove_logo.svelte"
import Loading from "../Base/Loading.svelte" import Loading from "../Base/Loading.svelte"
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import { MangroveIdentity } from "../../Logic/Web/MangroveReviews"
import ThemeViewState from "../../Models/ThemeViewState"
/** /**
* A panel showing all the reviews by the logged-in user * A panel showing all the reviews by the logged-in user
*/ */
export let state: SpecialVisualizationState export let state: ThemeViewState & {osmConnection: OsmConnection, userRelatedState: { mangroveIdentity: MangroveIdentity }}
let allReviews = state.userRelatedState.mangroveIdentity.getAllReviews() let allReviews = state.userRelatedState.mangroveIdentity.getAllReviews()
let reviews = state.userRelatedState.mangroveIdentity.getGeoReviews() let reviews = state.userRelatedState.mangroveIdentity.getGeoReviews()
let kid = state.userRelatedState.mangroveIdentity.getKeyId() let kid = state.userRelatedState.mangroveIdentity.getKeyId()

View file

@ -17,17 +17,24 @@
import ReviewForm from "./ReviewForm.svelte" import ReviewForm from "./ReviewForm.svelte"
import type { Feature } from "geojson" import type { Feature } from "geojson"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import FeatureReviews, { MangroveIdentity } from "../../Logic/Web/MangroveReviews" import { MangroveIdentity } from "../../Logic/Web/MangroveReviews"
import type { ReviewCollection } from "../../Logic/Web/MangroveReviews"
import ShieldExclamation from "@babeard/svelte-heroicons/solid/ShieldExclamation" import ShieldExclamation from "@babeard/svelte-heroicons/solid/ShieldExclamation"
import Delete_icon from "../../assets/svg/Delete_icon.svelte" import Delete_icon from "../../assets/svg/Delete_icon.svelte"
import ValidatedInput from "../InputElement/ValidatedInput.svelte" import ValidatedInput from "../InputElement/ValidatedInput.svelte"
import type { MapProperties } from "../../Models/MapProperties"
export let state: ThemeViewState = undefined export let state: ThemeViewState & {
mapProperties?: MapProperties,
userRelatedState?: { mangroveIdentity: MangroveIdentity }
} = undefined
export let tags: UIEventSource<Record<string, string>> = undefined export let tags: UIEventSource<Record<string, string>> = undefined
export let feature: Feature = undefined export let feature: Feature = undefined
export let layer: LayerConfig = undefined export let layer: LayerConfig = undefined
export let reviews: FeatureReviews export let reviews: ReviewCollection
export let showMenu: boolean
export let review: Review & { export let review: Review & {
kid: string kid: string
@ -212,7 +219,7 @@
{date} {date}
</a> </a>
</div> </div>
{#if review.signature} {#if review.signature && showMenu !== undefined}
<div class="self-start"> <div class="self-start">
<DotMenu> <DotMenu>
{#if $byLoggedInUser} {#if $byLoggedInUser}
@ -232,7 +239,7 @@
{/if} {/if}
</DotMenu> </DotMenu>
</div> </div>
{/if} {/if}
</div> </div>
{#if review.opinion} {#if review.opinion}

View file

@ -40,7 +40,6 @@
import DrawerLeft from "./Base/DrawerLeft.svelte" import DrawerLeft from "./Base/DrawerLeft.svelte"
import DrawerRight from "./Base/DrawerRight.svelte" import DrawerRight from "./Base/DrawerRight.svelte"
import SearchResults from "./Search/SearchResults.svelte" import SearchResults from "./Search/SearchResults.svelte"
import Hash from "../Logic/Web/Hash"
import Searchbar from "./Base/Searchbar.svelte" import Searchbar from "./Base/Searchbar.svelte"
import ChevronRight from "@babeard/svelte-heroicons/mini/ChevronRight" import ChevronRight from "@babeard/svelte-heroicons/mini/ChevronRight"
import { Drawer } from "flowbite-svelte" import { Drawer } from "flowbite-svelte"
@ -67,7 +66,6 @@
let selectedElement: UIEventSource<Feature> = new UIEventSource<Feature>(undefined) let selectedElement: UIEventSource<Feature> = new UIEventSource<Feature>(undefined)
let compass = Orientation.singleton.alpha let compass = Orientation.singleton.alpha
let compassLoaded = Orientation.singleton.gotMeasurement let compassLoaded = Orientation.singleton.gotMeasurement
let hash = Hash.hash
let addNewFeatureMode = state.userRelatedState.addNewFeatureMode let addNewFeatureMode = state.userRelatedState.addNewFeatureMode
let gpsAvailable = state.geolocation.geolocationState.gpsAvailable let gpsAvailable = state.geolocation.geolocationState.gpsAvailable
let gpsButtonAriaLabel = state.geolocation.geolocationState.gpsStateExplanation let gpsButtonAriaLabel = state.geolocation.geolocationState.gpsStateExplanation