ruby_native 0.11.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/ruby_native/helper.rb +115 -2
- data/lib/ruby_native/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 17263e207966bb624e15f4fc93399c2cfb28b15d9810cffa563cfa24b5a8b725
|
|
4
|
+
data.tar.gz: 6e7b9c3de16b19ef85ec7a614b207cf22e5d39f1517e5d9467950d315586660b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a9574b90d18f99866ee2d2c291475aeac0f4c7e57f8b15ea2c8cdc0aaa312818e2d7f5e1e5fa421e313e3f5214d36a314c975d56146b8d329660aed07aaab20c
|
|
7
|
+
data.tar.gz: 3c2da4697edaa56eb5194531352282c5c28aa1fa6c3c4edbd83d6a74da1f5d9a751e7175799244ad207741c73ecc4ba29a55a061cdcf563412bee7d9d21203fb
|
data/lib/ruby_native/helper.rb
CHANGED
|
@@ -85,6 +85,89 @@ module RubyNative
|
|
|
85
85
|
tag.div(data: data, hidden: true)
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
+
# No :root. Landing a page as the tab root — dropping what is behind it —
|
|
89
|
+
# is a distinct operation from replacing the current entry, and no shell
|
|
90
|
+
# implements it: every one of them mapped :root onto replace, which unwinds
|
|
91
|
+
# nothing. Offering the word without the behavior is worse than not
|
|
92
|
+
# offering it, so it is out until a shell can honor it.
|
|
93
|
+
ACTIONS = %w[push replace].freeze
|
|
94
|
+
|
|
95
|
+
# Validates a push/replace landing value. Returns the value as a string,
|
|
96
|
+
# raises otherwise.
|
|
97
|
+
def self.validate_action(value, label: "action")
|
|
98
|
+
value = value.to_s
|
|
99
|
+
unless ACTIONS.include?(value)
|
|
100
|
+
raise ArgumentError,
|
|
101
|
+
"#{label} must be :push or :replace, got #{value.inspect}"
|
|
102
|
+
end
|
|
103
|
+
value
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# The landing intents a page can declare about itself. Only :root so far.
|
|
107
|
+
# :modal is the obvious second member (both shells hardcode a /new + /edit
|
|
108
|
+
# rule today that no app can reach), which is why this is a vocabulary
|
|
109
|
+
# rather than a boolean tag.
|
|
110
|
+
PRESENTATION_INTENTS = %w[root].freeze
|
|
111
|
+
|
|
112
|
+
# Declares that this page is a root: it lands with nothing behind it, and no
|
|
113
|
+
# back affordance, wherever it lands.
|
|
114
|
+
#
|
|
115
|
+
# <%= native_presentation_tag :root %>
|
|
116
|
+
#
|
|
117
|
+
# Emits the fact on two channels, because the shells need it at two
|
|
118
|
+
# different moments and neither channel reaches both:
|
|
119
|
+
#
|
|
120
|
+
# 1. A `data-native-presentation` element, reported with every other signal
|
|
121
|
+
# once the page has rendered. This is what Normal Mode reads, and Normal
|
|
122
|
+
# Mode can act on it late: it has no push stack, so suppressing back is
|
|
123
|
+
# not a navigation and nothing refetches.
|
|
124
|
+
#
|
|
125
|
+
# 2. A `Native-Presentation` response header. Advanced Mode has to decide
|
|
126
|
+
# before the navigation commits, or it pushes and then visibly corrects
|
|
127
|
+
# itself. On a form submission Turbo has already fetched the destination
|
|
128
|
+
# by the time it proposes the visit, so the header is readable at
|
|
129
|
+
# `turbo:before-fetch-response` — before the proposal — and the header is
|
|
130
|
+
# the only part of that response readable synchronously, since the body
|
|
131
|
+
# arrives as a promise that consuming would take from Turbo.
|
|
132
|
+
#
|
|
133
|
+
# Nothing is declared at the origin. Both channels ride the response for the
|
|
134
|
+
# page itself, so a redirect chain carries the intent to wherever it actually
|
|
135
|
+
# lands rather than to wherever the link pointed.
|
|
136
|
+
#
|
|
137
|
+
# In Advanced Mode this takes effect before the navigation commits when the
|
|
138
|
+
# page arrives from a form submission, because that is the only case where
|
|
139
|
+
# Turbo has already fetched the destination by the time it proposes the
|
|
140
|
+
# visit. A link tap, a deep link and a cold boot are proposed before anything
|
|
141
|
+
# is fetched, so those fall back to the element and the shell corrects after
|
|
142
|
+
# the page renders. Normal Mode always uses the element.
|
|
143
|
+
#
|
|
144
|
+
# Do not call this inside a `cache` block. On a cache hit the element comes
|
|
145
|
+
# back from the cache and the header is never set, which silently leaves
|
|
146
|
+
# Advanced Mode with only the slower path.
|
|
147
|
+
def native_presentation_tag(presentation)
|
|
148
|
+
value = presentation.to_s
|
|
149
|
+
unless PRESENTATION_INTENTS.include?(value)
|
|
150
|
+
raise ArgumentError,
|
|
151
|
+
"native_presentation_tag must be #{PRESENTATION_INTENTS.map { |i| ":#{i}" }.join(" or ")}, " \
|
|
152
|
+
"got #{value.inspect}"
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# `respond_to?` rather than a nil check alone: ActionView forwards it to
|
|
156
|
+
# the controller for the delegated methods, so a view rendered without one
|
|
157
|
+
# answers false here instead of raising a DelegationError.
|
|
158
|
+
if respond_to?(:response) && response
|
|
159
|
+
if Rails.env.development? && response.committed?
|
|
160
|
+
Rails.logger.warn(
|
|
161
|
+
"[ruby_native] native_presentation_tag rendered after the response was committed, " \
|
|
162
|
+
"so Advanced Mode will not see it before the navigation commits."
|
|
163
|
+
)
|
|
164
|
+
end
|
|
165
|
+
response.headers["Native-Presentation"] = value
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
tag.div(data: { native_presentation: value }, hidden: true)
|
|
169
|
+
end
|
|
170
|
+
|
|
88
171
|
def native_navbar_tag(title = nil, pull_to_refresh: true, &block)
|
|
89
172
|
builder = NavbarBuilder.new(self)
|
|
90
173
|
capture(builder, &block) if block
|
|
@@ -131,14 +214,43 @@ module RubyNative
|
|
|
131
214
|
# form (`{ ios: "...", android: "..." }`). When both are given, a matching
|
|
132
215
|
# `icons[platform]` wins; otherwise falls back to `icon`. Returns nil when
|
|
133
216
|
# nothing resolves.
|
|
217
|
+
#
|
|
218
|
+
# A non-Hash `icons:` raises rather than falling through. `icons: [ios:
|
|
219
|
+
# "...", android: "..."]` is an easy slip in ERB and yields an Array holding
|
|
220
|
+
# one Hash, which used to skip the lookup and fall back to `icon` — usually
|
|
221
|
+
# nil, so the button rendered with no icon on either platform and nothing
|
|
222
|
+
# said why. That reads as "per-platform icons are broken" rather than "wrong
|
|
223
|
+
# bracket," and it is invisible on whichever platform you aren't testing.
|
|
134
224
|
def self.resolve_icon(icon: nil, icons: nil, platform: nil)
|
|
225
|
+
if !icons.nil? && !icons.is_a?(Hash)
|
|
226
|
+
raise ArgumentError,
|
|
227
|
+
"icons: must be a Hash like { ios: \"square.and.arrow.up\", android: \"share\" }, " \
|
|
228
|
+
"got #{icons.class}. Check for square brackets instead of curly braces."
|
|
229
|
+
end
|
|
230
|
+
|
|
135
231
|
if icons.is_a?(Hash) && platform
|
|
136
232
|
key = platform.to_sym
|
|
137
233
|
per_platform = icons[key] || icons[key.to_s]
|
|
138
234
|
return per_platform if per_platform
|
|
139
235
|
end
|
|
140
|
-
|
|
236
|
+
|
|
237
|
+
# Nothing matched, which is every web render: `native_platform` is nil in
|
|
238
|
+
# a browser. Fall back to `icon:`, then to any name in `icons:`, the same
|
|
239
|
+
# order `RubyNative.backfill_tab_icons` uses for the YAML config.
|
|
240
|
+
#
|
|
241
|
+
# Returning nil here instead would make `icons:` alone unusable, because
|
|
242
|
+
# `native_fab_tag` raises on a nil icon and would 500 a page that renders
|
|
243
|
+
# fine inside the app. The signal element is hidden and only read by the
|
|
244
|
+
# native app, so which name survives on the web doesn't matter.
|
|
245
|
+
icon || fallback_icon(icons)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def self.fallback_icon(icons)
|
|
249
|
+
return nil unless icons.is_a?(Hash)
|
|
250
|
+
|
|
251
|
+
icons[:ios] || icons["ios"] || icons[:android] || icons["android"]
|
|
141
252
|
end
|
|
253
|
+
private_class_method :fallback_icon
|
|
142
254
|
|
|
143
255
|
class NavbarBuilder
|
|
144
256
|
def initialize(context)
|
|
@@ -225,13 +337,14 @@ module RubyNative
|
|
|
225
337
|
@items = []
|
|
226
338
|
end
|
|
227
339
|
|
|
228
|
-
def item(title, href: nil, click: nil, icon: nil, icons: nil, selected: false)
|
|
340
|
+
def item(title, href: nil, click: nil, icon: nil, icons: nil, selected: false, action: nil)
|
|
229
341
|
resolved = RubyNative::Helper.resolve_icon(icon: icon, icons: icons, platform: @context.try(:native_platform))
|
|
230
342
|
data = { native_menu_item: "", native_title: title }
|
|
231
343
|
data[:native_href] = href if href
|
|
232
344
|
data[:native_click] = click if click
|
|
233
345
|
data[:native_icon] = resolved if resolved
|
|
234
346
|
data[:native_selected] = "" if selected
|
|
347
|
+
data[:native_action] = RubyNative::Helper.validate_action(action) if action
|
|
235
348
|
add(@context.tag.div(data: data))
|
|
236
349
|
end
|
|
237
350
|
|
data/lib/ruby_native/version.rb
CHANGED