forked from MapComplete/MapComplete
		
	Refactoring: port add-image-to-note to new element as well, remove obsolete classes, fix note creation
This commit is contained in:
		
							parent
							
								
									94ba18785d
								
							
						
					
					
						commit
						9a5a2e9924
					
				
					 10 changed files with 617 additions and 1001 deletions
				
			
		|  | @ -7,6 +7,7 @@ import { Store, UIEventSource } from "../UIEventSource"; | |||
| import { OsmConnection } from "../Osm/OsmConnection"; | ||||
| import { Changes } from "../Osm/Changes"; | ||||
| import Translations from "../../UI/i18n/Translations"; | ||||
| import NoteCommentElement from "../../UI/Popup/NoteCommentElement"; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  | @ -58,24 +59,25 @@ export class ImageUploadManager { | |||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Uploads the given image, applies the correct title and license for the known user | ||||
|    * Uploads the given image, applies the correct title and license for the known user. | ||||
|    * Will then add this image to the OSM-feature or the OSM-note | ||||
|    */ | ||||
|   public async uploadImageAndApply(file: File, tags: OsmTags) { | ||||
|   public async uploadImageAndApply(file: File, tagsStore: UIEventSource<OsmTags>) : Promise<void>{ | ||||
| 
 | ||||
|       const sizeInBytes = file.size | ||||
|     const featureId = <OsmId> tags.id | ||||
|       console.log(file.name + " has a size of " + sizeInBytes + " Bytes, attaching to", tags.id) | ||||
|       const self = this | ||||
|       if (sizeInBytes > this._uploader.maxFileSizeInMegabytes * 1000000) { | ||||
|           this.increaseCountFor(this._uploadStarted, featureId) | ||||
|           this.increaseCountFor(this._uploadFailed, featureId) | ||||
|           throw( | ||||
|             Translations.t.image.toBig.Subs({ | ||||
|                 actual_size: Math.floor(sizeInBytes / 1000000) + "MB", | ||||
|                 max_size: self._uploader.maxFileSizeInMegabytes + "MB", | ||||
|             }).txt | ||||
|           ) | ||||
|       } | ||||
|     const sizeInBytes = file.size; | ||||
|     const tags= tagsStore.data | ||||
|     const featureId = <OsmId>tags.id; | ||||
|     const self = this; | ||||
|     if (sizeInBytes > this._uploader.maxFileSizeInMegabytes * 1000000) { | ||||
|       this.increaseCountFor(this._uploadStarted, featureId); | ||||
|       this.increaseCountFor(this._uploadFailed, featureId); | ||||
|       throw ( | ||||
|         Translations.t.image.toBig.Subs({ | ||||
|           actual_size: Math.floor(sizeInBytes / 1000000) + "MB", | ||||
|           max_size: self._uploader.maxFileSizeInMegabytes + "MB" | ||||
|         }).txt | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     const licenseStore = this._osmConnection?.GetPreference("pictures-license", "CC0"); | ||||
|  | @ -93,8 +95,15 @@ export class ImageUploadManager { | |||
|       "osmid:" + tags.id | ||||
|     ].join("\n"); | ||||
| 
 | ||||
|     console.log("Upload done, creating ") | ||||
|     console.log("Upload done, creating "); | ||||
|     const action = await this.uploadImageWithLicense(featureId, title, description, file); | ||||
|     if(!isNaN(Number( featureId))){ | ||||
|       // THis is a map note
 | ||||
|       const url = action._url | ||||
|       await this._osmConnection.addCommentToNote(featureId, url) | ||||
|       NoteCommentElement.addCommentTo(url, <UIEventSource<any>> tagsStore, {osmConnection: this._osmConnection}) | ||||
|       return | ||||
|     } | ||||
|     await this._changes.applyAction(action); | ||||
|   } | ||||
| 
 | ||||
|  | @ -121,7 +130,7 @@ export class ImageUploadManager { | |||
|       } | ||||
| 
 | ||||
|     } | ||||
|     console.log("Uploading done, creating action for", featureId) | ||||
|     console.log("Uploading done, creating action for", featureId); | ||||
|     const action = new LinkImageAction(featureId, key, value, properties, { | ||||
|       theme: this._layout.id, | ||||
|       changeType: "add-image" | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ import { Store } from "../../UIEventSource"; | |||
| 
 | ||||
| export default class LinkImageAction extends OsmChangeAction { | ||||
|     private readonly _proposedKey: "image" | "mapillary" | "wiki_commons" | string; | ||||
|     private readonly _url: string; | ||||
|     public readonly _url: string; | ||||
|     private readonly _currentTags: Store<Record<string, string>>; | ||||
|     private readonly _meta: { theme: string; changeType: "add-image" | "link-image" }; | ||||
| 
 | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1,192 +0,0 @@ | |||
| import { Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
| import Combine from "../Base/Combine" | ||||
| import Translations from "../i18n/Translations" | ||||
| import Svg from "../../Svg" | ||||
| import { Tag } from "../../Logic/Tags/Tag" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import Toggle from "../Input/Toggle" | ||||
| import FileSelectorButton from "../Input/FileSelectorButton" | ||||
| import ImgurUploader from "../../Logic/ImageProviders/ImgurUploader" | ||||
| import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction" | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
| import { FixedUiElement } from "../Base/FixedUiElement" | ||||
| import { VariableUiElement } from "../Base/VariableUIElement" | ||||
| import Loading from "../Base/Loading" | ||||
| import { LoginToggle } from "../Popup/LoginButton" | ||||
| import Constants from "../../Models/Constants" | ||||
| import { SpecialVisualizationState } from "../SpecialVisualization" | ||||
| import exp from "constants"; | ||||
| 
 | ||||
| export class ImageUploadFlow extends Combine { | ||||
|     private static readonly uploadCountsPerId = new Map<string, UIEventSource<number>>() | ||||
| 
 | ||||
|     constructor( | ||||
|         tagsSource: Store<any>, | ||||
|         state: SpecialVisualizationState, | ||||
|         imagePrefix: string = "image", | ||||
|         text: string = undefined | ||||
|     ) { | ||||
|         const perId = ImageUploadFlow.uploadCountsPerId | ||||
|         const id = tagsSource.data.id | ||||
|         if (!perId.has(id)) { | ||||
|             perId.set(id, new UIEventSource<number>(0)) | ||||
|         } | ||||
|         const uploadedCount = perId.get(id) | ||||
|         const uploader = new ImgurUploader(async (url) => { | ||||
|             // A file was uploaded - we add it to the tags of the object
 | ||||
| 
 | ||||
|             const tags = tagsSource.data | ||||
|             let key = imagePrefix | ||||
|             if (tags[imagePrefix] !== undefined) { | ||||
|                 let freeIndex = 0 | ||||
|                 while (tags[imagePrefix + ":" + freeIndex] !== undefined) { | ||||
|                     freeIndex++ | ||||
|                 } | ||||
|                 key = imagePrefix + ":" + freeIndex | ||||
|             } | ||||
| 
 | ||||
|             await state.changes.applyAction( | ||||
|                 new ChangeTagAction(tags.id, new Tag(key, url), tagsSource.data, { | ||||
|                     changeType: "add-image", | ||||
|                     theme: state.layout.id, | ||||
|                 }) | ||||
|             ) | ||||
|             console.log("Adding image:" + key, url) | ||||
|             uploadedCount.data++ | ||||
|             uploadedCount.ping() | ||||
|         }) | ||||
| 
 | ||||
|         const t = Translations.t.image | ||||
| 
 | ||||
|         let labelContent: BaseUIElement | ||||
|         if (text === undefined) { | ||||
|             labelContent = Translations.t.image.addPicture | ||||
|                 .Clone() | ||||
|                 .SetClass("block align-middle mt-1 ml-3 text-4xl ") | ||||
|         } else { | ||||
|             labelContent = new FixedUiElement(text).SetClass( | ||||
|                 "block align-middle mt-1 ml-3 text-2xl " | ||||
|             ) | ||||
|         } | ||||
|         const label = new Combine([ | ||||
|             Svg.camera_plus_svg().SetClass("block w-12 h-12 p-1 text-4xl "), | ||||
|             labelContent, | ||||
|         ]).SetClass("w-full flex justify-center items-center") | ||||
| 
 | ||||
|         const licenseStore = state?.osmConnection?.GetPreference("pictures-license", "CC0") | ||||
| 
 | ||||
|         const fileSelector = new FileSelectorButton(label, { | ||||
|             acceptType: "image/*", | ||||
|             allowMultiple: true, | ||||
|             labelClasses: "rounded-full border-2 border-black font-bold", | ||||
|         }) | ||||
|         /*    fileSelector.SetClass( | ||||
|             "p-2 border-4 border-detail rounded-full font-bold h-full align-middle w-full flex justify-center" | ||||
|         ) | ||||
|             .SetStyle(" border-color: var(--foreground-color);")*/ | ||||
|         fileSelector.GetValue().addCallback((filelist) => { | ||||
|             if (filelist === undefined || filelist.length === 0) { | ||||
|                 return | ||||
|             } | ||||
| 
 | ||||
|             for (var i = 0; i < filelist.length; i++) { | ||||
|                 const sizeInBytes = filelist[i].size | ||||
|                 console.log(filelist[i].name + " has a size of " + sizeInBytes + " Bytes") | ||||
|                 if (sizeInBytes > uploader.maxFileSizeInMegabytes * 1000000) { | ||||
|                     alert( | ||||
|                         Translations.t.image.toBig.Subs({ | ||||
|                             actual_size: Math.floor(sizeInBytes / 1000000) + "MB", | ||||
|                             max_size: uploader.maxFileSizeInMegabytes + "MB", | ||||
|                         }).txt | ||||
|                     ) | ||||
|                     return | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             const license = licenseStore?.data ?? "CC0" | ||||
| 
 | ||||
|             const tags = tagsSource.data | ||||
| 
 | ||||
|             const layout = state?.layout | ||||
|             let matchingLayer: LayerConfig = undefined | ||||
|             for (const layer of layout?.layers ?? []) { | ||||
|                 if (layer.source.osmTags.matchesProperties(tags)) { | ||||
|                     matchingLayer = layer | ||||
|                     break | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             const title = | ||||
|                 matchingLayer?.title?.GetRenderValue(tags)?.Subs(tags)?.ConstructElement() | ||||
|                     ?.textContent ?? | ||||
|                 tags.name ?? | ||||
|                 "https//osm.org/" + tags.id | ||||
|             const description = [ | ||||
|                 "author:" + state.osmConnection.userDetails.data.name, | ||||
|                 "license:" + license, | ||||
|                 "osmid:" + tags.id, | ||||
|             ].join("\n") | ||||
| 
 | ||||
|             uploader.uploadMany(title, description, filelist) | ||||
|         }) | ||||
| 
 | ||||
|         super([ | ||||
|             new VariableUiElement( | ||||
|                 uploader.queue | ||||
|                     .map((q) => q.length) | ||||
|                     .map((l) => { | ||||
|                         if (l == 0) { | ||||
|                             return undefined | ||||
|                         } | ||||
|                         if (l == 1) { | ||||
|                             return new Loading(t.uploadingPicture).SetClass("alert") | ||||
|                         } else { | ||||
|                             return new Loading( | ||||
|                                 t.uploadingMultiple.Subs({ count: "" + l }) | ||||
|                             ).SetClass("alert") | ||||
|                         } | ||||
|                     }) | ||||
|             ), | ||||
|             new VariableUiElement( | ||||
|                 uploader.failed | ||||
|                     .map((q) => q.length) | ||||
|                     .map((l) => { | ||||
|                         if (l == 0) { | ||||
|                             return undefined | ||||
|                         } | ||||
|                         console.log(l) | ||||
|                         return t.uploadFailed.SetClass("block alert") | ||||
|                     }) | ||||
|             ), | ||||
|             new VariableUiElement( | ||||
|                 uploadedCount.map((l) => { | ||||
|                     if (l == 0) { | ||||
|                         return undefined | ||||
|                     } | ||||
|                     if (l == 1) { | ||||
|                         return t.uploadDone.Clone().SetClass("thanks block") | ||||
|                     } | ||||
|                     return t.uploadMultipleDone.Subs({ count: l }).SetClass("thanks block") | ||||
|                 }) | ||||
|             ), | ||||
| 
 | ||||
|             fileSelector, | ||||
|             new Combine([ | ||||
|                 Translations.t.image.respectPrivacy, | ||||
|                 new VariableUiElement( | ||||
|                     licenseStore.map((license) => | ||||
|                         Translations.t.image.currentLicense.Subs({ license }) | ||||
|                     ) | ||||
|                 ) | ||||
|                     .onClick(() => { | ||||
|                         console.log("Opening the license settings... ") | ||||
|                         state.guistate.openUsersettings("picture-license") | ||||
|                     }) | ||||
|                     .SetClass("underline"), | ||||
|             ]).SetStyle("font-size:small;"), | ||||
|         ]) | ||||
|         this.SetClass("flex flex-col image-upload-flow mt-4 mb-8 text-center leading-none") | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | @ -16,6 +16,10 @@ import Svg from "../../Svg"; | |||
| export let state: SpecialVisualizationState; | ||||
| 
 | ||||
| export let tags: Store<OsmTags>; | ||||
| /** | ||||
|  * Image to show in the button | ||||
|  * NOT the image to upload! | ||||
|  */ | ||||
| export let image: string = undefined; | ||||
| if (image === "") { | ||||
|   image = undefined; | ||||
|  | @ -30,7 +34,7 @@ function handleFiles(files: FileList) { | |||
|     const file = files.item(i); | ||||
|     console.log("Got file", file.name) | ||||
|     try { | ||||
|       state.imageUploadManager.uploadImageAndApply(file, tags.data); | ||||
|       state.imageUploadManager.uploadImageAndApply(file, tags); | ||||
|     } catch (e) { | ||||
|       alert(e); | ||||
|     } | ||||
|  |  | |||
|  | @ -1,111 +0,0 @@ | |||
| import BaseUIElement from "../BaseUIElement" | ||||
| import { InputElement } from "./InputElement" | ||||
| import { UIEventSource } from "../../Logic/UIEventSource" | ||||
| 
 | ||||
| /** | ||||
|  * @deprecated | ||||
|  */ | ||||
| export default class FileSelectorButton extends InputElement<FileList> { | ||||
|     private static _nextid = 0 | ||||
|     private readonly _value = new UIEventSource<FileList>(undefined) | ||||
|     private readonly _label: BaseUIElement | ||||
|     private readonly _acceptType: string | ||||
|     private readonly allowMultiple: boolean | ||||
|     private readonly _labelClasses: string | ||||
| 
 | ||||
|     constructor( | ||||
|         label: BaseUIElement, | ||||
|         options?: { | ||||
|             acceptType: "image/*" | string | ||||
|             allowMultiple: true | boolean | ||||
|             labelClasses?: string | ||||
|         } | ||||
|     ) { | ||||
|         super() | ||||
|         this._label = label | ||||
|         this._acceptType = options?.acceptType ?? "image/*" | ||||
|         this._labelClasses = options?.labelClasses ?? "" | ||||
|         this.SetClass("block cursor-pointer") | ||||
|         label.SetClass("cursor-pointer") | ||||
|         this.allowMultiple = options?.allowMultiple ?? true | ||||
|     } | ||||
| 
 | ||||
|     GetValue(): UIEventSource<FileList> { | ||||
|         return this._value | ||||
|     } | ||||
| 
 | ||||
|     IsValid(t: FileList): boolean { | ||||
|         return true | ||||
|     } | ||||
| 
 | ||||
|     protected InnerConstructElement(): HTMLElement { | ||||
|         const self = this | ||||
|         const el = document.createElement("form") | ||||
|         const label = document.createElement("label") | ||||
|         label.appendChild(this._label.ConstructElement()) | ||||
|         label.classList.add(...this._labelClasses.split(" ").filter((t) => t !== "")) | ||||
|         el.appendChild(label) | ||||
| 
 | ||||
|         const actualInputElement = document.createElement("input") | ||||
|         actualInputElement.style.cssText = "display:none" | ||||
|         actualInputElement.type = "file" | ||||
|         actualInputElement.accept = this._acceptType | ||||
|         actualInputElement.name = "picField" | ||||
|         actualInputElement.multiple = this.allowMultiple | ||||
|         actualInputElement.id = "fileselector" + FileSelectorButton._nextid | ||||
|         FileSelectorButton._nextid++ | ||||
| 
 | ||||
|         label.htmlFor = actualInputElement.id | ||||
| 
 | ||||
|         actualInputElement.onchange = () => { | ||||
|             if (actualInputElement.files !== null) { | ||||
|                 self._value.setData(actualInputElement.files) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         el.addEventListener("submit", (e) => { | ||||
|             if (actualInputElement.files !== null) { | ||||
|                 self._value.setData(actualInputElement.files) | ||||
|             } | ||||
|             actualInputElement.classList.remove("glowing-shadow") | ||||
| 
 | ||||
|             e.preventDefault() | ||||
|         }) | ||||
| 
 | ||||
|         el.appendChild(actualInputElement) | ||||
| 
 | ||||
|         function setDrawAttention(isOn: boolean) { | ||||
|             if (isOn) { | ||||
|                 label.classList.add("glowing-shadow") | ||||
|             } else { | ||||
|                 label.classList.remove("glowing-shadow") | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         el.addEventListener("dragover", (event) => { | ||||
|             event.stopPropagation() | ||||
|             event.preventDefault() | ||||
|             setDrawAttention(true) | ||||
|             // Style the drag-and-drop as a "copy file" operation.
 | ||||
|             event.dataTransfer.dropEffect = "copy" | ||||
|         }) | ||||
| 
 | ||||
|         window.document.addEventListener("dragenter", () => { | ||||
|             setDrawAttention(true) | ||||
|         }) | ||||
| 
 | ||||
|         window.document.addEventListener("dragend", () => { | ||||
|             setDrawAttention(false) | ||||
|         }) | ||||
| 
 | ||||
|         el.addEventListener("drop", (event) => { | ||||
|             event.stopPropagation() | ||||
|             event.preventDefault() | ||||
|             label.classList.remove("glowing-shadow") | ||||
|             const fileList = event.dataTransfer.files | ||||
|             this._value.setData(fileList) | ||||
|         }) | ||||
| 
 | ||||
|         return el | ||||
|     } | ||||
| } | ||||
|  | @ -1,62 +0,0 @@ | |||
| import { InputElement } from "./InputElement" | ||||
| import { UIEventSource } from "../../Logic/UIEventSource" | ||||
| 
 | ||||
| /** | ||||
|  * @deprecated | ||||
|  */ | ||||
| export default class Slider extends InputElement<number> { | ||||
|     private readonly _value: UIEventSource<number> | ||||
|     private readonly min: number | ||||
|     private readonly max: number | ||||
|     private readonly step: number | ||||
|     private readonly vertical: boolean | ||||
| 
 | ||||
|     /** | ||||
|      * Constructs a slider input element for natural numbers | ||||
|      * @param min: the minimum value that is allowed, inclusive | ||||
|      * @param max: the max value that is allowed, inclusive | ||||
|      * @param options: value: injectable value; step: the step size of the slider | ||||
|      */ | ||||
|     constructor( | ||||
|         min: number, | ||||
|         max: number, | ||||
|         options?: { | ||||
|             value?: UIEventSource<number> | ||||
|             step?: 1 | number | ||||
|             vertical?: false | boolean | ||||
|         } | ||||
|     ) { | ||||
|         super() | ||||
|         this.max = max | ||||
|         this.min = min | ||||
|         this._value = options?.value ?? new UIEventSource<number>(min) | ||||
|         this.step = options?.step ?? 1 | ||||
|         this.vertical = options?.vertical ?? false | ||||
|     } | ||||
| 
 | ||||
|     GetValue(): UIEventSource<number> { | ||||
|         return this._value | ||||
|     } | ||||
| 
 | ||||
|     protected InnerConstructElement(): HTMLElement { | ||||
|         const el = document.createElement("input") | ||||
|         el.type = "range" | ||||
|         el.min = "" + this.min | ||||
|         el.max = "" + this.max | ||||
|         el.step = "" + this.step | ||||
|         const valuestore = this._value | ||||
|         el.oninput = () => { | ||||
|             valuestore.setData(Number(el.value)) | ||||
|         } | ||||
|         if (this.vertical) { | ||||
|             el.classList.add("vertical") | ||||
|             el.setAttribute("orient", "vertical") // firefox only workaround...
 | ||||
|         } | ||||
|         valuestore.addCallbackAndRunD((v) => (el.value = "" + valuestore.data)) | ||||
|         return el | ||||
|     } | ||||
| 
 | ||||
|     IsValid(t: number): boolean { | ||||
|         return Math.round(t) == t && t >= this.min && t <= this.max | ||||
|     } | ||||
| } | ||||
|  | @ -62,8 +62,13 @@ | |||
|     state.newFeatures.features.data.push(feature) | ||||
|     state.newFeatures.features.ping() | ||||
|     state.selectedElement?.setData(feature) | ||||
|     if(state.featureProperties.trackFeature){ | ||||
|       state.featureProperties.trackFeature(feature) | ||||
|     } | ||||
|     comment.setData("") | ||||
|     created = true | ||||
|     state.selectedElement.setData(feature) | ||||
|     state.selectedLayer.setData(state.layerState.filteredLayers.get("note")) | ||||
|   } | ||||
| </script> | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ import { MenuState } from "../Models/MenuState"; | |||
| import OsmObjectDownloader from "../Logic/Osm/OsmObjectDownloader"; | ||||
| import { RasterLayerPolygon } from "../Models/RasterLayers"; | ||||
| import { ImageUploadManager } from "../Logic/ImageProviders/ImageUploadManager"; | ||||
| import { OsmTags } from "../Models/OsmFeature"; | ||||
| 
 | ||||
| /** | ||||
|  * The state needed to render a special Visualisation. | ||||
|  | @ -26,7 +27,7 @@ export interface SpecialVisualizationState { | |||
|   readonly featureSwitches: FeatureSwitchState; | ||||
| 
 | ||||
|   readonly layerState: LayerState; | ||||
|   readonly featureProperties: { getStore(id: string): UIEventSource<Record<string, string>> }; | ||||
|   readonly featureProperties: { getStore(id: string): UIEventSource<Record<string, string>>, trackFeature?(feature: { properties: OsmTags }) }; | ||||
| 
 | ||||
|   readonly indexedFeatures: IndexedFeatureSource; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,78 +1,70 @@ | |||
| import Combine from "./Base/Combine" | ||||
| 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 TagApplyButton from "./Popup/TagApplyButton" | ||||
| 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 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 Svg from "../Svg" | ||||
| import NoteCommentElement from "./Popup/NoteCommentElement" | ||||
| import FileSelectorButton from "./Input/FileSelectorButton" | ||||
| import { LoginToggle } from "./Popup/LoginButton" | ||||
| import Toggle from "./Input/Toggle" | ||||
| 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 FeatureReviews from "../Logic/Web/MangroveReviews" | ||||
| import Maproulette from "../Logic/Maproulette" | ||||
| import SvelteUIElement from "./Base/SvelteUIElement" | ||||
| import { BBoxFeatureSourceForLayer } from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource" | ||||
| import QuestionViz from "./Popup/QuestionViz" | ||||
| 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" | ||||
| import LanguagePicker from "./LanguagePicker" | ||||
| import Link from "./Base/Link" | ||||
| import LayerConfig from "../Models/ThemeConfig/LayerConfig" | ||||
| import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig" | ||||
| import { OsmTags, WayId } from "../Models/OsmFeature" | ||||
| import MoveWizard from "./Popup/MoveWizard" | ||||
| import SplitRoadWizard from "./Popup/SplitRoadWizard" | ||||
| 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 WayImportButtonViz from "./Popup/ImportButtons/WayImportButtonViz" | ||||
| import ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonViz" | ||||
| import DeleteWizard from "./Popup/DeleteFlow/DeleteWizard.svelte" | ||||
| import { OpenJosm } from "./BigComponents/OpenJosm" | ||||
| import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte" | ||||
| import FediverseValidator from "./InputElement/Validators/FediverseValidator" | ||||
| import SendEmail from "./Popup/SendEmail.svelte" | ||||
| import NearbyImages from "./Popup/NearbyImages.svelte" | ||||
| import NearbyImagesCollapsed from "./Popup/NearbyImagesCollapsed.svelte" | ||||
| import Combine from "./Base/Combine"; | ||||
| 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 TagApplyButton from "./Popup/TagApplyButton"; | ||||
| 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 { 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 Svg from "../Svg"; | ||||
| import NoteCommentElement from "./Popup/NoteCommentElement"; | ||||
| 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 FeatureReviews from "../Logic/Web/MangroveReviews"; | ||||
| import Maproulette from "../Logic/Maproulette"; | ||||
| import SvelteUIElement from "./Base/SvelteUIElement"; | ||||
| import { BBoxFeatureSourceForLayer } from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"; | ||||
| import QuestionViz from "./Popup/QuestionViz"; | ||||
| 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"; | ||||
| import LanguagePicker from "./LanguagePicker"; | ||||
| import Link from "./Base/Link"; | ||||
| import LayerConfig from "../Models/ThemeConfig/LayerConfig"; | ||||
| import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig"; | ||||
| import { OsmTags, WayId } from "../Models/OsmFeature"; | ||||
| import MoveWizard from "./Popup/MoveWizard"; | ||||
| import SplitRoadWizard from "./Popup/SplitRoadWizard"; | ||||
| 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 WayImportButtonViz from "./Popup/ImportButtons/WayImportButtonViz"; | ||||
| import ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonViz"; | ||||
| import DeleteWizard from "./Popup/DeleteFlow/DeleteWizard.svelte"; | ||||
| import { OpenJosm } from "./BigComponents/OpenJosm"; | ||||
| import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"; | ||||
| import FediverseValidator from "./InputElement/Validators/FediverseValidator"; | ||||
| import SendEmail from "./Popup/SendEmail.svelte"; | ||||
| import NearbyImages from "./Popup/NearbyImages.svelte"; | ||||
| import NearbyImagesCollapsed from "./Popup/NearbyImagesCollapsed.svelte"; | ||||
| import UploadImage from "./Image/UploadImage.svelte"; | ||||
| 
 | ||||
| class NearbyImageVis implements SpecialVisualization { | ||||
|  | @ -272,6 +264,7 @@ export default class SpecialVisualizations { | |||
|                     SpecialVisualizations.specialVisualizations | ||||
|                         .map((sp) => sp.funcName + "()") | ||||
|                         .join(", ") | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -628,7 +621,6 @@ export default class SpecialVisualizations { | |||
|                     return new SvelteUIElement(UploadImage, { | ||||
|                         state,tags, labelText: args[1], image: args[0] | ||||
|                     }) | ||||
|                    // return new ImageUploadFlow(tags, state, args[0], args[1])
 | ||||
|                 }, | ||||
|             }, | ||||
|             { | ||||
|  | @ -867,43 +859,11 @@ export default class SpecialVisualizations { | |||
|                     }, | ||||
|                 ], | ||||
|                 constr: (state, tags, args) => { | ||||
|                     const isUploading = new UIEventSource(false) | ||||
|                     const t = Translations.t.notes | ||||
|                     const id = tags.data[args[0] ?? "id"] | ||||
| 
 | ||||
|                     const uploader = new ImgurUploader(async (url) => { | ||||
|                         isUploading.setData(false) | ||||
|                         await state.osmConnection.addCommentToNote(id, url) | ||||
|                         NoteCommentElement.addCommentTo(url, tags, state) | ||||
|                     }) | ||||
| 
 | ||||
|                     const label = new Combine([ | ||||
|                         Svg.camera_plus_svg().SetClass("block w-12 h-12 p-1 text-4xl "), | ||||
|                         Translations.t.image.addPicture, | ||||
|                     ]).SetClass( | ||||
|                         "p-2 border-4 border-black rounded-full font-bold h-full align-center w-full flex justify-center" | ||||
|                     ) | ||||
| 
 | ||||
|                     const fileSelector = new FileSelectorButton(label) | ||||
|                     fileSelector.GetValue().addCallback((filelist) => { | ||||
|                         isUploading.setData(true) | ||||
|                         uploader.uploadMany("Image for osm.org/note/" + id, "CC0", filelist) | ||||
|                     }) | ||||
|                     const ti = Translations.t.image | ||||
|                     const uploadPanel = new Combine([ | ||||
|                         fileSelector, | ||||
|                         ti.respectPrivacy.SetClass("text-sm"), | ||||
|                     ]).SetClass("flex flex-col") | ||||
|                     return new LoginToggle( | ||||
|                         new Toggle( | ||||
|                             Translations.t.image.uploadingPicture.SetClass("alert"), | ||||
|                             uploadPanel, | ||||
|                             isUploading | ||||
|                         ), | ||||
|                         t.loginToAddPicture, | ||||
|                         state | ||||
|                     ) | ||||
|                 }, | ||||
|                     tags = state.featureProperties.getStore(id) | ||||
|                     console.log("Id is", id) | ||||
|                     return new SvelteUIElement(UploadImage, {state, tags}) | ||||
|                     } | ||||
|             }, | ||||
|             { | ||||
|                 funcName: "title", | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue