Merge branch 'master' into develop
This commit is contained in:
commit
e1c831884a
16 changed files with 242 additions and 150 deletions
|
@ -135,7 +135,6 @@
|
|||
"de": "Kletterschuhe können hier ausgeliehen werden"
|
||||
},
|
||||
"addExtraTags": [
|
||||
"service:climbing_shoes:rental:fee=",
|
||||
"service:climbing_shoes:rental:charge="
|
||||
]
|
||||
},
|
||||
|
|
|
@ -2154,20 +2154,23 @@
|
|||
{
|
||||
"id": "survey_date",
|
||||
"question": {
|
||||
"en": "When was this object last surveyed?"
|
||||
"en": "When was this object last surveyed?",
|
||||
"de": "Wann wurde dieses Objekt zuletzt geprüft?"
|
||||
},
|
||||
"freeform": {
|
||||
"key": "survey:date",
|
||||
"type": "date"
|
||||
},
|
||||
"render": {
|
||||
"en": "This object was last surveyed on <b>{survey:date}</b>"
|
||||
"en": "This object was last surveyed on <b>{survey:date}</b>",
|
||||
"de": "Dieses Objekt wurde zuletzt geprüft am <b>{survey:date}</b>"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "survey:date:={_now:date}",
|
||||
"then": {
|
||||
"en": "This object was last surveyed today"
|
||||
"en": "This object was last surveyed today",
|
||||
"de": "Dieses Objekt wurde heute zuletzt geprüft"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -39,7 +39,8 @@
|
|||
"if": "__url_parameter_initialized:language=yes",
|
||||
"icon": "./assets/layers/usersettings/translate_disabled.svg",
|
||||
"then": {
|
||||
"en": "The language was set via an URL-parameter and cannot be set by the user.²"
|
||||
"en": "The language was set via an URL-parameter and cannot be set by the user.²",
|
||||
"de": "Die Sprache wurde über einen URL-Parameter gesetzt und kann nicht vom Benutzer eingestellt werden.²"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -231,12 +231,7 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"and": [
|
||||
"fee=no",
|
||||
"charge="
|
||||
]
|
||||
},
|
||||
"if": "fee=no",
|
||||
"then": {
|
||||
"en": "Can be used for free",
|
||||
"id": "Boleh digunakan tanpa bayaran",
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"available": "Diese Gemeinschaft spricht {native}",
|
||||
"intro": "Treten Sie mit anderen Menschen in Kontakt, um sie kennen zu lernen, von ihnen zu lernen, …",
|
||||
"notAvailable": "Diese Gemeinschaft spricht nicht {native}",
|
||||
"title": "Index der Community"
|
||||
"title": "Mit anderen in Kontakt treten"
|
||||
},
|
||||
"delete": {
|
||||
"cancel": "Abbrechen",
|
||||
|
|
|
@ -5163,13 +5163,13 @@
|
|||
"hs-club-mate": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "In diesem Hackerspace gibt es Club Mate"
|
||||
"then": "In diesem Hackerspace gibt es Club-Mate"
|
||||
},
|
||||
"1": {
|
||||
"then": "In diesem Hackerspace gibt es kein Club Mate"
|
||||
"then": "In diesem Hackerspace gibt es kein Club-Mate"
|
||||
}
|
||||
},
|
||||
"question": "Gibt es in diesem Hackerspace Club Mate?"
|
||||
"question": "Gibt es in diesem Hackerspace Club-Mate?"
|
||||
},
|
||||
"is_makerspace": {
|
||||
"mappings": {
|
||||
|
@ -7318,6 +7318,15 @@
|
|||
},
|
||||
"question": "Ist das Rauchen in {title()} erlaubt?"
|
||||
},
|
||||
"survey_date": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Dieses Objekt wurde heute zuletzt geprüft"
|
||||
}
|
||||
},
|
||||
"question": "Wann wurde dieses Objekt zuletzt geprüft?",
|
||||
"render": "Dieses Objekt wurde zuletzt geprüft am <b>{survey:date}</b>"
|
||||
},
|
||||
"website": {
|
||||
"question": "Wie lautet die Webseite von {title()}?"
|
||||
},
|
||||
|
@ -9406,6 +9415,13 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"language_picker": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Die Sprache wurde über einen URL-Parameter gesetzt und kann nicht vom Benutzer eingestellt werden.²"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mangrove-keys": {
|
||||
"render": "<a href='data:application/json,{mangroveidentity}' download='mangrove_private_key_{_name}'>Laden Sie den privaten Schlüssel für Ihr Mangrove-Konto herunter</a> <p>Jeder, der diese Datei besitzt, kann mit Ihrer Identität Rezensionen vornehmen</p>"
|
||||
},
|
||||
|
|
|
@ -5163,13 +5163,13 @@
|
|||
"hs-club-mate": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "This hackerspace serves club mate"
|
||||
"then": "This hackerspace serves Club-Mate"
|
||||
},
|
||||
"1": {
|
||||
"then": "This hackerspace does not serve club mate"
|
||||
"then": "This hackerspace does not serve Club-Mate"
|
||||
}
|
||||
},
|
||||
"question": "Does this hackerspace serve Club Mate?"
|
||||
"question": "Does this hackerspace serve Club-Mate?"
|
||||
},
|
||||
"is_makerspace": {
|
||||
"mappings": {
|
||||
|
|
|
@ -4939,13 +4939,13 @@
|
|||
"hs-club-mate": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Deze hackerspace biedt clube-mate aan"
|
||||
"then": "Deze hackerspace biedt Club-Mate aan"
|
||||
},
|
||||
"1": {
|
||||
"then": "Deze hackerspace biedt geen club-mate aan"
|
||||
"then": "Deze hackerspace biedt geen Club-Mate aan"
|
||||
}
|
||||
},
|
||||
"question": "Biedt deze hackerspace club-mate aan?"
|
||||
"question": "Biedt deze hackerspace Club-Mate aan?"
|
||||
},
|
||||
"is_makerspace": {
|
||||
"mappings": {
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
{
|
||||
"advanced": {
|
||||
"title": "Avanserte funksjoner"
|
||||
},
|
||||
"centerMessage": {
|
||||
"loadingData": "Laster inn data …",
|
||||
"ready": "Ferdig",
|
||||
|
|
|
@ -557,7 +557,7 @@ function MergeTranslation(source: any, target: any, language: string, context: s
|
|||
if (context.endsWith(".tagRenderings")) {
|
||||
keyRemapping = new Map<string, string>()
|
||||
for (const key in target) {
|
||||
keyRemapping.set(target[key].id, key)
|
||||
keyRemapping.set(target[key].id ?? target[key].builtin, key)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import FilterConfigJson from "../Json/FilterConfigJson"
|
|||
import DeleteConfig from "../DeleteConfig"
|
||||
import {QuestionableTagRenderingConfigJson} from "../Json/QuestionableTagRenderingConfigJson"
|
||||
import Validators from "../../../UI/InputElement/Validators"
|
||||
import TagRenderingConfig from "../TagRenderingConfig";
|
||||
|
||||
class ValidateLanguageCompleteness extends DesugaringStep<any> {
|
||||
private readonly _languages: string[]
|
||||
|
@ -383,6 +384,51 @@ export class PrevalidateTheme extends Fuse<LayoutConfigJson> {
|
|||
}
|
||||
}
|
||||
|
||||
export class DetectConflictingAddExtraTags extends DesugaringStep<TagRenderingConfigJson> {
|
||||
constructor() {
|
||||
super("The `if`-part in a mapping might set some keys. Those key are not allowed to be set in the `addExtraTags`, as this might result in conflicting values", [], "DetectConflictingAddExtraTags");
|
||||
}
|
||||
|
||||
convert(json: TagRenderingConfigJson, context: string): {
|
||||
result: TagRenderingConfigJson;
|
||||
errors?: string[];
|
||||
warnings?: string[];
|
||||
information?: string[]
|
||||
} {
|
||||
|
||||
if (!(json.mappings?.length > 0)) {
|
||||
return {result: json}
|
||||
}
|
||||
|
||||
const tagRendering = new TagRenderingConfig(json)
|
||||
|
||||
const errors = []
|
||||
for (let i = 0; i < tagRendering.mappings.length; i++) {
|
||||
const mapping = tagRendering.mappings[i];
|
||||
if (!mapping.addExtraTags) {
|
||||
continue
|
||||
}
|
||||
const keysInMapping = new Set(mapping.if.usedKeys())
|
||||
|
||||
const keysInAddExtraTags = mapping.addExtraTags.map(t => t.key)
|
||||
|
||||
const duplicateKeys = keysInAddExtraTags.filter(k => keysInMapping.has(k))
|
||||
if (duplicateKeys.length > 0) {
|
||||
errors.push(
|
||||
"At " + context + ".mappings[" + i + "]: AddExtraTags overrides a key that is set in the `if`-clause of this mapping. Selecting this answer might thus first set one value (needed to match as answer) and then override it with a different value, resulting in an unsaveable question. The offending `addExtraTags` is " + duplicateKeys.join(", ")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
result: json,
|
||||
errors
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJson> {
|
||||
private readonly _calculatedTagNames: string[]
|
||||
|
||||
|
@ -669,6 +715,7 @@ export class ValidateTagRenderings extends Fuse<TagRenderingConfigJson> {
|
|||
super(
|
||||
"Various validation on tagRenderingConfigs",
|
||||
new DetectShadowedMappings(layerConfig),
|
||||
new DetectConflictingAddExtraTags(),
|
||||
new DetectMappingsWithImages(doesImageExist),
|
||||
new MiscTagRenderingChecks(options)
|
||||
)
|
||||
|
@ -865,6 +912,13 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
|||
}
|
||||
}
|
||||
|
||||
if (json.filter) {
|
||||
const r = new On("filter", new Each( new ValidateFilter())).convert(json, context)
|
||||
warnings.push(...(r.warnings ?? []))
|
||||
errors.push(...(r.errors ?? []))
|
||||
information.push(...(r.information ?? []))
|
||||
}
|
||||
|
||||
if (json.tagRenderings !== undefined) {
|
||||
const r = new On(
|
||||
"tagRenderings",
|
||||
|
@ -949,9 +1003,14 @@ export class ValidateFilter extends DesugaringStep<FilterConfigJson> {
|
|||
warnings?: string[]
|
||||
information?: string[]
|
||||
} {
|
||||
if (typeof filter === "string") {
|
||||
// Calling another filter, we skip
|
||||
return {result: filter}
|
||||
}
|
||||
const errors = []
|
||||
for (const option of filter.options) {
|
||||
for (let i = 0; i < option.fields.length; i++) {
|
||||
|
||||
for (let i = 0; i < option.fields?.length ?? 0; i++) {
|
||||
const field = option.fields[i]
|
||||
const type = field.type ?? "string"
|
||||
if (Validators.availableTypes.find((t) => t === type) === undefined) {
|
||||
|
|
|
@ -58,7 +58,7 @@ export default class TagRenderingConfig {
|
|||
|
||||
public readonly freeform?: {
|
||||
readonly key: string
|
||||
readonly type: string
|
||||
readonly type: ValidatorType
|
||||
readonly placeholder: Translation
|
||||
readonly addExtraTags: UploadableTag[]
|
||||
readonly inline: boolean
|
||||
|
@ -133,7 +133,17 @@ export default class TagRenderingConfig {
|
|||
) {
|
||||
throw `Freeform.addExtraTags should be a list of strings - not a single string (at ${context})`
|
||||
}
|
||||
const type = json.freeform.type ?? "string"
|
||||
if (
|
||||
json.freeform.type &&
|
||||
Validators.availableTypes.indexOf(<any>json.freeform.type) < 0
|
||||
) {
|
||||
throw `At ${context}: invalid type, perhaps you meant ${Utils.sortedByLevenshteinDistance(
|
||||
json.freeform.key,
|
||||
<any>Validators.availableTypes,
|
||||
(s) => <any>s
|
||||
)}`
|
||||
}
|
||||
const type: ValidatorType = <any>json.freeform.type ?? "string"
|
||||
|
||||
let placeholder: Translation = Translations.T(json.freeform.placeholder)
|
||||
if (placeholder === undefined) {
|
||||
|
@ -622,7 +632,7 @@ export default class TagRenderingConfig {
|
|||
*
|
||||
* @param singleSelectedMapping (Only used if multiAnswer == false): the single mapping to apply. Use (mappings.length) for the freeform
|
||||
* @param multiSelectedMapping (Only used if multiAnswer == true): all the mappings that must be applied. Set multiSelectedMapping[mappings.length] to use the freeform as well
|
||||
* @param currentProperties: The current properties of the object for which the question should be answered
|
||||
* @param currentProperties The current properties of the object for which the question should be answered
|
||||
*/
|
||||
public constructChangeSpecification(
|
||||
freeformValue: string | undefined,
|
||||
|
@ -685,7 +695,8 @@ export default class TagRenderingConfig {
|
|||
return undefined
|
||||
}
|
||||
return and
|
||||
} else {
|
||||
}
|
||||
|
||||
// Is at least one mapping shown in the answer?
|
||||
const someMappingIsShown = this.mappings.some((m) => {
|
||||
if (typeof m.hideInAnswer === "boolean") {
|
||||
|
@ -697,7 +708,9 @@ export default class TagRenderingConfig {
|
|||
// If all mappings are hidden for the current tags, we can safely assume that we should use the freeform key
|
||||
const useFreeform =
|
||||
freeformValue !== undefined &&
|
||||
(singleSelectedMapping === this.mappings.length || !someMappingIsShown)
|
||||
(singleSelectedMapping === this.mappings.length ||
|
||||
!someMappingIsShown ||
|
||||
singleSelectedMapping === undefined)
|
||||
if (useFreeform) {
|
||||
return new And([
|
||||
new Tag(this.freeform.key, freeformValue),
|
||||
|
@ -709,16 +722,17 @@ export default class TagRenderingConfig {
|
|||
...(this.mappings[singleSelectedMapping].addExtraTags ?? []),
|
||||
])
|
||||
} else {
|
||||
console.warn("TagRenderingConfig.ConstructSpecification has a weird fallback for", {
|
||||
console.error("TagRenderingConfig.ConstructSpecification has a weird fallback for", {
|
||||
freeformValue,
|
||||
singleSelectedMapping,
|
||||
multiSelectedMapping,
|
||||
currentProperties,
|
||||
useFreeform,
|
||||
})
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GenerateDocumentation(): BaseUIElement {
|
||||
let withRender: (BaseUIElement | string)[] = []
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
}
|
||||
|
||||
if (unit && isNaN(Number(v))) {
|
||||
console.debug("Not a number, but a unit is required")
|
||||
value.setData(undefined)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import BaseUIElement from "../BaseUIElement"
|
||||
import { Translation } from "../i18n/Translation"
|
||||
import Translations from "../i18n/Translations"
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import { Translation } from "../i18n/Translation";
|
||||
import Translations from "../i18n/Translations";
|
||||
|
||||
/**
|
||||
* A 'TextFieldValidator' contains various methods to check and cleanup an entered value or to give feedback.
|
||||
|
@ -16,13 +16,13 @@ export abstract class Validator {
|
|||
/**
|
||||
* What HTML-inputmode to use
|
||||
*/
|
||||
public readonly inputmode?: string
|
||||
public readonly inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'
|
||||
public readonly textArea: boolean
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
explanation: string | BaseUIElement,
|
||||
inputmode?: string,
|
||||
inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search',
|
||||
textArea?: false | boolean
|
||||
) {
|
||||
this.name = name
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { Translation } from "../../i18n/Translation"
|
||||
import Translations from "../../i18n/Translations"
|
||||
import { Validator } from "../Validator"
|
||||
import { ValidatorType } from "../Validators";
|
||||
|
||||
export default class FloatValidator extends Validator {
|
||||
inputmode = "decimal"
|
||||
inputmode: "decimal" = "decimal"
|
||||
|
||||
constructor(name?: string, explanation?: string) {
|
||||
constructor(name?: ValidatorType, explanation?: string) {
|
||||
super(name ?? "float", explanation ?? "A decimal number", "decimal")
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
export let selectedTags: TagsFilter = undefined
|
||||
|
||||
let mappings: Mapping[] = config?.mappings
|
||||
let searchTerm: Store<string> = new UIEventSource("")
|
||||
let searchTerm: UIEventSource<string> = new UIEventSource("")
|
||||
|
||||
$: {
|
||||
try {
|
||||
|
|
Loading…
Reference in a new issue