| 
									
										
										
										
											2021-04-07 01:32:39 +02:00
										 |  |  | import Combine from "../Base/Combine"; | 
					
						
							|  |  |  | import Translations from "../i18n/Translations"; | 
					
						
							|  |  |  | import Attribution from "./Attribution"; | 
					
						
							|  |  |  | import State from "../../State"; | 
					
						
							|  |  |  | import {UIEventSource} from "../../Logic/UIEventSource"; | 
					
						
							|  |  |  | import {FixedUiElement} from "../Base/FixedUiElement"; | 
					
						
							| 
									
										
										
										
											2021-04-09 02:57:06 +02:00
										 |  |  | 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"; | 
					
						
							| 
									
										
										
										
											2021-05-10 23:48:00 +02:00
										 |  |  | import {VariableUiElement} from "../Base/VariableUIElement"; | 
					
						
							| 
									
										
										
										
											2021-05-19 19:39:55 +02:00
										 |  |  | import * as contributors from "../../assets/contributors.json" | 
					
						
							| 
									
										
										
										
											2021-06-12 02:58:32 +02:00
										 |  |  | import BaseUIElement from "../BaseUIElement"; | 
					
						
							| 
									
										
										
										
											2021-08-07 23:11:34 +02:00
										 |  |  | import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; | 
					
						
							| 
									
										
										
										
											2021-10-30 01:55:32 +02:00
										 |  |  | 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"; | 
					
						
							| 
									
										
										
										
											2021-05-19 23:29:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-07 01:32:39 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * The attribution panel shown on mobile | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-10-30 01:55:32 +02:00
										 |  |  | export default class CopyrightPanel extends Combine { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private static LicenseObject = CopyrightPanel.GenerateLicenses(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     constructor(state: { | 
					
						
							|  |  |  |         layoutToUse: LayoutConfig, | 
					
						
							|  |  |  |         featurePipeline: FeaturePipeline, | 
					
						
							|  |  |  |         currentBounds: UIEventSource<BBox>, | 
					
						
							|  |  |  |         locationControl: UIEventSource<Loc>, | 
					
						
							|  |  |  |         osmConnection: OsmConnection | 
					
						
							|  |  |  |     }, contributions: UIEventSource<Map<string, number>>) { | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         const t =Translations.t.general.attribution | 
					
						
							|  |  |  |         const layoutToUse = state.layoutToUse | 
					
						
							|  |  |  |         const josmState = new UIEventSource<string>(undefined) | 
					
						
							|  |  |  |         // Reset after 15s
 | 
					
						
							|  |  |  |         josmState.stabilized(15000).addCallbackD(_ => josmState.setData(undefined)) | 
					
						
							|  |  |  |         const iconStyle = "height: 1.5rem; width: auto" | 
					
						
							|  |  |  |         const actionButtons = [ | 
					
						
							|  |  |  |             new SubtleButton(Svg.liberapay_ui().SetStyle(iconStyle), t.donate, { | 
					
						
							|  |  |  |                 url: "https://liberapay.com/pietervdvn/", | 
					
						
							|  |  |  |                 newTab: true | 
					
						
							|  |  |  |             }), | 
					
						
							|  |  |  |             new SubtleButton(Svg.bug_ui().SetStyle(iconStyle), t.openIssueTracker, { | 
					
						
							|  |  |  |                 url: "https://github.com/pietervdvn/MapComplete/issues", | 
					
						
							|  |  |  |                 newTab: true | 
					
						
							|  |  |  |             }), | 
					
						
							|  |  |  |             new SubtleButton(Svg.statistics_ui().SetStyle(iconStyle), t.openOsmcha.Subs({theme: state.layoutToUse.title}), { | 
					
						
							|  |  |  |                 url:  Utils.OsmChaLinkFor(31, state.layoutToUse.id), | 
					
						
							|  |  |  |                 newTab: true | 
					
						
							|  |  |  |             }), | 
					
						
							|  |  |  |             new VariableUiElement(state.locationControl.map(location => { | 
					
						
							|  |  |  |                 const idLink = `https://www.openstreetmap.org/edit?editor=id#map=${location?.zoom ?? 0}/${location?.lat ?? 0}/${location?.lon ?? 0}` | 
					
						
							|  |  |  |                 return new SubtleButton(Svg.pencil_ui().SetStyle(iconStyle), t.editId, {url: idLink, newTab: true}) | 
					
						
							|  |  |  |             })), | 
					
						
							| 
									
										
										
										
											2021-04-09 02:57:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 01:55:32 +02:00
										 |  |  |             new VariableUiElement(state.locationControl.map(location => { | 
					
						
							|  |  |  |                 const mapillaryLink = `https://www.mapillary.com/app/?focus=map&lat=${location?.lat ?? 0}&lng=${location?.lon ?? 0}&z=${Math.max((location?.zoom ?? 2) - 1, 1)}` | 
					
						
							|  |  |  |                 return new SubtleButton(Svg.mapillary_black_ui().SetStyle(iconStyle), t.openMapillary, {url: mapillaryLink, newTab: true}) | 
					
						
							|  |  |  |             })), | 
					
						
							|  |  |  |             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") | 
					
						
							|  |  |  |             })), | 
					
						
							|  |  |  |             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)), | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |         ].map(button => button.SetStyle("max-height: 3rem")) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         const iconAttributions = Utils.NoNull(Array.from(layoutToUse.ExtractImages())) | 
					
						
							|  |  |  |             .map(CopyrightPanel.IconAttribution) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         let maintainer  : BaseUIElement= undefined | 
					
						
							|  |  |  |         if(layoutToUse.maintainer !== undefined && layoutToUse.maintainer !== "" && layoutToUse.maintainer.toLowerCase() !== "mapcomplete"){ | 
					
						
							|  |  |  |             maintainer = Translations.t.general.attribution.themeBy.Subs({author: layoutToUse.maintainer}) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							| 
									
										
										
										
											2021-04-07 01:32:39 +02:00
										 |  |  |         super([ | 
					
						
							|  |  |  |             Translations.t.general.attribution.attributionContent, | 
					
						
							| 
									
										
										
										
											2021-10-30 01:55:32 +02:00
										 |  |  |             maintainer, | 
					
						
							|  |  |  |             new Combine(actionButtons).SetClass("block w-full"), | 
					
						
							|  |  |  |             new FixedUiElement(layoutToUse.credits), | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |             new Attribution(State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, State.state.currentBounds), | 
					
						
							| 
									
										
										
										
											2021-05-10 23:48:00 +02:00
										 |  |  |             new VariableUiElement(contributions.map(contributions => { | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02: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"); | 
					
						
							| 
									
										
										
										
											2021-05-10 23:48:00 +02:00
										 |  |  |                 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>`) | 
					
						
							|  |  |  |                 const contribs = links.join(", ") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-24 14:21:41 +02:00
										 |  |  |                 if (hiddenCount <= 0) { | 
					
						
							| 
									
										
										
										
											2021-05-10 23:48:00 +02:00
										 |  |  |                     return Translations.t.general.attribution.mapContributionsBy.Subs({ | 
					
						
							|  |  |  |                         contributors: contribs | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |                     }) | 
					
						
							| 
									
										
										
										
											2021-05-10 23:48:00 +02:00
										 |  |  |                 } else { | 
					
						
							|  |  |  |                     return Translations.t.general.attribution.mapContributionsByAndHidden.Subs({ | 
					
						
							|  |  |  |                         contributors: contribs, | 
					
						
							|  |  |  |                         hiddenCount: hiddenCount | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |                     }); | 
					
						
							| 
									
										
										
										
											2021-05-10 23:48:00 +02:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             })), | 
					
						
							| 
									
										
										
										
											2021-10-30 01:55:32 +02:00
										 |  |  |             CopyrightPanel.CodeContributors(), | 
					
						
							|  |  |  |             new Title(t.iconAttribution.title, 3), | 
					
						
							|  |  |  |             ...iconAttributions | 
					
						
							|  |  |  |         ].map(e => e?.SetClass("mt-4"))); | 
					
						
							| 
									
										
										
										
											2021-07-03 21:18:19 +02:00
										 |  |  |         this.SetClass("flex flex-col link-underline overflow-hidden") | 
					
						
							| 
									
										
										
										
											2021-10-30 01:55:32 +02:00
										 |  |  |         this.SetStyle("max-width: calc(100vw - 5em); width: 40rem; margin-left: 0.75rem; margin-right: 0.5rem") | 
					
						
							| 
									
										
										
										
											2021-04-09 02:57:06 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-12 02:58:32 +02:00
										 |  |  |     private static CodeContributors(): 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
										 |  |  | 
 | 
					
						
							|  |  |  |         return Translations.t.general.attribution.codeContributionsBy.Subs({ | 
					
						
							|  |  |  |             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 { | 
					
						
							| 
									
										
										
										
											2021-04-09 02:57:06 +02:00
										 |  |  |         if (iconPath.startsWith("http")) { | 
					
						
							|  |  |  |             iconPath = "." + new URL(iconPath).pathname; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 01:55:32 +02:00
										 |  |  |         const license: SmallLicense = CopyrightPanel.LicenseObject[iconPath] | 
					
						
							| 
									
										
										
										
											2021-04-09 02:57:06 +02:00
										 |  |  |         if (license == undefined) { | 
					
						
							|  |  |  |             return undefined; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-05-10 23:48:00 +02:00
										 |  |  |         if (license.license.indexOf("trivial") >= 0) { | 
					
						
							| 
									
										
										
										
											2021-04-09 02:57:06 +02:00
										 |  |  |             return undefined; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-10 23:48:00 +02:00
										 |  |  |         const sources = Utils.NoNull(Utils.NoEmpty(license.sources)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-09 02:57:06 +02:00
										 |  |  |         return new Combine([ | 
					
						
							| 
									
										
										
										
											2021-07-03 14:54:35 +02:00
										 |  |  |             `<img src='${iconPath}' style="width: 50px; height: 50px; min-width: 50px; min-height: 50px;  margin-right: 0.5em;">`, | 
					
						
							| 
									
										
										
										
											2021-04-09 02:57:06 +02:00
										 |  |  |             new Combine([ | 
					
						
							|  |  |  |                 new FixedUiElement(license.authors.join("; ")).SetClass("font-bold"), | 
					
						
							| 
									
										
										
										
											2021-05-10 23:48:00 +02:00
										 |  |  |                 new Combine([license.license, | 
					
						
							|  |  |  |                         sources.length > 0 ? " - " : "", | 
					
						
							|  |  |  |                         ...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); | 
					
						
							|  |  |  |                         }) | 
					
						
							|  |  |  |                     ] | 
					
						
							| 
									
										
										
										
											2021-07-03 21:18:19 +02:00
										 |  |  |                 ).SetClass("block m-2") | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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") | 
					
						
							| 
									
										
										
										
											2021-04-09 02:57:06 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private static GenerateLicenses() { | 
					
						
							|  |  |  |         const allLicenses = {} | 
					
						
							|  |  |  |         for (const key in licenses) { | 
					
						
							|  |  |  |             const license: SmallLicense = licenses[key]; | 
					
						
							|  |  |  |             allLicenses[license.path] = license | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return allLicenses; | 
					
						
							| 
									
										
										
										
											2021-04-07 01:32:39 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } |