diff --git a/app/lang/en.json b/app/lang/en.json index 3b63df26ba3c4e63d275c2a4e760e8e6017f232d..4a55883c43ecaab870c08e4def8c71721a44608c 100644 --- a/app/lang/en.json +++ b/app/lang/en.json @@ -49,5 +49,12 @@ "longer": "longer", "shorter": "shorter", "own position": "Own Position", - "add own position": "Add Own Position" + "add own position": "Add Own Position", + "settings": "Settings", + "usage of cookies in settings": "All changed settings are stored in your browser using cookies.", + "close": "Close", + "language": "Language", + "en": "English", + "de": "German", + "theme": "Theme" } \ No newline at end of file diff --git a/app/resources/js/Config.js b/app/resources/js/Config.js index 1633f6d7ceaceee99ca81e1a48d0e48a133613fe..37f3f1b4e525cbd5b93a3d9f780bcc85f3000a2b 100644 --- a/app/resources/js/Config.js +++ b/app/resources/js/Config.js @@ -1,6 +1,8 @@ import { bbox, bboxPolygon, circle } from "@turf/turf"; import { LngLatBounds, CenterZoomBearing, LngLat } from "maplibre-gl"; import { gpsManager } from "./GpsManager"; +import { UrlGenerator } from "./UrlGenerator"; +import { detectTheme, toggleTheme } from "./Theme"; export const config = { localization: { @@ -130,3 +132,36 @@ let module = document.querySelector("meta[name=module]"); if (module) { config.init.module = module.content; } + +(() => { + let settings_container = document.getElementById("settings"); + if ((new URLSearchParams(location.search)).has("settings")) { + settings_container.showModal(); + } + // Make settings close button work + settings_container.querySelector("button.close").addEventListener("click", e => { + settings_container.close(); + }); + + // Make Language switcher work + let language_switch_select = settings_container.querySelector(".settings-container #language"); + if (language_switch_select) { + language_switch_select.value = config.localization.current_locale; + language_switch_select.addEventListener("change", e => { + let new_language = e.target.value; + if (new_language != config.localization.current_locale) { + let new_url = UrlGenerator.to(location.pathname + location.search, new_language); + location.replace(new_url); + } + }); + } else { console.error("Language switch is not present") } + + // Make theme option work + let theme_switch_select = settings_container.querySelector(".settings-container #theme"); + if (theme_switch_select) { + theme_switch_select.value = detectTheme(); + theme_switch_select.addEventListener("change", e => { + toggleTheme(); + }); + } else { console.error("Theme switch is not present") } +})(); diff --git a/app/resources/js/SearchModule.js b/app/resources/js/SearchModule.js index 9404c165f17cf1854e5de4c109b2a6294d453263..d8285fd92db22f8064db9e07da44159ab068dbf4 100644 --- a/app/resources/js/SearchModule.js +++ b/app/resources/js/SearchModule.js @@ -667,9 +667,20 @@ class SearchModule { }/${JSON.stringify(current_pos.toArray())}`; } + let options = {}; + let params = {}; if (["enabled", "disabled"].includes(this.bounded)) { - url += "?bounded=" + this.bounded; + params.bounded = this.bounded; } + options.settings = document.getElementById("settings").open; + if (options.settings) { + params.settings = ""; + } + + if (Object.keys(params).length > 0) { + url += "?" + (new URLSearchParams(params).toString()) + } + let state = { module: "search", @@ -679,6 +690,7 @@ class SearchModule { reverse: this.reverse, bounded: this.bounded, timestamp: Date.now(), + options: options }; if ( diff --git a/app/resources/js/Theme.js b/app/resources/js/Theme.js index 5014da114e43eacf9d044cbb748c8404bb1c7d40..d8c7eb64bb6257e593847a2c9a615f2aae5db776 100644 --- a/app/resources/js/Theme.js +++ b/app/resources/js/Theme.js @@ -42,7 +42,7 @@ function getDefaultTheme() { } } -function toggleTheme() { +export function toggleTheme() { let theme = detectTheme(); if (theme == "light") { theme = "dark"; diff --git a/app/resources/js/UrlGenerator.js b/app/resources/js/UrlGenerator.js index f10f74e6de5ee23bca7dfd99cb99415d6985085e..057456acc1a531e4c57b1324a8093cde724a0c7c 100644 --- a/app/resources/js/UrlGenerator.js +++ b/app/resources/js/UrlGenerator.js @@ -13,6 +13,7 @@ export class UrlGenerator { if (!config.localization.supported_locales.includes(locale)) { locale = config.localization.fallback_locale; } + for (let supported_locale of config.localization.supported_locales) { supported_locale = supported_locale.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"); // Regex escape let reg = new RegExp(`^\/${supported_locale}`); diff --git a/app/resources/js/app.js b/app/resources/js/app.js index 5256d04dbcd1e9b3296d0313af34be1f9cbe3bf7..2e09ee0b8a60133987c2829127b129e88497ce01 100644 --- a/app/resources/js/app.js +++ b/app/resources/js/app.js @@ -76,6 +76,15 @@ function initializeInterface() { window.addEventListener("popstate", (event) => { event.preventDefault(); + if (event.state.hasOwnProperty("options") && event.state.options.hasOwnProperty("settings")) { + if (document.getElementById("settings").open != event.state.options.settings) { + if (event.state.options.settings) { + document.getElementById("settings").showModal(); + } else { + document.getElementById("settings").close(); + } + } + } // Check which module the state is based on switch (event.state.module) { case "search": @@ -236,4 +245,4 @@ export function switchModule(name, args) { default: return; } -} +} \ No newline at end of file diff --git a/app/resources/less/map.less b/app/resources/less/map.less index d20d1368b4f9dc2b01838d024875e7a2c4cf35a6..bdd9a7fa252412040053c9595ba9c378b45e6c2b 100644 --- a/app/resources/less/map.less +++ b/app/resources/less/map.less @@ -3,6 +3,7 @@ @import "../../node_modules/ol-popup/src/ol-popup.css"; @import "./openlayers/controls.less"; @import "./openlayers/popups.less"; +@import "./settings.less"; .maplibregl-popup-content { background-color: var(--background-color); diff --git a/app/resources/less/settings.less b/app/resources/less/settings.less new file mode 100644 index 0000000000000000000000000000000000000000..46ae56ca70c9e9226190833f9ef04b9f96bca1da --- /dev/null +++ b/app/resources/less/settings.less @@ -0,0 +1,44 @@ +#settings { + margin: 1rem 0 0 auto; + padding: 1rem; + width: 320px; + height: 100vh; + display: flex; + flex-direction: column; + row-gap: 1rem; + + &::backdrop { + background-color: #0006; + } + + >h1 { + font-size: 1.5rem; + text-align: center; + border-bottom: 1px solid; + padding-bottom: .5rem; + } + + >.settings-container { + flex-grow: 1; + overflow-y: scroll; + padding: .25rem; + display: flex; + flex-direction: column; + row-gap: 1rem; + + >.setting { + display: flex; + justify-content: space-between; + align-items: center; + + >select { + padding: .5rem 1rem; + } + } + } + + >button.close { + cursor: pointer; + padding: .5rem 1rem; + } +} \ No newline at end of file diff --git a/app/resources/views/map.blade.php b/app/resources/views/map.blade.php index 8ec4893ef6983d552eb3d05ce7858fcf03fb9457..6295364429e7186392f0dd361a166bedc82ca45d 100644 --- a/app/resources/views/map.blade.php +++ b/app/resources/views/map.blade.php @@ -85,6 +85,28 @@ target="_blank">@lang('why?')</a> </div> </main> + <dialog id="settings"> + <h1>@lang("settings")</h1> + <p>@lang("usage of cookies in settings")</p> + <div class="settings-container"> + <div class="setting"> + <label for="language">@lang("language")</label> + <select name="language" id="language"> + @foreach(App\Http\Middleware\Localization::SUPPORTED_LOCALES as $supported_locale) + <option value="{{$supported_locale}}" @if(App::currentLocale() === $supported_locale)selected @endif>@lang($supported_locale)</option> + @endforeach + </select> + </div> + <div class="setting"> + <label for="theme">@lang("theme")</label> + <select name="theme" id="theme"> + <option value="light">@lang("light mode")</option> + <option value="dark">@lang("dark mode")</option> + </select> + </div> + </div> + <button class="close">@lang("close")</button> + </dialog> <div id="nav-menu"> <input type="checkbox" name="nav-opener" id="nav-opener"> <label for="nav-opener">≡</label> @@ -105,9 +127,11 @@ <div class="updates"> <label>@lang('last openstreetmap update')</label> <div>@lang('timezone'): <span class="data-date-timezone"></span></div> - <div>@lang('map'): <span class="data-date-tiles"><img src="/img/ajax-loader.gif" alt="@lang('loading data...')"></span> + <div>@lang('map'): <span class="data-date-tiles"><img src="/img/ajax-loader.gif" +a lt="@lang('loading data...')"></span> </div> - <div>@lang('search'): <span class="data-date-search"><img src="/img/ajax-loader.gif" alt="@lang('loading data...')"></span> + <div>@lang('search'): <span class="data-date-search"><img src="/img/ajax-loader.gif" + alt="@lang('loading data...')"></span> </div> <div>@lang('routes'): <span class="data-date-routing"><img src="/img/ajax-loader.gif" alt="@lang('loading data...')"></span></div>