forked from MapComplete/MapComplete
Feature(inspector): allow to load multiple contributors at once
This commit is contained in:
parent
7c6224fd3e
commit
063a912c82
8 changed files with 69 additions and 41 deletions
|
@ -52,8 +52,11 @@
|
|||
"render": 0,
|
||||
"mappings": [
|
||||
{
|
||||
"if": {"or":
|
||||
["_geometry:type=Polygon","_geometry:type=MultiPolygon"]
|
||||
"if": {
|
||||
"or": [
|
||||
"_geometry:type=Polygon",
|
||||
"_geometry:type=MultiPolygon"
|
||||
]
|
||||
},
|
||||
"then": 20
|
||||
}
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
{}
|
||||
{
|
||||
}
|
|
@ -5,11 +5,9 @@
|
|||
import OsmObjectDownloader from "../../Logic/Osm/OsmObjectDownloader"
|
||||
import { OsmObject } from "../../Logic/Osm/OsmObject"
|
||||
import Loading from "../Base/Loading.svelte"
|
||||
import AttributedImage from "../Image/AttributedImage.svelte"
|
||||
import AttributedPanoramaxImage from "./AttributedPanoramaxImage.svelte"
|
||||
import History from "./History.svelte"
|
||||
|
||||
export let onlyShowUsername: string
|
||||
export let onlyShowUsername: string[]
|
||||
export let features: Feature[]
|
||||
|
||||
const downloader = new OsmObjectDownloader()
|
||||
|
@ -23,11 +21,12 @@
|
|||
}
|
||||
return result
|
||||
}))
|
||||
let usernamesSet = new Set(onlyShowUsername)
|
||||
let allDiffs: Store<{
|
||||
key: string;
|
||||
value?: string;
|
||||
oldValue?: string
|
||||
}[]> = allHistories.mapD(histories => HistoryUtils.fullHistoryDiff(histories, onlyShowUsername))
|
||||
}[]> = allHistories.mapD(histories => HistoryUtils.fullHistoryDiff(histories, usernamesSet))
|
||||
|
||||
let addedImages = allDiffs.mapD(diffs => [].concat(...diffs.filter(({ key }) => imageKeys.has(key))))
|
||||
|
||||
|
@ -37,7 +36,11 @@
|
|||
{:else if $addedImages.length === 0}
|
||||
No images added by this contributor
|
||||
{:else}
|
||||
<div class="flex">
|
||||
{#each $addedImages as imgDiff}
|
||||
<div class="w-48 h-48">
|
||||
<AttributedPanoramaxImage hash={imgDiff.value} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -6,14 +6,16 @@
|
|||
import Loading from "../Base/Loading.svelte"
|
||||
import { HistoryUtils } from "./HistoryUtils"
|
||||
import * as shared_questions from "../../assets/generated/layers/questions.json"
|
||||
import TagRenderingQuestion from "../Popup/TagRendering/TagRenderingQuestion.svelte"
|
||||
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
|
||||
export let onlyShowUsername: string
|
||||
export let onlyShowUsername: string[]
|
||||
export let features: Feature[]
|
||||
|
||||
let usernames = new Set(onlyShowUsername)
|
||||
|
||||
const downloader = new OsmObjectDownloader()
|
||||
let allHistories: UIEventSource<OsmObject[][]> = UIEventSource.FromPromise(
|
||||
Promise.all(features.map(f => downloader.downloadHistory(f.properties.id)))
|
||||
|
@ -22,7 +24,7 @@
|
|||
key: string;
|
||||
value?: string;
|
||||
oldValue?: string
|
||||
}[]> = allHistories.mapD(histories => HistoryUtils.fullHistoryDiff(histories, onlyShowUsername))
|
||||
}[]> = allHistories.mapD(histories => HistoryUtils.fullHistoryDiff(histories, usernames))
|
||||
|
||||
const trs = shared_questions.tagRenderings.map(tr => new TagRenderingConfig(tr))
|
||||
|
||||
|
@ -69,6 +71,7 @@
|
|||
return perKey
|
||||
})
|
||||
|
||||
const t = Translations.t.inspector
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -85,8 +88,7 @@
|
|||
</h3>
|
||||
<AccordionSingle>
|
||||
<span slot="header">
|
||||
|
||||
Answered {diff.count} times
|
||||
<Tr t={t.answeredCountTimes.Subs(diff)} />
|
||||
</span>
|
||||
<ul>
|
||||
{#each diff.values as value}
|
||||
|
|
|
@ -9,10 +9,12 @@
|
|||
import { HistoryUtils } from "./HistoryUtils"
|
||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
|
||||
export let onlyShowChangesBy: string
|
||||
export let onlyShowChangesBy: string[]
|
||||
export let id: OsmId
|
||||
|
||||
let usernames = new Set(onlyShowChangesBy)
|
||||
let fullHistory = UIEventSource.FromPromise(new OsmObjectDownloader().downloadHistory(id))
|
||||
|
||||
let partOfLayer = fullHistory.mapD(history => history.map(step => ({
|
||||
|
@ -21,11 +23,11 @@
|
|||
})))
|
||||
let filteredHistory = partOfLayer.mapD(history =>
|
||||
history.filter(({ step }) => {
|
||||
if (!onlyShowChangesBy) {
|
||||
if (usernames.size == 0) {
|
||||
return true
|
||||
}
|
||||
console.log("Comparing ", step.tags["_last_edit:contributor"], onlyShowChangesBy, step.tags["_last_edit:contributor"] === onlyShowChangesBy)
|
||||
return step.tags["_last_edit:contributor"] === onlyShowChangesBy
|
||||
console.log("Checking if ", step.tags["_last_edit:contributor"],"is contained in", onlyShowChangesBy)
|
||||
return usernames.has(step.tags["_last_edit:contributor"])
|
||||
|
||||
}).map(({ step, layer }) => {
|
||||
const diff = HistoryUtils.tagHistoryDiff(step, fullHistory.data)
|
||||
|
@ -38,6 +40,8 @@
|
|||
* These layers are only shown if there are tag changes as well
|
||||
*/
|
||||
const ignoreLayersIfNoChanges: ReadonlySet<string> = new Set(["walls_and_buildings"])
|
||||
const t = Translations.t.inspector.previousContributors
|
||||
|
||||
</script>
|
||||
|
||||
{#if !$allGeometry || !ignoreLayersIfNoChanges.has($lastStep?.layer?.id)}
|
||||
|
@ -55,7 +59,7 @@
|
|||
{#if !$filteredHistory}
|
||||
<Loading>Loading history...</Loading>
|
||||
{:else if $filteredHistory.length === 0}
|
||||
Only geometry changes found
|
||||
<Tr t={t.onlyGeometry} />
|
||||
{:else}
|
||||
<table class="w-full m-1">
|
||||
{#each $filteredHistory as { step, layer }}
|
||||
|
@ -64,7 +68,7 @@
|
|||
<tr>
|
||||
<td colspan="3">
|
||||
<h3>
|
||||
Created by {step.tags["_last_edit:contributor"]}
|
||||
<Tr t={t.createdBy.Subs({contributor: step.tags["_last_edit:contributor"]})} />
|
||||
</h3>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -72,7 +76,7 @@
|
|||
{#if HistoryUtils.tagHistoryDiff(step, $fullHistory).length === 0}
|
||||
<tr>
|
||||
<td class="font-bold justify-center flex w-full" colspan="3">
|
||||
Only changes in geometry
|
||||
<Tr t={t.onlyGeometry} />
|
||||
</td>
|
||||
</tr>
|
||||
{:else}
|
||||
|
|
|
@ -33,10 +33,10 @@ export class HistoryUtils {
|
|||
}).filter(ch => ch.oldValue !== ch.value)
|
||||
}
|
||||
|
||||
public static fullHistoryDiff(histories: OsmObject[][], onlyShowUsername?: string){
|
||||
public static fullHistoryDiff(histories: OsmObject[][], onlyShowUsername?: Set<string>){
|
||||
const allDiffs: {key: string, oldValue?: string, value?: string}[] = [].concat(...histories.map(
|
||||
history => {
|
||||
const filtered = history.filter(step => !onlyShowUsername || step.tags["_last_edit:contributor"] === onlyShowUsername)
|
||||
const filtered = history.filter(step => !onlyShowUsername || onlyShowUsername?.has(step.tags["_last_edit:contributor"] ))
|
||||
const diffs: {
|
||||
key: string;
|
||||
value?: string;
|
||||
|
|
|
@ -88,7 +88,12 @@
|
|||
No labels
|
||||
{:else}
|
||||
{#each $labels as label}
|
||||
<div class="mx-2">{label} </div>
|
||||
<div class="mx-2">{label}
|
||||
<button class:disabled={!$inspectedContributors.some(c => c.label === label)} on:click={() => {dispatch("selectUser",
|
||||
inspectedContributors.data.filter(c =>c.label === label).map(c => c .name).join(";")
|
||||
)}}>See all changes for these users
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
<div class="interactive flex m-2 items-center gap-x-2 rounded-lg p-2">
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
import PreviouslySpiedUsers from "./History/PreviouslySpiedUsers.svelte"
|
||||
import { OsmConnection } from "../Logic/Osm/OsmConnection"
|
||||
import MagnifyingGlassCircle from "@babeard/svelte-heroicons/outline/MagnifyingGlassCircle"
|
||||
import Translations from "./i18n/Translations"
|
||||
import Tr from "./Base/Tr.svelte"
|
||||
|
||||
let username = QueryParameters.GetQueryParameter("user", undefined, "Inspect this user")
|
||||
let step = new UIEventSource<"waiting" | "loading" | "done">("waiting")
|
||||
|
@ -52,10 +54,12 @@
|
|||
lon.set(l.lon)
|
||||
})
|
||||
|
||||
|
||||
let allLayers = HistoryUtils.personalTheme.layers
|
||||
let layersNoFixme = allLayers.filter(l => l.id !== "fixme")
|
||||
let fixme = allLayers.find(l => l.id === "fixme")
|
||||
let featuresStore = new UIEventSource<Feature[]>([])
|
||||
let features = new StaticFeatureSource(featuresStore)
|
||||
ShowDataLayer.showMultipleLayers(map, features, HistoryUtils.personalTheme.layers, {
|
||||
ShowDataLayer.showMultipleLayers(map, features, [...layersNoFixme, fixme] , {
|
||||
zoomToFeatures: true,
|
||||
onClick: (f: Feature) => {
|
||||
selectedElement.set(undefined)
|
||||
|
@ -75,7 +79,7 @@
|
|||
|
||||
async function load() {
|
||||
const user = username.data
|
||||
{
|
||||
if(user.indexOf(";")<0){
|
||||
|
||||
const inspectedData = inspectedContributors.data
|
||||
const previousEntry = inspectedData.find(e => e.name === user)
|
||||
|
@ -93,7 +97,7 @@
|
|||
|
||||
step.setData("loading")
|
||||
featuresStore.set([])
|
||||
const overpass = new Overpass(undefined, ["nw(user_touched:\"" + user + "\");"], Constants.defaultOverpassUrls[0])
|
||||
const overpass = new Overpass(undefined, user.split(";").map(user => "nw(user_touched:\"" + user + "\");"), Constants.defaultOverpassUrls[0])
|
||||
if (!maplibremap.bounds.data) {
|
||||
return
|
||||
}
|
||||
|
@ -118,38 +122,44 @@
|
|||
let mode: "map" | "table" | "aggregate" | "images" = "map"
|
||||
|
||||
let showPreviouslyVisited = new UIEventSource(true)
|
||||
|
||||
const t = Translations.t.inspector
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col w-full h-full">
|
||||
|
||||
<div class="flex gap-x-2 items-center low-interaction p-2">
|
||||
<MagnifyingGlassCircle class="w-12 h-12"/>
|
||||
<h1 class="flex-shrink-0 m-0 mx-2">Inspect contributor</h1>
|
||||
<h1 class="flex-shrink-0 m-0 mx-2">
|
||||
<Tr t={t.title}/>
|
||||
</h1>
|
||||
<ValidatedInput type="string" value={username} on:submit={() => load()} />
|
||||
{#if loadingData}
|
||||
<Loading />
|
||||
{:else}
|
||||
<button class="primary" on:click={() => load()}>Load</button>
|
||||
<button class="primary" on:click={() => load()}>
|
||||
<Tr t={t.load}/>
|
||||
</button>
|
||||
{/if}
|
||||
<button on:click={() => showPreviouslyVisited.setData(true)}>
|
||||
Show earlier inspected contributors
|
||||
<Tr t={t.earlierInspected}/>
|
||||
</button>
|
||||
<a href="./index.html" class="button">Back to index</a>
|
||||
<a href="./index.html" class="button">
|
||||
<Tr t={t.backToIndex}/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<button class:primary={mode === "map"} on:click={() => mode = "map"}>
|
||||
Map view
|
||||
<Tr t={t.mapView}/>
|
||||
</button>
|
||||
<button class:primary={mode === "table"} on:click={() => mode = "table"}>
|
||||
Table view
|
||||
<Tr t={t.tableView}/>
|
||||
</button>
|
||||
<button class:primary={mode === "aggregate"} on:click={() => mode = "aggregate"}>
|
||||
Aggregate
|
||||
<Tr t={t.aggregateView}/>
|
||||
</button>
|
||||
<button class:primary={mode === "images"} on:click={() => mode = "images"}>
|
||||
Images
|
||||
<Tr t={t.images}/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -195,16 +205,16 @@
|
|||
{:else if mode === "table"}
|
||||
<div class="m-2 h-full overflow-y-auto">
|
||||
{#each $featuresStore as f}
|
||||
<History onlyShowChangesBy={$username} id={f.properties.id} />
|
||||
<History onlyShowChangesBy={$username?.split(";")} id={f.properties.id} />
|
||||
{/each}
|
||||
</div>
|
||||
{:else if mode === "aggregate"}
|
||||
<div class="m-2 h-full overflow-y-auto">
|
||||
<AggregateView features={$featuresStore} onlyShowUsername={$username} />
|
||||
<AggregateView features={$featuresStore} onlyShowUsername={$username?.split(";")} />
|
||||
</div>
|
||||
{:else if mode === "images"}
|
||||
<div class="m-2 h-full overflow-y-auto">
|
||||
<AggregateImages features={$featuresStore} onlyShowUsername={$username} />
|
||||
<AggregateImages features={$featuresStore} onlyShowUsername={$username?.split(";")} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue