From 64adf13d42689cb51ab0cbebe0a4d44220dc1fb4 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 30 Oct 2021 01:55:32 +0200 Subject: [PATCH] Rework copyright panel, fix filter view toggle --- Logic/Actors/SelectedFeatureHandler.ts | 2 +- UI/BigComponents/Attribution.ts | 1 + ...{AttributionPanel.ts => CopyrightPanel.ts} | 106 +++++++++++++++--- UI/BigComponents/LeftControls.ts | 6 +- UI/DefaultGUI.ts | 2 +- assets/svg/liberapay.svg | 1 + assets/svg/license_info.json | 10 ++ assets/themes/grb_import/grb.json | 3 +- langs/en.json | 8 ++ langs/themes/en.json | 3 + 10 files changed, 120 insertions(+), 22 deletions(-) rename UI/BigComponents/{AttributionPanel.ts => CopyrightPanel.ts} (50%) create mode 100644 assets/svg/liberapay.svg diff --git a/Logic/Actors/SelectedFeatureHandler.ts b/Logic/Actors/SelectedFeatureHandler.ts index 44a2940d3d..aba97ecb10 100644 --- a/Logic/Actors/SelectedFeatureHandler.ts +++ b/Logic/Actors/SelectedFeatureHandler.ts @@ -10,7 +10,7 @@ import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; * Makes sure the hash shows the selected element and vice-versa. */ export default class SelectedFeatureHandler { - private static readonly _no_trigger_on = new Set(["welcome", "copyright", "layers", "new", "filter","", undefined]) + private static readonly _no_trigger_on = new Set(["welcome", "copyright", "layers", "new", "filters","", undefined]) private readonly hash: UIEventSource; private readonly state: { selectedElement: UIEventSource, diff --git a/UI/BigComponents/Attribution.ts b/UI/BigComponents/Attribution.ts index 1bcdcbc9ea..267bd776cc 100644 --- a/UI/BigComponents/Attribution.ts +++ b/UI/BigComponents/Attribution.ts @@ -56,6 +56,7 @@ export default class Attribution extends Combine { ) ) super([mapComplete, reportBug, stats, editHere, editWithJosm, mapillary]); + this.SetClass("flex") } diff --git a/UI/BigComponents/AttributionPanel.ts b/UI/BigComponents/CopyrightPanel.ts similarity index 50% rename from UI/BigComponents/AttributionPanel.ts rename to UI/BigComponents/CopyrightPanel.ts index c18c8d2875..bcf7f37849 100644 --- a/UI/BigComponents/AttributionPanel.ts +++ b/UI/BigComponents/CopyrightPanel.ts @@ -12,23 +12,99 @@ import {VariableUiElement} from "../Base/VariableUIElement"; import * as contributors from "../../assets/contributors.json" 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"; /** * The attribution panel shown on mobile */ -export default class AttributionPanel extends Combine { +export default class CopyrightPanel extends Combine { - private static LicenseObject = AttributionPanel.GenerateLicenses(); + private static LicenseObject = CopyrightPanel.GenerateLicenses(); - constructor(layoutToUse: LayoutConfig, contributions: UIEventSource>) { + constructor(state: { + layoutToUse: LayoutConfig, + featurePipeline: FeaturePipeline, + currentBounds: UIEventSource, + locationControl: UIEventSource, + osmConnection: OsmConnection + }, contributions: UIEventSource>) { + + const t =Translations.t.general.attribution + const layoutToUse = state.layoutToUse + const josmState = new UIEventSource(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}) + })), + + 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}) + } + super([ Translations.t.general.attribution.attributionContent, - ((layoutToUse.maintainer ?? "") == "") ? "" : Translations.t.general.attribution.themeBy.Subs({author: layoutToUse.maintainer}), - layoutToUse.credits, - "
", + maintainer, + new Combine(actionButtons).SetClass("block w-full"), + new FixedUiElement(layoutToUse.credits), new Attribution(State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, State.state.currentBounds), - "
", - new VariableUiElement(contributions.map(contributions => { if(contributions === undefined){ return "" @@ -62,14 +138,12 @@ export default class AttributionPanel extends Combine { })), - "
", - AttributionPanel.CodeContributors(), - "

", Translations.t.general.attribution.iconAttribution.title.Clone().SetClass("pt-6 pb-3"), "

", - ...Utils.NoNull(Array.from(layoutToUse.ExtractImages())) - .map(AttributionPanel.IconAttribution) - ]); + CopyrightPanel.CodeContributors(), + new Title(t.iconAttribution.title, 3), + ...iconAttributions + ].map(e => e?.SetClass("mt-4"))); this.SetClass("flex flex-col link-underline overflow-hidden") - this.SetStyle("max-width: calc(100vw - 5em); width: 40rem;") + this.SetStyle("max-width: calc(100vw - 5em); width: 40rem; margin-left: 0.75rem; margin-right: 0.5rem") } private static CodeContributors(): BaseUIElement { @@ -97,7 +171,7 @@ export default class AttributionPanel extends Combine { iconPath = "." + new URL(iconPath).pathname; } - const license: SmallLicense = AttributionPanel.LicenseObject[iconPath] + const license: SmallLicense = CopyrightPanel.LicenseObject[iconPath] if (license == undefined) { return undefined; } diff --git a/UI/BigComponents/LeftControls.ts b/UI/BigComponents/LeftControls.ts index cc8b9e4c05..f2459d0028 100644 --- a/UI/BigComponents/LeftControls.ts +++ b/UI/BigComponents/LeftControls.ts @@ -1,7 +1,7 @@ import Combine from "../Base/Combine"; import ScrollableFullScreen from "../Base/ScrollableFullScreen"; import Translations from "../i18n/Translations"; -import AttributionPanel from "./AttributionPanel"; +import CopyrightPanel from "./CopyrightPanel"; import ContributorCount from "../../Logic/ContributorCount"; import Toggle from "../Input/Toggle"; import MapControlButton from "../MapControlButton"; @@ -37,8 +37,8 @@ export default class LeftControls extends Combine { const toggledCopyright = new ScrollableFullScreen( () => Translations.t.general.attribution.attributionTitle.Clone(), () => - new AttributionPanel( - state.layoutToUse, + new CopyrightPanel( + state, new ContributorCount(state).Contributors ), "copyright", diff --git a/UI/DefaultGUI.ts b/UI/DefaultGUI.ts index f6c551663b..a4de2d4601 100644 --- a/UI/DefaultGUI.ts +++ b/UI/DefaultGUI.ts @@ -65,7 +65,7 @@ export class DefaultGuiState { if(Hash.hash.data === "download"){ this.downloadControlIsOpened.setData(true) } - if(Hash.hash.data === "filter"){ + if(Hash.hash.data === "filters"){ this.filterViewIsOpened.setData(true) } if(Hash.hash.data === "copyright"){ diff --git a/assets/svg/liberapay.svg b/assets/svg/liberapay.svg new file mode 100644 index 0000000000..23a0df2061 --- /dev/null +++ b/assets/svg/liberapay.svg @@ -0,0 +1 @@ + diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index 837704d79e..a3e38008d1 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -879,6 +879,16 @@ ], "sources": [] }, + { + "path": "liberapay.svg", + "license": "Logo (all rights reserved)", + "authors": [ + "LiberaPay" + ], + "sources": [ + "https://liberapay.com/" + ] + }, { "path": "loading.svg", "license": "CC0; trivial", diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index 7979fe3248..6f3cbd5dbb 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -11,7 +11,8 @@ "en": "This theme is an attempt to help automating the GRB import.
Note that this is very hacky and 'steals' the GRB data from an external site; in order to do this, you need to install and activate this firefox extension for it to work." }, "language": [ - "nl" + "nl", + "en" ], "maintainer": "", "icon": "./assets/svg/bug.svg", diff --git a/langs/en.json b/langs/en.json index 0e8640fde0..f91d76a4ed 100644 --- a/langs/en.json +++ b/langs/en.json @@ -161,6 +161,14 @@ "iconAttribution": { "title": "Used icons" }, + "openIssueTracker": "File a bug", + "editJosm": "Edit here with JOSM", + "josmOpened": "JOSM is opened", + "josmNotOpened": "JOSM could not be reached. Make sure it is opened and remote control is enabled", + "editId": "Open the OpenStreetMap online editor here", + "openMapillary": "Open Mapillary here", + "donate": "Support MapComplete financially", + "openOsmcha": "See latest edits made with {theme}", "mapContributionsBy": "The current visible data has edits made by {contributors}", "mapContributionsByAndHidden": "The current visible data has edits made by {contributors} and {hiddenCount} more contributors", "codeContributionsBy": "MapComplete has been built by {contributors} and {hiddenCount} more contributors" diff --git a/langs/themes/en.json b/langs/themes/en.json index 283c144aab..7d5a45d348 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -792,6 +792,9 @@ "description": "A ghost bike is a memorial for a cyclist who died in a traffic accident, in the form of a white bicycle placed permanently near the accident location.

On this map, one can see all the ghost bikes which are known by OpenStreetMap. Is a ghost bike missing? Everyone can add or update information here - you only need to have a (free) OpenStreetMap account.", "title": "Ghost bikes" }, + "grb": { + "description": "This theme is an attempt to help automating the GRB import.
Note that this is very hacky and 'steals' the GRB data from an external site; in order to do this, you need to install and activate this firefox extension for it to work." + }, "hackerspaces": { "description": "On this map you can see hackerspaces, add a new hackerspace or update data directly", "layers": {