turbo-native-initializer 0.0.13 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6134975dfb64645d0fe32a2dfcaa61efe0719458540d0e294cffc5547646397
4
- data.tar.gz: df7dada5dc6d57ff85e58e2439c86a9e06cc0e4e89fe8a5547509da483493906
3
+ metadata.gz: d5b3658efa201b5340d0e5ecdc401395f6659c235a5177c1e7a6b908b69a2f09
4
+ data.tar.gz: 7327665391ec9be569354ad811f9565a6164da08aea90c3e8bfe04f65e03cf5a
5
5
  SHA512:
6
- metadata.gz: 2135c55c9a059bba1907bd90f776e352968d74af49cdc8173d8b6f1d92ecbf5b09e566d3626e02d5842a509fb1dea7b3d7dde0d847046da4cb9740186f5e04b4
7
- data.tar.gz: 4c648e53161c6ab00f22787b3dc059cf06d8f259a9eabdd8aad0617e33d56ccac29ca6556f4eaa31fd8eb93fbbf50ad15d2d18e5b9f5fe3d1875369679bae5bc
6
+ metadata.gz: 1dcedc375f6de7437ff80b816424e599b7b266713c94417a6752bf16104e8ac719f0cbbed03b500efe891a3590f84c000a16f254d25e8fac5c6546883a4b8a30
7
+ data.tar.gz: b5e35c0791bf2a6b3fdc6eeaec4c45b5b14388b796c9d51f3539d2547fd93f106c01d483ec6a90d7cb6d73359cdfb2d76501226dab9fd070535f082922c6ef1c
@@ -25,7 +25,6 @@ open class WebFragment : TurboWebFragment(), NavDestination {
25
25
 
26
26
  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
27
27
  super.onViewCreated(view, savedInstanceState)
28
- setupMenu()
29
28
  viewLifecycleOwner.lifecycle.addObserver(bridgeDelegate)
30
29
  }
31
30
 
@@ -60,8 +59,4 @@ open class WebFragment : TurboWebFragment(), NavDestination {
60
59
  override fun createErrorView(statusCode: Int): View {
61
60
  return layoutInflater.inflate(R.layout.error_web, null)
62
61
  }
63
-
64
- private fun setupMenu() {
65
- toolbarForNavigation()?.inflateMenu(R.menu.web)
66
- }
67
62
  }
@@ -1,6 +1,9 @@
1
1
  package <%= package_name %>.strada
2
2
 
3
3
  import android.util.Log
4
+ import android.view.LayoutInflater
5
+ import android.view.Menu
6
+ import android.view.MenuItem
4
7
  import androidx.appcompat.widget.Toolbar
5
8
  import androidx.fragment.app.Fragment
6
9
  import dev.hotwire.strada.BridgeComponent
@@ -8,6 +11,7 @@ import dev.hotwire.strada.BridgeDelegate
8
11
  import dev.hotwire.strada.Message
9
12
  import <%= package_name %>.R
10
13
  import <%= package_name %>.base.NavDestination
14
+ import <%= package_name %>.databinding.NavButtonComponentBinding
11
15
  import kotlinx.serialization.Serializable
12
16
 
13
17
  class NavButtonComponent(
@@ -15,6 +19,8 @@ class NavButtonComponent(
15
19
  private val delegate: BridgeDelegate<NavDestination>
16
20
  ) : BridgeComponent<NavDestination>(name, delegate) {
17
21
 
22
+ private val navButtonItemId = 10
23
+ private var navButtonMenuItem: MenuItem? = null
18
24
  private val fragment: Fragment
19
25
  get() = delegate.destination.fragment
20
26
  private val toolbar: Toolbar?
@@ -34,26 +40,27 @@ class NavButtonComponent(
34
40
  }
35
41
 
36
42
  private fun showToolbarButton(data: MessageData) {
37
- val toolbar = toolbar ?: return
43
+ val menu = toolbar?.menu ?: return
44
+ val inflater = LayoutInflater.from(fragment.requireContext())
45
+ val binding = NavButtonComponentBinding.inflate(inflater)
46
+ val order = 999 // Show as the right-most button
38
47
 
39
- toolbar.menu.findItem(R.id.nav_button)?.apply {
40
- isVisible = true
41
- title = data.title
48
+ binding.navButton.apply {
49
+ text = data.title
50
+ setOnClickListener {
51
+ performAction()
52
+ }
42
53
  }
43
54
 
44
- toolbar.setOnMenuItemClickListener {
45
- when (it.itemId) {
46
- R.id.nav_button -> {
47
- performClick()
48
- true
49
- }
50
- else -> false
51
- }
55
+ menu.removeItem(navButtonItemId)
56
+ navButtonMenuItem = menu.add(Menu.NONE, navButtonItemId, order, data.title).apply {
57
+ actionView = binding.root
58
+ setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
52
59
  }
53
60
  }
54
61
 
55
- private fun performClick() {
56
- replyTo("connect")
62
+ private fun performAction(): Boolean {
63
+ return replyTo("connect")
57
64
  }
58
65
 
59
66
  @Serializable
@@ -0,0 +1,14 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:layout_width="wrap_content"
4
+ android:layout_height="match_parent"
5
+ android:layout_gravity="end|center_vertical"
6
+ android:paddingEnd="16dp">
7
+
8
+ <com.google.android.material.button.MaterialButton
9
+ style="@style/Widget.Material3.Button.TextButton"
10
+ android:id="@+id/nav_button"
11
+ android:layout_width="wrap_content"
12
+ android:layout_height="48dp"
13
+ android:minWidth="0dip" />
14
+ </FrameLayout>
@@ -22,7 +22,6 @@ open class WebFragment : TurboWebFragment(), NavDestination {
22
22
 
23
23
  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
24
24
  super.onViewCreated(view, savedInstanceState)
25
- setupMenu()
26
25
  viewLifecycleOwner.lifecycle.addObserver(bridgeDelegate)
27
26
  }
28
27
 
@@ -50,8 +49,4 @@ open class WebFragment : TurboWebFragment(), NavDestination {
50
49
  override fun createErrorView(statusCode: Int): View {
51
50
  return layoutInflater.inflate(R.layout.error_web, null)
52
51
  }
53
-
54
- private fun setupMenu() {
55
- toolbarForNavigation()?.inflateMenu(R.menu.web)
56
- }
57
52
  }
@@ -1,6 +1,9 @@
1
1
  package <%= package_name %>.strada
2
2
 
3
3
  import android.util.Log
4
+ import android.view.LayoutInflater
5
+ import android.view.Menu
6
+ import android.view.MenuItem
4
7
  import androidx.appcompat.widget.Toolbar
5
8
  import androidx.fragment.app.Fragment
6
9
  import dev.hotwire.strada.BridgeComponent
@@ -8,6 +11,7 @@ import dev.hotwire.strada.BridgeDelegate
8
11
  import dev.hotwire.strada.Message
9
12
  import <%= package_name %>.R
10
13
  import <%= package_name %>.base.NavDestination
14
+ import <%= package_name %>.databinding.NavButtonComponentBinding
11
15
  import kotlinx.serialization.Serializable
12
16
 
13
17
  class NavButtonComponent(
@@ -15,6 +19,8 @@ class NavButtonComponent(
15
19
  private val delegate: BridgeDelegate<NavDestination>
16
20
  ) : BridgeComponent<NavDestination>(name, delegate) {
17
21
 
22
+ private val navButtonItemId = 10
23
+ private var navButtonMenuItem: MenuItem? = null
18
24
  private val fragment: Fragment
19
25
  get() = delegate.destination.fragment
20
26
  private val toolbar: Toolbar?
@@ -34,26 +40,27 @@ class NavButtonComponent(
34
40
  }
35
41
 
36
42
  private fun showToolbarButton(data: MessageData) {
37
- val toolbar = toolbar ?: return
43
+ val menu = toolbar?.menu ?: return
44
+ val inflater = LayoutInflater.from(fragment.requireContext())
45
+ val binding = NavButtonComponentBinding.inflate(inflater)
46
+ val order = 999 // Show as the right-most button
38
47
 
39
- toolbar.menu.findItem(R.id.nav_button)?.apply {
40
- isVisible = true
41
- title = data.title
48
+ binding.navButton.apply {
49
+ text = data.title
50
+ setOnClickListener {
51
+ performAction()
52
+ }
42
53
  }
43
54
 
44
- toolbar.setOnMenuItemClickListener {
45
- when (it.itemId) {
46
- R.id.nav_button -> {
47
- performClick()
48
- true
49
- }
50
- else -> false
51
- }
55
+ menu.removeItem(navButtonItemId)
56
+ navButtonMenuItem = menu.add(Menu.NONE, navButtonItemId, order, data.title).apply {
57
+ actionView = binding.root
58
+ setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
52
59
  }
53
60
  }
54
61
 
55
- private fun performClick() {
56
- replyTo("connect")
62
+ private fun performAction(): Boolean {
63
+ return replyTo("connect")
57
64
  }
58
65
 
59
66
  @Serializable
@@ -0,0 +1,14 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:layout_width="wrap_content"
4
+ android:layout_height="match_parent"
5
+ android:layout_gravity="end|center_vertical"
6
+ android:paddingEnd="16dp">
7
+
8
+ <com.google.android.material.button.MaterialButton
9
+ style="@style/Widget.Material3.Button.TextButton"
10
+ android:id="@+id/nav_button"
11
+ android:layout_width="wrap_content"
12
+ android:layout_height="48dp"
13
+ android:minWidth="0dip" />
14
+ </FrameLayout>
@@ -14,9 +14,7 @@ final class SceneDelegate: UIResponder {
14
14
  // MARK: - Setup
15
15
 
16
16
  private func configureRootViewController() {
17
- guard let window = window else { fatalError() }
18
-
19
- navigationController = window.rootViewController as? TurboNavigationController
17
+ navigationController = window!.rootViewController as? TurboNavigationController
20
18
  navigationController.session = session
21
19
  navigationController.modalSession = modalSession
22
20
  }
@@ -68,6 +66,14 @@ extension SceneDelegate: SessionDelegate {
68
66
  navigationController.route(url: proposal.url, options: proposal.options, properties: proposal.properties)
69
67
  }
70
68
 
69
+ func session(_ session: Session, openExternalURL url: URL) {
70
+ if url.host == rootURL.host, !url.pathExtension.isEmpty {
71
+ navigationController.present(SFSafariViewController(url: url), animated: true)
72
+ } else {
73
+ UIApplication.shared.open(url)
74
+ }
75
+ }
76
+
71
77
  func session(_ session: Session, didFailRequestForVisitable visitable: Visitable, error: Error) {
72
78
  if let turboError = error as? TurboError, case let .http(statusCode) = turboError, statusCode == 401 {
73
79
  promptForAuthentication()
@@ -78,58 +84,22 @@ extension SceneDelegate: SessionDelegate {
78
84
  }
79
85
  }
80
86
 
81
- // When a form submission completes in the modal session, we need to
82
- // manually clear the snapshot cache in the default session, since we
83
- // don't want potentially stale cached snapshots to be used
84
87
  func sessionDidFinishFormSubmission(_ session: Session) {
85
88
  if (session == modalSession) {
86
89
  self.session.clearSnapshotCache()
87
90
  }
88
91
  }
89
92
 
90
- func sessionDidLoadWebView(_ session: Session) {
91
- session.webView.navigationDelegate = self
92
- }
93
-
94
93
  func sessionWebViewProcessDidTerminate(_ session: Session) {
95
94
  session.reload()
96
95
  }
97
96
  }
98
97
 
99
- extension SceneDelegate: WKNavigationDelegate {
100
- func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
101
- if navigationAction.navigationType == .linkActivated {
102
- // Any link that's not on the same domain as the Turbo root url will go through here
103
- // Other links on the domain, but that have an extension that is non-html will also go here
104
- // You can decide how to handle those, by default if you're not the navigationDelegate
105
- // the Session will open them in the default browser
106
-
107
- let url = navigationAction.request.url!
108
-
109
- // For this demo, we'll load files from our domain in a SafariViewController so you
110
- // don't need to leave the app. You might expand this in your app
111
- // to open all audio/video/images in a native media viewer
112
- if url.host == rootURL.host, !url.pathExtension.isEmpty {
113
- let safariViewController = SFSafariViewController(url: url)
114
- navigationController.present(safariViewController, animated: true)
115
- } else {
116
- UIApplication.shared.open(url)
117
- }
118
-
119
- decisionHandler(.cancel)
120
- } else {
121
- decisionHandler(.allow)
122
- }
123
- }
124
- }
125
-
126
98
  extension SceneDelegate: WKUIDelegate {
127
99
  func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
128
100
  let confirm = UIAlertController(title: nil, message: message, preferredStyle: .alert)
129
101
  confirm.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in completionHandler(false) })
130
102
  confirm.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler(true) })
131
-
132
- // JavaScript alerts in Turbo Native
133
103
  navigationController.present(confirm, animated: true)
134
104
  }
135
105
  }
@@ -14,14 +14,8 @@ final class SceneDelegate: UIResponder {
14
14
  private let rootURL2 = <%= name %>.homeURL2
15
15
 
16
16
  private var tabBarController: UITabBarController!
17
-
18
- private lazy var navigationController1 = {
19
- tabBarController.viewControllers![0] as! TurboNavigationController
20
- }()
21
-
22
- private lazy var navigationController2 = {
23
- tabBarController.viewControllers![1] as! TurboNavigationController
24
- }()
17
+ private var navigationController1: TurboNavigationController!
18
+ private var navigationController2: TurboNavigationController!
25
19
 
26
20
  private func navigationController() -> TurboNavigationController {
27
21
  tabBarController.selectedViewController as! TurboNavigationController
@@ -30,13 +24,13 @@ final class SceneDelegate: UIResponder {
30
24
  // MARK: - Setup
31
25
 
32
26
  private func configureRootViewController() {
33
- guard let window = window else { fatalError() }
34
-
35
- tabBarController = window.rootViewController as? UITabBarController
27
+ tabBarController = window!.rootViewController as? UITabBarController
36
28
 
29
+ navigationController1 = tabBarController.viewControllers![0] as? TurboNavigationController
37
30
  navigationController1.session = session1
38
31
  navigationController1.modalSession = modalSession
39
32
 
33
+ navigationController2 = tabBarController.viewControllers![1] as? TurboNavigationController
40
34
  navigationController2.session = session2
41
35
  navigationController2.modalSession = modalSession
42
36
  }
@@ -88,6 +82,14 @@ extension SceneDelegate: SessionDelegate {
88
82
  navigationController().route(url: proposal.url, options: proposal.options, properties: proposal.properties)
89
83
  }
90
84
 
85
+ func session(_ session: Session, openExternalURL url: URL) {
86
+ if url.host == baseURL.host, !url.pathExtension.isEmpty {
87
+ navigationController().present(SFSafariViewController(url: url), animated: true)
88
+ } else {
89
+ UIApplication.shared.open(url)
90
+ }
91
+ }
92
+
91
93
  func session(_ session: Session, didFailRequestForVisitable visitable: Visitable, error: Error) {
92
94
  if let errorPresenter = visitable as? ErrorPresenter {
93
95
  errorPresenter.presentError(error) { session.reload() }
@@ -96,58 +98,22 @@ extension SceneDelegate: SessionDelegate {
96
98
  }
97
99
  }
98
100
 
99
- // When a form submission completes in the modal session, we need to
100
- // manually clear the snapshot cache in the default session, since we
101
- // don't want potentially stale cached snapshots to be used
102
101
  func sessionDidFinishFormSubmission(_ session: Session) {
103
102
  if (session == modalSession) {
104
103
  self.session().clearSnapshotCache()
105
104
  }
106
105
  }
107
106
 
108
- func sessionDidLoadWebView(_ session: Session) {
109
- session.webView.navigationDelegate = self
110
- }
111
-
112
107
  func sessionWebViewProcessDidTerminate(_ session: Session) {
113
108
  session.reload()
114
109
  }
115
110
  }
116
111
 
117
- extension SceneDelegate: WKNavigationDelegate {
118
- func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
119
- if navigationAction.navigationType == .linkActivated {
120
- // Any link that's not on the same domain as the Turbo root url will go through here
121
- // Other links on the domain, but that have an extension that is non-html will also go here
122
- // You can decide how to handle those, by default if you're not the navigationDelegate
123
- // the Session will open them in the default browser
124
-
125
- let url = navigationAction.request.url!
126
-
127
- // For this demo, we'll load files from our domain in a SafariViewController so you
128
- // don't need to leave the app. You might expand this in your app
129
- // to open all audio/video/images in a native media viewer
130
- if url.host == baseURL.host, !url.pathExtension.isEmpty {
131
- let safariViewController = SFSafariViewController(url: url)
132
- navigationController().present(safariViewController, animated: true)
133
- } else {
134
- UIApplication.shared.open(url)
135
- }
136
-
137
- decisionHandler(.cancel)
138
- } else {
139
- decisionHandler(.allow)
140
- }
141
- }
142
- }
143
-
144
112
  extension SceneDelegate: WKUIDelegate {
145
113
  func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
146
114
  let confirm = UIAlertController(title: nil, message: message, preferredStyle: .alert)
147
115
  confirm.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in completionHandler(false) })
148
- confirm.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler(true) })
149
-
150
- // JavaScript alerts in Turbo Native
116
+ confirm.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler(true) })
151
117
  navigationController().present(confirm, animated: true)
152
118
  }
153
119
  }
@@ -1,3 +1,3 @@
1
1
  module TurboNativeInitializer
2
- VERSION = "0.0.13"
2
+ VERSION = "0.0.15"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbo-native-initializer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.13
4
+ version: 0.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nixon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-30 00:00:00.000000000 Z
11
+ date: 2023-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -77,7 +77,7 @@ files:
77
77
  - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/fragment_web_home.xml
78
78
  - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/menu_component_adapter_row.xml
79
79
  - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/menu_component_bottom_sheet.xml
80
- - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/menu/web.xml
80
+ - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/nav_button_component.xml
81
81
  - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
82
82
  - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
83
83
  - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/mipmap-xhdpi/ic_launcher.png
@@ -136,8 +136,8 @@ files:
136
136
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/fragment_web_home.xml
137
137
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/menu_component_adapter_row.xml
138
138
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/menu_component_bottom_sheet.xml
139
+ - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/nav_button_component.xml
139
140
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/menu/bottom_navigation_menu.xml
140
- - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/menu/web.xml
141
141
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
142
142
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
143
143
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/mipmap-xhdpi/ic_launcher.png
@@ -1,9 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <menu xmlns:android="http://schemas.android.com/apk/res/android"
3
- xmlns:app="http://schemas.android.com/apk/res-auto">
4
- <item
5
- android:id="@+id/nav_button"
6
- android:orderInCategory="999"
7
- android:visible="false"
8
- app:showAsAction="always" />
9
- </menu>
@@ -1,9 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <menu xmlns:android="http://schemas.android.com/apk/res/android"
3
- xmlns:app="http://schemas.android.com/apk/res-auto">
4
- <item
5
- android:id="@+id/nav_button"
6
- android:orderInCategory="999"
7
- android:visible="false"
8
- app:showAsAction="always" />
9
- </menu>