turbo-native-initializer 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- 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 +59 -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 +59 -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: eb141a7b5a2949e9b65bdf004831663bf43377c858891129cd3a6959d73292f2
|
4
|
+
data.tar.gz: d8636b8bbf702f0483dc737fd49182a887d610d614ae44779fe7191d9391f1c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 912afab330194fcaa45897f1f69e92aff243287ada0032322e704163bd6f91be9ab2055df64e635c1ea998de937af603b5b98e8f0add070f7a4507be4d4906f6
|
7
|
+
data.tar.gz: bf237f3895adbc21f9095ea00ca016ac205ecad78d17da223a5227fa31afe2d207fd69c6c358c278654f897be06a6f50b2909a88618d3b8e342be9e25c330f47
|
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
|
}
|