mapcomplete/UI/BigComponents/CopyrightPanel.ts

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

261 lines
9.8 KiB
TypeScript
Raw Normal View History

import Combine from "../Base/Combine"
import Translations from "../i18n/Translations"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import { FixedUiElement } from "../Base/FixedUiElement"
import * as licenses from "../../assets/generated/license_info.json"
import SmallLicense from "../../Models/smallLicense"
2021-04-10 03:53:48 +02:00
import { Utils } from "../../Utils"
2021-04-23 17:22:01 +02:00
import Link from "../Base/Link"
import { VariableUiElement } from "../Base/VariableUIElement"
2021-05-19 19:39:55 +02:00
import * as contributors from "../../assets/contributors.json"
2022-02-14 19:53:23 +01:00
import * as 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 { SubtleButton } from "../Base/SubtleButton"
import Svg from "../../Svg"
import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"
import { BBox } from "../../Logic/BBox"
import Loc from "../../Models/Loc"
import Toggle from "../Input/Toggle"
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"
2021-05-19 23:29:23 +02:00
export class OpenIdEditor extends VariableUiElement {
2022-01-26 21:40:38 +01:00
constructor(
state: { readonly locationControl: Store<Loc> },
2022-01-26 21:40:38 +01:00
iconStyle?: string,
objectId?: string
) {
const t = Translations.t.general.attribution
super(
state.locationControl.map((location) => {
let elementSelect = ""
2022-01-26 21:40:38 +01:00
if (objectId !== undefined) {
const parts = objectId.split("/")
const tp = parts[0]
2022-01-26 21:40:38 +01:00
if (
parts.length === 2 &&
!isNaN(Number(parts[1])) &&
(tp === "node" || tp === "way" || tp === "relation")
) {
elementSelect = "&" + tp + "=" + parts[1]
2022-09-08 21:40:48 +02:00
}
}
const idLink = `https://www.openstreetmap.org/edit?editor=id${elementSelect}#map=${
location?.zoom ?? 0
}/${location?.lat ?? 0}/${location?.lon ?? 0}`
return new SubtleButton(Svg.pencil_ui().SetStyle(iconStyle), t.editId, {
url: idLink,
newTab: true,
})
})
2022-09-08 21:40:48 +02:00
)
}
}
export class OpenJosm extends Combine {
constructor(
state: { osmConnection: OsmConnection; currentBounds: Store<BBox> },
iconStyle?: string
) {
2022-01-26 21:40:38 +01:00
const t = Translations.t.general.attribution
const josmState = new UIEventSource<string>(undefined)
// Reset after 15s
josmState.stabilized(15000).addCallbackD((_) => josmState.setData(undefined))
2022-01-26 21:40:38 +01:00
const stateIndication = new VariableUiElement(
josmState.map((state) => {
if (state === undefined) {
return undefined
}
state = state.toUpperCase()
if (state === "OK") {
return t.josmOpened.SetClass("thanks")
}
return t.josmNotOpened.SetClass("alert")
})
2022-09-08 21:40:48 +02:00
)
2022-01-26 21:40:38 +01:00
const toggle = new Toggle(
new SubtleButton(Svg.josm_logo_ui().SetStyle(iconStyle), t.editJosm).onClick(() => {
const bounds: any = state.currentBounds.data
if (bounds === undefined) {
return undefined
}
const top = bounds.getNorth()
const bottom = bounds.getSouth()
const right = bounds.getEast()
const left = bounds.getWest()
const josmLink = `http://127.0.0.1:8111/load_and_zoom?left=${left}&right=${right}&top=${top}&bottom=${bottom}`
Utils.download(josmLink)
.then((answer) => josmState.setData(answer.replace(/\n/g, "").trim()))
.catch((_) => josmState.setData("ERROR"))
}),
undefined,
state.osmConnection.userDetails.map(
(ud) => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible
2022-09-08 21:40:48 +02:00
)
)
2022-01-26 21:40:38 +01:00
super([stateIndication, toggle])
}
}
/**
* The attribution panel shown on mobile
*/
export default class CopyrightPanel extends Combine {
private static LicenseObject = CopyrightPanel.GenerateLicenses()
constructor(state: {
layoutToUse: LayoutConfig
featurePipeline: FeaturePipeline
currentBounds: Store<BBox>
locationControl: UIEventSource<Loc>
2022-04-01 12:51:55 +02:00
osmConnection: OsmConnection
isTranslator: Store<boolean>
}) {
2021-11-07 16:34:51 +01:00
const t = Translations.t.general.attribution
const layoutToUse = state.layoutToUse
2021-11-07 16:34:51 +01:00
const iconAttributions = layoutToUse.usedImages.map(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,
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
}
}