forked from MapComplete/MapComplete
Merge develop
This commit is contained in:
commit
553ee6d5aa
165 changed files with 6745 additions and 1720 deletions
|
@ -5,23 +5,11 @@ import { Utils } from "../../Utils"
|
|||
import { Feature } from "geojson"
|
||||
|
||||
export default class PendingChangesUploader {
|
||||
private lastChange: Date
|
||||
|
||||
constructor(changes: Changes, selectedFeature: UIEventSource<Feature>) {
|
||||
const self = this
|
||||
this.lastChange = new Date()
|
||||
changes.pendingChanges.addCallback(() => {
|
||||
self.lastChange = new Date()
|
||||
changes.pendingChanges.stabilized(Constants.updateTimeoutSec * 1000).addCallback(() => changes.flushChanges("Flushing changes due to timeout"))
|
||||
|
||||
window.setTimeout(() => {
|
||||
const diff = (new Date().getTime() - self.lastChange.getTime()) / 1000
|
||||
if (Constants.updateTimeoutSec >= diff - 1) {
|
||||
changes.flushChanges("Flushing changes due to timeout")
|
||||
}
|
||||
}, Constants.updateTimeoutSec * 1000)
|
||||
})
|
||||
|
||||
selectedFeature.stabilized(10000).addCallback((feature) => {
|
||||
selectedFeature.stabilized(1000).addCallback((feature) => {
|
||||
if (feature === undefined) {
|
||||
// The popup got closed - we flush
|
||||
changes.flushChanges("Flushing changes due to popup closed")
|
||||
|
|
|
@ -97,7 +97,7 @@ export class ImageUploadManager {
|
|||
console.log("Upload done, creating ")
|
||||
const action = await this.uploadImageWithLicense(featureId, title, description, file)
|
||||
if (!isNaN(Number(featureId))) {
|
||||
// THis is a map note
|
||||
// This is a map note
|
||||
const url = action._url
|
||||
await this._osmConnection.addCommentToNote(featureId, url)
|
||||
NoteCommentElement.addCommentTo(url, <UIEventSource<any>>tagsStore, {
|
||||
|
@ -151,9 +151,13 @@ export class ImageUploadManager {
|
|||
}
|
||||
|
||||
private increaseCountFor(collection: Map<string, UIEventSource<number>>, key: string | "*") {
|
||||
const counter = this.getCounterFor(collection, key)
|
||||
counter.setData(counter.data + 1)
|
||||
const global = this.getCounterFor(collection, "*")
|
||||
global.setData(counter.data + 1)
|
||||
{
|
||||
const counter = this.getCounterFor(collection, key)
|
||||
counter.setData(counter.data + 1)
|
||||
}
|
||||
{
|
||||
const global = this.getCounterFor(collection, "*")
|
||||
global.setData(global.data + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ export class Changes {
|
|||
public readonly extraComment: UIEventSource<string> = new UIEventSource(undefined)
|
||||
public readonly backend: string
|
||||
public readonly isUploading = new UIEventSource(false)
|
||||
public readonly errors = new UIEventSource<string[]>([], "upload-errors")
|
||||
private readonly historicalUserLocations?: FeatureSource
|
||||
private _nextId: number = -1 // Newly assigned ID's are negative
|
||||
private readonly previouslyCreated: OsmObject[] = []
|
||||
|
@ -128,8 +129,11 @@ export class Changes {
|
|||
const csNumber = await this.flushChangesAsync()
|
||||
this.isUploading.setData(false)
|
||||
console.log("Changes flushed. Your changeset is " + csNumber)
|
||||
this.errors.setData([])
|
||||
} catch (e) {
|
||||
this.isUploading.setData(false)
|
||||
this.errors.data.push(e)
|
||||
this.errors.ping()
|
||||
console.error("Flushing changes failed due to", e)
|
||||
}
|
||||
}
|
||||
|
@ -415,6 +419,8 @@ export class Changes {
|
|||
id,
|
||||
" dropping it from the changes (" + e + ")"
|
||||
)
|
||||
this.errors.data.push(e)
|
||||
this.errors.ping()
|
||||
return undefined
|
||||
}
|
||||
})
|
||||
|
@ -572,9 +578,15 @@ export class Changes {
|
|||
openChangeset.data
|
||||
)
|
||||
|
||||
return await self.flushSelectChanges(pendingChanges, openChangeset)
|
||||
const result = await self.flushSelectChanges(pendingChanges, openChangeset)
|
||||
if(result){
|
||||
this.errors.setData([])
|
||||
}
|
||||
return result
|
||||
} catch (e) {
|
||||
console.error("Could not upload some changes:", e)
|
||||
this.errors.data.push(e)
|
||||
this.errors.ping()
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
@ -589,6 +601,8 @@ export class Changes {
|
|||
"Could not handle changes - probably an old, pending changeset in localstorage with an invalid format; erasing those",
|
||||
e
|
||||
)
|
||||
this.errors.data.push(e)
|
||||
this.errors.ping()
|
||||
self.pendingChanges.setData([])
|
||||
} finally {
|
||||
self.isUploading.setData(false)
|
||||
|
|
|
@ -72,7 +72,7 @@ export class OsmPreferences {
|
|||
let i = 0
|
||||
while (str !== "") {
|
||||
if (str === undefined || str === "undefined") {
|
||||
throw "Long pref became undefined?"
|
||||
throw "Got 'undefined' or a literal string containing 'undefined' for a long preference with name "+key
|
||||
}
|
||||
if (i > 100) {
|
||||
throw "This long preference is getting very long... "
|
||||
|
|
|
@ -119,6 +119,12 @@ export class GeoLocationState {
|
|||
return
|
||||
}
|
||||
|
||||
if(navigator.permissions === undefined && navigator.geolocation !== undefined){
|
||||
// This is probably safari - we just start watching right away
|
||||
this.startWatching()
|
||||
return
|
||||
}
|
||||
|
||||
this.permission.setData("requested")
|
||||
try {
|
||||
const status = await navigator?.permissions?.query({ name: "geolocation" })
|
||||
|
|
|
@ -39,6 +39,7 @@ export default class UserRelatedState {
|
|||
public readonly installedUserThemes: Store<string[]>
|
||||
public readonly showAllQuestionsAtOnce: UIEventSource<boolean>
|
||||
public readonly showTags: UIEventSource<"no" | undefined | "always" | "yes" | "full">
|
||||
public readonly showCrosshair: UIEventSource<"yes" | undefined>
|
||||
public readonly fixateNorth: UIEventSource<undefined | "yes">
|
||||
public readonly homeLocation: FeatureSource
|
||||
public readonly language: UIEventSource<string>
|
||||
|
@ -102,6 +103,7 @@ export default class UserRelatedState {
|
|||
)
|
||||
this.language = this.osmConnection.GetPreference("language")
|
||||
this.showTags = <UIEventSource<any>>this.osmConnection.GetPreference("show_tags")
|
||||
this.showCrosshair = <UIEventSource<any>>this.osmConnection.GetPreference("show_crosshair")
|
||||
this.fixateNorth = <UIEventSource<"yes">>this.osmConnection.GetPreference("fixate-north")
|
||||
this.mangroveIdentity = new MangroveIdentity(
|
||||
this.osmConnection.GetLongPreference("identity", "mangrove")
|
||||
|
|
|
@ -6,7 +6,7 @@ import { AuthConfig } from "../Logic/Osm/AuthConfig"
|
|||
export type PriviligedLayerType = (typeof Constants.priviliged_layers)[number]
|
||||
|
||||
export default class Constants {
|
||||
public static vNumber = packagefile.version
|
||||
public static vNumber : string = packagefile.version
|
||||
/**
|
||||
* API key for Maproulette
|
||||
*
|
||||
|
@ -63,7 +63,7 @@ export default class Constants {
|
|||
* Used by 'PendingChangesUploader', which waits this amount of seconds to upload changes.
|
||||
* (Note that pendingChanges might upload sooner if the popup is closed or similar)
|
||||
*/
|
||||
static updateTimeoutSec: number = 30
|
||||
static updateTimeoutSec: number = 15
|
||||
/**
|
||||
* If the contributor has their GPS location enabled and makes a change,
|
||||
* the points visited less then `nearbyVisitTime`-seconds ago will be inspected.
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</script>
|
||||
|
||||
<div
|
||||
class="absolute top-0 right-0 h-screen w-full overflow-auto drop-shadow-2xl md:w-6/12 lg:w-5/12 xl:w-4/12"
|
||||
class="absolute top-0 right-0 h-screen w-full overflow-y-auto drop-shadow-2xl md:w-6/12 lg:w-5/12 xl:w-4/12"
|
||||
style="max-width: 100vw; max-height: 100vh"
|
||||
>
|
||||
<div class="normal-background m-0 flex flex-col">
|
||||
|
|
|
@ -29,16 +29,25 @@
|
|||
* The start coordinate
|
||||
*/
|
||||
export let coordinate: { lon: number; lat: number }
|
||||
export let snapToLayers: string[] | undefined
|
||||
export let targetLayer: LayerConfig
|
||||
export let maxSnapDistance: number = undefined
|
||||
|
||||
export let snappedTo: UIEventSource<string | undefined>
|
||||
|
||||
/**
|
||||
* The center of the map at all times
|
||||
* If undefined at the beginning, 'coordinate' will be used
|
||||
*/
|
||||
export let value: UIEventSource<{ lon: number; lat: number }>
|
||||
if (value.data === undefined) {
|
||||
value.setData(coordinate)
|
||||
}
|
||||
if(coordinate === undefined){
|
||||
coordinate = value.data
|
||||
}
|
||||
export let snapToLayers: string[] | undefined
|
||||
export let targetLayer: LayerConfig | undefined
|
||||
export let maxSnapDistance: number = undefined
|
||||
|
||||
export let snappedTo: UIEventSource<string | undefined>
|
||||
|
||||
|
||||
|
||||
let preciseLocation: UIEventSource<{ lon: number; lat: number }> = new UIEventSource<{
|
||||
lon: number
|
||||
|
@ -66,12 +75,14 @@
|
|||
rasterLayer: UIEventSource.feedFrom(state.mapProperties.rasterLayer),
|
||||
}
|
||||
|
||||
const featuresForLayer = state.perLayer.get(targetLayer.id)
|
||||
if (featuresForLayer) {
|
||||
new ShowDataLayer(map, {
|
||||
layer: targetLayer,
|
||||
features: featuresForLayer,
|
||||
})
|
||||
if(targetLayer){
|
||||
const featuresForLayer = state.perLayer.get(targetLayer.id)
|
||||
if (featuresForLayer) {
|
||||
new ShowDataLayer(map, {
|
||||
layer: targetLayer,
|
||||
features: featuresForLayer,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (snapToLayers?.length > 0) {
|
||||
|
@ -114,4 +125,8 @@
|
|||
value={preciseLocation}
|
||||
initialCoordinate={coordinate}
|
||||
maxDistanceInMeters="50"
|
||||
/>
|
||||
>
|
||||
<slot name="image" slot="image">
|
||||
<img class="h-full max-h-24" src="./assets/svg/move-arrows.svg" />
|
||||
</slot>
|
||||
</LocationInput>
|
||||
|
|
32
src/UI/BigComponents/PendingChangesIndicator.svelte
Normal file
32
src/UI/BigComponents/PendingChangesIndicator.svelte
Normal file
|
@ -0,0 +1,32 @@
|
|||
<script lang="ts">
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import { Changes } from "../../Logic/Osm/Changes"
|
||||
import Loading from "../Base/Loading.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
|
||||
const changes: Changes = state.changes
|
||||
const isUploading: Store<boolean> = changes.isUploading
|
||||
const pendingChangesCount: Store<number> = changes.pendingChanges.map(ls => ls.length)
|
||||
const errors = changes.errors
|
||||
</script>
|
||||
|
||||
|
||||
<div class="flex flex-col pointer-events-auto" on:click={() => changes.flushChanges("Pending changes indicator clicked")}>
|
||||
{#if $isUploading}
|
||||
<Loading>
|
||||
<Tr cls="thx" t={Translations.t.general.uploadingChanges} />
|
||||
</Loading>
|
||||
{:else if $pendingChangesCount === 1}
|
||||
<Tr cls="alert" t={Translations.t.general.uploadPendingSingle} />
|
||||
{:else if $pendingChangesCount > 1}
|
||||
<Tr cls="alert" t={Translations.t.general.uploadPending.Subs({count: $pendingChangesCount})} />
|
||||
{/if}
|
||||
|
||||
{#each $errors as error}
|
||||
<Tr cls="alert" t={Translations.t.general.uploadError.Subs({error})} />
|
||||
{/each}
|
||||
</div>
|
|
@ -8,13 +8,26 @@
|
|||
import Tr from "../Base/Tr.svelte"
|
||||
import SubtleLink from "../Base/SubtleLink.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource"
|
||||
|
||||
export let theme: LayoutInformation
|
||||
export let isCustom: boolean = false
|
||||
export let userDetails: UIEventSource<UserDetails>
|
||||
export let state: { layoutToUse?: { id: string }; osmConnection: OsmConnection }
|
||||
export let selected: boolean = false
|
||||
|
||||
let unlockedPersonal = LocalStorageSource.GetParsed("unlocked_personal_theme", false)
|
||||
|
||||
userDetails.addCallbackAndRunD(userDetails => {
|
||||
if(!userDetails.loggedIn){
|
||||
return
|
||||
}
|
||||
if(userDetails.csCount > Constants.userJourney.personalLayoutUnlock){
|
||||
unlockedPersonal.setData(true)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
$: title = new Translation(
|
||||
theme.title,
|
||||
!isCustom && !theme.mustHaveLanguage ? "themes:" + theme.id + ".title" : undefined
|
||||
|
@ -72,7 +85,7 @@
|
|||
let href = createUrl(theme, isCustom, state)
|
||||
</script>
|
||||
|
||||
{#if theme.id !== personal.id || $userDetails.csCount > Constants.userJourney.personalLayoutUnlock}
|
||||
{#if theme.id !== personal.id || $unlockedPersonal}
|
||||
<SubtleLink href={$href} options={{ extraClasses: "w-full" }}>
|
||||
<img slot="image" src={theme.icon} class="mx-4 block h-11 w-11" alt="" />
|
||||
<span class="flex flex-col overflow-hidden text-ellipsis">
|
||||
|
|
|
@ -1,33 +1,38 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* Shows information about how much images are uploaded for the given feature
|
||||
*/
|
||||
/**
|
||||
* Shows information about how much images are uploaded for the given feature
|
||||
*
|
||||
* Either pass in a store with tags or a featureId.
|
||||
*/
|
||||
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import type { OsmTags } from "../../Models/OsmFeature"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Loading from "../Base/Loading.svelte"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import type { OsmTags } from "../../Models/OsmFeature"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Loading from "../Base/Loading.svelte"
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
export let tags: Store<OsmTags>
|
||||
const featureId = tags.data.id
|
||||
const { uploadStarted, uploadFinished, retried, failed } =
|
||||
state.imageUploadManager.getCountsFor(featureId)
|
||||
const t = Translations.t.image
|
||||
export let state: SpecialVisualizationState
|
||||
export let tags: Store<OsmTags>
|
||||
export let featureId = tags.data.id
|
||||
export let showThankYou: boolean = true
|
||||
const { uploadStarted, uploadFinished, retried, failed } =
|
||||
state.imageUploadManager.getCountsFor(featureId)
|
||||
const t = Translations.t.image
|
||||
</script>
|
||||
|
||||
{#if $uploadStarted == 1}
|
||||
{#if $uploadFinished == 1}
|
||||
<Tr cls="thanks" t={t.upload.one.done} />
|
||||
{:else if $failed == 1}
|
||||
{#if $uploadStarted === 1}
|
||||
{#if $uploadFinished === 1}
|
||||
{#if showThankYou}
|
||||
<Tr cls="thanks" t={t.upload.one.done} />
|
||||
{/if}
|
||||
{:else if $failed === 1}
|
||||
<div class="alert flex flex-col">
|
||||
<Tr cls="self-center" t={t.upload.one.failed} />
|
||||
<Tr t={t.upload.failReasons} />
|
||||
<Tr t={t.upload.failReasonsAdvanced} />
|
||||
</div>
|
||||
{:else if $retried == 1}
|
||||
{:else if $retried === 1}
|
||||
<Loading cls="alert">
|
||||
<Tr t={t.upload.one.retrying} />
|
||||
</Loading>
|
||||
|
@ -37,9 +42,11 @@
|
|||
</Loading>
|
||||
{/if}
|
||||
{:else if $uploadStarted > 1}
|
||||
{#if $uploadFinished + $failed == $uploadStarted && $uploadFinished > 0}
|
||||
<Tr cls="thanks" t={t.upload.multiple.done.Subs({ count: $uploadFinished })} />
|
||||
{:else if $uploadFinished == 0}
|
||||
{#if $uploadFinished + $failed === $uploadStarted && $uploadFinished > 0}
|
||||
{#if showThankYou}
|
||||
<Tr cls="thanks" t={t.upload.multiple.done.Subs({ count: $uploadFinished })} />
|
||||
{/if}
|
||||
{:else if $uploadFinished === 0}
|
||||
<Loading cls="alert">
|
||||
<Tr t={t.upload.multiple.uploading.Subs({ count: $uploadStarted })} />
|
||||
</Loading>
|
||||
|
|
|
@ -89,7 +89,9 @@
|
|||
<div
|
||||
class="pointer-events-none absolute top-0 left-0 flex h-full w-full items-center p-8 opacity-50"
|
||||
>
|
||||
<img class="h-full max-h-24" src="./assets/svg/move-arrows.svg" />
|
||||
<slot name="image">
|
||||
<img class="h-full max-h-24" src="./assets/svg/move-arrows.svg" />
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<DragInvitation hideSignal={mla.location} />
|
||||
|
|
11
src/UI/InputElement/Helpers/OpeningHoursInput.svelte
Normal file
11
src/UI/InputElement/Helpers/OpeningHoursInput.svelte
Normal file
|
@ -0,0 +1,11 @@
|
|||
<script lang="ts">/**
|
||||
* Opens the 'Opening hours input' in another top level window
|
||||
*/
|
||||
import { UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import ToSvelte from "../../Base/ToSvelte.svelte"
|
||||
import OpeningHoursInput from "../../OpeningHours/OpeningHoursInput"
|
||||
|
||||
export let value: UIEventSource<string>
|
||||
</script>
|
||||
|
||||
<ToSvelte construct={new OpeningHoursInput(value)}></ToSvelte>
|
|
@ -3,13 +3,17 @@ import { UIEventSource } from "../../Logic/UIEventSource"
|
|||
|
||||
import { MapProperties } from "../../Models/MapProperties"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import OpeningHoursInput from "../OpeningHours/OpeningHoursInput"
|
||||
import WikidataSearchBox from "../Wikipedia/WikidataSearchBox"
|
||||
import Wikidata from "../../Logic/Web/Wikidata"
|
||||
import { Utils } from "../../Utils"
|
||||
import Locale from "../i18n/Locale"
|
||||
import { Feature } from "geojson"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
import OpeningHoursInput from "./Helpers/OpeningHoursInput.svelte"
|
||||
import SvelteUIElement from "../Base/SvelteUIElement"
|
||||
import DirectionInput from "./Helpers/DirectionInput.svelte"
|
||||
import DateInput from "./Helpers/DateInput.svelte"
|
||||
import ColorInput from "./Helpers/ColorInput.svelte"
|
||||
|
||||
export interface InputHelperProperties {
|
||||
/**
|
||||
|
@ -46,8 +50,14 @@ export default class InputHelpers {
|
|||
>
|
||||
>
|
||||
> = {
|
||||
// TODO: remake in svelte,move selection logic to 'inputHelper.svelte'
|
||||
opening_hours: (value) => new OpeningHoursInput(value),
|
||||
direction: (value, properties) =>
|
||||
new SvelteUIElement(DirectionInput, {
|
||||
value,
|
||||
mapProperties: InputHelpers.constructMapProperties(properties),
|
||||
}),
|
||||
date: (value) => new SvelteUIElement(DateInput, { value }),
|
||||
color: (value) => new SvelteUIElement(ColorInput, { value }),
|
||||
opening_hours: (value) => new SvelteUIElement(OpeningHoursInput, { value }),
|
||||
wikidata: InputHelpers.constructWikidataHelper,
|
||||
} as const
|
||||
|
||||
|
|
64
src/UI/Leaderboard.svelte
Normal file
64
src/UI/Leaderboard.svelte
Normal file
|
@ -0,0 +1,64 @@
|
|||
<script lang="ts">
|
||||
|
||||
import { Utils } from "../Utils"
|
||||
import { Store, UIEventSource } from "../Logic/UIEventSource"
|
||||
import Loading from "./Base/Loading.svelte"
|
||||
import { OsmConnection } from "../Logic/Osm/OsmConnection"
|
||||
|
||||
const osmConnection = new OsmConnection({
|
||||
attemptLogin: true
|
||||
})
|
||||
let loggedInContributor: Store<string> = osmConnection.userDetails.map(ud => ud.name)
|
||||
export let source = "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/picture-leaderboard.json"
|
||||
let data: Store<undefined | {
|
||||
leaderboard: {
|
||||
rank: number,
|
||||
name: string,
|
||||
account: string,
|
||||
nrOfImages: number
|
||||
}[],
|
||||
median: number,
|
||||
totalAuthors: number,
|
||||
byLicense: {
|
||||
license: string, total: number, authors: string[]
|
||||
},
|
||||
date: string
|
||||
}> = UIEventSource.FromPromise(Utils.downloadJsonCached(source))
|
||||
|
||||
</script>
|
||||
|
||||
<h1>Contributed images with MapComplete: leaderboard</h1>
|
||||
|
||||
{#if $data}
|
||||
<table>
|
||||
<tr>
|
||||
<th>Rank</th>
|
||||
<th>Contributor</th>
|
||||
<th>Number of images contributed</th>
|
||||
</tr>
|
||||
{#each $data.leaderboard as contributor}
|
||||
<tr>
|
||||
<td>
|
||||
{contributor.rank}
|
||||
</td>
|
||||
<td>
|
||||
{#if $loggedInContributor === contributor.name}
|
||||
<a class="thanks" href="{contributor.account}">{contributor.name}</a>
|
||||
{:else}
|
||||
<a href="{contributor.account}">{contributor.name}</a>
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
<b>{contributor.nrOfImages}</b> total images
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
Statistics generated on {$data.date}
|
||||
{:else}
|
||||
<Loading />
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
Logged in as {$loggedInContributor}
|
||||
</div>
|
|
@ -162,16 +162,16 @@
|
|||
<LoginButton osmConnection={state.osmConnection} slot="not-logged-in">
|
||||
<Tr slot="message" t={Translations.t.general.add.pleaseLogin} />
|
||||
</LoginButton>
|
||||
{#if $isLoading}
|
||||
<div class="alert">
|
||||
<Loading>
|
||||
<Tr t={Translations.t.general.add.stillLoading} />
|
||||
</Loading>
|
||||
</div>
|
||||
{:else if $zoom < Constants.minZoomLevelToAddNewPoint}
|
||||
{#if $zoom < Constants.minZoomLevelToAddNewPoint}
|
||||
<div class="alert">
|
||||
<Tr t={Translations.t.general.add.zoomInFurther} />
|
||||
</div>
|
||||
{:else if $isLoading}
|
||||
<div class="alert">
|
||||
<Loading>
|
||||
<Tr t={Translations.t.general.add.stillLoading} />
|
||||
</Loading>
|
||||
</div>
|
||||
{:else if selectedPreset === undefined}
|
||||
<!-- First, select the correct preset -->
|
||||
<PresetList
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
if (flayer.isDisplayed.data === false) {
|
||||
// The layer is not displayed...
|
||||
if (!state.featureSwitches.featureSwitchFilter.data) {
|
||||
console.log("Not showing presets for layer", flayer.layerDef.id, "as not displayed and featureSwitchFilter.data is set",state.featureSwitches.featureSwitchFilter.data)
|
||||
// ...and we cannot enable the layer control -> we skip, as these presets can never be shown anyway
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -2,47 +2,50 @@
|
|||
/**
|
||||
* UIcomponent to create a new note at the given location
|
||||
*/
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource"
|
||||
import ValidatedInput from "../InputElement/ValidatedInput.svelte"
|
||||
import SubtleButton from "../Base/SubtleButton.svelte"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Translations from "../i18n/Translations.js"
|
||||
import type { Feature, Point } from "geojson"
|
||||
import LoginToggle from "../Base/LoginToggle.svelte"
|
||||
import FilteredLayer from "../../Models/FilteredLayer"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import { UIEventSource } from "../../Logic/UIEventSource";
|
||||
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource";
|
||||
import ValidatedInput from "../InputElement/ValidatedInput.svelte";
|
||||
import SubtleButton from "../Base/SubtleButton.svelte";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import Translations from "../i18n/Translations.js";
|
||||
import type { Feature, Point } from "geojson";
|
||||
import LoginToggle from "../Base/LoginToggle.svelte";
|
||||
import FilteredLayer from "../../Models/FilteredLayer";
|
||||
import NewPointLocationInput from "../BigComponents/NewPointLocationInput.svelte";
|
||||
import ToSvelte from "../Base/ToSvelte.svelte";
|
||||
import Svg from "../../Svg";
|
||||
|
||||
export let coordinate: { lon: number; lat: number }
|
||||
export let state: SpecialVisualizationState
|
||||
export let coordinate: UIEventSource<{ lon: number; lat: number }>;
|
||||
export let state: SpecialVisualizationState;
|
||||
|
||||
let comment: UIEventSource<string> = LocalStorageSource.Get("note-text")
|
||||
let created = false
|
||||
let comment: UIEventSource<string> = LocalStorageSource.Get("note-text");
|
||||
let created = false;
|
||||
|
||||
let notelayer: FilteredLayer = state.layerState.filteredLayers.get("note")
|
||||
let notelayer: FilteredLayer = state.layerState.filteredLayers.get("note");
|
||||
|
||||
let hasFilter = notelayer?.hasFilter
|
||||
let isDisplayed = notelayer?.isDisplayed
|
||||
let hasFilter = notelayer?.hasFilter;
|
||||
let isDisplayed = notelayer?.isDisplayed;
|
||||
|
||||
function enableNoteLayer() {
|
||||
state.guistate.closeAll()
|
||||
isDisplayed.setData(true)
|
||||
state.guistate.closeAll();
|
||||
isDisplayed.setData(true);
|
||||
}
|
||||
|
||||
async function uploadNote() {
|
||||
let txt = comment.data
|
||||
let txt = comment.data;
|
||||
if (txt === undefined || txt === "") {
|
||||
return
|
||||
return;
|
||||
}
|
||||
const loc = coordinate
|
||||
txt += "\n\n #MapComplete #" + state?.layout?.id
|
||||
const id = await state?.osmConnection?.openNote(loc.lat, loc.lon, txt)
|
||||
console.log("Created a note, got id", id)
|
||||
const loc = coordinate.data;
|
||||
txt += "\n\n #MapComplete #" + state?.layout?.id;
|
||||
const id = await state?.osmConnection?.openNote(loc.lat, loc.lon, txt);
|
||||
console.log("Created a note, got id", id);
|
||||
const feature = <Feature<Point>>{
|
||||
type: "Feature",
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [loc.lon, loc.lat],
|
||||
coordinates: [loc.lon, loc.lat]
|
||||
},
|
||||
properties: {
|
||||
id: "" + id.id,
|
||||
|
@ -53,22 +56,22 @@
|
|||
text: txt,
|
||||
html: txt,
|
||||
user: state.osmConnection?.userDetails?.data?.name,
|
||||
uid: state.osmConnection?.userDetails?.data?.uid,
|
||||
},
|
||||
]),
|
||||
},
|
||||
}
|
||||
uid: state.osmConnection?.userDetails?.data?.uid
|
||||
}
|
||||
])
|
||||
}
|
||||
};
|
||||
// Normally, the 'Changes' will generate the new element. The 'notes' are an exception to this
|
||||
state.newFeatures.features.data.push(feature)
|
||||
state.newFeatures.features.ping()
|
||||
state.selectedElement?.setData(feature)
|
||||
state.newFeatures.features.data.push(feature);
|
||||
state.newFeatures.features.ping();
|
||||
state.selectedElement?.setData(feature);
|
||||
if (state.featureProperties.trackFeature) {
|
||||
state.featureProperties.trackFeature(feature)
|
||||
state.featureProperties.trackFeature(feature);
|
||||
}
|
||||
comment.setData("")
|
||||
created = true
|
||||
state.selectedElement.setData(feature)
|
||||
state.selectedLayer.setData(state.layerState.filteredLayers.get("note"))
|
||||
comment.setData("");
|
||||
created = true;
|
||||
state.selectedElement.setData(feature);
|
||||
state.selectedLayer.setData(state.layerState.filteredLayers.get("note"));
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -106,6 +109,15 @@
|
|||
<ValidatedInput type="text" value={comment} />
|
||||
</div>
|
||||
|
||||
<div class="w-full h-56">
|
||||
<NewPointLocationInput value={coordinate} {state} >
|
||||
<div class="h-20 w-full pb-10" slot="image">
|
||||
<ToSvelte construct={Svg.note_svg().SetClass("h-10 w-full")}/>
|
||||
</div>
|
||||
</NewPointLocationInput>
|
||||
</div>
|
||||
|
||||
|
||||
<LoginToggle {state}>
|
||||
<span slot="loading"><!--empty: don't show a loading message--></span>
|
||||
<div slot="not-logged-in" class="alert">
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import { Translation } from "../i18n/Translation"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import Translations from "../i18n/Translations"
|
||||
|
||||
/**
|
||||
* A 'TagHint' will show the given tags in a human readable form.
|
||||
|
@ -25,7 +26,7 @@
|
|||
{#if !userDetails || $userDetails.loggedIn}
|
||||
<div>
|
||||
{#if tags === undefined}
|
||||
<slot name="no-tags">No tags</slot>
|
||||
<slot name="no-tags"><Tr cls="subtle" t={Translations.t.general.noTagsSelected}></Tr></slot>
|
||||
{:else if embedIn === undefined}
|
||||
<FromHtml src={tagsExplanation} />
|
||||
{:else}
|
||||
|
|
|
@ -178,8 +178,10 @@
|
|||
</script>
|
||||
|
||||
{#if config.question !== undefined}
|
||||
<div class="interactive border-interactive flex flex-col p-1 px-2">
|
||||
<div class="flex justify-between">
|
||||
<div class="interactive border-interactive flex flex-col p-1 px-2 relative overflow-y-auto" style="max-height: 85vh">
|
||||
<div class="sticky top-0" style="z-index: 11">
|
||||
|
||||
<div class="flex justify-between sticky top-0 interactive">
|
||||
<span class="font-bold">
|
||||
<SpecialTranslation t={config.question} {tags} {state} {layer} feature={selectedElement} />
|
||||
</span>
|
||||
|
@ -197,9 +199,10 @@
|
|||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if config.mappings?.length >= 8}
|
||||
<div class="flex w-full">
|
||||
<div class="flex w-full sticky">
|
||||
<img src="./assets/svg/search.svg" class="h-6 w-6" />
|
||||
<input type="text" bind:value={$searchTerm} class="w-full" />
|
||||
</div>
|
||||
|
@ -314,7 +317,7 @@
|
|||
<Tr t={$feedback} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex flex-wrap-reverse items-stretch justify-end sm:flex-nowrap">
|
||||
<div class="flex flex-wrap-reverse items-stretch justify-end sm:flex-nowrap sticky bottom-0 interactive" style="z-index: 11">
|
||||
<!-- TagRenderingQuestion-buttons -->
|
||||
<slot name="cancel" />
|
||||
<slot name="save-button" {selectedTags}>
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
<Tr t={Translations.t.reviews.no_reviews_yet} />
|
||||
{/if}
|
||||
<div class="flex justify-end">
|
||||
<ToSvelte construct={Svg.mangrove_logo_svg().SetClass("w-12 h-12")} />
|
||||
<Tr t={Translations.t.reviews.attribution} />
|
||||
<ToSvelte construct={Svg.mangrove_logo_svg().SetClass("w-12 h-12 shrink-0 p-1 ")} />
|
||||
<Tr cls="text-sm subtle" t={Translations.t.reviews.attribution} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -563,7 +563,10 @@ export default class SpecialVisualizations {
|
|||
feature: Feature
|
||||
): BaseUIElement {
|
||||
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
|
||||
return new SvelteUIElement(CreateNewNote, { state, coordinate: { lon, lat } })
|
||||
return new SvelteUIElement(CreateNewNote, {
|
||||
state,
|
||||
coordinate: new UIEventSource({ lon, lat }),
|
||||
})
|
||||
},
|
||||
},
|
||||
new CloseNoteButton(),
|
||||
|
|
|
@ -1,114 +1,115 @@
|
|||
<script lang="ts">
|
||||
import { Store, UIEventSource } from "../Logic/UIEventSource";
|
||||
import { Map as MlMap } from "maplibre-gl";
|
||||
import MaplibreMap from "./Map/MaplibreMap.svelte";
|
||||
import FeatureSwitchState from "../Logic/State/FeatureSwitchState";
|
||||
import MapControlButton from "./Base/MapControlButton.svelte";
|
||||
import ToSvelte from "./Base/ToSvelte.svelte";
|
||||
import If from "./Base/If.svelte";
|
||||
import { GeolocationControl } from "./BigComponents/GeolocationControl";
|
||||
import type { Feature } from "geojson";
|
||||
import SelectedElementView from "./BigComponents/SelectedElementView.svelte";
|
||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
|
||||
import Filterview from "./BigComponents/Filterview.svelte";
|
||||
import ThemeViewState from "../Models/ThemeViewState";
|
||||
import type { MapProperties } from "../Models/MapProperties";
|
||||
import Geosearch from "./BigComponents/Geosearch.svelte";
|
||||
import Translations from "./i18n/Translations";
|
||||
import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||
import { Store, UIEventSource } from "../Logic/UIEventSource"
|
||||
import { Map as MlMap } from "maplibre-gl"
|
||||
import MaplibreMap from "./Map/MaplibreMap.svelte"
|
||||
import FeatureSwitchState from "../Logic/State/FeatureSwitchState"
|
||||
import MapControlButton from "./Base/MapControlButton.svelte"
|
||||
import ToSvelte from "./Base/ToSvelte.svelte"
|
||||
import If from "./Base/If.svelte"
|
||||
import { GeolocationControl } from "./BigComponents/GeolocationControl"
|
||||
import type { Feature } from "geojson"
|
||||
import SelectedElementView from "./BigComponents/SelectedElementView.svelte"
|
||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
|
||||
import Filterview from "./BigComponents/Filterview.svelte"
|
||||
import ThemeViewState from "../Models/ThemeViewState"
|
||||
import type { MapProperties } from "../Models/MapProperties"
|
||||
import Geosearch from "./BigComponents/Geosearch.svelte"
|
||||
import Translations from "./i18n/Translations"
|
||||
import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import Tr from "./Base/Tr.svelte"
|
||||
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
|
||||
import FloatOver from "./Base/FloatOver.svelte"
|
||||
import PrivacyPolicy from "./BigComponents/PrivacyPolicy"
|
||||
import Constants from "../Models/Constants"
|
||||
import TabbedGroup from "./Base/TabbedGroup.svelte"
|
||||
import UserRelatedState from "../Logic/State/UserRelatedState"
|
||||
import LoginToggle from "./Base/LoginToggle.svelte"
|
||||
import LoginButton from "./Base/LoginButton.svelte"
|
||||
import CopyrightPanel from "./BigComponents/CopyrightPanel"
|
||||
import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte"
|
||||
import ModalRight from "./Base/ModalRight.svelte"
|
||||
import { Utils } from "../Utils"
|
||||
import Hotkeys from "./Base/Hotkeys"
|
||||
import { VariableUiElement } from "./Base/VariableUIElement"
|
||||
import SvelteUIElement from "./Base/SvelteUIElement"
|
||||
import OverlayToggle from "./BigComponents/OverlayToggle.svelte"
|
||||
import LevelSelector from "./BigComponents/LevelSelector.svelte"
|
||||
import ExtraLinkButton from "./BigComponents/ExtraLinkButton"
|
||||
import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte"
|
||||
import Svg from "../Svg"
|
||||
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte"
|
||||
import type { RasterLayerPolygon } from "../Models/RasterLayers"
|
||||
import { AvailableRasterLayers } from "../Models/RasterLayers"
|
||||
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte"
|
||||
import IfHidden from "./Base/IfHidden.svelte"
|
||||
import { onDestroy } from "svelte"
|
||||
import { OpenJosm } from "./BigComponents/OpenJosm"
|
||||
import MapillaryLink from "./BigComponents/MapillaryLink.svelte"
|
||||
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"
|
||||
import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte"
|
||||
import StateIndicator from "./BigComponents/StateIndicator.svelte"
|
||||
import LanguagePicker from "./LanguagePicker"
|
||||
import Locale from "./i18n/Locale"
|
||||
import ShareScreen from "./BigComponents/ShareScreen.svelte"
|
||||
import UploadingImageCounter from "./Image/UploadingImageCounter.svelte"
|
||||
import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte"
|
||||
|
||||
import Tr from "./Base/Tr.svelte";
|
||||
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte";
|
||||
import FloatOver from "./Base/FloatOver.svelte";
|
||||
import PrivacyPolicy from "./BigComponents/PrivacyPolicy";
|
||||
import Constants from "../Models/Constants";
|
||||
import TabbedGroup from "./Base/TabbedGroup.svelte";
|
||||
import UserRelatedState from "../Logic/State/UserRelatedState";
|
||||
import LoginToggle from "./Base/LoginToggle.svelte";
|
||||
import LoginButton from "./Base/LoginButton.svelte";
|
||||
import CopyrightPanel from "./BigComponents/CopyrightPanel";
|
||||
import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte";
|
||||
import ModalRight from "./Base/ModalRight.svelte";
|
||||
import { Utils } from "../Utils";
|
||||
import Hotkeys from "./Base/Hotkeys";
|
||||
import { VariableUiElement } from "./Base/VariableUIElement";
|
||||
import SvelteUIElement from "./Base/SvelteUIElement";
|
||||
import OverlayToggle from "./BigComponents/OverlayToggle.svelte";
|
||||
import LevelSelector from "./BigComponents/LevelSelector.svelte";
|
||||
import ExtraLinkButton from "./BigComponents/ExtraLinkButton";
|
||||
import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte";
|
||||
import Svg from "../Svg";
|
||||
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte";
|
||||
import type { RasterLayerPolygon } from "../Models/RasterLayers";
|
||||
import { AvailableRasterLayers } from "../Models/RasterLayers";
|
||||
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte";
|
||||
import IfHidden from "./Base/IfHidden.svelte";
|
||||
import { onDestroy } from "svelte";
|
||||
import { OpenJosm } from "./BigComponents/OpenJosm";
|
||||
import MapillaryLink from "./BigComponents/MapillaryLink.svelte";
|
||||
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte";
|
||||
import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte";
|
||||
import StateIndicator from "./BigComponents/StateIndicator.svelte";
|
||||
import LanguagePicker from "./LanguagePicker";
|
||||
import Locale from "./i18n/Locale";
|
||||
import ShareScreen from "./BigComponents/ShareScreen.svelte";
|
||||
export let state: ThemeViewState
|
||||
let layout = state.layout
|
||||
|
||||
export let state: ThemeViewState;
|
||||
let layout = state.layout;
|
||||
|
||||
let maplibremap: UIEventSource<MlMap> = state.map;
|
||||
let selectedElement: UIEventSource<Feature> = state.selectedElement;
|
||||
let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer;
|
||||
let maplibremap: UIEventSource<MlMap> = state.map
|
||||
let selectedElement: UIEventSource<Feature> = state.selectedElement
|
||||
let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer
|
||||
|
||||
const selectedElementView = selectedElement.map(
|
||||
(selectedElement) => {
|
||||
// Svelte doesn't properly reload some of the legacy UI-elements
|
||||
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
|
||||
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
|
||||
const layer = selectedLayer.data;
|
||||
const layer = selectedLayer.data
|
||||
if (selectedElement === undefined || layer === undefined) {
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (!(layer.tagRenderings?.length > 0) || layer.title === undefined) {
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
|
||||
const tags = state.featureProperties.getStore(selectedElement.properties.id);
|
||||
return new SvelteUIElement(SelectedElementView, { state, layer, selectedElement, tags });
|
||||
const tags = state.featureProperties.getStore(selectedElement.properties.id)
|
||||
return new SvelteUIElement(SelectedElementView, { state, layer, selectedElement, tags })
|
||||
},
|
||||
[selectedLayer]
|
||||
);
|
||||
[selectedLayer],
|
||||
)
|
||||
|
||||
const selectedElementTitle = selectedElement.map(
|
||||
(selectedElement) => {
|
||||
// Svelte doesn't properly reload some of the legacy UI-elements
|
||||
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
|
||||
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
|
||||
const layer = selectedLayer.data;
|
||||
const layer = selectedLayer.data
|
||||
if (selectedElement === undefined || layer === undefined) {
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
|
||||
const tags = state.featureProperties.getStore(selectedElement.properties.id);
|
||||
return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags });
|
||||
const tags = state.featureProperties.getStore(selectedElement.properties.id)
|
||||
return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags })
|
||||
},
|
||||
[selectedLayer]
|
||||
);
|
||||
[selectedLayer],
|
||||
)
|
||||
|
||||
let mapproperties: MapProperties = state.mapProperties;
|
||||
let featureSwitches: FeatureSwitchState = state.featureSwitches;
|
||||
let availableLayers = state.availableLayers;
|
||||
let userdetails = state.osmConnection.userDetails;
|
||||
let currentViewLayer = layout.layers.find((l) => l.id === "current_view");
|
||||
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer;
|
||||
let mapproperties: MapProperties = state.mapProperties
|
||||
let featureSwitches: FeatureSwitchState = state.featureSwitches
|
||||
let availableLayers = state.availableLayers
|
||||
let userdetails = state.osmConnection.userDetails
|
||||
let currentViewLayer = layout.layers.find((l) => l.id === "current_view")
|
||||
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
|
||||
let rasterLayerName =
|
||||
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name;
|
||||
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name
|
||||
onDestroy(
|
||||
rasterLayer.addCallbackAndRunD((l) => {
|
||||
rasterLayerName = l.properties.name;
|
||||
})
|
||||
);
|
||||
rasterLayerName = l.properties.name
|
||||
}),
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="absolute top-0 left-0 h-screen w-screen overflow-hidden">
|
||||
|
@ -154,6 +155,8 @@
|
|||
<ToSvelte
|
||||
construct={() => new ExtraLinkButton(state, layout.extraLink).SetClass("pointer-events-auto")}
|
||||
/>
|
||||
<UploadingImageCounter {state} featureId="*" showThankYou={false}/>
|
||||
<PendingChangesIndicator {state}/>
|
||||
<If condition={state.featureSwitchIsTesting}>
|
||||
<div class="alert w-fit">Testmode</div>
|
||||
</If>
|
||||
|
@ -233,6 +236,17 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<LoginToggle ignoreLoading={true} {state }>
|
||||
<If condition={state.userRelatedState.showCrosshair.map(s => s === "yes")}>
|
||||
<If condition={state.mapProperties.zoom.map(z => z >= 17)}>
|
||||
<div class="absolute top-0 left-0 flex items-center justify-center pointer-events-none w-full h-full">
|
||||
<ToSvelte construct={Svg.cross_svg()} />
|
||||
</div>
|
||||
</If>
|
||||
</If>
|
||||
</LoginToggle>
|
||||
|
||||
|
||||
<If
|
||||
condition={selectedElementView.map(
|
||||
(v) =>
|
||||
|
@ -257,6 +271,7 @@
|
|||
</ModalRight>
|
||||
</If>
|
||||
|
||||
|
||||
<If
|
||||
condition={selectedElementView.map(
|
||||
(v) =>
|
||||
|
@ -364,7 +379,8 @@
|
|||
<!-- Menu page -->
|
||||
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false) }>
|
||||
<span slot="close-button"><!-- Hide the default close button --></span>
|
||||
<TabbedGroup condition1={featureSwitches.featureSwitchEnableLogin} condition2={state.featureSwitches. featureSwitchCommunityIndex}
|
||||
<TabbedGroup condition1={featureSwitches.featureSwitchEnableLogin}
|
||||
condition2={state.featureSwitches. featureSwitchCommunityIndex}
|
||||
tab={state.guistate.menuViewTabIndex}>
|
||||
<div slot="post-tablist">
|
||||
<XCircleIcon
|
||||
|
@ -464,3 +480,5 @@
|
|||
</TabbedGroup>
|
||||
</FloatOver>
|
||||
</If>
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
{
|
||||
"layers": [
|
||||
{
|
||||
"name": "OpenStreetMap Carto",
|
||||
"url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
"category": "osmbasedmap",
|
||||
"id": "osm.carto",
|
||||
"type": "raster",
|
||||
"max_zoom": 19,
|
||||
"attribution": {
|
||||
"text": "OpenStreetMap",
|
||||
"url": "https://osm.org/copyright"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Americana",
|
||||
"url": "https://zelonewolf.github.io/openstreetmap-americana/style.json",
|
||||
|
|
|
@ -407,7 +407,6 @@ select:hover {
|
|||
.subtle {
|
||||
/* For all information that is not important for 99% of the users */
|
||||
color: #999;
|
||||
font-size: medium;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
|
|
4
src/leaderboard.ts
Normal file
4
src/leaderboard.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import SvelteUIElement from "./UI/Base/SvelteUIElement"
|
||||
import Leaderboard from "./UI/Leaderboard.svelte"
|
||||
|
||||
new SvelteUIElement(Leaderboard, {}).AttachTo("main")
|
Loading…
Add table
Add a link
Reference in a new issue