forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			255 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			255 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import { SpecialVisualization } from "../SpecialVisualization"
 | 
						|
import BaseUIElement from "../BaseUIElement"
 | 
						|
import { UIEventSource } from "../../Logic/UIEventSource"
 | 
						|
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
 | 
						|
import { VariableUiElement } from "../Base/VariableUIElement"
 | 
						|
import { OsmTags } from "../../Models/OsmFeature"
 | 
						|
import * as all_languages from "../../assets/language_translations.json"
 | 
						|
import { Translation } from "../i18n/Translation"
 | 
						|
import Combine from "../Base/Combine"
 | 
						|
import Title from "../Base/Title"
 | 
						|
import Lazy from "../Base/Lazy"
 | 
						|
import { SubstitutedTranslation } from "../SubstitutedTranslation"
 | 
						|
import List from "../Base/List"
 | 
						|
import { AllLanguagesSelector } from "./AllLanguagesSelector"
 | 
						|
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
 | 
						|
import { And } from "../../Logic/Tags/And"
 | 
						|
import { Tag } from "../../Logic/Tags/Tag"
 | 
						|
import { EditButton, SaveButton } from "./SaveButton"
 | 
						|
import { FixedUiElement } from "../Base/FixedUiElement"
 | 
						|
import Translations from "../i18n/Translations"
 | 
						|
import Toggle from "../Input/Toggle"
 | 
						|
import { On } from "../../Models/ThemeConfig/Conversion/Conversion"
 | 
						|
 | 
						|
export class LanguageElement implements SpecialVisualization {
 | 
						|
    funcName: string = "language_chooser"
 | 
						|
 | 
						|
    docs: string | BaseUIElement =
 | 
						|
        "The language element allows to show and pick all known (modern) languages. The key can be set"
 | 
						|
 | 
						|
    args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] = [
 | 
						|
        {
 | 
						|
            name: "key",
 | 
						|
            required: true,
 | 
						|
            doc: "What key to use, e.g. `language`, `tactile_writing:braille:language`, ... If a language is supported, the language code will be appended to this key, resulting in `language:nl=yes` if nl is picked ",
 | 
						|
        },
 | 
						|
        {
 | 
						|
            name: "question",
 | 
						|
            required: true,
 | 
						|
            doc: "What to ask if no questions are known",
 | 
						|
        },
 | 
						|
        {
 | 
						|
            name: "render_list_item",
 | 
						|
            doc: "How a single language will be shown in the list of languages. Use `{language}` to indicate the language (which it must contain).",
 | 
						|
            defaultValue: "{language()}",
 | 
						|
        },
 | 
						|
        {
 | 
						|
            name: "render_single_language",
 | 
						|
            doc: "What will be shown if the feature only supports a single language",
 | 
						|
            required: true,
 | 
						|
        },
 | 
						|
        {
 | 
						|
            name: "render_all",
 | 
						|
            doc: "The full rendering. Use `{list}` to show where the list of languages must come. Optional if mode=single",
 | 
						|
            defaultValue: "{list()}",
 | 
						|
        },
 | 
						|
        {
 | 
						|
            name: "no_known_languages",
 | 
						|
            doc: "The text that is shown if no languages are known for this key. If this text is omitted, the languages will be prompted instead",
 | 
						|
        },
 | 
						|
        {
 | 
						|
            name: "mode",
 | 
						|
            doc: "If one or many languages can be selected. Should be 'multi' or 'single'",
 | 
						|
            defaultValue: "multi",
 | 
						|
        },
 | 
						|
    ]
 | 
						|
 | 
						|
    example: `
 | 
						|
    \`\`\`json
 | 
						|
     {"special":
 | 
						|
       "type": "language_chooser",
 | 
						|
       "key": "school:language",
 | 
						|
       "question": {"en": "What are the main (and administrative) languages spoken in this school?"},
 | 
						|
       "render_single_language": {"en": "{language()} is spoken on this school"},
 | 
						|
       "render_list_item": {"en": "{language()}"},
 | 
						|
       "render_all": {"en": "The following languages are spoken here:{list()}"}
 | 
						|
       "mode":"multi"
 | 
						|
     }
 | 
						|
     \`\`\`
 | 
						|
    `
 | 
						|
 | 
						|
    constr(
 | 
						|
        state: FeaturePipelineState,
 | 
						|
        tagSource: UIEventSource<OsmTags>,
 | 
						|
        argument: string[]
 | 
						|
    ): BaseUIElement {
 | 
						|
        let [key, question, item_render, single_render, all_render, on_no_known_languages, mode] =
 | 
						|
            argument
 | 
						|
        if (mode === undefined || mode.length == 0) {
 | 
						|
            mode = "multi"
 | 
						|
        }
 | 
						|
        if (item_render === undefined || item_render.trim() === "") {
 | 
						|
            item_render = "{language()}"
 | 
						|
        }
 | 
						|
        if (all_render === undefined || all_render.length == 0) {
 | 
						|
            all_render = "{list()}"
 | 
						|
        }
 | 
						|
        if (mode !== "single" && mode !== "multi") {
 | 
						|
            throw (
 | 
						|
                "Error while calling language_chooser: mode must be either 'single' or 'multi' but it is " +
 | 
						|
                mode
 | 
						|
            )
 | 
						|
        }
 | 
						|
        if (single_render.indexOf("{language()") < 0) {
 | 
						|
            throw (
 | 
						|
                "Error while calling language_chooser: render_single_language must contain '{language()}' but it is " +
 | 
						|
                single_render
 | 
						|
            )
 | 
						|
        }
 | 
						|
        if (item_render.indexOf("{language()") < 0) {
 | 
						|
            throw (
 | 
						|
                "Error while calling language_chooser: render_list_item must contain '{language()}' but it is " +
 | 
						|
                item_render
 | 
						|
            )
 | 
						|
        }
 | 
						|
        if (all_render.indexOf("{list()") < 0) {
 | 
						|
            throw "Error while calling language_chooser: render_all must contain '{list()}'"
 | 
						|
        }
 | 
						|
 | 
						|
        const prefix = key + ":"
 | 
						|
        const foundLanguages = tagSource.map((tags) => {
 | 
						|
            const foundLanguages: string[] = []
 | 
						|
            for (const k in tags) {
 | 
						|
                const v = tags[k]
 | 
						|
                if (v !== "yes") {
 | 
						|
                    continue
 | 
						|
                }
 | 
						|
                if (k.startsWith(prefix)) {
 | 
						|
                    foundLanguages.push(k.substring(prefix.length))
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return foundLanguages
 | 
						|
        })
 | 
						|
        const forceInputMode = new UIEventSource(false)
 | 
						|
        const inputEl = new Lazy(() => {
 | 
						|
            const selector = new AllLanguagesSelector({
 | 
						|
                mode: mode === "single" ? "select-one" : "select-many",
 | 
						|
                currentCountry: tagSource.map((tgs) => tgs["_country"]),
 | 
						|
            })
 | 
						|
            const cancelButton = Toggle.If(forceInputMode, () =>
 | 
						|
                Translations.t.general.cancel
 | 
						|
                    .Clone()
 | 
						|
                    .SetClass("btn btn-secondary")
 | 
						|
                    .onClick(() => forceInputMode.setData(false))
 | 
						|
            )
 | 
						|
 | 
						|
            const saveButton = new SaveButton(
 | 
						|
                selector.GetValue().map((lngs) => (lngs.length > 0 ? "true" : undefined)),
 | 
						|
                state.osmConnection
 | 
						|
            ).onClick(() => {
 | 
						|
                const selectedLanguages = selector.GetValue().data
 | 
						|
                const currentLanguages = foundLanguages.data
 | 
						|
                const selection: Tag[] = selectedLanguages.map((ln) => new Tag(prefix + ln, "yes"))
 | 
						|
 | 
						|
                for (const currentLanguage of currentLanguages) {
 | 
						|
                    if (selectedLanguages.indexOf(currentLanguage) >= 0) {
 | 
						|
                        continue
 | 
						|
                    }
 | 
						|
                    // Erase language that is not spoken anymore
 | 
						|
                    selection.push(new Tag(prefix + currentLanguage, ""))
 | 
						|
                }
 | 
						|
 | 
						|
                if (state.featureSwitchIsTesting.data) {
 | 
						|
                    for (const tag of selection) {
 | 
						|
                        tagSource.data[tag.key] = tag.value
 | 
						|
                    }
 | 
						|
                    tagSource.ping()
 | 
						|
                } else {
 | 
						|
                    ;(state?.changes)
 | 
						|
                        .applyAction(
 | 
						|
                            new ChangeTagAction(
 | 
						|
                                tagSource.data.id,
 | 
						|
                                new And(selection),
 | 
						|
                                tagSource.data,
 | 
						|
                                {
 | 
						|
                                    theme: state?.layoutToUse?.id ?? "unkown",
 | 
						|
                                    changeType: "answer",
 | 
						|
                                }
 | 
						|
                            )
 | 
						|
                        )
 | 
						|
                        .then((_) => {
 | 
						|
                            console.log("Tagchanges applied")
 | 
						|
                        })
 | 
						|
                }
 | 
						|
                forceInputMode.setData(false)
 | 
						|
            })
 | 
						|
 | 
						|
            return new Combine([
 | 
						|
                new Title(question),
 | 
						|
                selector,
 | 
						|
                new Combine([cancelButton, saveButton]).SetClass("flex justify-end"),
 | 
						|
            ]).SetClass("flex flex-col question disable-links")
 | 
						|
        })
 | 
						|
 | 
						|
        const editButton = new EditButton(state.osmConnection, () => forceInputMode.setData(true))
 | 
						|
 | 
						|
        return new VariableUiElement(
 | 
						|
            foundLanguages.map(
 | 
						|
                (foundLanguages) => {
 | 
						|
                    if (forceInputMode.data) {
 | 
						|
                        return inputEl
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (foundLanguages.length === 0) {
 | 
						|
                        // No languages found - we show the question and the input element
 | 
						|
                        if (
 | 
						|
                            on_no_known_languages !== undefined &&
 | 
						|
                            on_no_known_languages.length > 0
 | 
						|
                        ) {
 | 
						|
                            return new Combine([on_no_known_languages, editButton]).SetClass(
 | 
						|
                                "flex justify-end"
 | 
						|
                            )
 | 
						|
                        }
 | 
						|
                        return inputEl
 | 
						|
                    }
 | 
						|
 | 
						|
                    let rendered: BaseUIElement
 | 
						|
                    if (foundLanguages.length === 1) {
 | 
						|
                        const ln = foundLanguages[0]
 | 
						|
                        let mapping = new Map<string, BaseUIElement>()
 | 
						|
                        mapping.set("language", new Translation(all_languages[ln]))
 | 
						|
                        rendered = new SubstitutedTranslation(
 | 
						|
                            new Translation({ "*": single_render }, undefined),
 | 
						|
                            tagSource,
 | 
						|
                            state,
 | 
						|
                            mapping
 | 
						|
                        )
 | 
						|
                    } else {
 | 
						|
                        let mapping = new Map<string, BaseUIElement>()
 | 
						|
                        const languagesList = new List(
 | 
						|
                            foundLanguages.map((ln) => {
 | 
						|
                                let mappingLn = new Map<string, BaseUIElement>()
 | 
						|
                                mappingLn.set("language", new Translation(all_languages[ln]))
 | 
						|
                                return new SubstitutedTranslation(
 | 
						|
                                    new Translation({ "*": item_render }, undefined),
 | 
						|
                                    tagSource,
 | 
						|
                                    state,
 | 
						|
                                    mappingLn
 | 
						|
                                )
 | 
						|
                            })
 | 
						|
                        )
 | 
						|
                        mapping.set("list", languagesList)
 | 
						|
                        rendered = new SubstitutedTranslation(
 | 
						|
                            new Translation({ "*": all_render }, undefined),
 | 
						|
                            tagSource,
 | 
						|
                            state,
 | 
						|
                            mapping
 | 
						|
                        )
 | 
						|
                    }
 | 
						|
                    return new Combine([rendered, editButton]).SetClass("flex justify-between")
 | 
						|
                },
 | 
						|
                [forceInputMode]
 | 
						|
            )
 | 
						|
        )
 | 
						|
    }
 | 
						|
}
 |