From 1d40efc9d495d28db3949818ee350e9518b7eac9 Mon Sep 17 00:00:00 2001 From: Marwin Hochfelsner <50826859+hlfan@users.noreply.github.com> Date: Mon, 20 Jul 2026 01:14:14 +0000 Subject: [PATCH] Use Turbo frames for sidebar loading --- app/assets/javascripts/index.js | 62 ++++++++----------- app/assets/stylesheets/common.scss | 5 ++ app/controllers/application_controller.rb | 5 +- app/views/layouts/map.html.erb | 6 +- app/views/layouts/xhr.html.erb | 6 +- .../controllers/changesets_controller_test.rb | 6 +- test/controllers/notes_controller_test.rb | 2 +- test/controllers/searches_controller_test.rb | 4 +- test/controllers/site_controller_test.rb | 2 +- test/test_helper.rb | 4 +- 10 files changed, 52 insertions(+), 50 deletions(-) diff --git a/app/assets/javascripts/index.js b/app/assets/javascripts/index.js index 1ec154b85..0a9e4ae14 100644 --- a/app/assets/javascripts/index.js +++ b/app/assets/javascripts/index.js @@ -25,45 +25,37 @@ $(function () { }); OSM.loadSidebarContent = function (path) { - const atomSelector = "link[type=\"application/atom+xml\"]"; - map.setSidebarOverlaid(false); - $("#sidebar_loader").prop("hidden", false).addClass("delayed-fade-in"); - - // Prevent caching the XHR response as a full-page URL - // https://github.com/openstreetmap/openstreetmap-website/issues/5663 - const queryParamSeparator = path.includes("?") ? "&" : "?"; - const xhrPath = `${path}${queryParamSeparator}xhr=1`; - - $("#sidebar_content") - .empty(); - - return fetch(xhrPath, { headers: { "accept": "text/html", "x-requested-with": "XMLHttpRequest" } }) - .then(response => { - $("#flash").empty(); - $("#sidebar_loader").removeClass("delayed-fade-in").prop("hidden", true); - - return response.text().then(html => ({ response, html })); - }) - .then(({ response, html }) => { - const content = $($.parseHTML(html)); - - const title = response.headers.get("X-Page-Title"); - if (title) document.title = decodeURIComponent(title); - - $("head").find(atomSelector).remove(); - - $("head").append(content.filter(atomSelector)); - - $("#sidebar_content").html(content.not(atomSelector)); - - if (!response.ok) { - throw new Error(`HTTP Error ${response.status} ${response.statusText}`); - } - }); + return new Promise((resolve, reject) => { + $("#sidebar_content_frame") + .one("turbo:frame-render", event => { + const response = event.originalEvent.detail.fetchResponse.response; + + const title = response.headers.get("X-Page-Title"); + if (title) document.title = decodeURIComponent(title); + + if (response.ok) { + resolve(); + } else { + reject(new Error(`HTTP Error ${response.status} ${response.statusText}`)); + } + }) + .prop("src", path); + }); }; + $("#sidebar_content_frame") + .on("turbo:before-fetch-response", () => $("#flash").empty()) + .on("turbo:before-frame-render", event => { + const atomSelector = "link[type='application/atom+xml']"; + $("head").find(atomSelector).remove(); + $("head").append( + $(event.originalEvent.detail.newFrame) + .find(atomSelector) + .detach()); + }); + const token = $("head").data("oauthToken"); if (token) OSM.oauth = { authorization: "Bearer " + token }; diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index 59fd33b22..7fe92baae 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -237,6 +237,11 @@ header nav { } } + #sidebar_loader:has(+ #sidebar_content turbo-frame:not([busy])), + #sidebar_content turbo-frame[busy] { + display: none; + } + #content:not(.overlay-sidebar) :is(.welcome, #banner) { @extend .d-none; } diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1440f67eb..3e6fa3a0a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -208,7 +208,7 @@ class ApplicationController < ActionController::Base I18n.locale = Locale.available.preferred(preferred_languages) - response.headers["Vary"] = "Accept-Language" + response.headers["Vary"] = response.headers["Vary"].present? ? "#{response.headers['Vary']}, Accept-Language" : "Accept-Language" response.headers["Content-Language"] = I18n.locale.to_s end @@ -263,7 +263,8 @@ class ApplicationController < ActionController::Base flash.now[:warning] = { :partial => "layouts/offline_flash" } unless api_status == "online" - request.xhr? ? "xhr" : "map" + response.headers["Vary"] = response.headers["Vary"].present? ? "#{response.headers['Vary']}, Turbo-Frame" : "Turbo-Frame" + turbo_frame_request? ? "xhr" : "map" end def preferred_editor diff --git a/app/views/layouts/map.html.erb b/app/views/layouts/map.html.erb index 294f31761..89c77f654 100644 --- a/app/views/layouts/map.html.erb +++ b/app/views/layouts/map.html.erb @@ -20,10 +20,12 @@ <%= render :partial => "layouts/sidebar_close" %> - + <% unless current_user %> diff --git a/app/views/layouts/xhr.html.erb b/app/views/layouts/xhr.html.erb index ab33aae8d..95b4d8ca1 100644 --- a/app/views/layouts/xhr.html.erb +++ b/app/views/layouts/xhr.html.erb @@ -1,2 +1,4 @@ -<%= content_for :auto_discovery_link_tag %> -<%= yield %> + + <%= content_for :auto_discovery_link_tag %> + <%= yield %> + diff --git a/test/controllers/changesets_controller_test.rb b/test/controllers/changesets_controller_test.rb index ed1aef4d1..41c605391 100644 --- a/test/controllers/changesets_controller_test.rb +++ b/test/controllers/changesets_controller_test.rb @@ -62,7 +62,7 @@ class ChangesetsControllerTest < ActionDispatch::IntegrationTest def test_index_xhr changesets = create_list(:changeset, 30, :num_changes => 1) - get history_path(:format => "html"), :xhr => true + get history_path(:format => "html"), :headers => { "Turbo-Frame" => "sidebar_content_frame" } assert_response :success assert_template "history" assert_template :layout => "xhr" @@ -351,7 +351,7 @@ class ChangesetsControllerTest < ActionDispatch::IntegrationTest changeset1 = create(:changeset, :num_changes => 1) changeset2 = create(:changeset, :num_changes => 1) - get history_path(:format => "html", :before => changeset2.id), :xhr => true + get history_path(:format => "html", :before => changeset2.id), :headers => { "Turbo-Frame" => "sidebar_content_frame" } assert_response :success assert_template "history" assert_template :layout => "xhr" @@ -368,7 +368,7 @@ class ChangesetsControllerTest < ActionDispatch::IntegrationTest changeset1 = create(:changeset, :num_changes => 1) changeset2 = create(:changeset, :num_changes => 1) - get history_path(:format => "html", :after => changeset1.id), :xhr => true + get history_path(:format => "html", :after => changeset1.id), :headers => { "Turbo-Frame" => "sidebar_content_frame" } assert_response :success assert_template "history" assert_template :layout => "xhr" diff --git a/test/controllers/notes_controller_test.rb b/test/controllers/notes_controller_test.rb index e153f2924..994c440fc 100644 --- a/test/controllers/notes_controller_test.rb +++ b/test/controllers/notes_controller_test.rb @@ -126,7 +126,7 @@ class NotesControllerTest < ActionDispatch::IntegrationTest assert_template "browse/not_found" assert_template :layout => "map" - get note_path(hidden_note_with_comment), :xhr => true + get note_path(hidden_note_with_comment), :headers => { "Turbo-Frame" => "sidebar_content_frame" } assert_response :not_found assert_template "browse/not_found" assert_template :layout => "xhr" diff --git a/test/controllers/searches_controller_test.rb b/test/controllers/searches_controller_test.rb index 48b998fd8..0d9a54d9e 100644 --- a/test/controllers/searches_controller_test.rb +++ b/test/controllers/searches_controller_test.rb @@ -342,7 +342,7 @@ class SearchesControllerTest < ActionDispatch::IntegrationTest assert_in_delta lat, @controller.params[:lat].to_f assert_in_delta lon, @controller.params[:lon].to_f - get search_path(:query => query), :xhr => true + get search_path(:query => query), :headers => { "Turbo-Frame" => "sidebar_content_frame" } assert_response :success assert_template :show assert_template :layout => "xhr" @@ -361,7 +361,7 @@ class SearchesControllerTest < ActionDispatch::IntegrationTest assert_template :layout => "map" assert_equal sources, assigns(:sources).pluck(:name) - get search_path(:query => query), :xhr => true + get search_path(:query => query), :headers => { "Turbo-Frame" => "sidebar_content_frame" } assert_response :success assert_template :show assert_template :layout => "xhr" diff --git a/test/controllers/site_controller_test.rb b/test/controllers/site_controller_test.rb index d3f8f0cb1..49cc21d2d 100644 --- a/test/controllers/site_controller_test.rb +++ b/test/controllers/site_controller_test.rb @@ -470,7 +470,7 @@ class SiteControllerTest < ActionDispatch::IntegrationTest assert_template "export" assert_template :layout => "map" - get export_path, :xhr => true + get export_path, :headers => { "Turbo-Frame" => "sidebar_content_frame" } assert_response :success assert_template "export" assert_template :layout => "xhr" diff --git a/test/test_helper.rb b/test/test_helper.rb index e2c15b604..8cadf8021 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -404,7 +404,7 @@ module ActiveSupport assert_template "browse/not_found" assert_template :layout => "map" - get path_method.call(:id => 0), :xhr => true + get path_method.call(:id => 0), :headers => { "Turbo-Frame" => "sidebar_content_frame" } assert_response :not_found assert_template "browse/not_found" assert_template :layout => "xhr" @@ -414,7 +414,7 @@ module ActiveSupport assert_template template assert_template :layout => "map" - get path_method.call(:id => id), :xhr => true + get path_method.call(:id => id), :headers => { "Turbo-Frame" => "sidebar_content_frame" } assert_response :success assert_template template assert_template :layout => "xhr" -- 2.47.3