Fixing too many bugs, cleaning up some old parts of the code

This commit is contained in:
Pieter Vander Vennet 2020-09-03 16:44:48 +02:00
parent 3d05999f85
commit 00a6611e1f
21 changed files with 706 additions and 436 deletions

View file

@ -259,7 +259,13 @@ export class FromJSON {
const iconSize = FromJSON.TagRenderingWithDefault(json.iconSize, "iconSize", "40,40,center"); const iconSize = FromJSON.TagRenderingWithDefault(json.iconSize, "iconSize", "40,40,center");
const color = FromJSON.TagRenderingWithDefault(json.color, "layercolor", "#0000ff"); const color = FromJSON.TagRenderingWithDefault(json.color, "layercolor", "#0000ff");
const width = FromJSON.TagRenderingWithDefault(json.width, "layerwidth", "10"); const width = FromJSON.TagRenderingWithDefault(json.width, "layerwidth", "10");
const tagRenderings = json.tagRenderings?.map(FromJSON.TagRendering) ?? [];
let tagRenderingDefs = json.tagRenderings ?? [];
if (tagRenderingDefs.indexOf("images") < 0) {
tagRenderingDefs = ["images", ...tagRenderingDefs];
}
let tagRenderings = tagRenderingDefs.map(FromJSON.TagRendering);
const renderTags = {"id": "node/-1"} const renderTags = {"id": "node/-1"}
const presets: Preset[] = json?.presets?.map(preset => { const presets: Preset[] = json?.presets?.map(preset => {
@ -294,7 +300,7 @@ export class FromJSON {
// the anchor is always set from the center of the point // the anchor is always set from the center of the point
// x, y with x going right and y going down if the values are bigger // x, y with x going right and y going down if the values are bigger
const popupAnchor = [0, -iconAnchor[1]+3]; const popupAnchor = [0, 3 - iconAnchor[1]];
return { return {
color: color.GetContent(tags).txt, color: color.GetContent(tags).txt,

View file

@ -105,7 +105,16 @@ export class UIEventSource<T>{
}); });
return newSource; return newSource;
}
public static Chronic(millis: number):UIEventSource<Date>{
const source = new UIEventSource<Date>(undefined);
function run() {
source.setData(new Date());
window.setTimeout(run, millis);
}
run();
return source;
} }

View file

@ -23,16 +23,15 @@ export class State {
// The singleton of the global state // The singleton of the global state
public static state: State; public static state: State;
public static vNumber = "0.0.7d Refactored"; public static vNumber = "0.0.7e Fixing all the bugs";
// The user journey states thresholds when a new feature gets unlocked // The user journey states thresholds when a new feature gets unlocked
public static userJourney = { public static userJourney = {
customLayoutUnlock: 50, personalLayoutUnlock: 20,
themeGeneratorUnlock: 500, tagsVisibleAt: 100,
tagsVisibleAt: 200, tagsVisibleAndWikiLinked: 150,
tagsVisibleAndWikiLinked: 250 themeGeneratorReadOnlyUnlock: 200,
themeGeneratorFullUnlock: 500,
}; };
public static runningFromConsole: boolean = false; public static runningFromConsole: boolean = false;
@ -68,13 +67,6 @@ export class State {
* The message that should be shown at the center of the screen * The message that should be shown at the center of the screen
*/ */
public readonly centerMessage = new UIEventSource<string>(""); public readonly centerMessage = new UIEventSource<string>("");
/**
* The countdown: if set to e.g. ten, it'll start counting down. When reaching zero, changes will be saved. NB: this is implemented later, not in the eventSource
*/
public readonly secondsTillChangesAreSaved = new UIEventSource<number>(0);
/** /**
This message is shown full screen on mobile devices This message is shown full screen on mobile devices
*/ */
@ -116,10 +108,6 @@ export class State {
latlng: number, latlng: number,
accuracy: number accuracy: number
}> = new UIEventSource<{ latlng: number, accuracy: number }>(undefined); }> = new UIEventSource<{ latlng: number, accuracy: number }>(undefined);
/** After this many milliseconds without changes, saves are sent of to OSM
*/
public readonly saveTimeout = new UIEventSource<number>(30 * 1000);
public layoutDefinition: string; public layoutDefinition: string;
public installedThemes: UIEventSource<{ layout: Layout; definition: string }[]>; public installedThemes: UIEventSource<{ layout: Layout; definition: string }[]>;
@ -259,16 +247,5 @@ export class State {
return; return;
} }
}
public GetFilteredLayerFor(id: string) : FilteredLayer{
for (const flayer of this.filteredLayers.data) {
if(flayer.layerDef.id === id){
return flayer;
}
}
return undefined;
} }
} }

View file

@ -6,6 +6,7 @@ import {LayoutConfigJson} from "../../Customizations/JSON/LayoutConfigJson";
import Combine from "../Base/Combine"; import Combine from "../Base/Combine";
import {GenerateEmpty} from "./GenerateEmpty"; import {GenerateEmpty} from "./GenerateEmpty";
import LayerPanelWithPreview from "./LayerPanelWithPreview"; import LayerPanelWithPreview from "./LayerPanelWithPreview";
import {UserDetails} from "../../Logic/Osm/OsmConnection";
export default class AllLayersPanel extends UIElement { export default class AllLayersPanel extends UIElement {
@ -15,19 +16,19 @@ export default class AllLayersPanel extends UIElement {
private readonly languages: UIEventSource<string[]>; private readonly languages: UIEventSource<string[]>;
constructor(config: UIEventSource<LayoutConfigJson>, constructor(config: UIEventSource<LayoutConfigJson>,
languages: UIEventSource<any>) { languages: UIEventSource<any>, userDetails: UserDetails) {
super(undefined); super(undefined);
this._config = config; this._config = config;
this.languages = languages; this.languages = languages;
this.createPanels(); this.createPanels(userDetails);
const self = this; const self = this;
config.map<number>(config => config.layers.length).addCallback(() => self.createPanels()); config.map<number>(config => config.layers.length).addCallback(() => self.createPanels(userDetails));
} }
private createPanels() { private createPanels(userDetails: UserDetails) {
const self = this; const self = this;
const tabs = []; const tabs = [];
@ -36,7 +37,8 @@ export default class AllLayersPanel extends UIElement {
tabs.push({ tabs.push({
header: "<img src='./assets/bug.svg'>", header: "<img src='./assets/bug.svg'>",
content: new LayerPanelWithPreview(this._config, this.languages, i)}); content: new LayerPanelWithPreview(this._config, this.languages, i, userDetails)
});
} }
tabs.push({ tabs.push({
header: "<img src='./assets/layersAdd.svg'>", header: "<img src='./assets/layersAdd.svg'>",

View file

@ -0,0 +1,115 @@
import {UIElement} from "../UIElement";
import {OsmConnection, UserDetails} from "../../Logic/Osm/OsmConnection";
import {UIEventSource} from "../../Logic/UIEventSource";
import SingleSetting from "./SingleSetting";
import GeneralSettings from "./GeneralSettings";
import Combine from "../Base/Combine";
import {VariableUiElement} from "../Base/VariableUIElement";
import {TabbedComponent} from "../Base/TabbedComponent";
import PageSplit from "../Base/PageSplit";
import HelpText from "../../Customizations/HelpText";
import AllLayersPanel from "./AllLayersPanel";
import SharePanel from "./SharePanel";
import {LayoutConfigJson} from "../../Customizations/JSON/LayoutConfigJson";
import {SubtleButton} from "../Base/SubtleButton";
import {State} from "../../State";
import {FixedUiElement} from "../Base/FixedUiElement";
import SavePanel from "./SavePanel";
import {LocalStorageSource} from "../../Logic/Web/LocalStorageSource";
export default class CustomGeneratorPanel extends UIElement {
private mainPanel: UIElement;
private loginButton: UIElement;
private connection: OsmConnection;
constructor(connection: OsmConnection, layout: LayoutConfigJson) {
super(connection.userDetails);
this.connection = connection;
this.SetClass("main-tabs");
this.loginButton = new SubtleButton("", "Login to create a custom theme").onClick(() => connection.AttemptLogin())
const self = this;
self.mainPanel = new FixedUiElement("Attempting to log in...");
connection.OnLoggedIn(userDetails => {
self.InitMainPanel(layout, userDetails, connection);
self.Update();
})
}
private InitMainPanel(layout: LayoutConfigJson, userDetails: UserDetails, connection: OsmConnection) {
const es = new UIEventSource(layout);
const encoded = es.map(config => btoa(JSON.stringify(config)));
encoded.addCallback(encoded => LocalStorageSource.Get("\"last-custom-theme\""))
const liveUrl = encoded.map(encoded => `./index.html?userlayout=${es.data.id}#${encoded}`)
const iframe = liveUrl.map(url => `<iframe src='${url}' width='100%' height='99%' style="box-sizing: border-box" title='Theme Preview'></iframe>`);
const currentSetting = new UIEventSource<SingleSetting<any>>(undefined)
const generalSettings = new GeneralSettings(es, currentSetting);
const languages = generalSettings.languages;
const chronic = UIEventSource.Chronic(120 * 1000)
.map(date => {
if (es.data.id == undefined) {
return undefined
}
if (es.data.id === "") {
return undefined;
}
const pref = connection.GetLongPreference("installed-theme-" + es.data.id);
pref.setData(encoded.data);
return date;
});
const preview = new Combine([
new VariableUiElement(iframe.stabilized(2500))
]).SetClass("preview")
this.mainPanel = new TabbedComponent([
{
header: "<img src='./assets/gear.svg'>",
content:
new PageSplit(
generalSettings.SetStyle("width: 50vw;"),
new Combine([
new HelpText(currentSetting).SetStyle("height:calc(100% - 65vh); width: 100%; display:block; overflow-y: auto"),
preview.SetStyle("height:65vh; width:100%; display:block")
]).SetStyle("position:relative; width: 50%;")
)
},
{
header: "<img src='./assets/layers.svg'>",
content: new AllLayersPanel(es, languages, userDetails)
},
{
header: "<img src='./assets/floppy.svg'>",
content: new SavePanel(this.connection, es, chronic)
},
{
header: "<img src='./assets/share.svg'>",
content: new SharePanel(es, liveUrl)
}
])
}
InnerRender(): string {
const ud = this.connection.userDetails.data;
if (!ud.loggedIn) {
return new Combine([
"<h3>Not Logged in</h3>",
"You need to be logged in in order to create a custom theme",
this.loginButton
]).Render();
}
if (ud.csCount <= State.userJourney.themeGeneratorReadOnlyUnlock) {
return new Combine([
"<h3>Too little experience/h3>",
`Creating your own (readonly) themes can only be done if you have more then <b>${State.userJourney.themeGeneratorReadOnlyUnlock}</b> changesets made`,
`Making a theme including survey options can be done at <b>${State.userJourney.themeGeneratorFullUnlock}</b> changesets`,
this.loginButton
]).Render();
}
return this.mainPanel.Render()
}
}

View file

@ -16,6 +16,9 @@ import {TagRenderingConfigJson} from "../../Customizations/JSON/TagRenderingConf
import {MultiInput} from "../Input/MultiInput"; import {MultiInput} from "../Input/MultiInput";
import {LayerConfigJson} from "../../Customizations/JSON/LayerConfigJson"; import {LayerConfigJson} from "../../Customizations/JSON/LayerConfigJson";
import PresetInputPanel from "./PresetInputPanel"; import PresetInputPanel from "./PresetInputPanel";
import {UserDetails} from "../../Logic/Osm/OsmConnection";
import {State} from "../../State";
import {FixedUiElement} from "../Base/FixedUiElement";
/** /**
* Shows the configuration for a single layer * Shows the configuration for a single layer
@ -38,10 +41,11 @@ export default class LayerPanel extends UIElement {
constructor(config: UIEventSource<LayoutConfigJson>, constructor(config: UIEventSource<LayoutConfigJson>,
languages: UIEventSource<string[]>, languages: UIEventSource<string[]>,
index: number, index: number,
currentlySelected: UIEventSource<SingleSetting<any>>) { currentlySelected: UIEventSource<SingleSetting<any>>,
userDetails: UserDetails) {
super(); super();
this._config = config; this._config = config;
this.mapRendering = this.setupRenderOptions(config, languages, index, currentlySelected); this.mapRendering = this.setupRenderOptions(config, languages, index, currentlySelected, userDetails);
const actualDeleteButton = new SubtleButton( const actualDeleteButton = new SubtleButton(
"./assets/delete.svg", "./assets/delete.svg",
@ -100,7 +104,7 @@ export default class LayerPanel extends UIElement {
currentlySelected); currentlySelected);
const self = this; const self = this;
const popupTitleRendering = new TagRenderingPanel(languages, currentlySelected, { const popupTitleRendering = new TagRenderingPanel(languages, currentlySelected, userDetails, {
title: "Popup title", title: "Popup title",
description: "This is the rendering shown as title in the popup for this element", description: "This is the rendering shown as title in the popup for this element",
disableQuestions: true disableQuestions: true
@ -113,7 +117,7 @@ export default class LayerPanel extends UIElement {
const tagRenderings = new MultiInput<TagRenderingConfigJson>("Add a tag rendering/question", const tagRenderings = new MultiInput<TagRenderingConfigJson>("Add a tag rendering/question",
() => ({}), () => ({}),
() => { () => {
const tagPanel = new TagRenderingPanel(languages, currentlySelected) const tagPanel = new TagRenderingPanel(languages, currentlySelected, userDetails)
self.registerTagRendering(tagPanel); self.registerTagRendering(tagPanel);
return tagPanel; return tagPanel;
}); });
@ -124,11 +128,16 @@ export default class LayerPanel extends UIElement {
} }
) )
const presetPanel = new MultiInput("Add a preset", if (userDetails.csCount >= State.userJourney.themeGeneratorFullUnlock) {
() => ({tags: [], title: {}}),
() => new PresetInputPanel(currentlySelected, languages)); const presetPanel = new MultiInput("Add a preset",
this.presetsPanel = presetPanel; () => ({tags: [], title: {}}),
new SingleSetting(config, presetPanel, ["layers", index, "presets"], "Presets", "") () => new PresetInputPanel(currentlySelected, languages));
new SingleSetting(config, presetPanel, ["layers", index, "presets"], "Presets", "")
this.presetsPanel = presetPanel;
} else {
this.presetsPanel = new FixedUiElement(`Creating a custom theme which also edits OSM is only unlocked after ${State.userJourney.themeGeneratorFullUnlock} changesets`).SetClass("alert");
}
function loadTagRenderings() { function loadTagRenderings() {
const values = (config.data.layers[index] as LayerConfigJson).tagRenderings; const values = (config.data.layers[index] as LayerConfigJson).tagRenderings;
@ -152,27 +161,29 @@ export default class LayerPanel extends UIElement {
private setupRenderOptions(config: UIEventSource<LayoutConfigJson>, private setupRenderOptions(config: UIEventSource<LayoutConfigJson>,
languages: UIEventSource<string[]>, languages: UIEventSource<string[]>,
index: number, index: number,
currentlySelected: UIEventSource<SingleSetting<any>>): UIElement { currentlySelected: UIEventSource<SingleSetting<any>>,
userDetails: UserDetails
): UIElement {
const iconSelect = new TagRenderingPanel( const iconSelect = new TagRenderingPanel(
languages, currentlySelected, languages, currentlySelected, userDetails,
{ {
title: "Icon", title: "Icon",
description: "A visual representation for this layer and for the points on the map.", description: "A visual representation for this layer and for the points on the map.",
disableQuestions: true disableQuestions: true
}); });
const size = new TagRenderingPanel(languages, currentlySelected, const size = new TagRenderingPanel(languages, currentlySelected, userDetails,
{ {
title: "Icon Size", title: "Icon Size",
description: "The size of the icons on the map in pixels. Can vary based on the tagging", description: "The size of the icons on the map in pixels. Can vary based on the tagging",
disableQuestions: true disableQuestions: true
}); });
const color = new TagRenderingPanel(languages, currentlySelected, const color = new TagRenderingPanel(languages, currentlySelected, userDetails,
{ {
title: "Way and area color", title: "Way and area color",
description: "The color or a shown way or area. Can vary based on the tagging", description: "The color or a shown way or area. Can vary based on the tagging",
disableQuestions: true disableQuestions: true
}); });
const stroke = new TagRenderingPanel(languages, currentlySelected, const stroke = new TagRenderingPanel(languages, currentlySelected, userDetails,
{ {
title: "Stroke width", title: "Stroke width",
description: "The width of lines representing ways and the outline of areas. Can vary based on the tags", description: "The width of lines representing ways and the outline of areas. Can vary based on the tags",

View file

@ -8,16 +8,17 @@ import {FromJSON} from "../../Customizations/JSON/FromJSON";
import Combine from "../Base/Combine"; import Combine from "../Base/Combine";
import PageSplit from "../Base/PageSplit"; import PageSplit from "../Base/PageSplit";
import TagRenderingPreview from "./TagRenderingPreview"; import TagRenderingPreview from "./TagRenderingPreview";
import {UserDetails} from "../../Logic/Osm/OsmConnection";
export default class LayerPanelWithPreview extends UIElement{ export default class LayerPanelWithPreview extends UIElement{
private panel: UIElement; private panel: UIElement;
constructor(config: UIEventSource<any>, languages: UIEventSource<string[]>, index: number) { constructor(config: UIEventSource<any>, languages: UIEventSource<string[]>, index: number, userDetails: UserDetails) {
super(); super();
const currentlySelected = new UIEventSource<(SingleSetting<any>)>(undefined); const currentlySelected = new UIEventSource<(SingleSetting<any>)>(undefined);
const layer = new LayerPanel(config, languages, index, currentlySelected); const layer = new LayerPanel(config, languages, index, currentlySelected, userDetails);
const helpText = new HelpText(currentlySelected); const helpText = new HelpText(currentlySelected);
const previewTagInput = new MultiTagInput(); const previewTagInput = new MultiTagInput();

View file

@ -0,0 +1,49 @@
import {UIElement} from "../UIElement";
import {VariableUiElement} from "../Base/VariableUIElement";
import {UIEventSource} from "../../Logic/UIEventSource";
import {LayoutConfigJson} from "../../Customizations/JSON/LayoutConfigJson";
import Combine from "../Base/Combine";
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
import {FixedUiElement} from "../Base/FixedUiElement";
export default class SavePanel extends UIElement {
private json: UIElement;
private lastSaveEl: UIElement;
constructor(
connection: OsmConnection,
config: UIEventSource<LayoutConfigJson>,
chronic: UIEventSource<Date>) {
super();
this.lastSaveEl = new VariableUiElement(chronic
.map(date => {
if (date === undefined) {
return new FixedUiElement("Your theme will be saved automatically within two minutes... Click here to force saving").SetClass("alert").Render()
}
return "Your theme was last saved at " + date.toISOString()
})).onClick(() => chronic.setData(new Date()));
this.json = new VariableUiElement(config.map(config => {
return JSON.stringify(config, null, 2)
.replace(/\n/g, "<br/>")
.replace(/ /g, "&nbsp;");
}))
.SetClass("literal-code");
}
InnerRender(): string {
return new Combine([
"<h3>Saving</h3>",
this.lastSaveEl,
"<h3>JSON configuration</h3>",
"The url hash is actually no more then a BASE64-encoding of the below JSON. This json contains the full configuration of the theme.<br/>" +
"This configuration is mainly useful for debugging",
this.json
]).SetClass("scrollable")
.Render();
}
}

View file

@ -12,6 +12,8 @@ import {MultiInput} from "../Input/MultiInput";
import MappingInput from "./MappingInput"; import MappingInput from "./MappingInput";
import {AndOrTagConfigJson} from "../../Customizations/JSON/TagConfigJson"; import {AndOrTagConfigJson} from "../../Customizations/JSON/TagConfigJson";
import {TagRenderingConfigJson} from "../../Customizations/JSON/TagRenderingConfigJson"; import {TagRenderingConfigJson} from "../../Customizations/JSON/TagRenderingConfigJson";
import {UserDetails} from "../../Logic/Osm/OsmConnection";
import {State} from "../../State";
export default class TagRenderingPanel extends InputElement<TagRenderingConfigJson> { export default class TagRenderingPanel extends InputElement<TagRenderingConfigJson> {
@ -24,6 +26,7 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs
constructor(languages: UIEventSource<string[]>, constructor(languages: UIEventSource<string[]>,
currentlySelected: UIEventSource<SingleSetting<any>>, currentlySelected: UIEventSource<SingleSetting<any>>,
userDetails: UserDetails,
options?: { options?: {
title?: string, title?: string,
description?: string, description?: string,
@ -36,6 +39,10 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs
this.SetClass("min-height"); this.SetClass("min-height");
this.options = options ?? {}; this.options = options ?? {};
const questionsNotUnlocked = userDetails.csCount < State.userJourney.themeGeneratorFullUnlock;
this.options.disableQuestions =
(this.options.disableQuestions ?? false) &&
questionsNotUnlocked;
this.intro = new Combine(["<h3>", options?.title ?? "TagRendering", "</h3>", options?.description ?? ""]) this.intro = new Combine(["<h3>", options?.title ?? "TagRendering", "</h3>", options?.description ?? ""])
this.IsImage = options?.isImage ?? false; this.IsImage = options?.isImage ?? false;
@ -47,9 +54,9 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs
return new SingleSetting<any>(value, input, id, name, description); return new SingleSetting<any>(value, input, id, name, description);
} }
const questionSettings = [ const questionSettings = [
setting(new MultiLingualTextFields(languages), "question", "Question", "If the key or mapping doesn't match, this question is asked"), setting(new MultiLingualTextFields(languages), "question", "Question", "If the key or mapping doesn't match, this question is asked"),
setting(new AndOrTagInput(), "condition", "Condition", setting(new AndOrTagInput(), "condition", "Condition",
@ -70,6 +77,8 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs
const settings: (string | SingleSetting<any>)[] = [ const settings: (string | SingleSetting<any>)[] = [
setting(new MultiLingualTextFields(languages), "render", "Value to show", " Renders this value. Note that <span class='literal-code'>{key}</span>-parts are substituted by the corresponding values of the element. If neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value."), setting(new MultiLingualTextFields(languages), "render", "Value to show", " Renders this value. Note that <span class='literal-code'>{key}</span>-parts are substituted by the corresponding values of the element. If neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value."),
questionsNotUnlocked ? `You need at least ${State.userJourney.themeGeneratorFullUnlock} changesets to unlock the 'question'-field and to use your theme to edit OSM data`: "",
...(options?.disableQuestions ? [] : questionSettings), ...(options?.disableQuestions ? [] : questionSettings),
"<h3>Mappings</h3>", "<h3>Mappings</h3>",

View file

@ -26,10 +26,10 @@ export class FeatureInfoBox extends UIElement {
private readonly _title: UIElement; private readonly _title: UIElement;
private readonly _osmLink: UIElement; private readonly _osmLink: UIElement;
private readonly _wikipedialink: UIElement; private readonly _wikipedialink: UIElement;
private _infoboxes: TagDependantUIElement[]; private readonly _infoboxes: TagDependantUIElement[];
private _oneSkipped = Translations.t.general.oneSkippedQuestion.Clone(); private readonly _oneSkipped = Translations.t.general.oneSkippedQuestion.Clone();
private _someSkipped = Translations.t.general.skippedQuestions.Clone(); private readonly _someSkipped = Translations.t.general.skippedQuestions.Clone();
constructor( constructor(
feature: any, feature: any,
@ -53,7 +53,7 @@ export class FeatureInfoBox extends UIElement {
tagRenderingOption.construct(deps)); tagRenderingOption.construct(deps));
} }
function initTags() { function initTags() {
self._infoboxes = [] self._infoboxes.splice(0, self._infoboxes.length);
for (const tagRenderingOption of elementsToShow) { for (const tagRenderingOption of elementsToShow) {
self._infoboxes.push( self._infoboxes.push(
tagRenderingOption.construct(deps)); tagRenderingOption.construct(deps));
@ -101,10 +101,10 @@ export class FeatureInfoBox extends UIElement {
} }
let questionsHtml = ""; let questionElement: UIElement;
if (!State.state.osmConnection.userDetails.data.loggedIn) { if (!State.state.osmConnection.userDetails.data.loggedIn) {
let mostImportantQuestion; let mostImportantQuestion ;
let score = -1000; let score = -1000;
for (const question of questions) { for (const question of questions) {
@ -114,7 +114,7 @@ export class FeatureInfoBox extends UIElement {
} }
} }
questionsHtml = mostImportantQuestion?.Render() ?? ""; questionElement = mostImportantQuestion;
} else if (questions.length > 0) { } else if (questions.length > 0) {
// We select the most important question and render that one // We select the most important question and render that one
let mostImportantQuestion; let mostImportantQuestion;
@ -127,11 +127,11 @@ export class FeatureInfoBox extends UIElement {
} }
} }
questionsHtml = mostImportantQuestion?.Render() ?? ""; questionElement = mostImportantQuestion;
} else if (skippedQuestions == 1) { } else if (skippedQuestions == 1) {
questionsHtml = this._oneSkipped.Render(); questionElement = this._oneSkipped;
} else if (skippedQuestions > 0) { } else if (skippedQuestions > 0) {
questionsHtml = this._someSkipped.Render(); questionElement = this._someSkipped;
} }
const title = new Combine([ const title = new Combine([
@ -140,12 +140,16 @@ export class FeatureInfoBox extends UIElement {
this._osmLink]); this._osmLink]);
const infoboxcontents = new Combine( const infoboxcontents = new Combine(
[ new VerticalCombine(info, "infobox-information "), questionsHtml]); [ new VerticalCombine(info, "infobox-information "), questionElement ?? ""]);
return "<div class='featureinfobox'>" + return "<div class='featureinfobox'>" +
new Combine([ new Combine([
"<div class='featureinfoboxtitle'>" + title.Render() + "</div>", "<div class='featureinfoboxtitle'>",
"<div class='infoboxcontents'>" + infoboxcontents.Render() + "</div>"]).Render() + "</div>"; title,
"</div>",
"<div class='infoboxcontents'>",
infoboxcontents,
"</div>"]).Render() + "</div>";
} }

View file

@ -1,24 +1,36 @@
import {UIElement} from "./UIElement"; import {UIElement} from "./UIElement";
import {VariableUiElement} from "./Base/VariableUIElement";
import Translations from "./i18n/Translations"; import Translations from "./i18n/Translations";
import {State} from "../State"; import {State} from "../State";
import {UIEventSource} from "../Logic/UIEventSource"; import Combine from "./Base/Combine";
/** /**
* Handles the full screen popup on mobile * Handles the full screen popup on mobile
*/ */
export class FullScreenMessageBoxHandler { export class FullScreenMessageBox extends UIElement {
private _uielement: UIEventSource<UIElement>; private _uielement: UIElement;
private returnToTheMap: UIElement;
constructor(onClear: (() => void)) { constructor(onClear: (() => void)) {
this._uielement = State.state.fullScreenMessage; super(undefined);
const self = this;
this._uielement.addCallback(function () {
self.update();
});
this.update(); const self = this;
State.state.fullScreenMessage.addCallback(uielement => {
return self._uielement = uielement?.SetClass("messagesboxmobile-scroll")?.Activate();
});
this._uielement = State.state.fullScreenMessage.data;
this.ListenTo(State.state.fullScreenMessage);
this.HideOnEmpty(true);
State.state.fullScreenMessage.addCallback(latestData => {
if (latestData === undefined) {
location.hash = "";
} else {
location.hash = "#element";
}
this.Update();
})
if (window !== undefined) { if (window !== undefined) {
window.onhashchange = function () { window.onhashchange = function () {
@ -30,36 +42,24 @@ export class FullScreenMessageBoxHandler {
} }
} }
Translations.t.general.returnToTheMap this.returnToTheMap = Translations.t.general.returnToTheMap.Clone()
.SetClass("to-the-map")
.onClick(() => { .onClick(() => {
self._uielement.setData(undefined); console.log("Returning...")
State.state.fullScreenMessage.setData(undefined);
onClear(); onClear();
}) self.Update();
.AttachTo("to-the-map"); });
} }
update() { InnerRender(): string {
const wrapper = document.getElementById("messagesboxmobilewrapper"); if (this._uielement === undefined) {
const gen = this._uielement.data; return "";
if (gen === undefined) {
wrapper.classList.add("hidden")
if (location.hash !== "") {
location.hash = ""
}
return;
} }
location.hash = "#element" return new Combine([this._uielement, this.returnToTheMap]).SetStyle("").Render();
wrapper.classList.remove("hidden");
gen
?.HideOnEmpty(true)
?.AttachTo("messagesboxmobile")
?.Activate();
} }
} }

View file

@ -72,7 +72,7 @@ export class MoreScreen extends UIElement {
els.push(new VariableUiElement( els.push(new VariableUiElement(
State.state.osmConnection.userDetails.map(userDetails => { State.state.osmConnection.userDetails.map(userDetails => {
if (userDetails.csCount < State.userJourney.themeGeneratorUnlock) { if (userDetails.csCount < State.userJourney.themeGeneratorReadOnlyUnlock) {
return tr.requestATheme.Render(); return tr.requestATheme.Render();
} }
return new SubtleButton("./assets/pencil.svg", tr.createYourOwnTheme, { return new SubtleButton("./assets/pencil.svg", tr.createYourOwnTheme, {
@ -86,7 +86,7 @@ export class MoreScreen extends UIElement {
for (const k in AllKnownLayouts.allSets) { for (const k in AllKnownLayouts.allSets) {
const layout : Layout = AllKnownLayouts.allSets[k]; const layout : Layout = AllKnownLayouts.allSets[k];
if (k === PersonalLayout.NAME) { if (k === PersonalLayout.NAME) {
if (State.state.osmConnection.userDetails.data.csCount < State.userJourney.customLayoutUnlock) { if (State.state.osmConnection.userDetails.data.csCount < State.userJourney.personalLayoutUnlock) {
continue; continue;
} }
} }
@ -99,7 +99,6 @@ export class MoreScreen extends UIElement {
const customThemesNames = State.state.installedThemes.data ?? []; const customThemesNames = State.state.installedThemes.data ?? [];
if (customThemesNames.length > 0) { if (customThemesNames.length > 0) {
console.log(customThemesNames)
els.push(Translations.t.general.customThemeIntro) els.push(Translations.t.general.customThemeIntro)
for (const installed of State.state.installedThemes.data) { for (const installed of State.state.installedThemes.data) {

View file

@ -99,7 +99,7 @@ export abstract class UIElement extends UIEventSource<string> {
if (element.innerHTML === "") { if (element.innerHTML === "") {
element.parentElement.style.display = "none"; element.parentElement.style.display = "none";
} else { } else {
element.parentElement.style.display = undefined; element.parentElement.style.display = "block";
} }
} }
@ -174,7 +174,7 @@ export abstract class UIElement extends UIEventSource<string> {
public abstract InnerRender(): string; public abstract InnerRender(): string;
public Activate(): void { public Activate(): UIElement {
for (const i in this) { for (const i in this) {
const child = this[i]; const child = this[i];
if (child instanceof UIElement) { if (child instanceof UIElement) {
@ -187,6 +187,7 @@ export abstract class UIElement extends UIEventSource<string> {
} }
} }
} }
return this;
}; };
public IsEmpty(): boolean { public IsEmpty(): boolean {

View file

@ -1,7 +1,6 @@
import {UIElement} from "./UIElement"; import {UIElement} from "./UIElement";
import Locale from "../UI/i18n/Locale"; import Locale from "../UI/i18n/Locale";
import {State} from "../State"; import {State} from "../State";
import {Layout} from "../Customizations/Layout";
import Translations from "./i18n/Translations"; import Translations from "./i18n/Translations";
import Combine from "./Base/Combine"; import Combine from "./Base/Combine";
import {InitUiElements} from "../InitUiElements"; import {InitUiElements} from "../InitUiElements";
@ -20,27 +19,18 @@ export class WelcomeMessage extends UIElement {
super(State.state.osmConnection.userDetails); super(State.state.osmConnection.userDetails);
this.ListenTo(Locale.language); this.ListenTo(Locale.language);
this.languagePicker = InitUiElements.CreateLanguagePicker(Translations.t.general.pickLanguage); this.languagePicker = InitUiElements.CreateLanguagePicker(Translations.t.general.pickLanguage);
const layout = State.state.layoutToUse.data;
function fromLayout(f: (layout: Layout) => (string | UIElement)): UIElement { this.description =Translations.W(layout.welcomeMessage);
return Translations.W(f(State.state.layoutToUse.data));
}
this.description = fromLayout((layout) => layout.welcomeMessage);
this.plzLogIn = this.plzLogIn =
fromLayout((layout) => layout.gettingStartedPlzLogin Translations.W(layout.gettingStartedPlzLogin)
.onClick(() => {State.state.osmConnection.AttemptLogin()}) .onClick(() => {
); State.state.osmConnection.AttemptLogin()
this.welcomeBack = fromLayout((layout) => layout.welcomeBackMessage); });
this.tail = fromLayout((layout) => layout.welcomeTail); this.welcomeBack = Translations.W(layout.welcomeBackMessage);
this.tail = Translations.W(layout.welcomeTail);
} }
protected InnerUpdate(htmlElement: HTMLElement) {
super.InnerUpdate(htmlElement);
console.log("Innerupdating welcome message")
this.plzLogIn.Update();
}
InnerRender(): string { InnerRender(): string {
let loginStatus = undefined; let loginStatus = undefined;
@ -52,7 +42,8 @@ export class WelcomeMessage extends UIElement {
return new Combine([ return new Combine([
this.description, this.description,
"<br/></br>", "<br/></br>",
// TODO this button is broken - figure out why loginStatus, // TODO this button is broken - figure out why loginStatus,
loginStatus,
this.tail, this.tail,
"<br/>", "<br/>",
this.languagePicker this.languagePicker

View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="100"
height="100"
viewBox="0 0 26.458333 26.458334"
version="1.1"
id="svg8"
sodipodi:docname="arrow-left-smooth.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4"
inkscape:cx="19.262262"
inkscape:cy="36.323203"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1">
<sodipodi:guide
position="13.229167,23.859748"
orientation="1,0"
id="guide815"
inkscape:locked="false" />
<sodipodi:guide
position="14.944824,13.229167"
orientation="0,1"
id="guide817"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-270.54165)">
<path
style="fill:none;stroke:#ffffff;stroke-width:3.59588718;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 20.139011,294.16029 c 0,0 -13.7995299,-7.53922 -13.8484369,-10.36091 -0.04891,-2.82169 13.8484369,-10.38607 13.8484369,-10.38607"
id="path821"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="100"
height="100"
viewBox="0 0 26.458333 26.458334"
version="1.1"
id="svg8"
sodipodi:docname="arrow-right-smooth.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4"
inkscape:cx="-22.237738"
inkscape:cy="36.323203"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1">
<sodipodi:guide
position="13.229167,23.859748"
orientation="1,0"
id="guide815"
inkscape:locked="false" />
<sodipodi:guide
position="14.944824,13.229167"
orientation="0,1"
id="guide817"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-270.54165)">
<path
style="fill:none;stroke:#ffffff;stroke-width:3.59588718;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 6.3128214,273.41335 c 0,0 13.7995296,7.53922 13.8484366,10.36091 0.04891,2.82169 -13.8484366,10.38607 -13.8484366,10.38607"
id="path821"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -12,7 +12,11 @@
"amenity=bicycle_parking" "amenity=bicycle_parking"
] ]
}, },
"icon": "./assets/layers/bike_parking/parking.svg", "icon": {
"render": {
"en": "./assets/layers/bike_parking/parking.svg"
}
},
"size": { "size": {
"render": { "render": {
"en": "50,50,bottom" "en": "50,50,bottom"

View file

@ -1,70 +1,25 @@
import {UIEventSource} from "./Logic/UIEventSource"; import {UIEventSource} from "./Logic/UIEventSource";
import SingleSetting from "./UI/CustomGenerator/SingleSetting";
import Combine from "./UI/Base/Combine";
import {VariableUiElement} from "./UI/Base/VariableUIElement";
import GeneralSettings from "./UI/CustomGenerator/GeneralSettings";
import {TabbedComponent} from "./UI/Base/TabbedComponent";
import AllLayersPanel from "./UI/CustomGenerator/AllLayersPanel";
import SharePanel from "./UI/CustomGenerator/SharePanel";
import {GenerateEmpty} from "./UI/CustomGenerator/GenerateEmpty"; import {GenerateEmpty} from "./UI/CustomGenerator/GenerateEmpty";
import PageSplit from "./UI/Base/PageSplit";
import HelpText from "./Customizations/HelpText";
import {TagRendering} from "./Customizations/TagRendering"; import {TagRendering} from "./Customizations/TagRendering";
import {LayoutConfigJson} from "./Customizations/JSON/LayoutConfigJson"; import {LayoutConfigJson} from "./Customizations/JSON/LayoutConfigJson";
import {OsmConnection} from "./Logic/Osm/OsmConnection";
import CustomGeneratorPanel from "./UI/CustomGenerator/CustomGeneratorPanel";
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
let layout = GenerateEmpty.createEmptyLayout(); let layout = GenerateEmpty.createEmptyLayout();
if(window.location.hash.length > 10){ if (window.location.hash.length > 10) {
layout = JSON.parse(atob(window.location.hash.substr(1))) as LayoutConfigJson; layout = JSON.parse(atob(window.location.hash.substr(1))) as LayoutConfigJson;
} else {
const hash = LocalStorageSource.Get("last-custom-theme").data
if (hash !== undefined) {
layout = JSON.parse(atob(hash)) as LayoutConfigJson;
}
} }
const es = new UIEventSource(layout);
const encoded = es.map(config => btoa(JSON.stringify(config)));
const liveUrl = encoded.map(encoded => `./index.html?userlayout=${es.data.id}#${encoded}`)
const iframe = liveUrl.map(url => `<iframe src='${url}' width='100%' height='99%' style="box-sizing: border-box" title='Theme Preview'></iframe>`);
TagRendering.injectFunction(); TagRendering.injectFunction();
const currentSetting = new UIEventSource<SingleSetting<any>>(undefined) const connection = new OsmConnection(false, new UIEventSource<string>(undefined), "customGenerator", false);
const generalSettings = new GeneralSettings(es, currentSetting); new CustomGeneratorPanel(connection, layout)
const languages = generalSettings.languages;
// The preview
const preview = new Combine([
new VariableUiElement(iframe.stabilized(2500))
]).SetClass("preview")
new TabbedComponent([
{
header: "<img src='./assets/gear.svg'>",
content:
new PageSplit(
generalSettings.SetStyle("width: 50vw;"),
new Combine([
new HelpText(currentSetting).SetStyle("height:calc(100% - 65vh); width: 100%; display:block; overflow-y: auto"),
preview.SetStyle("height:65vh; width:100%; display:block")
]).SetStyle("position:relative; width: 50%;")
)
},
{
header: "<img src='./assets/layers.svg'>",
content: new AllLayersPanel(es, languages)
},
{
header: "<img src='./assets/floppy.svg'>",
content: new VariableUiElement(es.map(config => {
return JSON.stringify(config, null, 2)
.replace(/\n/g, "<br/>")
.replace(/ /g, "&nbsp;");
}))
},
{
header: "<img src='./assets/share.svg'>",
content: new SharePanel(es, liveUrl)
}
]).SetClass("main-tabs")
.AttachTo("maindiv"); .AttachTo("maindiv");

441
index.css
View file

@ -284,6 +284,9 @@
padding-top: 0.2em; padding-top: 0.2em;
padding-bottom: 0.2em; padding-bottom: 0.2em;
} }
#hidden-on-mobile {
display: none; /*Only shown on small screens*/
}
@media only screen and (max-height: 600px) and (not (max-width:700px)) { @media only screen and (max-height: 600px) and (not (max-width:700px)) {
@ -351,9 +354,6 @@
padding-right: 10px; /* Shadow offset */ padding-right: 10px; /* Shadow offset */
} }
#messagesboxmobilewrapper {
display: none; /*Only shown on small screens*/
}
.collapse-button { .collapse-button {
position: absolute; position: absolute;
@ -556,6 +556,10 @@
@media only screen and (max-width: 600px), only screen and (max-height: 600px) { @media only screen and (max-width: 600px), only screen and (max-height: 600px) {
#hidden-on-mobile {
display: block;
}
#messagesbox-wrapper { #messagesbox-wrapper {
display: none; display: none;
} }
@ -590,33 +594,20 @@
} }
#messagesboxmobilewrapper { .messagesboxmobile-scroll {
position: absolute;
padding: 0;
margin: 0;
z-index: 5050;
transition: all 500ms linear;
overflow: hidden;
border-radius: 0;
width: 100%;
height: 100%;
display: block;
background-color: white;
}
#messagesboxmobile-scroll {
display: block; display: block;
width: 100vw; width: 100vw;
overflow-y: auto; box-sizing: border-box;
padding: 0; overflow-y: scroll;
padding: 1em;
margin: 0; margin: 0;
height: calc(100% - 5em); /*Height of to-the-map is 5em*/ height: calc(100vh - 5em); /*Height of to-the-map is 5em*/
} }
#messagesboxmobile { #messagesboxmobile {
padding: 1em; display: block;
padding-bottom: 2em; position: absolute;
border-radius: 1em; z-index: 10000;
background-color: white; background-color: white;
} }
@ -639,72 +630,72 @@
} }
#to-the-map { .to-the-map {
position: relative; display: block;
}
#to-the-map > span{
position: absolute;
box-sizing: border-box;
height: 3em;
padding: 0.5em;
margin: 0;
padding-top: 0.75em;
text-align: center;
width: 100%;
color: white;
background-color: #7ebc6f;
cursor: pointer;
font-size: xx-large;
font-weight: bold;
}
@media only screen and (max-height: 400px) {
/* Landscape: small 'to the map' */
#to-the-map {
position: relative;
height: 100%;
width: 100%
}
#to-the-map span {
width: auto;
border-top-left-radius: 1.5em;
position: absolute;
z-index: 5;
right: 0;
bottom: 0;
height: auto;
margin:0;
padding: 1em;
padding-bottom: 0.75em;
height: 3em;
font-size: x-large;
}
#messagesboxmobile {
padding-bottom: 5em;
}
#messagesboxmobile-scroll {
position: absolute;
z-index: 2;
width: 100vw;
height: 100vh;
box-sizing: border-box; box-sizing: border-box;
height: 2.5em;
margin: 0;
padding: 0.5em;
padding-top: 0.75em;
text-align: center;
color: white;
background-color: #7ebc6f;
cursor: pointer;
font-size: xx-large;
font-weight: bold;
border-top-left-radius: 0.5em;
border-top-right-radius: 0.5em;
} }
#welcomeMessage{
box-shadow: unset;
max-height: 100vh;
}
} @media only screen and (max-height: 400px) {
/* Landscape: small 'to the map' */
#hidden-on-mobile {
display: unset;
}
.to-the-map {
position: relative;
height: 100%;
width: 100%
}
.to-the-map span {
width: auto;
border-top-left-radius: 1.5em;
position: absolute;
z-index: 5;
right: 0;
bottom: 0;
height: auto;
margin: 0;
padding: 1em;
padding-bottom: 0.75em;
height: 3em;
font-size: x-large;
}
#messagesboxmobile {
position: absolute;
display: block;
z-index: 10000;
padding-bottom: 5em;
}
.messagesboxmobile-scroll {
display: block;
width: 100vw;
height: calc(100vh - 5em);
box-sizing: border-box;
overflow-y: scroll;
}
#welcomeMessage {
box-shadow: unset;
max-height: 100vh;
}
}
.logo { .logo {
float:right; float:right;
@ -841,170 +832,173 @@
} }
.attribution-author { .attribution-author {
display: inline-block; display: inline-block;
} }
.license { .license {
font-size: small; font-size: small;
font-weight: lighter; font-weight: lighter;
} }
.attribution a { .attribution a {
color: white; color: white;
} }
/**************** Image upload flow ***************/ /**************** Image upload flow ***************/
.imageflow { .imageflow {
margin-top: 1em; margin-top: 1em;
margin-bottom: 2em; margin-bottom: 2em;
text-align: center; text-align: center;
} }
.imageflow-file-input-wrapper { .imageflow-file-input-wrapper {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
padding: 0.5em; padding: 0.5em;
border-radius: 1em; border-radius: 1em;
border: 3px solid black; border: 3px solid black;
} }
.imageflow-add-picture { .imageflow-add-picture {
font-size: 28px; font-size: 28px;
font-weight: bold; font-weight: bold;
float: left; float: left;
margin-top: 4px; margin-top: 4px;
padding-top: 4px; padding-top: 4px;
padding-bottom: 4px; padding-bottom: 4px;
padding-left: 13px; padding-left: 13px;
} }
.imageflow-file-input-wrapper img { .imageflow-file-input-wrapper img {
width: 36px; width: 36px;
height: 36px; height: 36px;
padding: 0.1em; padding: 0.1em;
margin-top: 5px; margin-top: 5px;
border-radius: 0; border-radius: 0;
float: left; float: left;
} }
.license-picker { .license-picker {
float: left; float: left;
} }
.imageflow > input { .imageflow > input {
display: none; display: none;
} }
/***************** Info box (box containing features and questions ******************/ /***************** Info box (box containing features and questions ******************/
.leaflet-popup-content { .leaflet-popup-content {
width: 40em !important; width: 40em !important;
} }
#messagesboxmobile .featureinfobox {
max-height: unset;
overflow-y: unset;
}
#messagesboxmobile .featureinfobox > div {
width: 100%;
max-width: unset;
padding-left: unset;
}
.featureinfobox {
max-height: 80vh;
overflow-y: auto;
}
.featureinfobox > div {
width: calc(100% - 2em);
padding-left: 1em;
}
.featureinfoboxtitle {
position: relative;
}
.question .form-text-field > input{
width: 100%;
box-sizing: border-box;
}
.osmlink {
position: absolute;
right: 0;
}
.osm-logo path {
fill: #7ebc6f;
}
.featureinfoboxtitle .answer {
display: inline;
margin-right: 3em;
}
.featureinfoboxtitle .answer-text {
display: inline;
}
.featureinfoboxtitle .editbutton {
float: none;
width: 0.8em;
height: 0.8em;
padding: 0.3em;
border-radius: 0.35em;
border: solid black 1px;
margin-left: 0.5em;
top: 0.2em;
vertical-align: middle;
}
.editbutton { #messagesboxmobile .featureinfobox {
width: 1.3em; max-height: unset;
height: 1.3em; overflow-y: unset;
padding: 0.5em; }
border-radius: 0.65em;
border: solid black 1px;
font-size: medium; #messagesboxmobile .featureinfobox > div {
float: right; width: 100%;
max-width: unset;
padding-left: unset;
}
.featureinfobox {
max-height: 80vh;
overflow-y: auto;
}
.featureinfobox > div {
width: calc(100% - 2em);
padding-left: 1em;
}
} .featureinfoboxtitle {
position: relative;
}
.wikipedialink { .question .form-text-field > input {
position: absolute; width: 100%;
right: 24px; box-sizing: border-box;
width: 24px; }
height: 24px;
padding-right: 12px;
}
.wikipedialink img { .osmlink {
width: 24px; position: absolute;
height: 24px; right: 0;
} }
.osm-logo path {
fill: #7ebc6f;
}
.featureinfoboxtitle .answer {
display: inline;
margin-right: 3em;
}
.featureinfoboxtitle .answer-text {
display: inline;
}
.featureinfoboxtitle .editbutton {
float: none;
width: 0.8em;
height: 0.8em;
padding: 0.3em;
border-radius: 0.35em;
border: solid black 1px;
margin-left: 0.5em;
top: 0.2em;
vertical-align: middle;
}
.featureinfoboxtitle span { .editbutton {
font-weight: bold; width: 1.3em;
font-size: x-large; height: 1.3em;
} padding: 0.5em;
border-radius: 0.65em;
border: solid black 1px;
.featureinfoboxtitle a { font-size: medium;
float: right; float: right;
margin-left: 1em;
}
.wikipedialink {
position: absolute;
right: 24px;
width: 24px;
height: 24px;
padding-right: 12px;
}
.wikipedialink img {
width: 24px;
height: 24px;
}
.featureinfoboxtitle span {
font-weight: bold;
font-size: x-large;
}
.featureinfoboxtitle a {
float: right;
margin-left: 1em;
} }
@ -1273,6 +1267,7 @@
padding: 0.5em; padding: 0.5em;
word-break: break-all; word-break: break-all;
color: black; color: black;
box-sizing: border-box;
} }
.iframe-code-block { .iframe-code-block {

View file

@ -14,7 +14,7 @@
<!-- $$$OG-META --> <!-- $$$OG-META -->
<style> <style>
.decoration-desktop img { #decoration-desktop img {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
@ -23,33 +23,16 @@
</head> </head>
<body> <body>
<div style="position: absolute; left: 1em; top: 1em; width:35vh; height:35vh;" class="decoration-desktop"> <div style="position: absolute; left: 1em; bottom: 1em; width:35vh; height:35vh;"
id="decoration-desktop">
<!-- A nice decoration while loading or on errors --> <!-- A nice decoration while loading or on errors -->
<!-- DECORATION 0 START --> <!-- DECORATION 0 START -->
<img src="./assets/add.svg"/> <img src="./assets/add.svg"/>
<!-- DECORATION 0 END --> <!-- DECORATION 0 END -->
</div> </div>
<div id="messagesboxmobilewrapper"> <div id="hidden-on-mobile">
<div id="decoration" style="position: absolute; left: 1em; top: 1em; z-index: 1; width: 15em; height: 15em"> <div id="messagesboxmobile">
<!-- A nice decoration while loading or on errors -->
<!-- DECORATION 1 START -->
<img src="./assets/add.svg"/>
<!-- DECORATION 1 END -->
</div>
<div id="messagesboxmobile-scroll">
<div id="messagesboxmobile">
<div style="position: absolute; top: 50vh; left: 25vw; width:50vw; box-sizing: border-box;font-size: x-large">
Loading MapComplete
</div>
</div>
</div>
<div id="to-the-map">
If this message persists:
<ul style="margin:0">
<li>Is javascript enabled?</li>
<li>Is a scriptblocker (such as UMatrix or Brave) active?</li>
</ul>
</div> </div>
</div> </div>

View file

@ -2,7 +2,6 @@ import {TagRendering} from "./Customizations/TagRendering";
import {UserBadge} from "./UI/UserBadge"; import {UserBadge} from "./UI/UserBadge";
import {CenterMessageBox} from "./UI/CenterMessageBox"; import {CenterMessageBox} from "./UI/CenterMessageBox";
import {TagUtils} from "./Logic/Tags"; import {TagUtils} from "./Logic/Tags";
import {FullScreenMessageBoxHandler} from "./UI/FullScreenMessageBoxHandler";
import {FeatureInfoBox} from "./UI/FeatureInfoBox"; import {FeatureInfoBox} from "./UI/FeatureInfoBox";
import {SimpleAddUI} from "./UI/SimpleAddUI"; import {SimpleAddUI} from "./UI/SimpleAddUI";
import {SearchAndGo} from "./UI/SearchAndGo"; import {SearchAndGo} from "./UI/SearchAndGo";
@ -17,6 +16,7 @@ import {QueryParameters} from "./Logic/Web/QueryParameters";
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource"; import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
import {PersonalLayout} from "./Logic/PersonalLayout"; import {PersonalLayout} from "./Logic/PersonalLayout";
import {FromJSON} from "./Customizations/JSON/FromJSON"; import {FromJSON} from "./Customizations/JSON/FromJSON";
import {FullScreenMessageBox} from "./UI/FullScreenMessageBoxHandler";
TagRendering.injectFunction(); TagRendering.injectFunction();
@ -28,9 +28,9 @@ if (location.href.startsWith("http://buurtnatuur.be")) {
window.location.replace("https://buurtnatuur.be"); window.location.replace("https://buurtnatuur.be");
} }
const testing = QueryParameters.GetQueryParameter("test", "true");
if (location.hostname === "localhost" || location.hostname === "127.0.0.1") { if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
// Set to true if testing and changes should NOT be saved // Set to true if testing and changes should NOT be saved
const testing = QueryParameters.GetQueryParameter("test", "true");
testing.setData(testing.data ?? "true") testing.setData(testing.data ?? "true")
// If you have a testfile somewhere, enable this to spoof overpass // If you have a testfile somewhere, enable this to spoof overpass
// This should be hosted independantly, e.g. with `cd assets; webfsd -p 8080` + a CORS plugin to disable cors rules // This should be hosted independantly, e.g. with `cd assets; webfsd -p 8080` + a CORS plugin to disable cors rules
@ -113,13 +113,17 @@ if (layoutToUse.hideFromOverview) {
if (layoutFromBase64 !== "false") { if (layoutFromBase64 !== "false") {
State.state.layoutDefinition = hash.substr(1); State.state.layoutDefinition = hash.substr(1);
State.state.osmConnection.OnLoggedIn(() => { if (!testing) {
State.state.osmConnection.GetLongPreference("installed-theme-" + layoutToUse.id).setData(State.state.layoutDefinition); State.state.osmConnection.OnLoggedIn(() => {
}) State.state.osmConnection.GetLongPreference("installed-theme-" + layoutToUse.id).setData(State.state.layoutDefinition);
})
}else{
console.warn("NOT saving custom layout to OSM as we are tesing -> probably in an iFrame")
}
} }
InitUiElements.InitBaseMap(); InitUiElements.InitBaseMap();
new FixedUiElement("").AttachTo("decoration"); // Remove the decoration new FixedUiElement("").AttachTo("decoration-desktop"); // Remove the decoration
function setupAllLayerElements() { function setupAllLayerElements() {
@ -232,9 +236,12 @@ InitUiElements.OnlyIf((State.state.featureSwitchSearch), () => {
new SearchAndGo().AttachTo("searchbox"); new SearchAndGo().AttachTo("searchbox");
}); });
new FullScreenMessageBoxHandler(() => {
new FullScreenMessageBox(() => {
State.state.selectedElement.setData(undefined) State.state.selectedElement.setData(undefined)
}).update(); }).AttachTo("messagesboxmobile");
InitUiElements.OnlyIf(State.state.featureSwitchWelcomeMessage, () => { InitUiElements.OnlyIf(State.state.featureSwitchWelcomeMessage, () => {
InitUiElements.InitWelcomeMessage() InitUiElements.InitWelcomeMessage()