forked from MapComplete/MapComplete
UX: fix #2416, small code cleanup
This commit is contained in:
parent
9392da0dbb
commit
7b466a1d53
8 changed files with 57 additions and 66 deletions
|
@ -85,9 +85,9 @@ export default class AllImageProviders {
|
||||||
let count = 0
|
let count = 0
|
||||||
|
|
||||||
const sources = [
|
const sources = [
|
||||||
|
Panoramax.singleton,
|
||||||
Imgur.singleton,
|
Imgur.singleton,
|
||||||
Mapillary.singleton,
|
Mapillary.singleton,
|
||||||
Panoramax.singleton,
|
|
||||||
AllImageProviders.genericImageProvider,
|
AllImageProviders.genericImageProvider,
|
||||||
]
|
]
|
||||||
const allPrefixes = Utils.Dedup(
|
const allPrefixes = Utils.Dedup(
|
||||||
|
@ -138,8 +138,8 @@ export default class AllImageProviders {
|
||||||
allSources.push(singleSource)
|
allSources.push(singleSource)
|
||||||
}
|
}
|
||||||
const source = Stores.fromStoresArray(allSources).map((result) => {
|
const source = Stores.fromStoresArray(allSources).map((result) => {
|
||||||
const all = [].concat(...result)
|
const all = Utils.concat(result)
|
||||||
return Utils.DedupOnId(all, (i) => i?.id ?? i?.url)
|
return Utils.DedupOnId(all, (i) => [i?.id, i?.url, i?.alt_id])
|
||||||
})
|
})
|
||||||
this._cachedImageStores[cachekey] = source
|
this._cachedImageStores[cachekey] = source
|
||||||
return source
|
return source
|
||||||
|
|
|
@ -10,6 +10,10 @@ export interface ProvidedImage {
|
||||||
key: string
|
key: string
|
||||||
provider: ImageProvider
|
provider: ImageProvider
|
||||||
id: string
|
id: string
|
||||||
|
/**
|
||||||
|
* An alternative ID, used to deduplicate some images
|
||||||
|
*/
|
||||||
|
alt_id?: string,
|
||||||
date?: Date
|
date?: Date
|
||||||
status?: string | "ready"
|
status?: string | "ready"
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -47,10 +47,11 @@ export default class ImageUploadQueue {
|
||||||
applyRemapping(oldId: string, newId: string) {
|
applyRemapping(oldId: string, newId: string) {
|
||||||
let hasChange = false
|
let hasChange = false
|
||||||
for (const img of this._imagesInQueue.data) {
|
for (const img of this._imagesInQueue.data) {
|
||||||
if (img.featureId === oldId) {
|
if (img.featureId !== oldId) {
|
||||||
img.featureId = newId
|
continue
|
||||||
hasChange = true
|
|
||||||
}
|
}
|
||||||
|
img.featureId = newId
|
||||||
|
hasChange = true
|
||||||
}
|
}
|
||||||
if (hasChange) {
|
if (hasChange) {
|
||||||
this._imagesInQueue.ping()
|
this._imagesInQueue.ping()
|
||||||
|
|
|
@ -22,7 +22,15 @@ export default class PanoramaxImageProvider extends ImageProvider {
|
||||||
3000
|
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"
|
public readonly name: string = "panoramax"
|
||||||
|
|
||||||
private static knownMeta: Record<
|
private static knownMeta: Record<
|
||||||
|
@ -154,10 +162,18 @@ export default class PanoramaxImageProvider extends ImageProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ExtractUrls(key: string, value: string): Promise<ProvidedImage[]> {
|
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)) {
|
if (!Panoramax.isId(value)) {
|
||||||
return undefined
|
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> {
|
public async getInfo(hash: string): Promise<ProvidedImage> {
|
||||||
|
|
|
@ -359,6 +359,9 @@ export class ChangesetHandler {
|
||||||
}
|
}
|
||||||
for (const mapping of mappings) {
|
for (const mapping of mappings) {
|
||||||
const [oldId, newId] = mapping
|
const [oldId, newId] = mapping
|
||||||
|
if (oldId === newId) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
this.allElements?.addAlias(oldId, newId)
|
this.allElements?.addAlias(oldId, newId)
|
||||||
if (newId !== undefined) {
|
if (newId !== undefined) {
|
||||||
this._remappings.set(oldId, newId)
|
this._remappings.set(oldId, newId)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
import Locale from "../../UI/i18n/Locale"
|
import Locale from "../../UI/i18n/Locale"
|
||||||
import Constants from "../../Models/Constants"
|
|
||||||
import FilterConfig, { FilterConfigOption } from "../../Models/ThemeConfig/FilterConfig"
|
import FilterConfig, { FilterConfigOption } from "../../Models/ThemeConfig/FilterConfig"
|
||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||||
import LayerState from "../State/LayerState"
|
import LayerState from "../State/LayerState"
|
||||||
|
@ -89,41 +88,6 @@ export default class FilterSearch {
|
||||||
}
|
}
|
||||||
return possibleFilters
|
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.
|
* Partitions the list of filters in such a way that identically appearing filters will be in the same sublist.
|
||||||
*
|
*
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
<div
|
<div
|
||||||
class="my-2 flex flex-col border-b border-gray-500"
|
class="my-2 flex flex-col border-b border-gray-500"
|
||||||
class:border-interactive={comment.highlighted}
|
class:border-interactive={comment.highlighted}
|
||||||
|
class:px-2={comment.highlighted}
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<!-- Action icon, e.g. 'created', 'commented', 'closed' -->
|
<!-- Action icon, e.g. 'created', 'commented', 'closed' -->
|
||||||
|
@ -101,7 +102,7 @@
|
||||||
{:else}
|
{:else}
|
||||||
<a href={comment.user_url} target="_blank">{comment.user}</a>
|
<a href={comment.user_url} target="_blank">{comment.user}</a>
|
||||||
{/if}
|
{/if}
|
||||||
{comment.date}
|
{comment.date ?? new Date().toISOString()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
44
src/Utils.ts
44
src/Utils.ts
|
@ -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: 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>): T[]
|
||||||
public static NoNull<T>(array: ReadonlyArray<T>): NonNullable<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> {
|
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
|
* @param toKey
|
||||||
* @constructor
|
* @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 uniq: T[] = []
|
||||||
const seen = new Set<string>()
|
const seen = new Set<string>()
|
||||||
if (toKey === undefined) {
|
if (toKey === undefined) {
|
||||||
|
@ -342,10 +342,21 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
||||||
if (!img) {
|
if (!img) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const k = toKey(img)
|
const ks = toKey(img)
|
||||||
if (!seen.has(k)) {
|
if (typeof ks === "string") {
|
||||||
seen.add(k)
|
if (!seen.has(ks)) {
|
||||||
uniq.push(img)
|
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
|
return uniq
|
||||||
|
@ -1657,7 +1668,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
const newObj = {}
|
const newObj = {}
|
||||||
for (let objKey in obj) {
|
for (const objKey in obj) {
|
||||||
let cleanKey = objKey
|
let cleanKey = objKey
|
||||||
if (objKey.startsWith("+") || objKey.startsWith("=")) {
|
if (objKey.startsWith("+") || objKey.startsWith("=")) {
|
||||||
cleanKey = objKey.substring(1)
|
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")
|
href = href.replaceAll(/ /g, "%20")
|
||||||
return href
|
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
|
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) {
|
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
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue