Skip to content

Commit 13173d2

Browse files
authored
Merge pull request #491 from code-payments/feat/buy-kin-kado-webview
feat: move kado flow into app via webview modal
2 parents 8f4a8c9 + 35b3682 commit 13173d2

24 files changed

Lines changed: 405 additions & 106 deletions

File tree

api/src/main/java/com/getcode/model/PrefBool.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ data class PrefBool(
1414
sealed interface InternalRouting
1515
sealed interface AppSetting
1616
sealed interface BetaFlag
17+
sealed interface DevSetting
1718

1819

1920
sealed class PrefsBool(val value: String) {
@@ -31,6 +32,9 @@ sealed class PrefsBool(val value: String) {
3132
data object CAMERA_START_BY_DEFAULT: PrefsBool("camera_start_default"), AppSetting
3233
data object REQUIRE_BIOMETRICS: PrefsBool("require_biometrics"), AppSetting
3334

35+
// dev settings
36+
data object ESTABLISH_CODE_RELATIONSHIP : PrefsBool("establish_code_relationship_enabled"), DevSetting
37+
3438
// beta flags
3539
data object BUCKET_DEBUGGER_ENABLED: PrefsBool("debug_buckets"), BetaFlag
3640
data object VIBRATE_ON_SCAN: PrefsBool("vibrate_on_scan"), BetaFlag
@@ -40,7 +44,7 @@ sealed class PrefsBool(val value: String) {
4044
data object GIVE_REQUESTS_ENABLED: PrefsBool("give_requests_enabled"), BetaFlag
4145
data object BUY_MODULE_ENABLED : PrefsBool("buy_kin_enabled"), BetaFlag
4246

43-
data object ESTABLISH_CODE_RELATIONSHIP : PrefsBool("establish_code_relationship_enabled"), BetaFlag
47+
4448
data object CHAT_UNSUB_ENABLED: PrefsBool("chat_unsub_enabled"), BetaFlag
4549
data object TIPS_ENABLED : PrefsBool("tips_enabled"), BetaFlag
4650
data object TIPS_CHAT_ENABLED: PrefsBool("tips_chat_enabled"), BetaFlag

api/src/main/java/com/getcode/network/repository/BetaFlagsRepository.kt

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ data class BetaOptions(
1313
val displayErrors: Boolean,
1414
val giveRequestsEnabled: Boolean,
1515
val buyModuleEnabled: Boolean,
16-
val establishCodeRelationship: Boolean,
1716
val chatUnsubEnabled: Boolean,
1817
val tipsEnabled: Boolean,
1918
val tipsChatEnabled: Boolean,
@@ -30,7 +29,6 @@ data class BetaOptions(
3029
displayErrors = false,
3130
giveRequestsEnabled = false,
3231
buyModuleEnabled = true,
33-
establishCodeRelationship = false,
3432
chatUnsubEnabled = false,
3533
tipsEnabled = false,
3634
tipsChatEnabled = false,
@@ -43,6 +41,8 @@ data class BetaOptions(
4341
class BetaFlagsRepository @Inject constructor(
4442
private val prefRepository: PrefRepository,
4543
) {
44+
suspend fun isEnabled() = prefRepository.get(PrefsBool.IS_DEBUG_ALLOWED, false)
45+
4646
fun enableBeta(allowed: Boolean) {
4747
prefRepository.set(
4848
PrefsBool.IS_DEBUG_ALLOWED,
@@ -62,7 +62,6 @@ class BetaFlagsRepository @Inject constructor(
6262
observeBetaFlag(PrefsBool.LOG_SCAN_TIMES, default = defaults.debugScanTimesEnabled),
6363
observeBetaFlag(PrefsBool.GIVE_REQUESTS_ENABLED, default = defaults.giveRequestsEnabled),
6464
observeBetaFlag(PrefsBool.BUY_MODULE_ENABLED, default = defaults.buyModuleEnabled),
65-
observeBetaFlag(PrefsBool.ESTABLISH_CODE_RELATIONSHIP, default = defaults.establishCodeRelationship),
6665
observeBetaFlag(PrefsBool.CHAT_UNSUB_ENABLED, default = defaults.chatUnsubEnabled),
6766
observeBetaFlag(PrefsBool.TIPS_ENABLED, default = defaults.tipsEnabled),
6867
observeBetaFlag(PrefsBool.TIPS_CHAT_ENABLED, default = defaults.tipsChatEnabled),
@@ -77,13 +76,12 @@ class BetaFlagsRepository @Inject constructor(
7776
debugScanTimesEnabled = it[3],
7877
giveRequestsEnabled = it[4],
7978
buyModuleEnabled = it[5],
80-
establishCodeRelationship = it[6],
81-
chatUnsubEnabled = it[7],
82-
tipsEnabled = it[8],
83-
tipsChatEnabled = it[9],
84-
tipsChatCashEnabled = it[10],
85-
balanceCurrencySelectionEnabled = it[11],
86-
displayErrors = it[12],
79+
chatUnsubEnabled = it[6],
80+
tipsEnabled = it[7],
81+
tipsChatEnabled = it[8],
82+
tipsChatCashEnabled = it[9],
83+
balanceCurrencySelectionEnabled = it[10],
84+
displayErrors = it[11],
8785
)
8886
}
8987
}

app/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ dependencies {
131131

132132
//hilt dependency injection
133133
implementation(Libs.hilt)
134+
implementation("androidx.webkit:webkit:1.11.0")
134135
kapt(Libs.hilt_android_compiler)
135136
kapt(Libs.hilt_compiler)
136137
androidTestImplementation(Libs.hilt)
@@ -159,6 +160,7 @@ dependencies {
159160
implementation(Libs.compose_voyager_navigation_transitions)
160161
implementation(Libs.compose_voyager_navigation_bottomsheet)
161162
implementation(Libs.compose_voyager_navigation_hilt)
163+
implementation(Libs.compose_webview)
162164

163165
implementation(Libs.androidx_biometrics)
164166

@@ -192,6 +194,10 @@ dependencies {
192194
implementation(Libs.zxing)
193195
implementation(Libs.mixpanel)
194196

197+
implementation(Libs.retrofit)
198+
implementation(Libs.retrofit_converter)
199+
implementation(Libs.okhttp_logging_interceptor)
200+
195201
implementation(Libs.cloudy)
196202

197203
androidTestImplementation(Libs.androidx_test_runner)

app/proguard-rules.pro

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@
6161
-keep class org.json.** { *; }
6262
-keepclassmembers class org.json.** { *; }
6363

64+
# Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items).
65+
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
66+
-keep,allowobfuscation,allowshrinking class retrofit2.Response
67+
68+
# With R8 full mode generic signatures are stripped for classes that are not
69+
# kept. Suspend functions are wrapped in continuations where the type argument
70+
# is used.
71+
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
72+
6473
# libsodium
6574
-keep class com.ionspin.kotlin.crypto.** { *; }
6675
-keep class com.sun.jna.** { *; }

app/src/main/java/com/getcode/CodeAppState.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ package com.getcode
33
import androidx.compose.material.ScaffoldState
44
import androidx.compose.material.rememberScaffoldState
55
import androidx.compose.runtime.Composable
6-
import androidx.compose.runtime.MutableState
76
import androidx.compose.runtime.Stable
87
import androidx.compose.runtime.remember
98
import androidx.compose.runtime.rememberCoroutineScope
10-
import androidx.lifecycle.MutableLiveData
119
import com.getcode.manager.BottomBarManager
1210
import com.getcode.manager.TopBarManager
1311
import com.getcode.navigation.core.CodeNavigator
@@ -87,7 +85,6 @@ class CodeAppState(
8785
val topBarMessage = MutableStateFlow<TopBarManager.TopBarMessage?>(null)
8886
val bottomBarMessage = MutableStateFlow<BottomBarManager.BottomBarMessage?>(null)
8987

90-
9188
fun upPress() {
9289
if (navigator.pop().not()) {
9390
navigator.hide()
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.getcode.api
2+
3+
import com.google.gson.JsonObject
4+
import okhttp3.ResponseBody
5+
import retrofit2.Response
6+
import retrofit2.http.GET
7+
import retrofit2.http.Path
8+
9+
interface KadoApi {
10+
@GET("v2/public/orders/{orderId}")
11+
suspend fun getOrderStatus(@Path("orderId") orderId: String): Response<ResponseBody>
12+
}

app/src/main/java/com/getcode/inject/ApiModule.kt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.getcode.BuildConfig
55
import com.getcode.R
66
import com.getcode.analytics.AnalyticsService
77
import com.getcode.annotations.DevManagedChannel
8+
import com.getcode.api.KadoApi
89
import com.getcode.manager.MnemonicManager
910
import com.getcode.model.CurrencyCode
1011
import com.getcode.model.PrefsString
@@ -44,8 +45,14 @@ import io.grpc.android.AndroidChannelBuilder
4445
import io.reactivex.rxjava3.core.Scheduler
4546
import io.reactivex.rxjava3.disposables.CompositeDisposable
4647
import io.reactivex.rxjava3.schedulers.Schedulers
48+
import okhttp3.OkHttpClient
49+
import okhttp3.logging.HttpLoggingInterceptor
4750
import org.kin.sdk.base.network.api.agora.OkHttpChannelBuilderForcedTls12
51+
import retrofit2.Retrofit
52+
import retrofit2.converter.gson.GsonConverterFactory
53+
import retrofit2.create
4854
import java.util.concurrent.TimeUnit
55+
import javax.inject.Named
4956
import javax.inject.Singleton
5057

5158
@Module
@@ -109,6 +116,37 @@ object ApiModule {
109116
return MixpanelAPI.getInstance(context, BuildConfig.MIXPANEL_API_KEY)
110117
}
111118

119+
@Singleton
120+
@Provides
121+
fun providesHttpLoggingInterceptor() = HttpLoggingInterceptor()
122+
.apply {
123+
level = HttpLoggingInterceptor.Level.BODY
124+
}
125+
126+
@Singleton
127+
@Provides
128+
fun providesOkHttpClient(httpLoggingInterceptor: HttpLoggingInterceptor): OkHttpClient =
129+
OkHttpClient
130+
.Builder()
131+
.addInterceptor(httpLoggingInterceptor)
132+
.build()
133+
134+
@Singleton
135+
@Provides
136+
@Named("kado-retrofit")
137+
fun provideKadoRetrofit(okHttpClient: OkHttpClient): Retrofit = Retrofit.Builder()
138+
.addConverterFactory(GsonConverterFactory.create())
139+
.baseUrl("https://api.kado.money/")
140+
.client(okHttpClient)
141+
.build()
142+
143+
@Singleton
144+
@Provides
145+
fun providesKadoApi(
146+
@Named("kado-retrofit")
147+
retrofit: Retrofit
148+
): KadoApi = retrofit.create(KadoApi::class.java)
149+
112150
@Singleton
113151
@Provides
114152
fun provideBalanceRepository(

app/src/main/java/com/getcode/mapper/AppSettingsMapper.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import javax.inject.Inject
1010

1111
class AppSettingsMapper @Inject constructor(
1212
private val biometricManager: BiometricManager,
13-
): Mapper<AppSettings, List<SettingItem>> {
14-
override fun map(from: AppSettings): List<SettingItem> {
13+
): SuspendMapper<AppSettings, List<SettingItem>> {
14+
override suspend fun map(from: AppSettings): List<SettingItem> {
1515

1616
return APP_SETTINGS.map { setting ->
1717
when (setting) {

app/src/main/java/com/getcode/navigation/screens/MainScreens.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package com.getcode.navigation.screens
22

33
import androidx.compose.runtime.Composable
4-
import androidx.compose.ui.platform.LocalLifecycleOwner
54
import androidx.compose.ui.res.stringResource
65
import androidx.lifecycle.Lifecycle
76
import cafe.adriel.voyager.core.screen.ScreenKey
87
import cafe.adriel.voyager.core.screen.uniqueScreenKey
98
import cafe.adriel.voyager.hilt.getViewModel
109
import com.getcode.R
11-
import com.getcode.analytics.AnalyticsManager
1210
import com.getcode.model.KinAmount
1311
import com.getcode.navigation.core.LocalCodeNavigator
1412
import com.getcode.ui.utils.RepeatOnLifecycle
@@ -186,6 +184,7 @@ data object ShareDownloadLinkModal : MainGraph, ModalRoot {
186184
}
187185
}
188186

187+
189188
@Composable
190189
fun <T> AppScreen.OnScreenResult(block: (T) -> Unit) {
191190
RepeatOnLifecycle(

app/src/main/java/com/getcode/navigation/screens/ModalScreens.kt

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
package com.getcode.navigation.screens
22

3+
import android.webkit.JavascriptInterface
4+
import androidx.compose.foundation.isSystemInDarkTheme
5+
import androidx.compose.foundation.layout.fillMaxSize
6+
import androidx.compose.foundation.layout.imePadding
37
import androidx.compose.runtime.Composable
48
import androidx.compose.runtime.LaunchedEffect
9+
import androidx.compose.ui.Alignment
10+
import androidx.compose.ui.Modifier
11+
import androidx.compose.ui.graphics.Color
512
import androidx.compose.ui.res.stringResource
613
import cafe.adriel.voyager.core.screen.ScreenKey
714
import cafe.adriel.voyager.core.screen.uniqueScreenKey
815
import cafe.adriel.voyager.hilt.getViewModel
916
import com.getcode.R
1017
import com.getcode.navigation.core.LocalCodeNavigator
18+
import com.getcode.theme.CodeTheme
19+
import com.getcode.ui.components.CodeCircularProgressIndicator
20+
import com.getcode.ui.components.SheetTitleDefaults
1121
import com.getcode.ui.utils.getActivityScopedViewModel
1222
import com.getcode.ui.utils.getStackScopedViewModel
23+
import com.getcode.ui.utils.toAGColor
1324
import com.getcode.view.login.PhoneConfirm
1425
import com.getcode.view.login.PhoneVerify
1526
import com.getcode.view.login.PhoneVerifyViewModel
@@ -29,10 +40,15 @@ import com.getcode.view.main.getKin.BuyAndSellKin
2940
import com.getcode.view.main.getKin.BuyKinScreen
3041
import com.getcode.view.main.getKin.GetKinSheet
3142
import com.getcode.view.main.getKin.GetKinSheetViewModel
43+
import com.getcode.view.main.getKin.KadoWebScreen
44+
import com.getcode.view.main.tip.ConnectAccountScreen
3245
import com.getcode.view.main.tip.EnterTipScreen
3346
import com.getcode.view.main.tip.IdentityConnectionReason
34-
import com.getcode.view.main.tip.ConnectAccountScreen
3547
import com.getcode.view.main.tip.TipConnectViewModel
48+
import com.kevinnzou.web.LoadingState
49+
import com.kevinnzou.web.WebView
50+
import com.kevinnzou.web.rememberWebViewNavigator
51+
import com.kevinnzou.web.rememberWebViewState
3652
import kotlinx.parcelize.IgnoredOnParcel
3753
import kotlinx.parcelize.Parcelize
3854

@@ -261,7 +277,8 @@ data object DeleteConfirmationScreen : MainGraph, ModalContent {
261277
}
262278

263279
@Parcelize
264-
data class CurrencySelectionModal(val kind: CurrencySelectKind = CurrencySelectKind.Entry) : MainGraph, ModalContent {
280+
data class CurrencySelectionModal(val kind: CurrencySelectKind = CurrencySelectKind.Entry) :
281+
MainGraph, ModalContent {
265282
@IgnoredOnParcel
266283
override val key: ScreenKey = uniqueScreenKey
267284

@@ -309,11 +326,7 @@ data class BuyMoreKinModal(
309326
BuyKinScreen(
310327
viewModel = getViewModel(),
311328
onRedirected = {
312-
if (showClose) {
313-
navigator.hide()
314-
} else {
315-
navigator.popAll()
316-
}
329+
navigator.hide()
317330
}
318331
)
319332
}
@@ -346,6 +359,46 @@ data class BuyMoreKinModal(
346359
}
347360
}
348361

362+
@Parcelize
363+
data class KadoWebScreen(val url: String) : MainGraph, ModalContent {
364+
365+
@IgnoredOnParcel
366+
override val key: ScreenKey = uniqueScreenKey
367+
368+
override val name: String
369+
@Composable get() = stringResource(id = R.string.action_buyMoreKin)
370+
371+
@Composable
372+
override fun Content() {
373+
val state = rememberWebViewState(url = url)
374+
val navigator = LocalCodeNavigator.current
375+
val webNavigator = rememberWebViewNavigator()
376+
ModalContainer(
377+
modalColor = if (isSystemInDarkTheme()) {
378+
Color(0xFF0A121F)
379+
} else {
380+
CodeTheme.colors.background
381+
},
382+
backButtonEnabled = { true },
383+
backButton = { SheetTitleDefaults.CloseButton() },
384+
onBackClicked = { navigator.hide() },
385+
closeButtonEnabled = { true },
386+
closeButton = { SheetTitleDefaults.RefreshButton() },
387+
onCloseClicked = { webNavigator.reload() }
388+
) {
389+
KadoWebScreen(viewModel = getViewModel(), state = state, webNavigator = webNavigator)
390+
}
391+
}
392+
393+
class BuyKinWebInterface {
394+
395+
@JavascriptInterface
396+
fun handleMessage(message: String) {
397+
println("KADO BUY KIN MESSAGE :: $message")
398+
}
399+
}
400+
}
401+
349402
@Parcelize
350403
data class EnterTipModal(val isInChat: Boolean = false) : MainGraph, ModalRoot {
351404

0 commit comments

Comments
 (0)