diff --git a/src/UI/Popup/SendEmail.svelte b/src/UI/Popup/SendEmail.svelte new file mode 100644 index 000000000..ca91bb06b --- /dev/null +++ b/src/UI/Popup/SendEmail.svelte @@ -0,0 +1,31 @@ + + + + {button_text} + +SEND EMAIL to {to} +
+subject: {subject} +
+body: {body} +
+{button_text} diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index bf3dce5ab..035841065 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -1,52 +1,56 @@ import Combine from "./Base/Combine" -import {FixedUiElement} from "./Base/FixedUiElement" +import { FixedUiElement } from "./Base/FixedUiElement" import BaseUIElement from "./BaseUIElement" import Title from "./Base/Title" import Table from "./Base/Table" -import {RenderingSpecification, SpecialVisualization, SpecialVisualizationState,} from "./SpecialVisualization" -import {HistogramViz} from "./Popup/HistogramViz" -import {MinimapViz} from "./Popup/MinimapViz" -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 { + RenderingSpecification, + SpecialVisualization, + SpecialVisualizationState, +} from "./SpecialVisualization" +import { HistogramViz } from "./Popup/HistogramViz" +import { MinimapViz } from "./Popup/MinimapViz" +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 {CloseNoteButton} from "./Popup/CloseNoteButton" -import {MapillaryLinkVis} from "./Popup/MapillaryLinkVis" -import {Store, Stores, UIEventSource} from "../Logic/UIEventSource" +import { CloseNoteButton } from "./Popup/CloseNoteButton" +import { MapillaryLinkVis } from "./Popup/MapillaryLinkVis" +import { Store, Stores, UIEventSource } from "../Logic/UIEventSource" import AllTagsPanel from "./Popup/AllTagsPanel.svelte" import AllImageProviders from "../Logic/ImageProviders/AllImageProviders" -import {ImageCarousel} from "./Image/ImageCarousel" -import {ImageUploadFlow} from "./Image/ImageUploadFlow" -import {VariableUiElement} from "./Base/VariableUIElement" -import {Utils} from "../Utils" -import Wikidata, {WikidataResponse} from "../Logic/Web/Wikidata" -import {Translation} from "./i18n/Translation" +import { ImageCarousel } from "./Image/ImageCarousel" +import { ImageUploadFlow } from "./Image/ImageUploadFlow" +import { VariableUiElement } from "./Base/VariableUIElement" +import { Utils } from "../Utils" +import Wikidata, { WikidataResponse } from "../Logic/Web/Wikidata" +import { Translation } from "./i18n/Translation" import Translations from "./i18n/Translations" import ReviewForm from "./Reviews/ReviewForm" import ReviewElement from "./Reviews/ReviewElement" import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization" import LiveQueryHandler from "../Logic/Web/LiveQueryHandler" -import {SubtleButton} from "./Base/SubtleButton" +import { SubtleButton } from "./Base/SubtleButton" import Svg from "../Svg" import NoteCommentElement from "./Popup/NoteCommentElement" import ImgurUploader from "../Logic/ImageProviders/ImgurUploader" import FileSelectorButton from "./Input/FileSelectorButton" -import {LoginToggle} from "./Popup/LoginButton" +import { LoginToggle } from "./Popup/LoginButton" import Toggle from "./Input/Toggle" -import {SubstitutedTranslation} from "./SubstitutedTranslation" +import { SubstitutedTranslation } from "./SubstitutedTranslation" import List from "./Base/List" import StatisticsPanel from "./BigComponents/StatisticsPanel" import AutoApplyButton from "./Popup/AutoApplyButton" -import {LanguageElement} from "./Popup/LanguageElement" +import { LanguageElement } from "./Popup/LanguageElement" import FeatureReviews from "../Logic/Web/MangroveReviews" import Maproulette from "../Logic/Maproulette" 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 {Feature, Point} from "geojson" -import {GeoOperations} from "../Logic/GeoOperations" +import { Feature, Point } from "geojson" +import { GeoOperations } from "../Logic/GeoOperations" import CreateNewNote from "./Popup/CreateNewNote.svelte" import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte" import UserProfile from "./BigComponents/UserProfile.svelte" @@ -54,27 +58,32 @@ import LanguagePicker from "./LanguagePicker" import Link from "./Base/Link" import LayerConfig from "../Models/ThemeConfig/LayerConfig" import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig" -import NearbyImages, {NearbyImageOptions, P4CPicture, SelectOneNearbyImage,} from "./Popup/NearbyImages" -import {Tag} from "../Logic/Tags/Tag" +import NearbyImages, { + NearbyImageOptions, + P4CPicture, + SelectOneNearbyImage, +} from "./Popup/NearbyImages" +import { Tag } from "../Logic/Tags/Tag" import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction" -import {And} from "../Logic/Tags/And" -import {SaveButton} from "./Popup/SaveButton" +import { And } from "../Logic/Tags/And" +import { SaveButton } from "./Popup/SaveButton" import Lazy from "./Base/Lazy" -import {CheckBox} from "./Input/Checkboxes" +import { CheckBox } from "./Input/Checkboxes" import Slider from "./Input/Slider" -import {OsmTags, WayId} from "../Models/OsmFeature" +import { OsmTags, WayId } from "../Models/OsmFeature" import MoveWizard from "./Popup/MoveWizard" import SplitRoadWizard from "./Popup/SplitRoadWizard" -import {ExportAsGpxViz} from "./Popup/ExportAsGpxViz" +import { ExportAsGpxViz } from "./Popup/ExportAsGpxViz" import WikipediaPanel from "./Wikipedia/WikipediaPanel.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 ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonViz" import DeleteWizard from "./Popup/DeleteFlow/DeleteWizard.svelte" -import {OpenJosm} from "./BigComponents/OpenJosm" +import { OpenJosm } from "./BigComponents/OpenJosm" 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 must be in SpecialVisualisations due to weird cyclical import that breaks the tests @@ -173,7 +182,7 @@ class NearbyImageVis implements SpecialVisualization { towardsCenter, new Combine([ new VariableUiElement( - radius.GetValue().map((radius) => t.withinRadius.Subs({radius})) + radius.GetValue().map((radius) => t.withinRadius.Subs({ radius })) ), radius, ]).SetClass("flex justify-between"), @@ -386,24 +395,24 @@ export default class SpecialVisualizations { viz.docs, viz.args.length > 0 ? new Table( - ["name", "default", "description"], - viz.args.map((arg) => { - let defaultArg = arg.defaultValue ?? "_undefined_" - if (defaultArg == "") { - defaultArg = "_empty string_" - } - return [arg.name, defaultArg, arg.doc] - }) - ) + ["name", "default", "description"], + viz.args.map((arg) => { + let defaultArg = arg.defaultValue ?? "_undefined_" + if (defaultArg == "") { + defaultArg = "_empty string_" + } + return [arg.name, defaultArg, arg.doc] + }) + ) : undefined, new Title("Example usage of " + viz.funcName, 4), new FixedUiElement( viz.example ?? - "`{" + - viz.funcName + - "(" + - viz.args.map((arg) => arg.defaultValue).join(",") + - ")}`" + "`{" + + viz.funcName + + "(" + + viz.args.map((arg) => arg.defaultValue).join(",") + + ")}`" ).SetClass("literal-code"), ]) } @@ -462,14 +471,14 @@ export default class SpecialVisualizations { s.structuredExamples === undefined ? [] : s.structuredExamples().map((e) => { - return s.constr( - state, - new UIEventSource>(e.feature.properties), - e.args, - e.feature, - undefined - ) - }) + return s.constr( + state, + new UIEventSource>(e.feature.properties), + e.args, + e.feature, + undefined + ) + }) return new Combine([new Title(s.funcName), s.docs, ...examples]) } @@ -484,7 +493,7 @@ export default class SpecialVisualizations { let [lon, lat] = GeoOperations.centerpointCoordinates(feature) return new SvelteUIElement(AddNewPoint, { state, - coordinate: {lon, lat}, + coordinate: { lon, lat }, }) }, }, @@ -603,7 +612,7 @@ export default class SpecialVisualizations { feature: Feature ): BaseUIElement { const [lon, lat] = GeoOperations.centerpointCoordinates(feature) - return new SvelteUIElement(CreateNewNote, {state, coordinate: {lon, lat}}) + return new SvelteUIElement(CreateNewNote, { state, coordinate: { lon, lat } }) }, }, new CloseNoteButton(), @@ -680,7 +689,7 @@ export default class SpecialVisualizations { docs: "Prints all key-value pairs of the object - used for debugging", args: [], constr: (state, tags: UIEventSource) => - new SvelteUIElement(AllTagsPanel, {tags, state}), + new SvelteUIElement(AllTagsPanel, { tags, state }), }, { funcName: "image_carousel", @@ -1229,23 +1238,7 @@ export default class SpecialVisualizations { }, ], constr(__, tags, args) { - return new VariableUiElement( - 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, - }) - }) - ) + return new SvelteUIElement(SendEmail, { args, tags }) }, }, { @@ -1319,7 +1312,7 @@ export default class SpecialVisualizations { ], constr(state, featureTags, args) { const [key, tr] = args - const translation = new Translation({"*": tr}) + const translation = new Translation({ "*": tr }) return new VariableUiElement( featureTags.map((tags) => { const properties: object[] = JSON.parse(tags[key]) @@ -1340,29 +1333,46 @@ export default class SpecialVisualizations { { funcName: "fediverse_link", docs: "Converts a fediverse username or link into a clickable link", - args: [{ - name: "key", - doc: "The attribute-name containing the link", - required: true - }], - constr(state: SpecialVisualizationState, tagSource: UIEventSource>, argument: string[], feature: Feature, layer: LayerConfig): BaseUIElement { + args: [ + { + name: "key", + doc: "The attribute-name containing the link", + required: true, + }, + ], + constr( + state: SpecialVisualizationState, + tagSource: UIEventSource>, + argument: string[], + feature: Feature, + layer: LayerConfig + ): BaseUIElement { const key = argument[0] const validator = new FediverseValidator() - return new VariableUiElement(tagSource.map(tags => tags[key]).map(fediAccount => { - fediAccount = validator.reformat(fediAccount) - const [_, username, host] = fediAccount.match(FediverseValidator.usernameAtServer) + return new VariableUiElement( + tagSource + .map((tags) => tags[key]) + .map((fediAccount) => { + fediAccount = validator.reformat(fediAccount) + 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)) const invalid = specialVisualizations - .map((sp, i) => ({sp, i})) + .map((sp, i) => ({ sp, i })) .filter((sp) => sp.sp.funcName === undefined) if (invalid.length > 0) { throw ( diff --git a/src/Utils.ts b/src/Utils.ts index 57ff3658a..9767027f4 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -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}{def}ghi", {def: 'XYZ'}) // => "abcXYZXYZghi" * 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 tags @@ -456,7 +457,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be if (txt === undefined) { return undefined } - const regex = /(.*?){([^}]*)}(.*)/ + const regex = /(.*?){([^}]*)}(.*)/s let match = txt.match(regex)