turbo-native-initializer 0.0.10 → 0.0.12
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/README.md +3 -1
- data/lib/turbo_native_initializer/generator.rb +1 -0
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/base/NavDestination.kt.tt +17 -2
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebFragment.kt.tt +38 -0
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebModalFragment.kt.tt +2 -2
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/main/MainActivity.kt.tt +7 -0
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/main/MainSessionNavHostFragment.kt.tt +5 -5
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/BridgeComponentFactories.kt.tt +9 -0
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/FlashMessageComponent.kt.tt +41 -0
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/FormComponent.kt.tt +87 -0
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/NavButtonComponent.kt.tt +70 -0
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/util/Extension.kt.tt +19 -0
- data/lib/turbo_native_initializer/templates/android_stack/base/app/build.gradle.kts.tt +9 -3
- data/lib/turbo_native_initializer/templates/android_stack/base/app/src/main/assets/json/configuration.json +1 -1
- data/lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/form_component_submit.xml +12 -0
- data/lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/nav_button_component.xml +14 -0
- data/lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/values/colors.xml +1 -1
- data/lib/turbo_native_initializer/templates/android_stack/base/build.gradle.kts +2 -1
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/base/NavDestination.kt.tt +17 -2
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebFragment.kt.tt +38 -0
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebModalFragment.kt.tt +2 -2
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/main/BaseSessionNavHostFragment.kt.tt +5 -5
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/main/MainActivity.kt.tt +4 -0
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/BridgeComponentFactories.kt.tt +9 -0
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/FlashMessageComponent.kt.tt +41 -0
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/FormComponent.kt.tt +87 -0
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/NavButtonComponent.kt.tt +70 -0
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/util/Extension.kt.tt +19 -0
- data/lib/turbo_native_initializer/templates/android_tabs/base/app/build.gradle.kts.tt +9 -3
- data/lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/assets/json/configuration.json +1 -1
- data/lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/form_component_submit.xml +12 -0
- data/lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/nav_button_component.xml +14 -0
- data/lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/values/colors.xml +1 -1
- data/lib/turbo_native_initializer/templates/android_tabs/base/build.gradle.kts +2 -1
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Configuration/path-configuration.json +1 -1
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Controllers/TurboNavigationController.swift +12 -8
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Controllers/TurboWebViewController.swift +65 -0
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Controllers/UIViewController+Toast.swift +79 -0
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Controllers/WKWebViewConfiguration+App.swift +21 -0
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Delegates/SceneDelegate.swift.tt +4 -5
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Resources/Assets.xcassets/AccentColor.colorset/Contents.json +8 -8
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Resources/Base.lproj/{Main.storyboard.tt → Main.storyboard} +1 -1
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/BridgeComponent+App.swift +8 -0
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/FlashMessageComponent.swift +47 -0
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/FormComponent.swift +78 -0
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/NavButtonComponent.swift +60 -0
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject.xcodeproj/project.pbxproj.tt +55 -6
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +9 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Configuration/path-configuration.json +1 -1
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Controllers/TurboNavigationController.swift +11 -8
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Controllers/TurboWebViewController.swift +65 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Controllers/UIViewController+Toast.swift +79 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Controllers/WKWebViewConfiguration+App.swift +21 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Delegates/SceneDelegate.swift.tt +4 -5
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Resources/Assets.xcassets/AccentColor.colorset/Contents.json +8 -8
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Resources/Base.lproj/Main.storyboard +2 -2
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/BridgeComponent+App.swift +8 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/FlashMessageComponent.swift +47 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/FormComponent.swift +78 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/NavButtonComponent.swift +60 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject.xcodeproj/project.pbxproj.tt +55 -6
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +9 -0
- data/lib/turbo_native_initializer/version.rb +1 -1
- metadata +32 -6
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Controllers/ViewController.swift +0 -22
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Controllers/ViewController.swift +0 -22
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c1f5d9801c82d83f38a465a6375d87b1b90ac03a19bcf292b78555f9bc925cee
|
|
4
|
+
data.tar.gz: 5405c39714a62d1f5823d46be8bb9935ac52743f2c02d9a0b544b82f73fd6f40
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 65fbcd8000abe1b6c9c4c0c0ca4ab715ecbc8ac828ca1b859bc93b7d9a32718163913b12b881ee6ea2e41c244df50c7d869dc8fe420d86a09e9dfcd50b274a28
|
|
7
|
+
data.tar.gz: d3c907983b7c31767b99c9bd34bb0b0185fbb4bb8028c19a6c5e6648c739baf99a08e6a4318207ed25644a41c2a6d4d84bcabd529288dd85004c42b52f21f0a4
|
data/README.md
CHANGED
|
@@ -11,6 +11,8 @@ A turbo native project generator for iOS and Android.
|
|
|
11
11
|
- Added presentations `pop`, `refresh`, `none`, `replace`, `clear-all`, and `replace-all`. (iOS)
|
|
12
12
|
- Added `visitable` property in order to avoid visits. (iOS)
|
|
13
13
|
- Added support for tab navigation. (iOS/Android)
|
|
14
|
+
- Added support for flash messages. (iOS/Android)
|
|
15
|
+
- Added navigation bar button component. (iOS/Android)
|
|
14
16
|
|
|
15
17
|
## Installation
|
|
16
18
|
|
|
@@ -56,7 +58,7 @@ Options:
|
|
|
56
58
|
|
|
57
59
|
## Development
|
|
58
60
|
|
|
59
|
-
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
61
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment, you can run something like `TurboNativeInitializer::Generator.start(["TurboNativeProject", "--platform=ios", "--navigation=stack"])`.
|
|
60
62
|
|
|
61
63
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
62
64
|
|
|
@@ -23,6 +23,7 @@ module TurboNativeInitializer
|
|
|
23
23
|
directory "#{project}/TurboNativeProject/Controllers", "#{name}/#{name}/Controllers"
|
|
24
24
|
directory "#{project}/TurboNativeProject/Delegates", "#{name}/#{name}/Delegates"
|
|
25
25
|
directory "#{project}/TurboNativeProject/Resources", "#{name}/#{name}/Resources"
|
|
26
|
+
directory "#{project}/TurboNativeProject/Strada", "#{name}/#{name}/Strada"
|
|
26
27
|
directory "#{project}/TurboNativeProject.xcodeproj", "#{name}/#{name}.xcodeproj"
|
|
27
28
|
template "#{project}/TurboNativeProject/TurboNativeProject.swift", "#{name}/#{name}/#{name}.swift"
|
|
28
29
|
when "android"
|
|
@@ -6,17 +6,20 @@ import androidx.browser.customtabs.CustomTabsIntent
|
|
|
6
6
|
import androidx.browser.customtabs.CustomTabsIntent.SHARE_STATE_ON
|
|
7
7
|
import androidx.navigation.NavOptions
|
|
8
8
|
import androidx.navigation.navOptions
|
|
9
|
+
import com.google.android.material.snackbar.Snackbar
|
|
10
|
+
import dev.hotwire.strada.BridgeDestination
|
|
9
11
|
import dev.hotwire.turbo.config.TurboPathConfigurationProperties
|
|
10
12
|
import dev.hotwire.turbo.config.context
|
|
11
13
|
import <%= package_name %>.R
|
|
12
14
|
import <%= package_name %>.util.BASE_URL
|
|
13
15
|
import dev.hotwire.turbo.nav.TurboNavDestination
|
|
16
|
+
import dev.hotwire.turbo.nav.TurboNavPresentationContext.DEFAULT
|
|
14
17
|
import dev.hotwire.turbo.nav.TurboNavPresentationContext.MODAL
|
|
15
18
|
|
|
16
|
-
interface NavDestination : TurboNavDestination {
|
|
19
|
+
interface NavDestination : TurboNavDestination, BridgeDestination {
|
|
17
20
|
override fun shouldNavigateTo(newLocation: String): Boolean {
|
|
18
21
|
return when (isNavigable(newLocation)) {
|
|
19
|
-
true -> true
|
|
22
|
+
true -> { displayNoticeMessage(newLocation); true }
|
|
20
23
|
else -> { launchCustomTab(newLocation); false }
|
|
21
24
|
}
|
|
22
25
|
}
|
|
@@ -31,10 +34,22 @@ interface NavDestination : TurboNavDestination {
|
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
override fun bridgeWebViewIsReady(): Boolean {
|
|
38
|
+
return session.isReady
|
|
39
|
+
}
|
|
40
|
+
|
|
34
41
|
private fun isNavigable(location: String): Boolean {
|
|
35
42
|
return location.startsWith(BASE_URL)
|
|
36
43
|
}
|
|
37
44
|
|
|
45
|
+
private fun displayNoticeMessage(location: String) {
|
|
46
|
+
var message = Uri.parse(location).getQueryParameter("notice")
|
|
47
|
+
|
|
48
|
+
if (pathProperties.context == DEFAULT && message != null) {
|
|
49
|
+
Snackbar.make(fragment.requireView(), message, Snackbar.LENGTH_SHORT).show()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
38
53
|
private fun launchCustomTab(location: String) {
|
|
39
54
|
val context = fragment.context ?: return
|
|
40
55
|
val color = context.getColor(R.color.white)
|
|
@@ -1,16 +1,54 @@
|
|
|
1
1
|
package <%= package_name %>.features.web
|
|
2
2
|
|
|
3
|
+
import android.os.Bundle
|
|
3
4
|
import android.view.View
|
|
5
|
+
import dev.hotwire.strada.BridgeDelegate
|
|
4
6
|
import <%= package_name %>.base.NavDestination
|
|
5
7
|
import <%= package_name %>.util.SIGN_IN_URL
|
|
6
8
|
import dev.hotwire.turbo.fragments.TurboWebFragment
|
|
7
9
|
import dev.hotwire.turbo.nav.TurboNavGraphDestination
|
|
8
10
|
import <%= package_name %>.R
|
|
11
|
+
import <%= package_name %>.strada.bridgeComponentFactories
|
|
12
|
+
import dev.hotwire.turbo.views.TurboWebView
|
|
9
13
|
import dev.hotwire.turbo.visit.TurboVisitAction.REPLACE
|
|
10
14
|
import dev.hotwire.turbo.visit.TurboVisitOptions
|
|
11
15
|
|
|
12
16
|
@TurboNavGraphDestination(uri = "turbo://fragment/web")
|
|
13
17
|
open class WebFragment : TurboWebFragment(), NavDestination {
|
|
18
|
+
private val bridgeDelegate by lazy {
|
|
19
|
+
BridgeDelegate(
|
|
20
|
+
location = location,
|
|
21
|
+
destination = this,
|
|
22
|
+
componentFactories = bridgeComponentFactories
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
27
|
+
super.onViewCreated(view, savedInstanceState)
|
|
28
|
+
viewLifecycleOwner.lifecycle.addObserver(bridgeDelegate)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
override fun onDestroyView() {
|
|
32
|
+
super.onDestroyView()
|
|
33
|
+
viewLifecycleOwner.lifecycle.removeObserver(bridgeDelegate)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
override fun onColdBootPageStarted(location: String) {
|
|
37
|
+
bridgeDelegate.onColdBootPageStarted()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
override fun onColdBootPageCompleted(location: String) {
|
|
41
|
+
bridgeDelegate.onColdBootPageCompleted()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override fun onWebViewAttached(webView: TurboWebView) {
|
|
45
|
+
bridgeDelegate.onWebViewAttached(webView)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override fun onWebViewDetached(webView: TurboWebView) {
|
|
49
|
+
bridgeDelegate.onWebViewDetached()
|
|
50
|
+
}
|
|
51
|
+
|
|
14
52
|
override fun onVisitErrorReceived(location: String, errorCode: Int) {
|
|
15
53
|
when (errorCode) {
|
|
16
54
|
401 -> navigate(SIGN_IN_URL, TurboVisitOptions(action = REPLACE))
|
|
@@ -3,7 +3,7 @@ package <%= package_name %>.features.web
|
|
|
3
3
|
import android.os.Bundle
|
|
4
4
|
import android.view.View
|
|
5
5
|
import dev.hotwire.turbo.nav.TurboNavGraphDestination
|
|
6
|
-
import <%= package_name %>.
|
|
6
|
+
import <%= package_name %>.util.displayBackButtonAsCloseIcon
|
|
7
7
|
|
|
8
8
|
@TurboNavGraphDestination(uri = "turbo://fragment/web/modal")
|
|
9
9
|
class WebModalFragment : WebFragment() {
|
|
@@ -12,6 +12,6 @@ class WebModalFragment : WebFragment() {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
private fun initToolbar() {
|
|
15
|
-
toolbarForNavigation()?.
|
|
15
|
+
toolbarForNavigation()?.displayBackButtonAsCloseIcon()
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -2,6 +2,8 @@ package <%= package_name %>.main
|
|
|
2
2
|
|
|
3
3
|
import android.os.Bundle
|
|
4
4
|
import androidx.appcompat.app.AppCompatActivity
|
|
5
|
+
import dev.hotwire.strada.KotlinXJsonConverter
|
|
6
|
+
import dev.hotwire.strada.Strada
|
|
5
7
|
import dev.hotwire.turbo.activities.TurboActivity
|
|
6
8
|
import dev.hotwire.turbo.delegates.TurboActivityDelegate
|
|
7
9
|
import <%= package_name %>.R
|
|
@@ -14,5 +16,10 @@ class MainActivity : AppCompatActivity(), TurboActivity {
|
|
|
14
16
|
setContentView(R.layout.activity_main)
|
|
15
17
|
|
|
16
18
|
delegate = TurboActivityDelegate(this, R.id.main_nav_host)
|
|
19
|
+
configApp()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private fun configApp() {
|
|
23
|
+
Strada.config.jsonConverter = KotlinXJsonConverter()
|
|
17
24
|
}
|
|
18
25
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
package <%= package_name %>.main
|
|
2
2
|
|
|
3
|
-
import android.webkit.WebView
|
|
4
3
|
import androidx.appcompat.app.AppCompatActivity
|
|
5
4
|
import androidx.fragment.app.Fragment
|
|
5
|
+
import dev.hotwire.strada.Bridge
|
|
6
6
|
import dev.hotwire.turbo.config.TurboPathConfiguration
|
|
7
7
|
import <%= package_name %>.features.native.NumbersFragment
|
|
8
8
|
import <%= package_name %>.features.web.WebFragment
|
|
@@ -10,6 +10,7 @@ import <%= package_name %>.features.web.WebHomeFragment
|
|
|
10
10
|
import <%= package_name %>.features.web.WebModalFragment
|
|
11
11
|
import <%= package_name %>.util.CURRENT_URL
|
|
12
12
|
import dev.hotwire.turbo.session.TurboSessionNavHostFragment
|
|
13
|
+
import <%= package_name %>.util.customUserAgent
|
|
13
14
|
import kotlin.reflect.KClass
|
|
14
15
|
|
|
15
16
|
@Suppress("unused")
|
|
@@ -29,10 +30,9 @@ class MainSessionNavHostFragment : TurboSessionNavHostFragment() {
|
|
|
29
30
|
|
|
30
31
|
override fun onSessionCreated() {
|
|
31
32
|
super.onSessionCreated()
|
|
32
|
-
session.webView.settings.userAgentString =
|
|
33
|
-
}
|
|
33
|
+
session.webView.settings.userAgentString = session.webView.customUserAgent
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// Initialize Strada bridge with new WebView instance
|
|
36
|
+
Bridge.initialize(session.webView)
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
package <%= package_name %>.strada
|
|
2
|
+
|
|
3
|
+
import dev.hotwire.strada.BridgeComponentFactory
|
|
4
|
+
|
|
5
|
+
val bridgeComponentFactories = listOf(
|
|
6
|
+
BridgeComponentFactory("form", ::FormComponent),
|
|
7
|
+
BridgeComponentFactory("nav-button", ::NavButtonComponent),
|
|
8
|
+
BridgeComponentFactory("flash-message", ::FlashMessageComponent),
|
|
9
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
package <%= package_name %>.strada
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import androidx.fragment.app.Fragment
|
|
5
|
+
import com.google.android.material.snackbar.Snackbar
|
|
6
|
+
import dev.hotwire.strada.BridgeComponent
|
|
7
|
+
import dev.hotwire.strada.BridgeDelegate
|
|
8
|
+
import dev.hotwire.strada.Message
|
|
9
|
+
import <%= package_name %>.base.NavDestination
|
|
10
|
+
import kotlinx.serialization.Serializable
|
|
11
|
+
|
|
12
|
+
class FlashMessageComponent(
|
|
13
|
+
name: String,
|
|
14
|
+
private val delegate: BridgeDelegate<NavDestination>
|
|
15
|
+
) : BridgeComponent<NavDestination>(name, delegate) {
|
|
16
|
+
|
|
17
|
+
private val fragment: Fragment
|
|
18
|
+
get() = delegate.destination.fragment
|
|
19
|
+
|
|
20
|
+
override fun onReceive(message: Message) {
|
|
21
|
+
if (message.event == "connect") {
|
|
22
|
+
handleConnectEvent(message)
|
|
23
|
+
} else {
|
|
24
|
+
Log.w("TurboNative", "Unknown event for message: $message")
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private fun handleConnectEvent(message: Message) {
|
|
29
|
+
val data = message.data<MessageData>() ?: return
|
|
30
|
+
showSnackBar(data)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private fun showSnackBar(data: MessageData) {
|
|
34
|
+
Snackbar.make(fragment.requireView(), data.title, Snackbar.LENGTH_SHORT).show()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Serializable
|
|
38
|
+
data class MessageData(
|
|
39
|
+
val title: String
|
|
40
|
+
)
|
|
41
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
package <%= package_name %>.strada
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import android.view.LayoutInflater
|
|
5
|
+
import android.view.Menu
|
|
6
|
+
import android.view.MenuItem
|
|
7
|
+
import androidx.appcompat.widget.Toolbar
|
|
8
|
+
import androidx.fragment.app.Fragment
|
|
9
|
+
import dev.hotwire.strada.BridgeComponent
|
|
10
|
+
import dev.hotwire.strada.BridgeDelegate
|
|
11
|
+
import dev.hotwire.strada.Message
|
|
12
|
+
import <%= package_name %>.R
|
|
13
|
+
import <%= package_name %>.base.NavDestination
|
|
14
|
+
import <%= package_name %>.databinding.FormComponentSubmitBinding
|
|
15
|
+
import kotlinx.serialization.Serializable
|
|
16
|
+
|
|
17
|
+
class FormComponent(
|
|
18
|
+
name: String,
|
|
19
|
+
private val delegate: BridgeDelegate<NavDestination>
|
|
20
|
+
) : BridgeComponent<NavDestination>(name, delegate) {
|
|
21
|
+
|
|
22
|
+
private val submitButtonItemId = 10
|
|
23
|
+
private var submitMenuItem: MenuItem? = null
|
|
24
|
+
private val fragment: Fragment
|
|
25
|
+
get() = delegate.destination.fragment
|
|
26
|
+
private val toolbar: Toolbar?
|
|
27
|
+
get() = fragment.view?.findViewById(R.id.toolbar)
|
|
28
|
+
|
|
29
|
+
override fun onReceive(message: Message) {
|
|
30
|
+
when (message.event) {
|
|
31
|
+
"connect" -> handleConnectEvent(message)
|
|
32
|
+
"submitEnabled" -> handleSubmitEnabled()
|
|
33
|
+
"submitDisabled" -> handleSubmitDisabled()
|
|
34
|
+
else -> Log.w("TurboNative", "Unknown event for message: $message")
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private fun handleConnectEvent(message: Message) {
|
|
39
|
+
val data = message.data<MessageData>() ?: return
|
|
40
|
+
showToolbarButton(data)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private fun handleSubmitEnabled() {
|
|
44
|
+
toggleSubmitButton(true)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private fun handleSubmitDisabled() {
|
|
48
|
+
toggleSubmitButton(false)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private fun showToolbarButton(data: MessageData) {
|
|
52
|
+
val menu = toolbar?.menu ?: return
|
|
53
|
+
val inflater = LayoutInflater.from(fragment.requireContext())
|
|
54
|
+
val binding = FormComponentSubmitBinding.inflate(inflater)
|
|
55
|
+
val order = 999 // Show as the right-most button
|
|
56
|
+
|
|
57
|
+
binding.formSubmit.apply {
|
|
58
|
+
text = data.title
|
|
59
|
+
setOnClickListener {
|
|
60
|
+
performSubmit()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
menu.removeItem(submitButtonItemId)
|
|
65
|
+
submitMenuItem = menu.add(Menu.NONE, submitButtonItemId, order, data.title).apply {
|
|
66
|
+
actionView = binding.root
|
|
67
|
+
setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private fun toggleSubmitButton(enable: Boolean) {
|
|
72
|
+
val layout = submitMenuItem?.actionView ?: return
|
|
73
|
+
|
|
74
|
+
FormComponentSubmitBinding.bind(layout).apply {
|
|
75
|
+
formSubmit.isEnabled = enable
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private fun performSubmit(): Boolean {
|
|
80
|
+
return replyTo("connect")
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@Serializable
|
|
84
|
+
data class MessageData(
|
|
85
|
+
val title: String
|
|
86
|
+
)
|
|
87
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
package <%= package_name %>.strada
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import android.view.LayoutInflater
|
|
5
|
+
import android.view.Menu
|
|
6
|
+
import android.view.MenuItem
|
|
7
|
+
import androidx.appcompat.widget.Toolbar
|
|
8
|
+
import androidx.fragment.app.Fragment
|
|
9
|
+
import dev.hotwire.strada.BridgeComponent
|
|
10
|
+
import dev.hotwire.strada.BridgeDelegate
|
|
11
|
+
import dev.hotwire.strada.Message
|
|
12
|
+
import <%= package_name %>.R
|
|
13
|
+
import <%= package_name %>.base.NavDestination
|
|
14
|
+
import <%= package_name %>.databinding.NavButtonComponentBinding
|
|
15
|
+
import kotlinx.serialization.Serializable
|
|
16
|
+
|
|
17
|
+
class NavButtonComponent(
|
|
18
|
+
name: String,
|
|
19
|
+
private val delegate: BridgeDelegate<NavDestination>
|
|
20
|
+
) : BridgeComponent<NavDestination>(name, delegate) {
|
|
21
|
+
|
|
22
|
+
private val navButtonItemId = 20
|
|
23
|
+
private var navButtonMenuItem: MenuItem? = null
|
|
24
|
+
private val fragment: Fragment
|
|
25
|
+
get() = delegate.destination.fragment
|
|
26
|
+
private val toolbar: Toolbar?
|
|
27
|
+
get() = fragment.view?.findViewById(R.id.toolbar)
|
|
28
|
+
|
|
29
|
+
override fun onReceive(message: Message) {
|
|
30
|
+
if (message.event == "connect") {
|
|
31
|
+
handleConnectEvent(message)
|
|
32
|
+
} else {
|
|
33
|
+
Log.w("TurboNative", "Unknown event for message: $message")
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private fun handleConnectEvent(message: Message) {
|
|
38
|
+
val data = message.data<MessageData>() ?: return
|
|
39
|
+
showToolbarButton(data)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private fun showToolbarButton(data: MessageData) {
|
|
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
|
|
47
|
+
|
|
48
|
+
binding.navButton.apply {
|
|
49
|
+
text = data.title
|
|
50
|
+
setOnClickListener {
|
|
51
|
+
performAction()
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
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)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private fun performAction(): Boolean {
|
|
63
|
+
return replyTo("connect")
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Serializable
|
|
67
|
+
data class MessageData(
|
|
68
|
+
val title: String
|
|
69
|
+
)
|
|
70
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
package <%= package_name %>.util
|
|
2
|
+
|
|
3
|
+
import android.webkit.WebView
|
|
4
|
+
import androidx.appcompat.widget.Toolbar
|
|
5
|
+
import androidx.core.content.ContextCompat
|
|
6
|
+
import dev.hotwire.strada.Strada
|
|
7
|
+
import <%= package_name %>.R
|
|
8
|
+
import <%= package_name %>.strada.bridgeComponentFactories
|
|
9
|
+
|
|
10
|
+
fun Toolbar.displayBackButtonAsCloseIcon() {
|
|
11
|
+
navigationIcon = ContextCompat.getDrawable(context, R.drawable.ic_close)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
val WebView.customUserAgent: String
|
|
15
|
+
get() {
|
|
16
|
+
val turboSubstring = "Turbo Native Android"
|
|
17
|
+
val stradaSubstring = Strada.userAgentSubstring(bridgeComponentFactories)
|
|
18
|
+
return "$turboSubstring; $stradaSubstring; ${settings.userAgentString}"
|
|
19
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
plugins {
|
|
2
2
|
id("com.android.application")
|
|
3
3
|
id("org.jetbrains.kotlin.android")
|
|
4
|
+
id("org.jetbrains.kotlin.plugin.serialization")
|
|
4
5
|
}
|
|
5
6
|
|
|
6
7
|
android {
|
|
@@ -25,13 +26,14 @@ android {
|
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
compileOptions {
|
|
28
|
-
sourceCompatibility = JavaVersion.
|
|
29
|
-
targetCompatibility = JavaVersion.
|
|
29
|
+
sourceCompatibility = JavaVersion.VERSION_17
|
|
30
|
+
targetCompatibility = JavaVersion.VERSION_17
|
|
30
31
|
}
|
|
31
32
|
kotlinOptions {
|
|
32
|
-
jvmTarget = "
|
|
33
|
+
jvmTarget = "17"
|
|
33
34
|
}
|
|
34
35
|
buildFeatures {
|
|
36
|
+
viewBinding = true
|
|
35
37
|
compose = true
|
|
36
38
|
}
|
|
37
39
|
composeOptions {
|
|
@@ -54,4 +56,8 @@ dependencies {
|
|
|
54
56
|
|
|
55
57
|
// Turbo Android
|
|
56
58
|
implementation("dev.hotwire:turbo:7.0.0")
|
|
59
|
+
|
|
60
|
+
// Strada
|
|
61
|
+
implementation("dev.hotwire:strada:1.0.0-beta2")
|
|
62
|
+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
|
57
63
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
{ "patterns": ["/recede_historical_location"], "properties": { "presentation": "pop" } },
|
|
7
7
|
{ "patterns": ["/resume_historical_location"], "properties": { "presentation": "none" } },
|
|
8
8
|
{ "patterns": ["^/$"], "properties": { "uri": "turbo://fragment/web/home", "presentation": "replace_all" } },
|
|
9
|
-
{ "patterns": ["/new$", "/edit$", "/signin$"], "properties": { "context": "modal", "uri": "turbo://fragment/web/modal" } },
|
|
9
|
+
{ "patterns": ["/new$", "/edit$", "/signin$", "/strada-form$"], "properties": { "context": "modal", "uri": "turbo://fragment/web/modal", "pull_to_refresh_enabled": false } },
|
|
10
10
|
{ "patterns": ["/numbers$"], "properties": { "uri": "turbo://fragment/numbers", "title": "Numbers" } }
|
|
11
11
|
]
|
|
12
12
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
android:id="@+id/form_submit"
|
|
10
|
+
android:layout_width="wrap_content"
|
|
11
|
+
android:layout_height="48dp" />
|
|
12
|
+
</FrameLayout>
|
|
@@ -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>
|
|
@@ -6,17 +6,20 @@ import androidx.browser.customtabs.CustomTabsIntent
|
|
|
6
6
|
import androidx.browser.customtabs.CustomTabsIntent.SHARE_STATE_ON
|
|
7
7
|
import androidx.navigation.NavOptions
|
|
8
8
|
import androidx.navigation.navOptions
|
|
9
|
+
import com.google.android.material.snackbar.Snackbar
|
|
10
|
+
import dev.hotwire.strada.BridgeDestination
|
|
9
11
|
import dev.hotwire.turbo.config.TurboPathConfigurationProperties
|
|
10
12
|
import dev.hotwire.turbo.config.context
|
|
11
13
|
import <%= package_name %>.R
|
|
12
14
|
import <%= package_name %>.util.BASE_URL
|
|
13
15
|
import dev.hotwire.turbo.nav.TurboNavDestination
|
|
16
|
+
import dev.hotwire.turbo.nav.TurboNavPresentationContext.DEFAULT
|
|
14
17
|
import dev.hotwire.turbo.nav.TurboNavPresentationContext.MODAL
|
|
15
18
|
|
|
16
|
-
interface NavDestination : TurboNavDestination {
|
|
19
|
+
interface NavDestination : TurboNavDestination, BridgeDestination {
|
|
17
20
|
override fun shouldNavigateTo(newLocation: String): Boolean {
|
|
18
21
|
return when (isNavigable(newLocation)) {
|
|
19
|
-
true -> true
|
|
22
|
+
true -> { displayNoticeMessage(newLocation); true }
|
|
20
23
|
else -> { launchCustomTab(newLocation); false }
|
|
21
24
|
}
|
|
22
25
|
}
|
|
@@ -31,10 +34,22 @@ interface NavDestination : TurboNavDestination {
|
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
override fun bridgeWebViewIsReady(): Boolean {
|
|
38
|
+
return session.isReady
|
|
39
|
+
}
|
|
40
|
+
|
|
34
41
|
private fun isNavigable(location: String): Boolean {
|
|
35
42
|
return location.startsWith(BASE_URL)
|
|
36
43
|
}
|
|
37
44
|
|
|
45
|
+
private fun displayNoticeMessage(location: String) {
|
|
46
|
+
var message = Uri.parse(location).getQueryParameter("notice")
|
|
47
|
+
|
|
48
|
+
if (pathProperties.context == DEFAULT && message != null) {
|
|
49
|
+
Snackbar.make(fragment.requireView(), message, Snackbar.LENGTH_SHORT).show()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
38
53
|
private fun launchCustomTab(location: String) {
|
|
39
54
|
val context = fragment.context ?: return
|
|
40
55
|
val color = context.getColor(R.color.white)
|
|
@@ -1,13 +1,51 @@
|
|
|
1
1
|
package <%= package_name %>.features.web
|
|
2
2
|
|
|
3
|
+
import android.os.Bundle
|
|
3
4
|
import android.view.View
|
|
5
|
+
import dev.hotwire.strada.BridgeDelegate
|
|
4
6
|
import <%= package_name %>.base.NavDestination
|
|
5
7
|
import dev.hotwire.turbo.fragments.TurboWebFragment
|
|
6
8
|
import dev.hotwire.turbo.nav.TurboNavGraphDestination
|
|
7
9
|
import <%= package_name %>.R
|
|
10
|
+
import <%= package_name %>.strada.bridgeComponentFactories
|
|
11
|
+
import dev.hotwire.turbo.views.TurboWebView
|
|
8
12
|
|
|
9
13
|
@TurboNavGraphDestination(uri = "turbo://fragment/web")
|
|
10
14
|
open class WebFragment : TurboWebFragment(), NavDestination {
|
|
15
|
+
private val bridgeDelegate by lazy {
|
|
16
|
+
BridgeDelegate(
|
|
17
|
+
location = location,
|
|
18
|
+
destination = this,
|
|
19
|
+
componentFactories = bridgeComponentFactories
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
24
|
+
super.onViewCreated(view, savedInstanceState)
|
|
25
|
+
viewLifecycleOwner.lifecycle.addObserver(bridgeDelegate)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
override fun onDestroyView() {
|
|
29
|
+
super.onDestroyView()
|
|
30
|
+
viewLifecycleOwner.lifecycle.removeObserver(bridgeDelegate)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override fun onColdBootPageStarted(location: String) {
|
|
34
|
+
bridgeDelegate.onColdBootPageStarted()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
override fun onColdBootPageCompleted(location: String) {
|
|
38
|
+
bridgeDelegate.onColdBootPageCompleted()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
override fun onWebViewAttached(webView: TurboWebView) {
|
|
42
|
+
bridgeDelegate.onWebViewAttached(webView)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
override fun onWebViewDetached(webView: TurboWebView) {
|
|
46
|
+
bridgeDelegate.onWebViewDetached()
|
|
47
|
+
}
|
|
48
|
+
|
|
11
49
|
override fun createErrorView(statusCode: Int): View {
|
|
12
50
|
return layoutInflater.inflate(R.layout.error_web, null)
|
|
13
51
|
}
|
|
@@ -3,7 +3,7 @@ package <%= package_name %>.features.web
|
|
|
3
3
|
import android.os.Bundle
|
|
4
4
|
import android.view.View
|
|
5
5
|
import dev.hotwire.turbo.nav.TurboNavGraphDestination
|
|
6
|
-
import <%= package_name %>.
|
|
6
|
+
import <%= package_name %>.util.displayBackButtonAsCloseIcon
|
|
7
7
|
|
|
8
8
|
@TurboNavGraphDestination(uri = "turbo://fragment/web/modal")
|
|
9
9
|
class WebModalFragment : WebFragment() {
|
|
@@ -12,6 +12,6 @@ class WebModalFragment : WebFragment() {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
private fun initToolbar() {
|
|
15
|
-
toolbarForNavigation()?.
|
|
15
|
+
toolbarForNavigation()?.displayBackButtonAsCloseIcon()
|
|
16
16
|
}
|
|
17
17
|
}
|