forked from MapComplete/MapComplete
Fix: actually search for keywords in theme view
This commit is contained in:
parent
821c1fabd7
commit
cdc1e05499
9 changed files with 193 additions and 95 deletions
|
|
@ -41,14 +41,22 @@ export interface LayerConfigJson {
|
|||
name?: Translatable
|
||||
|
||||
/**
|
||||
* question: How would you describe the features that are shown on this layer?
|
||||
*
|
||||
* A description for the features shown in this layer.
|
||||
* This often resembles the introduction of the wiki.osm.org-page for this feature.
|
||||
*
|
||||
* group: Basic
|
||||
* question: How would you describe the features that are shown on this layer?
|
||||
*/
|
||||
description?: Translatable
|
||||
|
||||
/**
|
||||
* question: What are some other terms used to describe these objects?
|
||||
*
|
||||
* This is used in the search functionality
|
||||
*/
|
||||
searchTerms?: Record<string, string[]>
|
||||
|
||||
/**
|
||||
* Question: Where should the data be fetched from?
|
||||
* title: Data Source
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ export default class LayerConfig extends WithContextLoader {
|
|||
public readonly id: string
|
||||
public readonly name: Translation
|
||||
public readonly description: Translation
|
||||
public readonly searchTerms: Record<string, string[]>
|
||||
/**
|
||||
* Only 'null' for special, privileged layers
|
||||
*/
|
||||
|
|
@ -113,8 +114,8 @@ export default class LayerConfig extends WithContextLoader {
|
|||
json.description = undefined
|
||||
}
|
||||
}
|
||||
|
||||
this.description = Translations.T(json.description, translationContext + ".description")
|
||||
this.searchTerms = json.searchTerms ?? {}
|
||||
|
||||
this.calculatedTags = undefined
|
||||
if (json.calculatedTags !== undefined) {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export class MinimalLayoutInformation {
|
|||
definition?: Translatable
|
||||
mustHaveLanguage?: boolean
|
||||
hideFromOverview?: boolean
|
||||
keywords?: (Translatable | TagRenderingConfigJson)[]
|
||||
keywords?: Record<string, string[]>
|
||||
}
|
||||
/**
|
||||
* Minimal information about a theme
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ export default class MoreScreen {
|
|||
MoreScreen.officialThemesById.set(th.id, th)
|
||||
}
|
||||
}
|
||||
|
||||
public static applySearch(searchTerm: string) {
|
||||
searchTerm = searchTerm.toLowerCase()
|
||||
if (!searchTerm) {
|
||||
|
|
@ -43,13 +44,13 @@ export default class MoreScreen {
|
|||
(th) =>
|
||||
th.hideFromOverview == false &&
|
||||
th.id !== "personal" &&
|
||||
MoreScreen.MatchesLayout(th, searchTerm)
|
||||
MoreScreen.MatchesLayout(th, searchTerm),
|
||||
)
|
||||
if (publicTheme !== undefined) {
|
||||
window.location.href = MoreScreen.createUrlFor(publicTheme, false)
|
||||
}
|
||||
const hiddenTheme = MoreScreen.officialThemes.find(
|
||||
(th) => th.id !== "personal" && MoreScreen.MatchesLayout(th, searchTerm)
|
||||
(th) => th.id !== "personal" && MoreScreen.MatchesLayout(th, searchTerm),
|
||||
)
|
||||
if (hiddenTheme !== undefined) {
|
||||
window.location.href = MoreScreen.createUrlFor(hiddenTheme, false)
|
||||
|
|
@ -57,34 +58,47 @@ export default class MoreScreen {
|
|||
}
|
||||
|
||||
public static MatchesLayout(
|
||||
layout: {
|
||||
id: string
|
||||
title: Translatable
|
||||
shortDescription: Translatable
|
||||
keywords?: (Translatable | TagRenderingConfigJson)[]
|
||||
},
|
||||
search: string
|
||||
layout: MinimalLayoutInformation,
|
||||
search: string,
|
||||
language?: string,
|
||||
): boolean {
|
||||
if (search === undefined) {
|
||||
return true
|
||||
}
|
||||
search = Utils.RemoveDiacritics(search.toLocaleLowerCase()) // See #1729
|
||||
search = Utils.simplifyStringForSearch(search.toLocaleLowerCase()) // See #1729
|
||||
if (search.length > 3 && layout.id.toLowerCase().indexOf(search) >= 0) {
|
||||
return true
|
||||
}
|
||||
if (layout.id === "personal") {
|
||||
return false
|
||||
}
|
||||
if(Utils.simplifyStringForSearch(layout.id) === Utils.simplifyStringForSearch(search)){
|
||||
if (Utils.simplifyStringForSearch(layout.id) === Utils.simplifyStringForSearch(search)) {
|
||||
return true
|
||||
}
|
||||
const entitiesToSearch = [layout.shortDescription, layout.title, ...(layout.keywords ?? [])]
|
||||
language ??= Locale.language.data
|
||||
|
||||
const entitiesToSearch: (string | Record<string, string> | Record<string, string[]>)[] = [layout.shortDescription, layout.title, layout.keywords]
|
||||
for (const entity of entitiesToSearch) {
|
||||
if (entity === undefined) {
|
||||
continue
|
||||
}
|
||||
const term: string = entity["*"] ?? entity[Locale.language.data]
|
||||
if (Utils.RemoveDiacritics(term?.toLowerCase())?.indexOf(search) >= 0) {
|
||||
|
||||
let term: string[]
|
||||
if (typeof entity === "string") {
|
||||
term = [entity]
|
||||
} else {
|
||||
const terms = [].concat(entity["*"], entity[language])
|
||||
if (Array.isArray(terms)) {
|
||||
term = terms
|
||||
} else {
|
||||
term = [terms]
|
||||
}
|
||||
}
|
||||
|
||||
const minLevehnstein = Math.min(...Utils.NoNull(term).map(t => Utils.levenshteinDistance(search,
|
||||
Utils.simplifyStringForSearch(t).slice(0, search.length))))
|
||||
|
||||
if (minLevehnstein < 1 || minLevehnstein / search.length < 0.2) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -95,7 +109,7 @@ export default class MoreScreen {
|
|||
public static createUrlFor(
|
||||
layout: { id: string },
|
||||
isCustom: boolean,
|
||||
state?: { layoutToUse?: { id } }
|
||||
state?: { layoutToUse?: { id } },
|
||||
): string {
|
||||
if (layout === undefined) {
|
||||
return undefined
|
||||
|
|
@ -141,7 +155,7 @@ export default class MoreScreen {
|
|||
new Set<string>(
|
||||
Object.keys(preferences)
|
||||
.filter((key) => key.startsWith(prefix))
|
||||
.map((key) => key.substring(prefix.length, key.length - "-enabled".length))
|
||||
.map((key) => key.substring(prefix.length, key.length - "-enabled".length)),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import UserDetails, { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import Constants from "../../Models/Constants"
|
||||
import type { LayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import type { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource"
|
||||
import Marker from "../Map/Marker.svelte"
|
||||
|
||||
export let theme: LayoutInformation
|
||||
export let theme: MinimalLayoutInformation
|
||||
export let isCustom: boolean = false
|
||||
export let userDetails: UIEventSource<UserDetails>
|
||||
export let state: { layoutToUse?: { id: string }; osmConnection: OsmConnection }
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@
|
|||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import ThemeButton from "./ThemeButton.svelte"
|
||||
import { LayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import { LayoutInformation, MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import MoreScreen from "./MoreScreen"
|
||||
import themeOverview from "../../assets/generated/theme_overview.json"
|
||||
|
||||
export let search: UIEventSource<string>
|
||||
export let themes: LayoutInformation[]
|
||||
export let themes: MinimalLayoutInformation[]
|
||||
export let state: { osmConnection: OsmConnection }
|
||||
export let isCustom: boolean = false
|
||||
export let hideThemes: boolean = true
|
||||
|
|
|
|||
|
|
@ -1602,6 +1602,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
* @constructor
|
||||
*
|
||||
* Utils.RemoveDiacritics("bâtiments") // => "batiments"
|
||||
* Utils.RemoveDiacritics(undefined) // => undefined
|
||||
*/
|
||||
public static RemoveDiacritics(str?: string): string {
|
||||
// See #1729
|
||||
|
|
@ -1616,9 +1617,10 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
* @param str
|
||||
* Utils.simplifyStringForSearch("abc def; ghi 564") // => "abcdefghi564"
|
||||
* Utils.simplifyStringForSearch("âbc déf; ghi 564") // => "abcdefghi564"
|
||||
* Utils.simplifyStringForSearch(undefined) // => undefined
|
||||
*/
|
||||
public static simplifyStringForSearch(str: string): string {
|
||||
return Utils.RemoveDiacritics(str).toLowerCase().replace(/[^a-z0-9]/g, "")
|
||||
return Utils.RemoveDiacritics(str)?.toLowerCase()?.replace(/[^a-z0-9]/g, "")
|
||||
}
|
||||
|
||||
public static randomString(length: number): string {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue