UX: fix #2416, small code cleanup

This commit is contained in:
Pieter Vander Vennet 2025-05-13 16:13:05 +02:00
parent 9392da0dbb
commit 7b466a1d53
8 changed files with 57 additions and 66 deletions

View file

@ -85,9 +85,9 @@ export default class AllImageProviders {
let count = 0
const sources = [
Panoramax.singleton,
Imgur.singleton,
Mapillary.singleton,
Panoramax.singleton,
AllImageProviders.genericImageProvider,
]
const allPrefixes = Utils.Dedup(
@ -138,8 +138,8 @@ export default class AllImageProviders {
allSources.push(singleSource)
}
const source = Stores.fromStoresArray(allSources).map((result) => {
const all = [].concat(...result)
return Utils.DedupOnId(all, (i) => i?.id ?? i?.url)
const all = Utils.concat(result)
return Utils.DedupOnId(all, (i) => [i?.id, i?.url, i?.alt_id])
})
this._cachedImageStores[cachekey] = source
return source

View file

@ -10,6 +10,10 @@ export interface ProvidedImage {
key: string
provider: ImageProvider
id: string
/**
* An alternative ID, used to deduplicate some images
*/
alt_id?: string,
date?: Date
status?: string | "ready"
/**

View file

@ -47,10 +47,11 @@ export default class ImageUploadQueue {
applyRemapping(oldId: string, newId: string) {
let hasChange = false
for (const img of this._imagesInQueue.data) {
if (img.featureId === oldId) {
img.featureId = newId
hasChange = true
if (img.featureId !== oldId) {
continue
}
img.featureId = newId
hasChange = true
}
if (hasChange) {
this._imagesInQueue.ping()

View file

@ -22,7 +22,15 @@ export default class PanoramaxImageProvider extends ImageProvider {
3000
)
public defaultKeyPrefixes: string[] = ["panoramax"]
/**
*
* const url = "https://panoramax.mapcomplete.org/api/pictures/e931ce57-4591-4dd5-aa4c-595e89c37e84/hd.jpg"
* const match = url.match(PanoramaxImageProvider.isDirectLink)
* match[1] // => "e931ce57-4591-4dd5-aa4c-595e89c37e84"
*/
public static readonly isDirectLink = /https:\/\/panoramax.mapcomplete.org\/api\/pictures\/([0-9a-f-]+)\/(hd)|(sd)|(thumb).jpg/
public defaultKeyPrefixes: string[] = ["panoramax", "image"]
public readonly name: string = "panoramax"
private static knownMeta: Record<
@ -154,10 +162,18 @@ export default class PanoramaxImageProvider extends ImageProvider {
}
public async ExtractUrls(key: string, value: string): Promise<ProvidedImage[]> {
const match = value.match(PanoramaxImageProvider.isDirectLink)
let alt_id = undefined
if (match) {
alt_id = value
value = match[1] // The ID
}
if (!Panoramax.isId(value)) {
return undefined
}
return [await this.getInfo(value)]
const providedImage = await this.getInfo(value)
providedImage.alt_id = alt_id
return [providedImage]
}
public async getInfo(hash: string): Promise<ProvidedImage> {

View file

@ -359,6 +359,9 @@ export class ChangesetHandler {
}
for (const mapping of mappings) {
const [oldId, newId] = mapping
if (oldId === newId) {
continue
}
this.allElements?.addAlias(oldId, newId)
if (newId !== undefined) {
this._remappings.set(oldId, newId)

View file

@ -1,6 +1,5 @@
import { Utils } from "../../Utils"
import Locale from "../../UI/i18n/Locale"
import Constants from "../../Models/Constants"
import FilterConfig, { FilterConfigOption } from "../../Models/ThemeConfig/FilterConfig"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import LayerState from "../State/LayerState"
@ -89,41 +88,6 @@ export default class FilterSearch {
}
return possibleFilters
}
/**
* Create a random list of filters
*/
getSuggestions(): FilterSearchResult[] {
const result: FilterSearchResult[] = []
for (const [id, filteredLayer] of this._state.layerState.filteredLayers) {
if (!Array.isArray(filteredLayer.layerDef.filters)) {
continue
}
if (Constants.isPriviliged(id)) {
continue
}
for (const filter of filteredLayer.layerDef.filters) {
const singleFilterResults: FilterSearchResult[] = []
for (let i = 0; i < Math.min(filter.options.length, 5); i++) {
const option = filter.options[i]
if (option.osmTags === undefined) {
continue
}
singleFilterResults.push({
option,
filter,
index: i,
layer: filteredLayer.layerDef,
})
}
Utils.shuffle(singleFilterResults)
result.push(...singleFilterResults.slice(0, 3))
}
}
Utils.shuffle(result)
return result.slice(0, 6)
}
/**
* Partitions the list of filters in such a way that identically appearing filters will be in the same sublist.
*

View file

@ -54,6 +54,7 @@
<div
class="my-2 flex flex-col border-b border-gray-500"
class:border-interactive={comment.highlighted}
class:px-2={comment.highlighted}
>
<div class="flex items-center">
<!-- Action icon, e.g. 'created', 'commented', 'closed' -->
@ -101,7 +102,7 @@
{:else}
<a href={comment.user_url} target="_blank">{comment.user}</a>
{/if}
{comment.date}
{comment.date ?? new Date().toISOString()}
</span>
</div>
</div>

View file

@ -175,10 +175,10 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
}
public static NoNull<T>(array: ReadonlyArray<T> | undefined): T[] | undefined
public static NoNull<T>(array: undefined): undefined
public static NoNull(array: undefined): undefined
public static NoNull<T>(array: ReadonlyArray<T>): T[]
public static NoNull<T>(array: ReadonlyArray<T>): NonNullable<T>[] {
return <any>array?.filter((o) => o !== undefined && o !== null)
return <NonNullable<T>[]><unknown>array?.filter((o) => o !== undefined && o !== null)
}
public static Hist(array: ReadonlyArray<string>): Map<string, number> {
@ -332,7 +332,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
* @param toKey
* @constructor
*/
public static DedupOnId<T = { id: string }>(arr: T[], toKey?: (t: T) => string): T[] {
public static DedupOnId<T = { id: string }>(arr: T[], toKey?: (t: T) => string | string[]): T[] {
const uniq: T[] = []
const seen = new Set<string>()
if (toKey === undefined) {
@ -342,10 +342,21 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
if (!img) {
continue
}
const k = toKey(img)
if (!seen.has(k)) {
seen.add(k)
uniq.push(img)
const ks = toKey(img)
if (typeof ks === "string") {
if (!seen.has(ks)) {
seen.add(ks)
uniq.push(img)
}
} else {
const ksNoNull = Utils.NoNull(ks)
const hasBeenSeen = ksNoNull.some(k => seen.has(k))
if (!hasBeenSeen) {
uniq.push(img)
}
for (const k of ksNoNull) {
seen.add(k)
}
}
}
return uniq
@ -1657,7 +1668,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
return obj
}
const newObj = {}
for (let objKey in obj) {
for (const objKey in obj) {
let cleanKey = objKey
if (objKey.startsWith("+") || objKey.startsWith("=")) {
cleanKey = objKey.substring(1)
@ -1794,19 +1805,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
href = href.replaceAll(/ /g, "%20")
return href
}
/** Randomize array in-place using Durstenfeld shuffle algorithm
* Source: https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
* */
static shuffle(array: any[]) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
const temp = array[i]
array[i] = array[j]
array[j] = temp
}
}
private static emojiRegex = /[\p{Extended_Pictographic}🛰]/u
/**
@ -1826,4 +1824,8 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
public static isEmojiFlag(string: string) {
return /[🇦-🇿]{2}/u.test(string) // flags, see https://stackoverflow.com/questions/53360006/detect-with-regex-if-emoji-is-country-flag
}
public static concat<T>(param: T[][]): T[] {
return [].concat(...param)
}
}