Merge pull request #2714 from basecamp/mobile/application-bridge-components
Apply Hotwire Native bridge attributes to the `<body>` element from the user-agent
This commit is contained in:
@@ -43,6 +43,17 @@ class ApplicationPlatform < PlatformAgent
|
||||
operating_system == "Windows"
|
||||
end
|
||||
|
||||
def bridge_name
|
||||
case
|
||||
when native? && android? then :android
|
||||
when native? && ios? then :ios
|
||||
end
|
||||
end
|
||||
|
||||
def bridge_components
|
||||
extract_list_from_native_user_agent("bridge-components")
|
||||
end
|
||||
|
||||
def type
|
||||
if native? && android?
|
||||
"native android"
|
||||
@@ -67,4 +78,13 @@ class ApplicationPlatform < PlatformAgent
|
||||
os =~ /Linux/ ? "Linux" : os
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def extract_list_from_native_user_agent(prefix)
|
||||
if native?
|
||||
user_agent.to_s.match(/#{Regexp.escape(prefix)}: \[(.*?)\]/) { |matches| matches[1] }.to_s
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
<body class="<%= @body_class %>"
|
||||
data-controller="local-time timezone-cookie turbo-navigation theme bridge--title bridge--text-size bridge--insets"
|
||||
data-action="turbo:morph@window->local-time#refreshAll turbo:before-visit@document->turbo-navigation#rememberLocation"
|
||||
data-platform="<%= platform.type %>" data-bridge--title-title-value="<%= @page_title %>">
|
||||
data-platform="<%= platform.type %>"
|
||||
data-bridge-platform="<%= platform.bridge_name %>"
|
||||
data-bridge-components="<%= platform.bridge_components %>"
|
||||
data-bridge--title-title-value="<%= @page_title %>">
|
||||
<div id="global-container" data-controller="bridge--buttons bridge--overflow-menu">
|
||||
<header class="header header--mobile-actions-stack <%= @header_class %>" id="header">
|
||||
<a href="#main" class="header__skip-navigation btn" data-turbo="false">Skip to main content</a>
|
||||
|
||||
@@ -2,13 +2,13 @@ require "test_helper"
|
||||
|
||||
class SetPlatformTest < ActionDispatch::IntegrationTest
|
||||
DESKTOP_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
NATIVE_IOS_UA = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Hotwire Native iOS/1.0"
|
||||
NATIVE_IOS_UA = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Hotwire Native iOS/1.0 bridge-components: [buttons overflow-menu]"
|
||||
|
||||
test "uses the request user agent by default" do
|
||||
sign_in_as :david
|
||||
|
||||
get board_path(boards(:writebook)), headers: { "User-Agent" => DESKTOP_UA }
|
||||
assert_match 'data-platform="desktop web"', response.body
|
||||
assert_select "body[data-platform='desktop web'][data-bridge-platform=''][data-bridge-components='']"
|
||||
end
|
||||
|
||||
test "prefers x_user_agent cookie over request user agent" do
|
||||
@@ -16,6 +16,6 @@ class SetPlatformTest < ActionDispatch::IntegrationTest
|
||||
|
||||
cookies[:x_user_agent] = NATIVE_IOS_UA
|
||||
get board_path(boards(:writebook)), headers: { "User-Agent" => DESKTOP_UA }
|
||||
assert_match 'data-platform="native ios"', response.body
|
||||
assert_select "body[data-platform='native ios'][data-bridge-platform='ios'][data-bridge-components='buttons overflow-menu']"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
require "test_helper"
|
||||
|
||||
class ApplicationPlatformTest < ActiveSupport::TestCase
|
||||
NATIVE_ANDROID_UA = "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Hotwire Native Android/1.0 bridge-components: [buttons overflow-menu form]"
|
||||
NATIVE_IOS_UA = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Hotwire Native iOS/1.0 bridge-components: [title]"
|
||||
MOBILE_WEB_IOS_UA = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1"
|
||||
DESKTOP_WEB_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
NATIVE_WITHOUT_COMPONENTS_UA = "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Hotwire Native Android/1.0"
|
||||
NATIVE_WITH_EMPTY_COMPONENTS_UA = "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Hotwire Native Android/1.0 bridge-components: []"
|
||||
NATIVE_WITH_MULTIPLE_LISTS_UA = "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Hotwire Native Android/1.0 bridge-components: [buttons] metadata: [ignored]"
|
||||
|
||||
test "bridge_name returns android for native android user agents" do
|
||||
assert_equal :android, platform_for(NATIVE_ANDROID_UA).bridge_name
|
||||
end
|
||||
|
||||
test "bridge_name returns ios for native ios user agents" do
|
||||
assert_equal :ios, platform_for(NATIVE_IOS_UA).bridge_name
|
||||
end
|
||||
|
||||
test "bridge_name is nil for non-native user agents" do
|
||||
assert_nil platform_for(MOBILE_WEB_IOS_UA).bridge_name
|
||||
assert_nil platform_for(DESKTOP_WEB_UA).bridge_name
|
||||
end
|
||||
|
||||
test "bridge_components returns extracted components for native user agents" do
|
||||
assert_equal "buttons overflow-menu form", platform_for(NATIVE_ANDROID_UA).bridge_components
|
||||
assert_equal "title", platform_for(NATIVE_IOS_UA).bridge_components
|
||||
end
|
||||
|
||||
test "bridge_components is blank for non-native user agents" do
|
||||
assert_equal "", platform_for(MOBILE_WEB_IOS_UA).bridge_components
|
||||
assert_equal "", platform_for(DESKTOP_WEB_UA).bridge_components
|
||||
end
|
||||
|
||||
test "bridge_components is blank when native user agent does not include bridge-components metadata" do
|
||||
assert_equal "", platform_for(NATIVE_WITHOUT_COMPONENTS_UA).bridge_components
|
||||
end
|
||||
|
||||
test "bridge_components supports empty lists" do
|
||||
assert_equal "", platform_for(NATIVE_WITH_EMPTY_COMPONENTS_UA).bridge_components
|
||||
end
|
||||
|
||||
test "bridge_components only matches through the first closing bracket" do
|
||||
assert_equal "buttons", platform_for(NATIVE_WITH_MULTIPLE_LISTS_UA).bridge_components
|
||||
end
|
||||
|
||||
private
|
||||
def platform_for(user_agent)
|
||||
ApplicationPlatform.new(user_agent)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user