Module: Everywhere::NativeHelper
- Defined in:
- lib/everywhere/native_helper.rb
Overview
Server-side view helpers mirroring the JS bridge's platform detection, so
ERB can branch on the native shell the same way Everywhere.platform
does on the client:
<%= link_to "Install the app", … unless native_app? %>
<div class="<%= "native-inset" if native_app? %>">
<% if native_version && native_version >= Gem::Version.new("1.2") %>
Detection is by User-Agent: the RubyEverywhere shells prepend
"RubyEverywhere/
Instance Method Summary collapse
-
#everywhere_auth_redirect(to) ⇒ Object
Where to send the user after an auth change.
-
#everywhere_badge(count) ⇒ Object
App icon badge count, server-rendered.
-
#everywhere_biometric_lock(reason: nil, name: nil, allow_passcode: true, unlock_label: "Unlock", locked_message: nil, wrapper_class: nil, locked_class: nil, message_class: nil, unlock_class: nil, &block) ⇒ Object
Biometric gate: wraps sensitive markup so the mobile shell keeps it hidden until Face ID / Touch ID passes — but only when the user has the device-local lock turned on (everywhere_biometric_toggle, or Everywhere.biometrics.setLockEnabled).
-
#everywhere_biometric_toggle(css_class: nil, id: nil) ⇒ Object
The settings switch for the device-local biometric lock.
-
#everywhere_tab_badge(path, count) ⇒ Object
Native tab bar badge for the tab whose everywhere.yml path is
path. -
#native_app? ⇒ Boolean
True inside any RubyEverywhere native shell (iOS or Android).
-
#native_platform ⇒ Object
:ios, :android, or nil in a plain browser.
-
#native_version ⇒ Object
The shell's version as a Gem::Version (from the "RubyEverywhere/
" UA prefix), or nil outside the shell / when unparseable.
Instance Method Details
#everywhere_auth_redirect(to) ⇒ Object
Where to send the user after an auth change. In a native shell, route
through the reset page (/everywhere/reset) so the app rebuilds cleanly —
fresh web views, re-fetched tabs — then lands on to. In a browser it's
just to. Use it in controllers:
redirect_to everywhere_auth_redirect(after_authentication_url)
to may be a full URL or a path; only the same-origin path is forwarded.
55 56 57 58 59 60 61 |
# File 'lib/everywhere/native_helper.rb', line 55 def everywhere_auth_redirect(to) return to unless native_app? && respond_to?(:everywhere_reset_path) path = to.to_s.sub(%r{\Ahttps?://[^/]+}, "") path = "/" if path.empty? || !path.start_with?("/") everywhere_reset_path(to: path) end |
#everywhere_badge(count) ⇒ Object
App icon badge count, server-rendered. Emits a meta tag the JS bridge applies on every Turbo visit — CSP-safe (no inline script) and correct on the first paint. 0 clears the badge. Put it in the layout
:<%= everywhere_badge Current.user.unread_count %>
In the mobile shell this badges the app icon; in an installed PWA the Badging API; elsewhere it renders inert metadata.
71 72 73 |
# File 'lib/everywhere/native_helper.rb', line 71 def everywhere_badge(count) tag.(name: "everywhere:badge", content: count.to_i) end |
#everywhere_biometric_lock(reason: nil, name: nil, allow_passcode: true, unlock_label: "Unlock", locked_message: nil, wrapper_class: nil, locked_class: nil, message_class: nil, unlock_class: nil, &block) ⇒ Object
Biometric gate: wraps sensitive markup so the mobile shell keeps it
hidden until Face ID / Touch ID passes — but only when the user has the
device-local lock turned on (everywhere_biometric_toggle, or
Everywhere.biometrics.setLockEnabled). Browsers and the desktop shell
render the content plainly. Requires biometrics in everywhere.yml's
permissions.
<%= everywhere_biometric_lock reason: "Unlock account settings" do %>
...profile, password, sessions...
<% end %>
name keys the once-per-session unlock (defaults to the page path).
For a fully custom overlay, hand-write the contract this emits:
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/everywhere/native_helper.rb', line 105 def everywhere_biometric_lock(reason: nil, name: nil, allow_passcode: true, unlock_label: "Unlock", locked_message: nil, wrapper_class: nil, locked_class: nil, message_class: nil, unlock_class: nil, &block) content = respond_to?(:capture) ? capture(&block) : block.call return content unless native_app? attrs = [%(data-everywhere-biometric-lock="#{_everywhere_attr(name)}")] attrs << %(data-everywhere-biometric-reason="#{_everywhere_attr(reason)}") if reason attrs << "data-everywhere-biometric-passcode" if allow_passcode attrs << %(class="#{_everywhere_attr(wrapper_class)}") if wrapper_class html = +"<div #{attrs.join(" ")}>" html << %(<div data-everywhere-biometric-content hidden>#{content}</div>) html << %(<div data-everywhere-biometric-locked hidden#{%( class="#{_everywhere_attr(locked_class)}") if locked_class}>) html << %(<p#{%( class="#{_everywhere_attr()}") if }>#{_everywhere_attr()}</p>) if html << %(<button type="button" data-everywhere-biometric-unlock#{%( class="#{_everywhere_attr(unlock_class)}") if unlock_class}>#{_everywhere_attr(unlock_label)}</button>) html << "</div></div>" html.respond_to?(:html_safe) ? html.html_safe : html end |
#everywhere_biometric_toggle(css_class: nil, id: nil) ⇒ Object
The settings switch for the device-local biometric lock. Renders hidden
and disabled; the bridge reveals and enables it only in the mobile shell
with working biometrics (toggling runs the biometric check first). Put
data-everywhere-biometric-toggle-row + hidden on the surrounding row
to reveal the whole thing together:
135 136 137 138 139 140 141 |
# File 'lib/everywhere/native_helper.rb', line 135 def everywhere_biometric_toggle(css_class: nil, id: nil) attrs = +"" attrs << %( id="#{_everywhere_attr(id)}") if id attrs << %( class="#{_everywhere_attr(css_class)}") if css_class html = %(<input type="checkbox" hidden disabled data-everywhere-biometric-toggle#{attrs}>) html.respond_to?(:html_safe) ? html.html_safe : html end |
#everywhere_tab_badge(path, count) ⇒ Object
Native tab bar badge for the tab whose everywhere.yml path is path.
Same delivery as everywhere_badge; 0 clears. Mobile shell only.
<%= everywhere_tab_badge "/inbox", Current.user.unread_count %>
79 80 81 82 |
# File 'lib/everywhere/native_helper.rb', line 79 def everywhere_tab_badge(path, count) tag.(name: "everywhere:tab-badge", content: JSON.generate({ path: path, count: count.to_i })) end |
#native_app? ⇒ Boolean
True inside any RubyEverywhere native shell (iOS or Android).
21 22 23 |
# File 'lib/everywhere/native_helper.rb', line 21 def native_app? !native_platform.nil? end |
#native_platform ⇒ Object
:ios, :android, or nil in a plain browser. Reads Hotwire Native's UA marker, so it's correct even before the JS bridge has booted.
27 28 29 30 31 32 33 |
# File 'lib/everywhere/native_helper.rb', line 27 def native_platform ua = _everywhere_user_agent or return nil return :ios if ua.include?("Hotwire Native iOS") return :android if ua.include?("Hotwire Native Android") nil end |
#native_version ⇒ Object
The shell's version as a Gem::Version (from the "RubyEverywhere/
38 39 40 41 42 43 44 45 |
# File 'lib/everywhere/native_helper.rb', line 38 def native_version ua = _everywhere_user_agent or return nil match = ua.match(%r{RubyEverywhere/([\w.\-]+)}) or return nil Gem::Version.new(match[1]) rescue ArgumentError nil end |