forked from MapComplete/MapComplete
		
	Feature(inspector): keep track of previously visited contributors
This commit is contained in:
		
							parent
							
								
									b86b8dd1ee
								
							
						
					
					
						commit
						4d0d92d250
					
				
					 2 changed files with 148 additions and 18 deletions
				
			
		
							
								
								
									
										102
									
								
								src/UI/History/PreviouslySpiedUsers.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/UI/History/PreviouslySpiedUsers.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,102 @@ | |||
| <script lang="ts"> | ||||
| 
 | ||||
|   import { UIEventSource } from "../../Logic/UIEventSource" | ||||
|   import { OsmConnection } from "../../Logic/Osm/OsmConnection" | ||||
|   import LoginToggle from "../Base/LoginToggle.svelte" | ||||
|   import { createEventDispatcher } from "svelte" | ||||
|   import { XCircleIcon } from "@babeard/svelte-heroicons/solid" | ||||
|   import AccordionSingle from "../Flowbite/AccordionSingle.svelte" | ||||
|   import Dropdown from "../Base/Dropdown.svelte" | ||||
| 
 | ||||
|   export let osmConnection: OsmConnection | ||||
|   export let inspectedContributors: UIEventSource<{ | ||||
|     name: string, | ||||
|     visitedTime: string, | ||||
|     label: string | ||||
|   }[]> | ||||
|   let dispatch = createEventDispatcher<{ selectUser: string }>() | ||||
| 
 | ||||
|   let labels = UIEventSource.asObject<string[]>(osmConnection.getPreference("previously-spied-labels"), []) | ||||
|   let labelField = "" | ||||
| 
 | ||||
|   function remove(user: string) { | ||||
|     inspectedContributors.set(inspectedContributors.data.filter(entry => entry.name !== user)) | ||||
|   } | ||||
| 
 | ||||
|   function addLabel() { | ||||
|     if (labels.data.indexOf(labelField) >= 0) { | ||||
|       return | ||||
|     } | ||||
|     labels.data.push(labelField) | ||||
|     labels.ping() | ||||
|     labelField = "" | ||||
|   } | ||||
| 
 | ||||
|   function sort(key: string) { | ||||
|     console.log("Sorting on", key) | ||||
|     inspectedContributors.data.sort((a, b) => (a[key] ?? "").localeCompare(b[key] ?? "")) | ||||
|     inspectedContributors.ping() | ||||
|   } | ||||
| </script> | ||||
| 
 | ||||
| <LoginToggle ignoreLoading state={{osmConnection}}> | ||||
|   <table class="w-full"> | ||||
|     <tr> | ||||
|       <td> | ||||
|         <button class="as-link cursor-pointer" on:click={() => sort("name")}> | ||||
|           Contributor | ||||
|         </button> | ||||
|       </td> | ||||
|       <td> | ||||
| 
 | ||||
|         <button class="as-link cursor-pointer" on:click={() => sort("visitedTime")}> | ||||
|           Visited time | ||||
|         </button> | ||||
|       </td> | ||||
|       <td> | ||||
|         <button class="as-link cursor-pointer" on:click={() => sort("label")}>Label</button> | ||||
|       </td> | ||||
|       <td>Remove</td> | ||||
|     </tr> | ||||
|     {#each $inspectedContributors as c} | ||||
|       <tr> | ||||
|         <td> | ||||
|           <button class="as-link" on:click={() => dispatch("selectUser", c.name)}>{c.name}</button> | ||||
|         </td> | ||||
|         <td> | ||||
|           {c.visitedTime} | ||||
|         </td> | ||||
|         <td> | ||||
|           <select bind:value={c.label} on:change={() => inspectedContributors.ping()}> | ||||
|             <option value={undefined}><i>No label</i></option> | ||||
|             {#each $labels as l} | ||||
|               <option value={l}>{l}</option> | ||||
|             {/each} | ||||
|           </select> | ||||
|         </td> | ||||
|         <td> | ||||
|           <XCircleIcon class="w-6 h-6" on:click={() => remove(c.name)} /> | ||||
|         </td> | ||||
|       </tr> | ||||
|     {/each} | ||||
|   </table> | ||||
| 
 | ||||
|   <AccordionSingle> | ||||
| 
 | ||||
|     <div slot="header">Labels</div> | ||||
|     {#if $labels.length === 0} | ||||
|       No labels | ||||
|     {:else} | ||||
|       {#each $labels as label} | ||||
|         <div class="mx-2">{label} </div> | ||||
|       {/each} | ||||
|     {/if} | ||||
|     <div class="interactive flex m-2 items-center gap-x-2 rounded-lg p-2"> | ||||
|       <div class="shrink-0">Create a new label</div> | ||||
|       <input bind:value={labelField} type="text" /> | ||||
|       <button on:click={() => addLabel()} class:disabled={!(labelField?.length > 0) } class="disabled shrink-0">Add | ||||
|         label | ||||
|       </button> | ||||
|     </div> | ||||
|   </AccordionSingle> | ||||
| </LoginToggle> | ||||
|  | @ -23,6 +23,9 @@ | |||
|   import AggregateView from "./History/AggregateView.svelte" | ||||
|   import { HistoryUtils } from "./History/HistoryUtils" | ||||
|   import AggregateImages from "./History/AggregateImages.svelte" | ||||
|   import Page from "./Base/Page.svelte" | ||||
|   import PreviouslySpiedUsers from "./History/PreviouslySpiedUsers.svelte" | ||||
|   import { OsmConnection } from "../Logic/Osm/OsmConnection" | ||||
| 
 | ||||
|   let username = QueryParameters.GetQueryParameter("user", undefined, "Inspect this user") | ||||
|   let step = new UIEventSource<"waiting" | "loading" | "done">("waiting") | ||||
|  | @ -32,8 +35,8 @@ | |||
|   let lon = UIEventSource.asFloat(QueryParameters.GetQueryParameter("lon", "0")) | ||||
|   let theme = new ThemeConfig(<any>inspector_theme, true) | ||||
|   let layer = theme.layers.find(l => l.id === "usertouched") | ||||
|   theme.getMatchingLayer = () => { | ||||
|   // Is this a dirty hack? Yes it is! | ||||
|   theme.getMatchingLayer = () => { | ||||
|     return layer | ||||
|   } | ||||
|   let loadingData = false | ||||
|  | @ -61,23 +64,35 @@ | |||
|     } | ||||
|   }) | ||||
| 
 | ||||
|   /*  new ShowDataLayer(map, | ||||
|       { | ||||
|         layer, | ||||
|         zoomToFeatures: true, | ||||
|         features, | ||||
|         onClick: (f: Feature) => { | ||||
|           selectedElement.set(undefined) | ||||
|           Utils.waitFor(200).then(() => { | ||||
|             selectedElement.set(f) | ||||
|           }) | ||||
|         } | ||||
|       })*/ | ||||
|   let osmConnection = new OsmConnection() | ||||
|   let inspectedContributors: UIEventSource<{ | ||||
|     name: string, | ||||
|     visitedTime: string, | ||||
|     label: string | ||||
|   }[]> = UIEventSource.asObject( | ||||
|     osmConnection.getPreference("spied-upon-users"), []) | ||||
| 
 | ||||
|   async function load() { | ||||
|     const user = username.data | ||||
|     { | ||||
| 
 | ||||
|       const inspectedData = inspectedContributors.data | ||||
|       const previousEntry = inspectedData.find(e => e.name === user) | ||||
|       if (previousEntry) { | ||||
|         previousEntry.visitedTime = new Date().toISOString() | ||||
|       } else { | ||||
|         inspectedData.push({ | ||||
|           label: undefined, | ||||
|           visitedTime: new Date().toISOString(), | ||||
|           name: user | ||||
|         }) | ||||
|       } | ||||
|       inspectedContributors.ping() | ||||
|     } | ||||
| 
 | ||||
|     step.setData("loading") | ||||
|     const overpass = new Overpass(undefined, ["nw(user_touched:\"" + username.data + "\");"], Constants.defaultOverpassUrls[0]) | ||||
|     featuresStore.set([]) | ||||
|     const overpass = new Overpass(undefined, ["nw(user_touched:\"" + user + "\");"], Constants.defaultOverpassUrls[0]) | ||||
|     if (!maplibremap.bounds.data) { | ||||
|       return | ||||
|     } | ||||
|  | @ -100,6 +115,9 @@ | |||
|   }) | ||||
| 
 | ||||
|   let mode: "map" | "table" | "aggregate" | "images" = "map" | ||||
| 
 | ||||
|   let showPreviouslyVisited = new UIEventSource(true) | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <div class="flex flex-col w-full h-full"> | ||||
|  | @ -112,6 +130,9 @@ | |||
|     {:else} | ||||
|       <button class="primary" on:click={() => load()}>Load</button> | ||||
|     {/if} | ||||
|     <button on:click={() => showPreviouslyVisited.setData(true)}> | ||||
|       Show earlier inspected contributors | ||||
|     </button> | ||||
|     <a href="./index.html" class="button">Back to index</a> | ||||
|   </div> | ||||
| 
 | ||||
|  | @ -185,3 +206,10 @@ | |||
|     </div> | ||||
|   {/if} | ||||
| </div> | ||||
| 
 | ||||
| <Page shown={showPreviouslyVisited}> | ||||
|   <div slot="header">Earlier inspected constributors</div> | ||||
|   <PreviouslySpiedUsers {osmConnection} {inspectedContributors} on:selectUser={(e) => { | ||||
|     username.set(e.detail); load();showPreviouslyVisited.set(false) | ||||
|   }}  /> | ||||
| </Page> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue