From d7df6a7a80cf5334b67f2d7f54386f3b8aae4c1a Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 13 Mar 2022 02:46:42 +0100 Subject: [PATCH] Improvements to notes layer and elements --- UI/Image/SlideShow.ts | 2 +- UI/Input/TextField.ts | 2 +- UI/Input/ValidatedTextField.ts | 2 + UI/Popup/NoteCommentElement.ts | 18 +++++-- UI/SpecialVisualizations.ts | 21 +++++--- assets/layers/note/note.json | 48 +++++++++++++++++-- assets/svg/license_info.json | 12 ++++- assets/svg/speech_bubble.svg | 33 ++++++++++++- assets/themes/etymology.json | 2 +- .../mapcomplete-changes.json | 42 ++++++++++------ css/index-tailwind-output.css | 20 ++++++-- langs/en.json | 1 + langs/layers/en.json | 14 ++++++ langs/shared-questions/id.json | 6 ++- 14 files changed, 185 insertions(+), 38 deletions(-) diff --git a/UI/Image/SlideShow.ts b/UI/Image/SlideShow.ts index e12e0be4b..9d37036fe 100644 --- a/UI/Image/SlideShow.ts +++ b/UI/Image/SlideShow.ts @@ -11,7 +11,7 @@ export class SlideShow extends BaseUIElement { constructor(embeddedElements: UIEventSource) { super() this.embeddedElements = embeddedElements; - this.SetStyle("scroll-snap-type: x mandatory; overflow-x: scroll") + this.SetStyle("scroll-snap-type: x mandatory; overflow-x: auto") } protected InnerConstructElement(): HTMLElement { diff --git a/UI/Input/TextField.ts b/UI/Input/TextField.ts index 051ff1652..d3eb86340 100644 --- a/UI/Input/TextField.ts +++ b/UI/Input/TextField.ts @@ -13,7 +13,7 @@ export class TextField extends InputElement { constructor(options?: { placeholder?: string | BaseUIElement, value?: UIEventSource, - htmlType?: string, + htmlType?: "area" | "field", inputMode?: string, label?: BaseUIElement, textAreaRows?: number, diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index 9164a2851..874e830bc 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -98,11 +98,13 @@ export class TextFieldDef { } else { options["isValid"] = str => self.isValid(str, options.country); } + options["cssText"] = "width: 100%;" options["inputMode"] = this.inputmode; if (this.inputmode === "text") { options["htmlType"] = "area" + options["textAreaRows"] = 4 } diff --git a/UI/Popup/NoteCommentElement.ts b/UI/Popup/NoteCommentElement.ts index effb794e7..d0ba42d19 100644 --- a/UI/Popup/NoteCommentElement.ts +++ b/UI/Popup/NoteCommentElement.ts @@ -9,6 +9,8 @@ import Img from "../Base/Img"; import {SlideShow} from "../Image/SlideShow"; import {UIEventSource} from "../../Logic/UIEventSource"; import {OsmConnection} from "../../Logic/Osm/OsmConnection"; +import {UIElement} from "../UIElement"; +import {VariableUiElement} from "../Base/VariableUIElement"; export default class NoteCommentElement extends Combine { @@ -39,7 +41,15 @@ export default class NoteCommentElement extends Combine { user = new Link(comment.user, comment.user_url ?? "", true) } - + let userinfo = UIEventSource.FromPromise( Utils.downloadJsonCached("https://www.openstreetmap.org/api/0.6/user/"+comment.uid, 24*60*60*1000)) + let userImg = new VariableUiElement( userinfo.map(userinfo => { + const href = userinfo?.user?.img?.href; + if(href !== undefined){ + return new Img(href).SetClass("rounded-full w-8 h-8 mr-4") + } + return undefined + })) + const htmlElement = document.createElement("div") htmlElement.innerHTML = comment.html const images = Array.from(htmlElement.getElementsByTagName("a")) @@ -55,7 +65,7 @@ export default class NoteCommentElement extends Combine { const imageEls = images.map(i => new Img(i) .SetClass("w-full block") .SetStyle("min-width: 50px; background: grey;")); - imagesEl = new SlideShow(new UIEventSource(imageEls)) + imagesEl = new SlideShow(new UIEventSource(imageEls)).SetClass("mb-1") } super([ @@ -64,9 +74,9 @@ export default class NoteCommentElement extends Combine { new FixedUiElement(comment.html).SetClass("flex flex-col").SetStyle("margin: 0"), ]).SetClass("flex"), imagesEl, - new Combine([user.SetClass("mr-2"), comment.date]).SetClass("flex justify-end subtle") + new Combine([userImg, user.SetClass("mr-2"), comment.date]).SetClass("flex justify-end items-center subtle") ]) - this.SetClass("flex flex-col") + this.SetClass("flex flex-col pb-2 mb-2 border-gray-500 border-b") } diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 1b4cf7f41..68f9e75dc 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -39,14 +39,13 @@ import * as left_right_style_json from "../assets/layers/left_right_style/left_r import {OpenIdEditor} from "./BigComponents/CopyrightPanel"; import Toggle from "./Input/Toggle"; import Img from "./Base/Img"; -import ValidatedTextField from "./Input/ValidatedTextField"; import NoteCommentElement from "./Popup/NoteCommentElement"; import ImgurUploader from "../Logic/ImageProviders/ImgurUploader"; import FileSelectorButton from "./Input/FileSelectorButton"; import {LoginToggle} from "./Popup/LoginButton"; import {start} from "repl"; import {SubstitutedTranslation} from "./SubstitutedTranslation"; -import {Feature} from "@turf/turf"; +import {TextField} from "./Input/TextField"; export interface SpecialVisualization { funcName: string, @@ -752,7 +751,14 @@ export default class SpecialVisualizations { constr: (state, tags, args) => { const t = Translations.t.notes; - const textField = ValidatedTextField.ForType("text").ConstructInputElement({placeholder: t.addCommentPlaceholder}) + const textField = new TextField( + { + placeholder: t.addCommentPlaceholder, + inputStyle: "width: 100%; height: 6rem;", + textAreaRows: 3, + htmlType: "area" + } + ) textField.SetClass("rounded-l border border-grey") const txt = textField.GetValue() @@ -810,8 +816,11 @@ export default class SpecialVisualizations { new Title("Add a comment"), textField, new Combine([ - new Toggle(addCommentButton, undefined, textField.GetValue().map(t => t !== undefined && t.length > 1)).SetClass("mr-2") - , stateButtons]).SetClass("flex justify-end") + stateButtons.SetClass("sm:mr-2"), + new Toggle(addCommentButton, + new Combine([t.typeText]).SetClass("flex items-center h-full subtle"), + textField.GetValue().map(t => t !== undefined && t.length >= 1)).SetClass("sm:mr-2") + ]).SetClass("sm:flex sm:justify-between sm:items-stretch") ]).SetClass("border-2 border-black rounded-xl p-4 block"), t.loginToAddComment, state) } @@ -896,7 +905,7 @@ export default class SpecialVisualizations { args: [], docs: "Shows the title of the popup. Useful for some cases, e.g. 'What is phone number of {title()}?'", example: "`What is the phone number of {title()}`, which might automatically become `What is the phone number of XYZ`.", - constr: (state, tagsSource, args, guistate) => + constr: (state, tagsSource) => new VariableUiElement(tagsSource.map(tags => { const layer = state.layoutToUse.getMatchingLayer(tags) const title = layer?.title?.GetRenderValue(tags) diff --git a/assets/layers/note/note.json b/assets/layers/note/note.json index 10a55aaf6..c2e978a80 100644 --- a/assets/layers/note/note.json +++ b/assets/layers/note/note.json @@ -25,11 +25,13 @@ ] }, "calculatedTags": [ + "_total_comments:=feat.get('comments').length", "_first_comment:=feat.get('comments')[0].text.toLowerCase()", "_opened_by_anonymous_user:=feat.get('comments')[0].user === undefined", "_first_user:=feat.get('comments')[0].user", "_first_user_lc:=feat.get('comments')[0].user?.toLowerCase()", "_first_user_id:=feat.get('comments')[0].uid", + "_last_user_lc:=(() => {const comms = feat.get('comments'); return comms[comms.length - 1].user?.toLowerCase()})()", "_is_import_note:=(() => {const lines = feat.properties['_first_comment'].split('\\n'); const matchesMapCompleteURL = lines.map(l => l.match(\".*https://mapcomplete.osm.be/\\([a-zA-Z_-]+\\)\\(.html\\).*#import\")); const matchedIndexes = matchesMapCompleteURL.map((doesMatch, i) => [doesMatch !== null, i]).filter(v => v[0]).map(v => v[1]); return matchedIndexes[0] })()" ], "titleIcons": [ @@ -79,7 +81,13 @@ } ] }, - "iconSize": "40,40,bottom" + "iconSize": "40,40,bottom", + "iconBadges": [ + { + "if": "_total_comments>1", + "then": "speech_bubble" + } + ] } ], "filter": [ @@ -147,6 +155,38 @@ } ] }, + { + "id": "edited_by", + "options": [ + { + "osmTags": "_lastt_user_lc~.*{search}.*", + "fields": [ + { + "name": "search" + } + ], + "question": { + "en": "Opened before {search}" + } + } + ] + }, + { + "id": "not_edited_by", + "options": [ + { + "osmTags": "_last_user_lc!~.*{search}.*", + "fields": [ + { + "name": "search" + } + ], + "question": { + "en": "Opened after {search}" + } + } + ] + }, { "id": "opened_before", "options": [ @@ -159,7 +199,7 @@ } ], "question": { - "en": "Opened before {search}" + "en": "Opened by anonymous user" } } ] @@ -176,7 +216,7 @@ } ], "question": { - "en": "Opened after {search}" + "en": "Only show open notes" } } ] @@ -187,7 +227,7 @@ { "osmTags": "_opened_by_anonymous_user=true", "question": { - "en": "Opened by anonymous user" + "en": "Hide import notes" } } ] diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index 9b88eec4a..e750cada2 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -1146,6 +1146,16 @@ "sources": [ "https://github.com/twitter/twemoji" ] + }, + { + "path": "speech_bubble_black_outline.svg", + "license": "CC-BY 4.0", + "authors": [ + "Twemoji" + ], + "sources": [ + "https://github.com/twitter/twemoji" + ] }, { "path": "square.svg", @@ -1341,4 +1351,4 @@ "https://www.wikipedia.org/" ] } -] \ No newline at end of file +] diff --git a/assets/svg/speech_bubble.svg b/assets/svg/speech_bubble.svg index d09189719..c647c0325 100644 --- a/assets/svg/speech_bubble.svg +++ b/assets/svg/speech_bubble.svg @@ -1 +1,32 @@ - \ No newline at end of file + + + + + + diff --git a/assets/themes/etymology.json b/assets/themes/etymology.json index c76f41286..9eb22f16a 100644 --- a/assets/themes/etymology.json +++ b/assets/themes/etymology.json @@ -227,4 +227,4 @@ } ], "hideFromOverview": false -} +} \ No newline at end of file diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index da73732b3..bba28b38f 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -1,13 +1,16 @@ { "id": "mapcomplete-changes", "title": { - "en": "Changes made with MapComplete" + "en": "Changes made with MapComplete", + "de": "Änderungen mit MapComplete" }, "shortDescription": { - "en": "Shows changes made by MapComplete" + "en": "Shows changes made by MapComplete", + "de": "Zeigt Änderungen, die von MapComplete vorgenommen wurden" }, "description": { - "en": "This maps shows all the changes made with MapComplete" + "en": "This maps shows all the changes made with MapComplete", + "de": "Diese Karte zeigt alle mit MapComplete vorgenommenen Änderungen" }, "maintainer": "", "icon": "./assets/svg/logo.svg", @@ -22,7 +25,8 @@ { "id": "mapcomplete-changes", "name": { - "en": "Changeset centers" + "en": "Changeset centers", + "de": "Schwerpunkte von Änderungssätzen" }, "minzoom": 0, "source": { @@ -36,35 +40,41 @@ ], "title": { "render": { - "en": "Changeset for {theme}" + "en": "Changeset for {theme}", + "de": "Änderungssatz für {theme}" } }, "description": { - "en": "Shows all MapComplete changes" + "en": "Shows all MapComplete changes", + "de": "Zeigt alle MapComplete-Änderungen" }, "tagRenderings": [ { "id": "render_id", "render": { - "en": "Changeset {id}" + "en": "Changeset {id}", + "de": "Änderungssatz {id}" } }, { "id": "contributor", "render": { - "en": "Change made by {_last_edit:contributor}" + "en": "Change made by {_last_edit:contributor}", + "de": "Geändert von {_last_edit:contributor}" } }, { "id": "theme", "render": { - "en": "Change with theme {theme}" + "en": "Change with theme {theme}", + "de": "Änderung mit Thema {theme}" }, "mappings": [ { "if": "theme~http.*", "then": { - "en": "Change with unofficial theme {theme}" + "en": "Change with unofficial theme {theme}", + "de": "Änderung mit inoffiziellem Thema {theme}" } } ] @@ -328,7 +338,8 @@ } ], "question": { - "en": "Themename contains {search}" + "en": "Themename contains {search}", + "de": "Themenname enthält {search}" } } ] @@ -344,7 +355,8 @@ } ], "question": { - "en": "Made by contributor {search}" + "en": "Made by contributor {search}", + "de": "Erstellt von Mitwirkendem {search}" } } ] @@ -360,7 +372,8 @@ } ], "question": { - "en": "Not made by contributor {search}" + "en": "Not made by contributor {search}", + "de": " Nicht erstellt von Mitwirkendem {search}" } } ] @@ -375,7 +388,8 @@ { "id": "link_to_more", "render": { - "en": "More statistics can be found here" + "en": "More statistics can be found here", + "de": "Weitere Statistiken finden Sie hier" } }, { diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index f6586b3df..06d2646a0 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -863,10 +863,6 @@ video { margin-top: 1rem; } -.mr-2 { - margin-right: 0.5rem; -} - .mt-1 { margin-top: 0.25rem; } @@ -887,6 +883,10 @@ video { margin-top: 0.5rem; } +.mr-2 { + margin-right: 0.5rem; +} + .mb-2 { margin-bottom: 0.5rem; } @@ -2457,10 +2457,18 @@ input { margin-right: auto; } + .sm\:mr-2 { + margin-right: 0.5rem; + } + .sm\:mt-5 { margin-top: 1.25rem; } + .sm\:flex { + display: flex; + } + .sm\:h-24 { height: 6rem; } @@ -2493,6 +2501,10 @@ input { align-items: flex-start; } + .sm\:items-stretch { + align-items: stretch; + } + .sm\:justify-between { justify-content: space-between; } diff --git a/langs/en.json b/langs/en.json index bffbdaec7..3abc78b65 100644 --- a/langs/en.json +++ b/langs/en.json @@ -369,6 +369,7 @@ "isCreated": "Your note has been created!", "loginToAddComment": "Login to add a comment", "loginToAddPicture": "Login to add a picture", + "typeText": "Type some text to add a comment", "loginToClose": "Login to close this note", "noteIsPublic": "This will be visible to everyone", "noteLayerDoEnable": "Enable the layer showing notes", diff --git a/langs/layers/en.json b/langs/layers/en.json index fa20120cf..28db106a2 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -3758,6 +3758,20 @@ "question": "Hide import notes" } } + }, + "9": { + "options": { + "0": { + "question": "Only show open notes" + } + } + }, + "10": { + "options": { + "0": { + "question": "Hide import notes" + } + } } }, "name": "OpenStreetMap notes", diff --git a/langs/shared-questions/id.json b/langs/shared-questions/id.json index 91589a6d7..b7a10a3ba 100644 --- a/langs/shared-questions/id.json +++ b/langs/shared-questions/id.json @@ -13,8 +13,12 @@ }, "2": { "then": "Anjing diperbolehkan, tetapi mereka harus diikat" + }, + "3": { + "then": "Anjing diperbolehkan dan dapat berkeliaran dengan bebas" } - } + }, + "question": "Apakah anjing diperbolehkan dalam bisnis ini?" }, "email": { "question": "Apa alamat surel dari {title()}?"