forked from MapComplete/MapComplete
		
	Fix: substituteKeys works correctly if newline is in the text, fix 'send email to report broken'-button by porting it to svelte
This commit is contained in:
		
							parent
							
								
									2bbf966d22
								
							
						
					
					
						commit
						b4f65bf2f7
					
				
					 3 changed files with 137 additions and 95 deletions
				
			
		
							
								
								
									
										31
									
								
								src/UI/Popup/SendEmail.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/UI/Popup/SendEmail.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | <script lang="ts"> | ||||||
|  |    | ||||||
|  |   import type { OsmTags } from "../../Models/OsmFeature"; | ||||||
|  |   import Svg from "../../Svg"; | ||||||
|  |   import ToSvelte from "../Base/ToSvelte.svelte"; | ||||||
|  |   import { Utils } from "../../Utils"; | ||||||
|  | 
 | ||||||
|  |   export let tags: Store<OsmTags> | ||||||
|  |   export let args: string[] | ||||||
|  | 
 | ||||||
|  |   let [to, subject, body, button_text] = args.map(a => Utils.SubstituteKeys(a, $tags)) | ||||||
|  |   let url = "mailto:" + | ||||||
|  |     to + | ||||||
|  |     "?subject=" + | ||||||
|  |     encodeURIComponent(subject) + | ||||||
|  |     "&body=" + | ||||||
|  |     encodeURIComponent(body) | ||||||
|  |   $: console.log(url) | ||||||
|  |   console.log(">>> args", args) | ||||||
|  | </script> | ||||||
|  | <a class="button flex items-center w-full" href={url}> | ||||||
|  |   <ToSvelte construct={Svg.envelope_svg().SetClass("w-8 h-8 mr-4 shrink-0")}/> | ||||||
|  |   {button_text} | ||||||
|  | </a> | ||||||
|  | SEND EMAIL to {to}  | ||||||
|  | <br/> | ||||||
|  | subject: {subject} | ||||||
|  | <br/> | ||||||
|  | body: {body} | ||||||
|  | <br/> | ||||||
|  | {button_text} | ||||||
|  | @ -1,52 +1,56 @@ | ||||||
| import Combine from "./Base/Combine" | import Combine from "./Base/Combine" | ||||||
| import {FixedUiElement} from "./Base/FixedUiElement" | import { FixedUiElement } from "./Base/FixedUiElement" | ||||||
| import BaseUIElement from "./BaseUIElement" | import BaseUIElement from "./BaseUIElement" | ||||||
| import Title from "./Base/Title" | import Title from "./Base/Title" | ||||||
| import Table from "./Base/Table" | import Table from "./Base/Table" | ||||||
| import {RenderingSpecification, SpecialVisualization, SpecialVisualizationState,} from "./SpecialVisualization" | import { | ||||||
| import {HistogramViz} from "./Popup/HistogramViz" |     RenderingSpecification, | ||||||
| import {MinimapViz} from "./Popup/MinimapViz" |     SpecialVisualization, | ||||||
| import {ShareLinkViz} from "./Popup/ShareLinkViz" |     SpecialVisualizationState, | ||||||
| import {UploadToOsmViz} from "./Popup/UploadToOsmViz" | } from "./SpecialVisualization" | ||||||
| import {MultiApplyViz} from "./Popup/MultiApplyViz" | import { HistogramViz } from "./Popup/HistogramViz" | ||||||
| import {AddNoteCommentViz} from "./Popup/AddNoteCommentViz" | import { MinimapViz } from "./Popup/MinimapViz" | ||||||
| import {PlantNetDetectionViz} from "./Popup/PlantNetDetectionViz" | import { ShareLinkViz } from "./Popup/ShareLinkViz" | ||||||
|  | import { UploadToOsmViz } from "./Popup/UploadToOsmViz" | ||||||
|  | import { MultiApplyViz } from "./Popup/MultiApplyViz" | ||||||
|  | import { AddNoteCommentViz } from "./Popup/AddNoteCommentViz" | ||||||
|  | import { PlantNetDetectionViz } from "./Popup/PlantNetDetectionViz" | ||||||
| import TagApplyButton from "./Popup/TagApplyButton" | import TagApplyButton from "./Popup/TagApplyButton" | ||||||
| import {CloseNoteButton} from "./Popup/CloseNoteButton" | import { CloseNoteButton } from "./Popup/CloseNoteButton" | ||||||
| import {MapillaryLinkVis} from "./Popup/MapillaryLinkVis" | import { MapillaryLinkVis } from "./Popup/MapillaryLinkVis" | ||||||
| import {Store, Stores, UIEventSource} from "../Logic/UIEventSource" | import { Store, Stores, UIEventSource } from "../Logic/UIEventSource" | ||||||
| import AllTagsPanel from "./Popup/AllTagsPanel.svelte" | import AllTagsPanel from "./Popup/AllTagsPanel.svelte" | ||||||
| import AllImageProviders from "../Logic/ImageProviders/AllImageProviders" | import AllImageProviders from "../Logic/ImageProviders/AllImageProviders" | ||||||
| import {ImageCarousel} from "./Image/ImageCarousel" | import { ImageCarousel } from "./Image/ImageCarousel" | ||||||
| import {ImageUploadFlow} from "./Image/ImageUploadFlow" | import { ImageUploadFlow } from "./Image/ImageUploadFlow" | ||||||
| import {VariableUiElement} from "./Base/VariableUIElement" | import { VariableUiElement } from "./Base/VariableUIElement" | ||||||
| import {Utils} from "../Utils" | import { Utils } from "../Utils" | ||||||
| import Wikidata, {WikidataResponse} from "../Logic/Web/Wikidata" | import Wikidata, { WikidataResponse } from "../Logic/Web/Wikidata" | ||||||
| import {Translation} from "./i18n/Translation" | import { Translation } from "./i18n/Translation" | ||||||
| import Translations from "./i18n/Translations" | import Translations from "./i18n/Translations" | ||||||
| import ReviewForm from "./Reviews/ReviewForm" | import ReviewForm from "./Reviews/ReviewForm" | ||||||
| import ReviewElement from "./Reviews/ReviewElement" | import ReviewElement from "./Reviews/ReviewElement" | ||||||
| import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization" | import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization" | ||||||
| import LiveQueryHandler from "../Logic/Web/LiveQueryHandler" | import LiveQueryHandler from "../Logic/Web/LiveQueryHandler" | ||||||
| import {SubtleButton} from "./Base/SubtleButton" | import { SubtleButton } from "./Base/SubtleButton" | ||||||
| import Svg from "../Svg" | import Svg from "../Svg" | ||||||
| import NoteCommentElement from "./Popup/NoteCommentElement" | import NoteCommentElement from "./Popup/NoteCommentElement" | ||||||
| import ImgurUploader from "../Logic/ImageProviders/ImgurUploader" | import ImgurUploader from "../Logic/ImageProviders/ImgurUploader" | ||||||
| import FileSelectorButton from "./Input/FileSelectorButton" | import FileSelectorButton from "./Input/FileSelectorButton" | ||||||
| import {LoginToggle} from "./Popup/LoginButton" | import { LoginToggle } from "./Popup/LoginButton" | ||||||
| import Toggle from "./Input/Toggle" | import Toggle from "./Input/Toggle" | ||||||
| import {SubstitutedTranslation} from "./SubstitutedTranslation" | import { SubstitutedTranslation } from "./SubstitutedTranslation" | ||||||
| import List from "./Base/List" | import List from "./Base/List" | ||||||
| import StatisticsPanel from "./BigComponents/StatisticsPanel" | import StatisticsPanel from "./BigComponents/StatisticsPanel" | ||||||
| import AutoApplyButton from "./Popup/AutoApplyButton" | import AutoApplyButton from "./Popup/AutoApplyButton" | ||||||
| import {LanguageElement} from "./Popup/LanguageElement" | import { LanguageElement } from "./Popup/LanguageElement" | ||||||
| import FeatureReviews from "../Logic/Web/MangroveReviews" | import FeatureReviews from "../Logic/Web/MangroveReviews" | ||||||
| import Maproulette from "../Logic/Maproulette" | import Maproulette from "../Logic/Maproulette" | ||||||
| import SvelteUIElement from "./Base/SvelteUIElement" | import SvelteUIElement from "./Base/SvelteUIElement" | ||||||
| import {BBoxFeatureSourceForLayer} from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource" | import { BBoxFeatureSourceForLayer } from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource" | ||||||
| import QuestionViz from "./Popup/QuestionViz" | import QuestionViz from "./Popup/QuestionViz" | ||||||
| import {Feature, Point} from "geojson" | import { Feature, Point } from "geojson" | ||||||
| import {GeoOperations} from "../Logic/GeoOperations" | import { GeoOperations } from "../Logic/GeoOperations" | ||||||
| import CreateNewNote from "./Popup/CreateNewNote.svelte" | import CreateNewNote from "./Popup/CreateNewNote.svelte" | ||||||
| import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte" | import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte" | ||||||
| import UserProfile from "./BigComponents/UserProfile.svelte" | import UserProfile from "./BigComponents/UserProfile.svelte" | ||||||
|  | @ -54,27 +58,32 @@ import LanguagePicker from "./LanguagePicker" | ||||||
| import Link from "./Base/Link" | import Link from "./Base/Link" | ||||||
| import LayerConfig from "../Models/ThemeConfig/LayerConfig" | import LayerConfig from "../Models/ThemeConfig/LayerConfig" | ||||||
| import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig" | import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig" | ||||||
| import NearbyImages, {NearbyImageOptions, P4CPicture, SelectOneNearbyImage,} from "./Popup/NearbyImages" | import NearbyImages, { | ||||||
| import {Tag} from "../Logic/Tags/Tag" |     NearbyImageOptions, | ||||||
|  |     P4CPicture, | ||||||
|  |     SelectOneNearbyImage, | ||||||
|  | } from "./Popup/NearbyImages" | ||||||
|  | import { Tag } from "../Logic/Tags/Tag" | ||||||
| import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction" | import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction" | ||||||
| import {And} from "../Logic/Tags/And" | import { And } from "../Logic/Tags/And" | ||||||
| import {SaveButton} from "./Popup/SaveButton" | import { SaveButton } from "./Popup/SaveButton" | ||||||
| import Lazy from "./Base/Lazy" | import Lazy from "./Base/Lazy" | ||||||
| import {CheckBox} from "./Input/Checkboxes" | import { CheckBox } from "./Input/Checkboxes" | ||||||
| import Slider from "./Input/Slider" | import Slider from "./Input/Slider" | ||||||
| import {OsmTags, WayId} from "../Models/OsmFeature" | import { OsmTags, WayId } from "../Models/OsmFeature" | ||||||
| import MoveWizard from "./Popup/MoveWizard" | import MoveWizard from "./Popup/MoveWizard" | ||||||
| import SplitRoadWizard from "./Popup/SplitRoadWizard" | import SplitRoadWizard from "./Popup/SplitRoadWizard" | ||||||
| import {ExportAsGpxViz} from "./Popup/ExportAsGpxViz" | import { ExportAsGpxViz } from "./Popup/ExportAsGpxViz" | ||||||
| import WikipediaPanel from "./Wikipedia/WikipediaPanel.svelte" | import WikipediaPanel from "./Wikipedia/WikipediaPanel.svelte" | ||||||
| import TagRenderingEditable from "./Popup/TagRendering/TagRenderingEditable.svelte" | import TagRenderingEditable from "./Popup/TagRendering/TagRenderingEditable.svelte" | ||||||
| import {PointImportButtonViz} from "./Popup/ImportButtons/PointImportButtonViz" | import { PointImportButtonViz } from "./Popup/ImportButtons/PointImportButtonViz" | ||||||
| import WayImportButtonViz from "./Popup/ImportButtons/WayImportButtonViz" | import WayImportButtonViz from "./Popup/ImportButtons/WayImportButtonViz" | ||||||
| import ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonViz" | import ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonViz" | ||||||
| import DeleteWizard from "./Popup/DeleteFlow/DeleteWizard.svelte" | import DeleteWizard from "./Popup/DeleteFlow/DeleteWizard.svelte" | ||||||
| import {OpenJosm} from "./BigComponents/OpenJosm" | import { OpenJosm } from "./BigComponents/OpenJosm" | ||||||
| import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte" | import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte" | ||||||
| import FediverseValidator from "./InputElement/Validators/FediverseValidator"; | import FediverseValidator from "./InputElement/Validators/FediverseValidator" | ||||||
|  | import SendEmail from "./Popup/SendEmail.svelte" | ||||||
| 
 | 
 | ||||||
| class NearbyImageVis implements SpecialVisualization { | class NearbyImageVis implements SpecialVisualization { | ||||||
|     // Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
 |     // Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
 | ||||||
|  | @ -173,7 +182,7 @@ class NearbyImageVis implements SpecialVisualization { | ||||||
|                 towardsCenter, |                 towardsCenter, | ||||||
|                 new Combine([ |                 new Combine([ | ||||||
|                     new VariableUiElement( |                     new VariableUiElement( | ||||||
|                         radius.GetValue().map((radius) => t.withinRadius.Subs({radius})) |                         radius.GetValue().map((radius) => t.withinRadius.Subs({ radius })) | ||||||
|                     ), |                     ), | ||||||
|                     radius, |                     radius, | ||||||
|                 ]).SetClass("flex justify-between"), |                 ]).SetClass("flex justify-between"), | ||||||
|  | @ -484,7 +493,7 @@ export default class SpecialVisualizations { | ||||||
|                     let [lon, lat] = GeoOperations.centerpointCoordinates(feature) |                     let [lon, lat] = GeoOperations.centerpointCoordinates(feature) | ||||||
|                     return new SvelteUIElement(AddNewPoint, { |                     return new SvelteUIElement(AddNewPoint, { | ||||||
|                         state, |                         state, | ||||||
|                         coordinate: {lon, lat}, |                         coordinate: { lon, lat }, | ||||||
|                     }) |                     }) | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|  | @ -603,7 +612,7 @@ export default class SpecialVisualizations { | ||||||
|                     feature: Feature |                     feature: Feature | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     const [lon, lat] = GeoOperations.centerpointCoordinates(feature) |                     const [lon, lat] = GeoOperations.centerpointCoordinates(feature) | ||||||
|                     return new SvelteUIElement(CreateNewNote, {state, coordinate: {lon, lat}}) |                     return new SvelteUIElement(CreateNewNote, { state, coordinate: { lon, lat } }) | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|             new CloseNoteButton(), |             new CloseNoteButton(), | ||||||
|  | @ -680,7 +689,7 @@ export default class SpecialVisualizations { | ||||||
|                 docs: "Prints all key-value pairs of the object - used for debugging", |                 docs: "Prints all key-value pairs of the object - used for debugging", | ||||||
|                 args: [], |                 args: [], | ||||||
|                 constr: (state, tags: UIEventSource<any>) => |                 constr: (state, tags: UIEventSource<any>) => | ||||||
|                     new SvelteUIElement(AllTagsPanel, {tags, state}), |                     new SvelteUIElement(AllTagsPanel, { tags, state }), | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                 funcName: "image_carousel", |                 funcName: "image_carousel", | ||||||
|  | @ -1229,23 +1238,7 @@ export default class SpecialVisualizations { | ||||||
|                     }, |                     }, | ||||||
|                 ], |                 ], | ||||||
|                 constr(__, tags, args) { |                 constr(__, tags, args) { | ||||||
|                     return new VariableUiElement( |                     return new SvelteUIElement(SendEmail, { args, tags }) | ||||||
|                         tags.map((tags) => { |  | ||||||
|                             const [to, subject, body, button_text] = args.map((str) => |  | ||||||
|                                 Utils.SubstituteKeys(str, tags) |  | ||||||
|                             ) |  | ||||||
|                             const url = |  | ||||||
|                                 "mailto:" + |  | ||||||
|                                 to + |  | ||||||
|                                 "?subject=" + |  | ||||||
|                                 encodeURIComponent(subject) + |  | ||||||
|                                 "&body=" + |  | ||||||
|                                 encodeURIComponent(body) |  | ||||||
|                             return new SubtleButton(Svg.envelope_svg(), button_text, { |  | ||||||
|                                 url, |  | ||||||
|                             }) |  | ||||||
|                         }) |  | ||||||
|                     ) |  | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|  | @ -1319,7 +1312,7 @@ export default class SpecialVisualizations { | ||||||
|                 ], |                 ], | ||||||
|                 constr(state, featureTags, args) { |                 constr(state, featureTags, args) { | ||||||
|                     const [key, tr] = args |                     const [key, tr] = args | ||||||
|                     const translation = new Translation({"*": tr}) |                     const translation = new Translation({ "*": tr }) | ||||||
|                     return new VariableUiElement( |                     return new VariableUiElement( | ||||||
|                         featureTags.map((tags) => { |                         featureTags.map((tags) => { | ||||||
|                             const properties: object[] = JSON.parse(tags[key]) |                             const properties: object[] = JSON.parse(tags[key]) | ||||||
|  | @ -1340,29 +1333,46 @@ export default class SpecialVisualizations { | ||||||
|             { |             { | ||||||
|                 funcName: "fediverse_link", |                 funcName: "fediverse_link", | ||||||
|                 docs: "Converts a fediverse username or link into a clickable link", |                 docs: "Converts a fediverse username or link into a clickable link", | ||||||
|                 args: [{ |                 args: [ | ||||||
|  |                     { | ||||||
|                         name: "key", |                         name: "key", | ||||||
|                         doc: "The attribute-name containing the link", |                         doc: "The attribute-name containing the link", | ||||||
|                     required: true |                         required: true, | ||||||
|                 }], |                     }, | ||||||
|                 constr(state: SpecialVisualizationState, tagSource: UIEventSource<Record<string, string>>, argument: string[], feature: Feature, layer: LayerConfig): BaseUIElement { |                 ], | ||||||
|  |                 constr( | ||||||
|  |                     state: SpecialVisualizationState, | ||||||
|  |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|  |                     argument: string[], | ||||||
|  |                     feature: Feature, | ||||||
|  |                     layer: LayerConfig | ||||||
|  |                 ): BaseUIElement { | ||||||
|                     const key = argument[0] |                     const key = argument[0] | ||||||
|                     const validator = new FediverseValidator() |                     const validator = new FediverseValidator() | ||||||
|                     return new VariableUiElement(tagSource.map(tags => tags[key]).map(fediAccount => { |                     return new VariableUiElement( | ||||||
|  |                         tagSource | ||||||
|  |                             .map((tags) => tags[key]) | ||||||
|  |                             .map((fediAccount) => { | ||||||
|                                 fediAccount = validator.reformat(fediAccount) |                                 fediAccount = validator.reformat(fediAccount) | ||||||
|                             const [_, username, host] = fediAccount.match(FediverseValidator.usernameAtServer) |                                 const [_, username, host] = fediAccount.match( | ||||||
|  |                                     FediverseValidator.usernameAtServer | ||||||
|  |                                 ) | ||||||
| 
 | 
 | ||||||
|                             return new Link(fediAccount, "https://" + host + "/@" + username, true) |                                 return new Link( | ||||||
|                         } |                                     fediAccount, | ||||||
|                     )) |                                     "https://" + host + "/@" + username, | ||||||
|                 } |                                     true | ||||||
|             } |                                 ) | ||||||
|  |                             }) | ||||||
|  |                     ) | ||||||
|  |                 }, | ||||||
|  |             }, | ||||||
|         ] |         ] | ||||||
| 
 | 
 | ||||||
|         specialVisualizations.push(new AutoApplyButton(specialVisualizations)) |         specialVisualizations.push(new AutoApplyButton(specialVisualizations)) | ||||||
| 
 | 
 | ||||||
|         const invalid = specialVisualizations |         const invalid = specialVisualizations | ||||||
|             .map((sp, i) => ({sp, i})) |             .map((sp, i) => ({ sp, i })) | ||||||
|             .filter((sp) => sp.sp.funcName === undefined) |             .filter((sp) => sp.sp.funcName === undefined) | ||||||
|         if (invalid.length > 0) { |         if (invalid.length > 0) { | ||||||
|             throw ( |             throw ( | ||||||
|  |  | ||||||
|  | @ -442,6 +442,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be | ||||||
|      * Utils.SubstituteKeys("abc{def}ghi", {def: 'XYZ'}) // => "abcXYZghi"
 |      * Utils.SubstituteKeys("abc{def}ghi", {def: 'XYZ'}) // => "abcXYZghi"
 | ||||||
|      * Utils.SubstituteKeys("abc{def}{def}ghi", {def: 'XYZ'}) // => "abcXYZXYZghi"
 |      * Utils.SubstituteKeys("abc{def}{def}ghi", {def: 'XYZ'}) // => "abcXYZXYZghi"
 | ||||||
|      * Utils.SubstituteKeys("abc{def}ghi", {def: '{XYZ}'}) // => "abc{XYZ}ghi"
 |      * Utils.SubstituteKeys("abc{def}ghi", {def: '{XYZ}'}) // => "abc{XYZ}ghi"
 | ||||||
|  |      * Utils.SubstituteKeys("abc\n\n{def}ghi", {def: '{XYZ}'}) // => "abc\n\n{XYZ}ghi"
 | ||||||
|      * |      * | ||||||
|      * @param txt |      * @param txt | ||||||
|      * @param tags |      * @param tags | ||||||
|  | @ -456,7 +457,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be | ||||||
|         if (txt === undefined) { |         if (txt === undefined) { | ||||||
|             return undefined |             return undefined | ||||||
|         } |         } | ||||||
|         const regex = /(.*?){([^}]*)}(.*)/ |         const regex = /(.*?){([^}]*)}(.*)/s | ||||||
| 
 | 
 | ||||||
|         let match = txt.match(regex) |         let match = txt.match(regex) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue