forked from MapComplete/MapComplete
		
	Refactoring: port CloseNoteButton to svelte
This commit is contained in:
		
							parent
							
								
									732189955b
								
							
						
					
					
						commit
						c9fa625c98
					
				
					 6 changed files with 120 additions and 209 deletions
				
			
		|  | @ -1,6 +1,6 @@ | ||||||
| { | { | ||||||
|   "name": "mapcomplete", |   "name": "mapcomplete", | ||||||
|   "version": "0.44.13", |   "version": "0.44.14", | ||||||
|   "repository": "https://github.com/pietervdvn/MapComplete", |   "repository": "https://github.com/pietervdvn/MapComplete", | ||||||
|   "description": "A small website to edit OSM easily", |   "description": "A small website to edit OSM easily", | ||||||
|   "bugs": "https://github.com/pietervdvn/MapComplete/issues", |   "bugs": "https://github.com/pietervdvn/MapComplete/issues", | ||||||
|  |  | ||||||
|  | @ -1,96 +0,0 @@ | ||||||
| import { SubtleButton } from "../Base/SubtleButton" |  | ||||||
| import BaseUIElement from "../BaseUIElement" |  | ||||||
| import { OsmConnection, OsmServiceState } from "../../Logic/Osm/OsmConnection" |  | ||||||
| import { VariableUiElement } from "../Base/VariableUIElement" |  | ||||||
| import Loading from "../Base/Loading" |  | ||||||
| import Translations from "../i18n/Translations" |  | ||||||
| import { ImmutableStore, Store } from "../../Logic/UIEventSource" |  | ||||||
| import Combine from "../Base/Combine" |  | ||||||
| import { Translation } from "../i18n/Translation" |  | ||||||
| import SvelteUIElement from "../Base/SvelteUIElement" |  | ||||||
| import Login from "../../assets/svg/Login.svelte" |  | ||||||
| import Invalid from "../../assets/svg/Invalid.svelte" |  | ||||||
| 
 |  | ||||||
| class LoginButton extends SubtleButton { |  | ||||||
|     constructor( |  | ||||||
|         text: BaseUIElement | string, |  | ||||||
|         state: { |  | ||||||
|             osmConnection?: OsmConnection |  | ||||||
|         }, |  | ||||||
|         icon?: BaseUIElement | string |  | ||||||
|     ) { |  | ||||||
|         super(icon ?? new SvelteUIElement(Login), text) |  | ||||||
|         this.onClick(() => { |  | ||||||
|             state.osmConnection?.AttemptLogin() |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export class LoginToggle extends VariableUiElement { |  | ||||||
|     /** |  | ||||||
|      * Constructs an element which shows 'el' if the user is logged in |  | ||||||
|      * If not logged in, 'text' is shown on the button which invites to login. |  | ||||||
|      * |  | ||||||
|      * If logging in is not possible for some reason, an appropriate error message is shown |  | ||||||
|      * |  | ||||||
|      * State contains the 'osmConnection' to work with |  | ||||||
|      * @param el: Element to show when logged in |  | ||||||
|      * @param text: To show on the login button. Default: nothing |  | ||||||
|      * @param state: if no osmConnection is given, assumes test situation and will show 'el' as if logged in |  | ||||||
|      */ |  | ||||||
|     constructor( |  | ||||||
|         el: BaseUIElement, |  | ||||||
|         text: BaseUIElement | string, |  | ||||||
|         state: { |  | ||||||
|             readonly osmConnection?: OsmConnection |  | ||||||
|             readonly featureSwitchUserbadge?: Store<boolean> |  | ||||||
|         } |  | ||||||
|     ) { |  | ||||||
|         const loading = new Loading("Trying to log in...") |  | ||||||
|         const login = text === undefined ? undefined : new LoginButton(text, state) |  | ||||||
|         const t = Translations.t.general |  | ||||||
|         const offlineModes: Partial<Record<OsmServiceState, Translation>> = { |  | ||||||
|             offline: t.loginFailedOfflineMode, |  | ||||||
|             unreachable: t.loginFailedUnreachableMode, |  | ||||||
|             readonly: t.loginFailedReadonlyMode, |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         super( |  | ||||||
|             state.osmConnection?.loadingStatus?.map( |  | ||||||
|                 (osmConnectionState) => { |  | ||||||
|                     if (state.featureSwitchUserbadge?.data == false) { |  | ||||||
|                         // All features to login with are disabled
 |  | ||||||
|                         return undefined |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     const apiState = state.osmConnection?.apiIsOnline?.data ?? "online" |  | ||||||
|                     const apiTranslation = offlineModes[apiState] |  | ||||||
|                     if (apiTranslation !== undefined) { |  | ||||||
|                         return new Combine([ |  | ||||||
|                             new SvelteUIElement(Invalid).SetClass("w-8 h-8 m-2 shrink-0"), |  | ||||||
|                             apiTranslation, |  | ||||||
|                         ]).SetClass("flex items-center alert max-w-64") |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (osmConnectionState === "loading") { |  | ||||||
|                         return loading |  | ||||||
|                     } |  | ||||||
|                     if (osmConnectionState === "not-attempted") { |  | ||||||
|                         return login |  | ||||||
|                     } |  | ||||||
|                     if (osmConnectionState === "logged-in") { |  | ||||||
|                         return el |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     // Fallback
 |  | ||||||
|                     return new LoginButton( |  | ||||||
|                         Translations.t.general.loginFailed, |  | ||||||
|                         state, |  | ||||||
|                         new SvelteUIElement(Invalid) |  | ||||||
|                     ) |  | ||||||
|                 }, |  | ||||||
|                 [state.featureSwitchUserbadge, state.osmConnection?.apiIsOnline] |  | ||||||
|             ) ?? new ImmutableStore(el) |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										54
									
								
								src/UI/Popup/Notes/CloseNoteButton.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/UI/Popup/Notes/CloseNoteButton.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,54 @@ | ||||||
|  | <script lang="ts"> | ||||||
|  |   import type { SpecialVisualizationState } from "../../SpecialVisualization" | ||||||
|  |   import { UIEventSource } from "../../../Logic/UIEventSource" | ||||||
|  |   import LoginToggle from "../../Base/LoginToggle.svelte" | ||||||
|  |   import Translations from "../../i18n/Translations" | ||||||
|  |   import Tr from "../../Base/Tr.svelte" | ||||||
|  |   import Icon from "../../Map/Icon.svelte" | ||||||
|  |   import NoteCommentElement from "./NoteCommentElement" | ||||||
|  |   import { Translation } from "../../i18n/Translation" | ||||||
|  | 
 | ||||||
|  |   const t = Translations.t.notes | ||||||
|  |   export let state: SpecialVisualizationState | ||||||
|  |   export let tags: UIEventSource<Record<string, string>> | ||||||
|  |   export let icon: string = "checkmark" | ||||||
|  |   export let idkey: string = "id" | ||||||
|  |   export let message: string | ||||||
|  |   export let text: Translation = t.closeNote | ||||||
|  |   export let minzoom: number | ||||||
|  |   export let zoomMoreMessage: string | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   let curZoom = state.mapProperties.zoom | ||||||
|  |   const isClosed = tags.map((tags) => (tags["closed_at"] ?? "") !== "") | ||||||
|  | 
 | ||||||
|  |   async function closeNote() { | ||||||
|  |     const id = tags.data[idkey] | ||||||
|  |     await state.osmConnection.closeNote(id, message) | ||||||
|  |     NoteCommentElement.addCommentTo(message, tags, state) | ||||||
|  |     tags.data["closed_at"] = new Date().toISOString() | ||||||
|  |     tags.ping() | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <LoginToggle {state}> | ||||||
|  |   <div slot="not-logged-in"> | ||||||
|  |     <Tr t={t.loginToClose} /> | ||||||
|  |   </div> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   {#if $isClosed} | ||||||
|  |     <Tr cls="thanks" t={t.isClosed} /> | ||||||
|  |   {:else if minzoom <= $curZoom} | ||||||
|  |     <button on:click={() => closeNote()}> | ||||||
|  |       <div class="flex items-center gap-x-2"> | ||||||
|  |         <Icon {icon} clss="w-10 h-10" /> | ||||||
|  |         <Tr t={text} /> | ||||||
|  |       </div> | ||||||
|  |     </button> | ||||||
|  |   {:else if zoomMoreMessage} | ||||||
|  |     {zoomMoreMessage} | ||||||
|  |   {/if} | ||||||
|  | 
 | ||||||
|  | </LoginToggle> | ||||||
|  | @ -1,106 +0,0 @@ | ||||||
| import BaseUIElement from "../../BaseUIElement" |  | ||||||
| import Translations from "../../i18n/Translations" |  | ||||||
| import { Utils } from "../../../Utils" |  | ||||||
| import Img from "../../Base/Img" |  | ||||||
| import { SubtleButton } from "../../Base/SubtleButton" |  | ||||||
| import Toggle from "../../Input/Toggle" |  | ||||||
| import { LoginToggle } from ".././LoginButton" |  | ||||||
| import { SpecialVisualization, SpecialVisualizationState } from "../../SpecialVisualization" |  | ||||||
| import { UIEventSource } from "../../../Logic/UIEventSource" |  | ||||||
| import Constants from "../../../Models/Constants" |  | ||||||
| import SvelteUIElement from "../../Base/SvelteUIElement" |  | ||||||
| import Checkmark from "../../../assets/svg/Checkmark.svelte" |  | ||||||
| import NoteCommentElement from "./NoteCommentElement" |  | ||||||
| import Icon from "../../Map/Icon.svelte" |  | ||||||
| 
 |  | ||||||
| export class CloseNoteButton implements SpecialVisualization { |  | ||||||
|     public readonly funcName = "close_note" |  | ||||||
|     public readonly needsUrls = [Constants.osmAuthConfig.url] |  | ||||||
|     public readonly docs = |  | ||||||
|         "Button to close a note. A predefined text can be defined to close the note with. If the note is already closed, will show a small text." |  | ||||||
|     public readonly args = [ |  | ||||||
|         { |  | ||||||
|             name: "text", |  | ||||||
|             doc: "Text to show on this button", |  | ||||||
|             required: true, |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             name: "icon", |  | ||||||
|             doc: "Icon to show", |  | ||||||
|             defaultValue: "checkmark.svg", |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             name: "idkey", |  | ||||||
|             doc: "The property name where the ID of the note to close can be found", |  | ||||||
|             defaultValue: "id", |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             name: "comment", |  | ||||||
|             doc: "Text to add onto the note when closing", |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             name: "minZoom", |  | ||||||
|             doc: "If set, only show the closenote button if zoomed in enough", |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             name: "zoomButton", |  | ||||||
|             doc: "Text to show if not zoomed in enough", |  | ||||||
|         }, |  | ||||||
|     ] |  | ||||||
| 
 |  | ||||||
|     public constr( |  | ||||||
|         state: SpecialVisualizationState, |  | ||||||
|         tags: UIEventSource<Record<string, string>>, |  | ||||||
|         args: string[] |  | ||||||
|     ): BaseUIElement { |  | ||||||
|         const t = Translations.t.notes |  | ||||||
| 
 |  | ||||||
|         const params: { |  | ||||||
|             text: string |  | ||||||
|             icon: string |  | ||||||
|             idkey: string |  | ||||||
|             comment: string |  | ||||||
|             minZoom: string |  | ||||||
|             zoomButton: string |  | ||||||
|         } = <any>Utils.ParseVisArgs(this.args, args) |  | ||||||
| 
 |  | ||||||
|         let icon: BaseUIElement = new SvelteUIElement(Icon, { |  | ||||||
|             icon: params.icon ?? "checkmark.svg", |  | ||||||
|         }) |  | ||||||
|         let textToShow = t.closeNote |  | ||||||
|         if ((params.text ?? "") !== "") { |  | ||||||
|             textToShow = Translations.T(args[0]) |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         let closeButton: BaseUIElement = new SubtleButton(icon, textToShow) |  | ||||||
|         const isClosed = tags.map((tags) => (tags["closed_at"] ?? "") !== "") |  | ||||||
|         closeButton.onClick(() => { |  | ||||||
|             const id = tags.data[args[2] ?? "id"] |  | ||||||
|             const text = args[3] |  | ||||||
|             state.osmConnection.closeNote(id, text)?.then((_) => { |  | ||||||
|                 NoteCommentElement.addCommentTo(text, tags, state) |  | ||||||
|                 tags.data["closed_at"] = new Date().toISOString() |  | ||||||
|                 tags.ping() |  | ||||||
|             }) |  | ||||||
|         }) |  | ||||||
| 
 |  | ||||||
|         if ((params.minZoom ?? "") !== "" && !isNaN(Number(params.minZoom))) { |  | ||||||
|             closeButton = new Toggle( |  | ||||||
|                 closeButton, |  | ||||||
|                 params.zoomButton ?? "", |  | ||||||
|                 state.mapProperties.zoom.map((zoom) => zoom >= Number(params.minZoom)) |  | ||||||
|             ) |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return new LoginToggle( |  | ||||||
|             new Toggle( |  | ||||||
|                 t.isClosed.SetClass("thanks"), |  | ||||||
|                 closeButton, |  | ||||||
| 
 |  | ||||||
|                 isClosed |  | ||||||
|             ), |  | ||||||
|             t.loginToClose, |  | ||||||
|             state |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -15,7 +15,6 @@ import { MultiApplyViz } from "./Popup/MultiApplyViz" | ||||||
| import { AddNoteCommentViz } from "./Popup/Notes/AddNoteCommentViz" | import { AddNoteCommentViz } from "./Popup/Notes/AddNoteCommentViz" | ||||||
| import { PlantNetDetectionViz } from "./Popup/PlantNetDetectionViz" | import { PlantNetDetectionViz } from "./Popup/PlantNetDetectionViz" | ||||||
| import TagApplyButton from "./Popup/TagApplyButton" | import TagApplyButton from "./Popup/TagApplyButton" | ||||||
| import { CloseNoteButton } from "./Popup/Notes/CloseNoteButton" |  | ||||||
| import { MapillaryLinkVis } from "./Popup/MapillaryLinkVis" | import { MapillaryLinkVis } from "./Popup/MapillaryLinkVis" | ||||||
| import { ImmutableStore, Store, Stores, UIEventSource } from "../Logic/UIEventSource" | import { ImmutableStore, Store, Stores, UIEventSource } from "../Logic/UIEventSource" | ||||||
| import AllTagsPanel from "./Popup/AllTagsPanel.svelte" | import AllTagsPanel from "./Popup/AllTagsPanel.svelte" | ||||||
|  | @ -99,6 +98,7 @@ import Trash from "@babeard/svelte-heroicons/mini/Trash" | ||||||
| import NothingKnown from "./Popup/NothingKnown.svelte" | import NothingKnown from "./Popup/NothingKnown.svelte" | ||||||
| import { CombinedFetcher } from "../Logic/Web/NearbyImagesSearch" | import { CombinedFetcher } from "../Logic/Web/NearbyImagesSearch" | ||||||
| import { And } from "../Logic/Tags/And" | import { And } from "../Logic/Tags/And" | ||||||
|  | import CloseNoteButton from "./Popup/Notes/CloseNoteButton.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
 | ||||||
|  | @ -215,6 +215,66 @@ class StealViz implements SpecialVisualization { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | class CloseNoteViz implements SpecialVisualization { | ||||||
|  |     public readonly funcName = "close_note" | ||||||
|  |     public readonly needsUrls = [Constants.osmAuthConfig.url] | ||||||
|  |     public readonly docs = | ||||||
|  |         "Button to close a note. A predefined text can be defined to close the note with. If the note is already closed, will show a small text." | ||||||
|  |     public readonly args = [ | ||||||
|  |         { | ||||||
|  |             name: "text", | ||||||
|  |             doc: "Text to show on this button", | ||||||
|  |             required: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             name: "icon", | ||||||
|  |             doc: "Icon to show", | ||||||
|  |             defaultValue: "checkmark.svg", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             name: "idkey", | ||||||
|  |             doc: "The property name where the ID of the note to close can be found", | ||||||
|  |             defaultValue: "id", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             name: "comment", | ||||||
|  |             doc: "Text to add onto the note when closing", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             name: "minZoom", | ||||||
|  |             doc: "If set, only show the closenote button if zoomed in enough", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             name: "zoomButton", | ||||||
|  |             doc: "Text to show if not zoomed in enough", | ||||||
|  |         }, | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     public constr(state: SpecialVisualizationState, tags: UIEventSource<Record<string, string>>, args: string[], feature: Feature, layer: LayerConfig): SvelteUIElement { | ||||||
|  | 
 | ||||||
|  |         const { | ||||||
|  |             text, | ||||||
|  |             icon, | ||||||
|  |             idkey, | ||||||
|  |             comment, | ||||||
|  |             minZoom, | ||||||
|  |             zoomButton | ||||||
|  |         } = Utils.ParseVisArgs(this.args, args) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         return new SvelteUIElement(CloseNoteButton, { | ||||||
|  |             state, | ||||||
|  |             tags, | ||||||
|  |             icon, | ||||||
|  |             idkey, | ||||||
|  |             message: comment, | ||||||
|  |             text: Translations.T(text), | ||||||
|  |             minzoom: minZoom, | ||||||
|  |             zoomMoreMessage: zoomButton | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Thin wrapper around QuestionBox.svelte to include it into the special Visualisations |  * Thin wrapper around QuestionBox.svelte to include it into the special Visualisations | ||||||
|  */ |  */ | ||||||
|  | @ -526,7 +586,7 @@ export default class SpecialVisualizations { | ||||||
|                     }) |                     }) | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|             new CloseNoteButton(), |             new CloseNoteViz(), | ||||||
|             new PlantNetDetectionViz(), |             new PlantNetDetectionViz(), | ||||||
| 
 | 
 | ||||||
|             new TagApplyButton(), |             new TagApplyButton(), | ||||||
|  | @ -534,7 +594,6 @@ export default class SpecialVisualizations { | ||||||
|             new PointImportButtonViz(), |             new PointImportButtonViz(), | ||||||
|             new WayImportButtonViz(), |             new WayImportButtonViz(), | ||||||
|             new ConflateImportButtonViz(), |             new ConflateImportButtonViz(), | ||||||
| 
 |  | ||||||
|             new NearbyImageVis(), |             new NearbyImageVis(), | ||||||
| 
 | 
 | ||||||
|             { |             { | ||||||
|  |  | ||||||
|  | @ -170,10 +170,10 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be | ||||||
|     /** |     /** | ||||||
|      * Parses the arguments for special visualisations |      * Parses the arguments for special visualisations | ||||||
|      */ |      */ | ||||||
|     public static ParseVisArgs( |     public static ParseVisArgs<T extends Record<string, string>>( | ||||||
|         specs: { name: string; defaultValue?: string }[], |         specs: { name: string; defaultValue?: string }[], | ||||||
|         args: string[] |         args: string[] | ||||||
|     ): Record<string, string> { |     ): T { | ||||||
|         const parsed: Record<string, string> = {} |         const parsed: Record<string, string> = {} | ||||||
|         if (args.length > specs.length) { |         if (args.length > specs.length) { | ||||||
|             throw ( |             throw ( | ||||||
|  | @ -193,7 +193,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be | ||||||
|             parsed[spec.name] = arg |             parsed[spec.name] = arg | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return parsed |         return <T> parsed | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static EncodeXmlValue(str) { |     static EncodeXmlValue(str) { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue