Refactoring: remove old, no longer needed classes

This commit is contained in:
Pieter Vander Vennet 2025-07-11 02:14:52 +02:00
parent b8de80bee3
commit 61f20d0092
14 changed files with 60 additions and 293 deletions

View file

@ -216,6 +216,7 @@
"panoramaxHelp": "<b>Panoramax</b> is an online service which gathers street-level pictures and offers them under a free license. Contributors are allowed to use these pictures to improve OpenStreetMap",
"panoramaxLicenseCCBYSA": "Your pictures are published under CC-BY-SA - everyone can reuse your image if they mention your name",
"seeOnMapillary": "See this image on Mapillary",
"themeBy": "Theme maintained by {author}",
"title": "Copyright and attribution",
"translatedBy": "MapComplete has been translated by {contributors} and <a href='https://source.mapcomplete.org/MapComplete/MapComplete/activity/contributors' target='_blank'>{hiddenCount} more contributors</a>"
@ -941,4 +942,4 @@
"startsWithQ": "A wikidata identifier starts with Q and is followed by a number"
}
}
}
}

View file

@ -1,10 +1,9 @@
import ImageProvider, { PanoramaView, ProvidedImage } from "./ImageProvider"
import BaseUIElement from "../../UI/BaseUIElement"
import { Utils } from "../../Utils"
import { LicenseInfo } from "./LicenseInfo"
import Constants from "../../Models/Constants"
import SvelteUIElement from "../../UI/Base/SvelteUIElement"
import MapillaryIcon from "./MapillaryIcon.svelte"
import { default as MapillaryIcon } from "./Mapillary.svelte"
import { Feature, Point } from "geojson"
import { Store, UIEventSource } from "../UIEventSource"
import { ServerSourceInfo } from "../../Models/SourceOverview"
@ -139,19 +138,8 @@ export class Mapillary extends ImageProvider {
}))
}
SourceIcon(
img: { id: string; url: string },
location?: {
lon: number
lat: number
}
): BaseUIElement {
let url: string = undefined
const id = img.id
if (id) {
url = Mapillary.createLink(location, 16, "" + id)
}
return new SvelteUIElement(MapillaryIcon, { url })
SourceIcon(): SvelteUIElement {
return new SvelteUIElement(MapillaryIcon)
}
async ExtractUrls(key: string, value: string): Promise<ProvidedImage[]> {

View file

@ -1,23 +0,0 @@
<script lang="ts">
import Mapillary from "../../assets/svg/Mapillary.svelte"
import { ariaLabel } from "../../Utils/ariaLabel"
import Translations from "../../UI/i18n/Translations"
/**
* Accessible, linked mapillary icon
*/
export let url: string = undefined
</script>
{#if url}
<a
href={url}
use:ariaLabel={Translations.t.general.attribution.seeOnMapillary}
target="_blank"
rel="noopener nofollower"
>
<Mapillary />
</a>
{:else}
<Mapillary />
{/if}

View file

@ -9,7 +9,6 @@ import Constants from "../../Models/Constants"
import { Store, Stores, UIEventSource } from "../UIEventSource"
import SvelteUIElement from "../../UI/Base/SvelteUIElement"
import Panoramax_bw from "../../assets/svg/Panoramax_bw.svelte"
import Link from "../../UI/Base/Link"
import { Feature, Point } from "geojson"
import { AddImageOptions } from "panoramax-js/dist/Panoramax"
import { ServerSourceInfo } from "../../Models/SourceOverview"
@ -40,23 +39,8 @@ export default class PanoramaxImageProvider extends ImageProvider {
{ data: Promise<{ data: ImageData; url: string }>; time: Date }
> = {}
public SourceIcon(
img?: { id: string; url: string },
location?: {
lon: number
lat: number
}
): BaseUIElement {
const host = "https://" + new URL(img.url).host
const p = new Panoramax(host)
return new Link(
new SvelteUIElement(Panoramax_bw),
p.createViewLink({
imageId: img?.id,
location,
}),
true
)
public SourceIcon(): SvelteUIElement {
return new SvelteUIElement(Panoramax_bw)
}
visitUrl(

View file

@ -3,7 +3,7 @@ import BaseUIElement from "../../UI/BaseUIElement"
import { WikimediaImageProvider } from "./WikimediaImageProvider"
import Wikidata from "../Web/Wikidata"
import SvelteUIElement from "../../UI/Base/SvelteUIElement"
import * as Wikidata_icon from "../../assets/svg/Wikidata.svelte"
import Wikidata_icon from "../../assets/svg/Wikidata.svelte"
import { Utils } from "../../Utils"
import { Feature, Point } from "geojson"
import { ServerSourceInfo } from "../../Models/SourceOverview"

View file

@ -12,7 +12,6 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
import usersettings from "../../../public/assets/generated/layers/usersettings.json"
import Locale from "../../UI/i18n/Locale"
import LinkToWeblate from "../../UI/Base/LinkToWeblate"
import FeatureSwitchState from "./FeatureSwitchState"
import Constants from "../../Models/Constants"
import { QueryParameters } from "../Web/QueryParameters"
@ -20,6 +19,7 @@ import { ThemeMetaTagging } from "./UserSettingsMetaTagging"
import Showdown from "showdown"
import { LocalStorageSource } from "../Web/LocalStorageSource"
import { GeocodeResult } from "../Search/GeocodingProvider"
import Translations from "../../UI/i18n/Translations"
class RoundRobinStore<T> {
private readonly _store: UIEventSource<T[]>
@ -562,7 +562,7 @@ export default class UserRelatedState {
hasMissingTheme
? {
id: "theme:" + layout.id,
link: LinkToWeblate.hrefToWeblateZen(
link: Translations.hrefToWeblateZen(
language,
"themes",
layout.id
@ -571,7 +571,7 @@ export default class UserRelatedState {
: undefined,
...missingLayers.map((id) => ({
id: "layer:" + id,
link: LinkToWeblate.hrefToWeblateZen(language, "layers", id),
link: Translations.hrefToWeblateZen(language, "layers", id),
})),
])
const untranslated_count = untranslated.length

View file

@ -1,82 +0,0 @@
import { Utils } from "../../Utils"
import BaseUIElement from "../BaseUIElement"
/**
* @deprecated
*/
export default class Img extends BaseUIElement {
private readonly _src: string
private readonly _rawSvg: boolean
private readonly _options: { readonly fallbackImage?: string }
/** @deprecated
*/
constructor(
src: string,
rawSvg = false,
options?: {
fallbackImage?: string
}
) {
super()
if (src === undefined || src === "undefined") {
throw "Undefined src for image"
}
this._src = src
this._rawSvg = rawSvg
this._options = options
}
static AsData(source: string) {
if (Utils.runningFromConsole) {
return source
}
try {
return `data:image/svg+xml;base64,${btoa(source)}`
} catch (e) {
console.error("Cannot create an image for", source.slice(0, 100))
console.trace("Cannot create an image for the given source string due to ", e)
return ""
}
}
static AsImageElement(source: string, css_class: string = "", style = ""): string {
return `<img class="${css_class}" style="${style}" alt="" src="${Img.AsData(source)}">`
}
AsMarkdown(): string {
if (this._rawSvg === true) {
console.warn("Converting raw svgs to markdown is not supported")
return undefined
}
let src = this._src
if (this._src.startsWith("./")) {
src = "https://mapcomplete.org/" + src
}
return "![](" + src + ")"
}
protected InnerConstructElement(): HTMLElement {
if (this._rawSvg) {
const e = document.createElement("div")
e.innerHTML = this._src
return e
}
const el = document.createElement("img")
el.src = this._src
el.onload = () => {
el.style.opacity = "1"
}
el.onerror = () => {
if (this._options?.fallbackImage) {
if (el.src === this._options.fallbackImage) {
// Sigh... nothing to be done anymore
return
}
el.src = this._options.fallbackImage
}
}
return el
}
}

View file

@ -1,15 +0,0 @@
import BaseUIElement from "../BaseUIElement"
export default class Lazy extends BaseUIElement {
private readonly _f: () => BaseUIElement
constructor(f: () => BaseUIElement) {
super()
this._f = f
}
protected InnerConstructElement(): HTMLElement {
// The caching of the BaseUIElement will guarantee that _f will only be called once
return this._f().ConstructElement()
}
}

View file

@ -1,82 +0,0 @@
import { VariableUiElement } from "./VariableUIElement"
import Locale from "../i18n/Locale"
import Link from "./Link"
import SvelteUIElement from "./SvelteUIElement"
import Translate from "../../assets/svg/Translate.svelte"
import Constants from "../../Models/Constants"
/**
* The little 'translate'-icon next to every icon + some static helper functions
*/
export default class LinkToWeblate extends VariableUiElement {
constructor(context: string, availableTranslations: object) {
super(
Locale.language.map(
(ln) => {
if (Locale.showLinkToWeblate.data === false) {
return undefined
}
if (availableTranslations["*"] !== undefined) {
return undefined
}
if (context === undefined || context.indexOf(":") < 0) {
return undefined
}
const icon = new SvelteUIElement(Translate).SetClass(
"rounded-full inline-block w-3 h-3 ml-1 weblate-link self-center"
)
if (availableTranslations[ln] === undefined) {
icon.SetClass("bg-red-400")
}
return new Link(icon, LinkToWeblate.hrefToWeblate(ln, context), true).SetClass(
"weblate-link"
)
},
[Locale.showLinkToWeblate]
)
)
this.SetClass("enable-links")
const self = this
Locale.showLinkOnMobile.addCallbackAndRunD((showOnMobile) => {
if (showOnMobile) {
self.RemoveClass("hidden-on-mobile")
} else {
self.SetClass("hidden-on-mobile")
}
})
}
/**
* Creates the url to Hosted weblate
*
* LinkToWeblate.hrefToWeblate("nl", "category:some.context") // => "https://translate.mapcomplete.org/translate/mapcomplete/category/nl/?offset=1&q=context%3A%3D%22some.context%22"
*/
public static hrefToWeblate(language: string, contextKey: string): string {
if (contextKey === undefined || contextKey.indexOf(":") < 0) {
return undefined
}
const [category, ...rest] = contextKey.split(":")
const key = rest.join(":")
const baseUrl = Constants.weblate + "translate/mapcomplete/"
return baseUrl + category + "/" + language + "/?offset=1&q=context%3A%3D%22" + key + "%22"
}
public static hrefToWeblateZen(
language: string,
category: "core" | "themes" | "layers" | "shared-questions" | "glossary" | string,
searchKey: string
): string {
const baseUrl = Constants.weblate + "zen/mapcomplete/"
// ?offset=1&q=+state%3A%3Ctranslated+context%3Acampersite&sort_by=-priority%2Cposition&checksum=
return (
baseUrl +
category +
"/" +
language +
"?offset=1&q=+state%3A%3Ctranslated+context%3A" +
encodeURIComponent(searchKey) +
"&sort_by=-priority%2Cposition&checksum="
)
}
}

View file

@ -1,8 +1,8 @@
<script lang="ts">
import Locale from "../i18n/Locale"
import LinkToWeblate from "./LinkToWeblate"
import Translate from "../../assets/svg/Translate.svelte"
import { LanguageIcon } from "@babeard/svelte-heroicons/solid"
import Translations from "../i18n/Translations"
/**
* Shows a small icon which will open up weblate; a contributor can translate the item for 'context' there
@ -17,7 +17,7 @@
{#if !!context && context.indexOf(":") > 0}
{#if $linkOnMobile}
<a
href={LinkToWeblate.hrefToWeblate($language, context)}
href={Translations.hrefToWeblate($language, context)}
target="_blank"
class="weblate-link mx-1"
tabindex="-1"
@ -26,7 +26,7 @@
</a>
{:else if $linkToWeblate}
<a
href={LinkToWeblate.hrefToWeblate($language, context)}
href={Translations.hrefToWeblate($language, context)}
class="weblate-link hidden-on-mobile"
target="_blank"
tabindex="-1"

View file

@ -18,6 +18,7 @@
? new ImmutableStore(image.license)
: UIEventSource.FromPromise(image.provider?.DownloadAttribution(image))
let icon = image.provider?.SourceIcon(image)
let openOriginal = image.provider?.visitUrl(image)
</script>
{#if $license !== undefined}
@ -25,9 +26,15 @@
class="no-images bg-black-transparent flex items-center rounded-lg p-0.5 px-3 text-sm text-white"
>
{#if icon !== undefined}
<div class="mr-2 h-6 w-6">
<ToSvelte construct={icon} />
</div>
{#if openOriginal}
<a href={openOriginal} target="_blank" rel="noopener" class="mr-2 h-6 w-6">
<ToSvelte construct={icon} />
</a>
{:else}
<div class="mr-2 h-6 w-6">
<ToSvelte construct={icon} />
</div>
{/if}
{/if}
<div class="flex gap-x-2" class:flex-col={attributionFormat !== "minimal"}>

View file

@ -1,30 +0,0 @@
import BaseUIElement from "./BaseUIElement"
/**
* @deprecated
*/
export abstract class UIElement extends BaseUIElement {
/**
* Should be overridden for specific HTML functionality
*/
protected InnerConstructElement(): HTMLElement {
// Uses the old fashioned way to construct an element using 'InnerRender'
const innerRender = this.InnerRender()
if (innerRender === undefined || innerRender === "") {
return undefined
}
const el = document.createElement("span")
if (typeof innerRender === "string") {
el.innerHTML = innerRender
} else {
const subElement = innerRender.ConstructElement()
if (subElement === undefined) {
return undefined
}
el.appendChild(subElement)
}
return el
}
protected abstract InnerRender(): string | BaseUIElement
}

View file

@ -1,7 +1,6 @@
import Locale from "./Locale"
import { Utils } from "../../Utils"
import BaseUIElement from "../BaseUIElement"
import LinkToWeblate from "../Base/LinkToWeblate"
import { Store } from "../../Logic/UIEventSource"
export class Translation extends BaseUIElement {
@ -209,22 +208,7 @@ export class Translation extends BaseUIElement {
}
})
if (this.context === undefined || this.context?.indexOf(":") < 0) {
return el
}
const wrapper = document.createElement("span")
wrapper.appendChild(el)
Locale.showLinkToWeblate.addCallbackAndRun((doShow) => {
if (!doShow) {
return
}
const linkToWeblate = new LinkToWeblate(this.context, this.translations)
wrapper.appendChild(linkToWeblate.ConstructElement())
return true
})
return wrapper
return el
}
public SupportedLanguages(): string[] {

View file

@ -6,6 +6,7 @@ import LanguageUtils from "../../Utils/LanguageUtils"
import { Store } from "../../Logic/UIEventSource"
import Locale from "./Locale"
import { Utils } from "../../Utils"
import Constants from "../../Models/Constants"
export default class Translations {
static readonly t: Readonly<typeof CompiledTranslations.t> = CompiledTranslations.t
@ -169,4 +170,38 @@ export default class Translations {
return true
}
/**
* Creates the url to Hosted weblate
*
* LinkToWeblate.hrefToWeblate("nl", "category:some.context") // => "https://translate.mapcomplete.org/translate/mapcomplete/category/nl/?offset=1&q=context%3A%3D%22some.context%22"
*/
public static hrefToWeblate(language: string, contextKey: string): string {
if (contextKey === undefined || contextKey.indexOf(":") < 0) {
return undefined
}
const [category, ...rest] = contextKey.split(":")
const key = rest.join(":")
const baseUrl = Constants.weblate + "translate/mapcomplete/"
return baseUrl + category + "/" + language + "/?offset=1&q=context%3A%3D%22" + key + "%22"
}
public static hrefToWeblateZen(
language: string,
category: "core" | "themes" | "layers" | "shared-questions" | "glossary" | string,
searchKey: string
): string {
const baseUrl = Constants.weblate + "zen/mapcomplete/"
// ?offset=1&q=+state%3A%3Ctranslated+context%3Acampersite&sort_by=-priority%2Cposition&checksum=
return (
baseUrl +
category +
"/" +
language +
"?offset=1&q=+state%3A%3Ctranslated+context%3A" +
encodeURIComponent(searchKey) +
"&sort_by=-priority%2Cposition&checksum="
)
}
}