diff --git a/src/Logic/ImageProviders/AllImageProviders.ts b/src/Logic/ImageProviders/AllImageProviders.ts index cd5042dd31..0ce8569c06 100644 --- a/src/Logic/ImageProviders/AllImageProviders.ts +++ b/src/Logic/ImageProviders/AllImageProviders.ts @@ -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 diff --git a/src/Logic/ImageProviders/ImageProvider.ts b/src/Logic/ImageProviders/ImageProvider.ts index 570705b142..85c131104b 100644 --- a/src/Logic/ImageProviders/ImageProvider.ts +++ b/src/Logic/ImageProviders/ImageProvider.ts @@ -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" /** diff --git a/src/Logic/ImageProviders/ImageUploadQueue.ts b/src/Logic/ImageProviders/ImageUploadQueue.ts index a641db33a2..aab2011027 100644 --- a/src/Logic/ImageProviders/ImageUploadQueue.ts +++ b/src/Logic/ImageProviders/ImageUploadQueue.ts @@ -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() diff --git a/src/Logic/ImageProviders/Panoramax.ts b/src/Logic/ImageProviders/Panoramax.ts index c44d3b9449..dcba6a1359 100644 --- a/src/Logic/ImageProviders/Panoramax.ts +++ b/src/Logic/ImageProviders/Panoramax.ts @@ -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 { + 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 { diff --git a/src/Logic/Osm/ChangesetHandler.ts b/src/Logic/Osm/ChangesetHandler.ts index 72c3df8854..060acfc47a 100644 --- a/src/Logic/Osm/ChangesetHandler.ts +++ b/src/Logic/Osm/ChangesetHandler.ts @@ -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) diff --git a/src/Logic/Search/FilterSearch.ts b/src/Logic/Search/FilterSearch.ts index b0b737f01f..bf4b32d453 100644 --- a/src/Logic/Search/FilterSearch.ts +++ b/src/Logic/Search/FilterSearch.ts @@ -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. * diff --git a/src/UI/Popup/Notes/NoteCommentElement.svelte b/src/UI/Popup/Notes/NoteCommentElement.svelte index 1ab0b8e0ba..280fe1acf1 100644 --- a/src/UI/Popup/Notes/NoteCommentElement.svelte +++ b/src/UI/Popup/Notes/NoteCommentElement.svelte @@ -54,6 +54,7 @@
@@ -101,7 +102,7 @@ {:else} {comment.user} {/if} - {comment.date} + {comment.date ?? new Date().toISOString()}
diff --git a/src/Utils.ts b/src/Utils.ts index 280e66e1df..775fa14a79 100644 --- a/src/Utils.ts +++ b/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(array: ReadonlyArray | undefined): T[] | undefined - public static NoNull(array: undefined): undefined + public static NoNull(array: undefined): undefined public static NoNull(array: ReadonlyArray): T[] public static NoNull(array: ReadonlyArray): NonNullable[] { - return array?.filter((o) => o !== undefined && o !== null) + return []>array?.filter((o) => o !== undefined && o !== null) } public static Hist(array: ReadonlyArray): Map { @@ -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(arr: T[], toKey?: (t: T) => string): T[] { + public static DedupOnId(arr: T[], toKey?: (t: T) => string | string[]): T[] { const uniq: T[] = [] const seen = new Set() 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(param: T[][]): T[] { + return [].concat(...param) + } }