mapcomplete/UI/BigComponents/CopyrightPanel.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

210 lines
8 KiB
TypeScript
Raw Normal View History

import Combine from "../Base/Combine"
import Translations from "../i18n/Translations"
import { Store } from "../../Logic/UIEventSource"
import { FixedUiElement } from "../Base/FixedUiElement"
import licenses from "../../assets/generated/license_info.json"
import SmallLicense from "../../Models/smallLicense"
import { Utils } from "../../Utils"
2021-04-23 17:22:01 +02:00
import Link from "../Base/Link"
import { VariableUiElement } from "../Base/VariableUIElement"
import contributors from "../../assets/contributors.json"
import translators from "../../assets/translators.json"
2021-06-12 02:58:32 +02:00
import BaseUIElement from "../BaseUIElement"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import Title from "../Base/Title"
import { BBox } from "../../Logic/BBox"
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import Constants from "../../Models/Constants"
import ContributorCount from "../../Logic/ContributorCount"
import Img from "../Base/Img"
import { TypedTranslation } from "../i18n/Translation"
2023-03-28 05:13:48 +02:00
import GeoIndexedStore from "../../Logic/FeatureSource/Actors/GeoIndexedStore"
import { RasterLayerPolygon } from "../../Models/RasterLayers"
2021-05-19 23:29:23 +02:00
/**
* The attribution panel in the theme menu.
*/
export default class CopyrightPanel extends Combine {
private static LicenseObject = CopyrightPanel.GenerateLicenses()
constructor(state: {
2023-03-28 05:13:48 +02:00
layout: LayoutConfig
mapProperties: {
readonly bounds: Store<BBox>
readonly rasterLayer: Store<RasterLayerPolygon>
}
2022-04-01 12:51:55 +02:00
osmConnection: OsmConnection
2023-03-28 05:13:48 +02:00
dataIsLoading: Store<boolean>
perLayer: ReadonlyMap<string, GeoIndexedStore>
}) {
2021-11-07 16:34:51 +01:00
const t = Translations.t.general.attribution
2023-03-28 05:13:48 +02:00
const layoutToUse = state.layout
2021-11-07 16:34:51 +01:00
const iconAttributions: BaseUIElement[] = Utils.Dedup(layoutToUse.usedImages).map(
2023-02-03 03:56:31 +01:00
CopyrightPanel.IconAttribution
)
2021-11-07 16:34:51 +01:00
let maintainer: BaseUIElement = undefined
2022-08-22 14:46:55 +02:00
if (layoutToUse.credits !== undefined && layoutToUse.credits !== "") {
maintainer = t.themeBy.Subs({ author: layoutToUse.credits })
}
2021-11-07 16:34:51 +01:00
2022-01-26 21:40:38 +01:00
const contributions = new ContributorCount(state).Contributors
2022-04-01 12:51:55 +02:00
const dataContributors = new VariableUiElement(
contributions.map((contributions) => {
2021-11-07 16:34:51 +01:00
if (contributions === undefined) {
return ""
}
2021-06-27 02:41:30 +02:00
const sorted = Array.from(contributions, ([name, value]) => ({
name,
value,
})).filter((x) => x.name !== undefined && x.name !== "undefined")
if (sorted.length === 0) {
return ""
}
sorted.sort((a, b) => b.value - a.value)
let hiddenCount = 0
if (sorted.length > 10) {
hiddenCount = sorted.length - 10
sorted.splice(10, sorted.length - 10)
}
const links = sorted.map(
(kv) =>
`<a href="https://openstreetmap.org/user/${kv.name}" target="_blank">${kv.name}</a>`
2022-09-08 21:40:48 +02:00
)
const contribs = links.join(", ")
2021-06-24 14:21:41 +02:00
if (hiddenCount <= 0) {
2022-04-01 12:51:55 +02:00
return t.mapContributionsBy.Subs({
contributors: contribs,
2021-06-10 01:36:20 +02:00
})
} else {
2022-04-01 12:51:55 +02:00
return t.mapContributionsByAndHidden.Subs({
contributors: contribs,
hiddenCount: hiddenCount,
2021-06-10 01:36:20 +02:00
})
}
2022-04-01 12:51:55 +02:00
})
2022-09-08 21:40:48 +02:00
)
2022-04-01 12:51:55 +02:00
super(
[
new Title(t.attributionTitle),
t.attributionContent,
2023-05-18 15:44:54 +02:00
new VariableUiElement(
state.mapProperties.rasterLayer.mapD((layer) => {
const props = layer.properties
const attrUrl = props.attribution?.url
const attrText = props.attribution?.text
2023-05-18 15:44:54 +02:00
let bgAttr: BaseUIElement | string = undefined
if (attrText && attrUrl) {
bgAttr =
"<a href='" + attrUrl + "' target='_blank'>" + attrText + "</a>"
} else if (attrUrl) {
bgAttr = attrUrl
} else {
bgAttr = attrText
}
if (bgAttr) {
return Translations.t.general.attribution.attributionBackgroundLayerWithCopyright.Subs(
{
name: props.name,
copyright: bgAttr,
}
)
}
return Translations.t.general.attribution.attributionBackgroundLayer.Subs(
props
)
})
),
2023-05-18 15:44:54 +02:00
2022-04-01 12:51:55 +02:00
maintainer,
dataContributors,
CopyrightPanel.CodeContributors(contributors, t.codeContributionsBy),
CopyrightPanel.CodeContributors(translators, t.translatedBy),
new FixedUiElement("MapComplete " + Constants.vNumber).SetClass("font-bold"),
new Title(t.iconAttribution.title, 3),
...iconAttributions,
].map((e) => e?.SetClass("mt-4"))
2022-09-08 21:40:48 +02:00
)
2021-07-03 21:18:19 +02:00
this.SetClass("flex flex-col link-underline overflow-hidden")
this.SetStyle("max-width:100%; width: 40rem; margin-left: 0.75rem; margin-right: 0.5rem")
}
private static CodeContributors(
contributors,
translation: TypedTranslation<{ contributors; hiddenCount }>
): BaseUIElement {
2021-05-19 19:39:55 +02:00
const total = contributors.contributors.length
2021-06-27 02:41:30 +02:00
let filtered = [...contributors.contributors]
2021-05-19 23:29:23 +02:00
2021-06-27 02:41:30 +02:00
filtered.splice(10, total - 10)
2021-05-19 23:29:23 +02:00
2021-06-27 02:41:30 +02:00
let contribsStr = filtered.map((c) => c.contributor).join(", ")
if (contribsStr === "") {
2021-05-27 18:56:02 +02:00
// Hmm, something went wrong loading the contributors list. Lets show nothing
return undefined
}
2021-05-19 23:29:23 +02:00
2022-02-14 19:53:23 +01:00
return translation.Subs({
2021-05-19 23:29:23 +02:00
contributors: contribsStr,
hiddenCount: total - 10,
})
2021-05-19 19:39:55 +02:00
}
2021-05-19 23:29:23 +02:00
2021-06-12 02:58:32 +02:00
private static IconAttribution(iconPath: string): BaseUIElement {
if (iconPath.startsWith("http")) {
2022-04-01 12:51:55 +02:00
try {
2022-02-22 13:43:36 +01:00
iconPath = "." + new URL(iconPath).pathname
2022-04-01 12:51:55 +02:00
} catch (e) {
2022-02-22 13:43:36 +01:00
console.warn(e)
}
}
const license: SmallLicense = CopyrightPanel.LicenseObject[iconPath]
if (license == undefined) {
return undefined
}
if (license.license.indexOf("trivial") >= 0) {
return undefined
}
const sources = Utils.NoNull(Utils.NoEmpty(license.sources))
return new Combine([
new Img(iconPath).SetClass("w-12 min-h-12 mr-2 mb-2"),
new Combine([
new FixedUiElement(license.authors.join("; ")).SetClass("font-bold"),
2022-04-01 12:51:55 +02:00
license.license,
new Combine([
...sources.map((lnk) => {
let sourceLinkContent = lnk
try {
sourceLinkContent = new URL(lnk).hostname
} catch {
console.error("Not a valid URL:", lnk)
}
return new Link(sourceLinkContent, lnk, true).SetClass("mr-2 mb-2")
}),
]).SetClass("flex flex-wrap"),
2021-07-03 21:18:19 +02:00
])
.SetClass("flex flex-col")
.SetStyle("width: calc(100% - 50px - 0.5em); min-width: 12rem;"),
]).SetClass("flex flex-wrap border-b border-gray-300 m-2 border-box")
}
private static GenerateLicenses() {
const allLicenses = {}
for (const key in licenses) {
const license: SmallLicense = licenses[key]
allLicenses[license.path] = license
}
return allLicenses
}
}