diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2f5209785..31e85cf5f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -138,6 +138,18 @@
android:scheme="codewallet" />
+
+
+
+
+
+
+
+
+
()
trace("home rendered")
- HomeScreen(vm, cashLink, requestPayload)
+ HomeScreen(vm, cashLink, request)
OnScreenResult { result ->
when (result) {
diff --git a/app/src/main/java/com/getcode/ui/components/AuthCheck.kt b/app/src/main/java/com/getcode/ui/components/AuthCheck.kt
index 4b3821814..6734bbe67 100644
--- a/app/src/main/java/com/getcode/ui/components/AuthCheck.kt
+++ b/app/src/main/java/com/getcode/ui/components/AuthCheck.kt
@@ -85,6 +85,7 @@ fun AuthCheck(
when (result.type) {
is DeeplinkHandler.Type.Login -> true
is DeeplinkHandler.Type.Cash,
+ is DeeplinkHandler.Type.Tip,
is DeeplinkHandler.Type.Sdk -> {
val hasAuth = state.isAuthenticated == true
if (!hasAuth) {
diff --git a/app/src/main/java/com/getcode/util/DeeplinkHandler.kt b/app/src/main/java/com/getcode/util/DeeplinkHandler.kt
index 813209b9e..db994f286 100644
--- a/app/src/main/java/com/getcode/util/DeeplinkHandler.kt
+++ b/app/src/main/java/com/getcode/util/DeeplinkHandler.kt
@@ -3,10 +3,12 @@ package com.getcode.util
import android.content.Intent
import android.net.Uri
import cafe.adriel.voyager.core.screen.Screen
+import com.getcode.models.DeepLinkRequest
import com.getcode.navigation.screens.HomeScreen
import com.getcode.navigation.screens.LoginScreen
import com.getcode.network.repository.urlDecode
import com.getcode.utils.TraceType
+import com.getcode.utils.base64EncodedData
import com.getcode.utils.trace
import kotlinx.coroutines.flow.MutableStateFlow
import timber.log.Timber
@@ -61,9 +63,18 @@ class DeeplinkHandler @Inject constructor() {
is Type.Sdk -> {
Timber.d("sdk=${type.payload}")
+ val request = type.payload?.base64EncodedData()?.let { DeepLinkRequest.from(it) }
DeeplinkResult(
type,
- listOf(HomeScreen(requestPayload = type.payload)),
+ listOf(HomeScreen(request = request)),
+ )
+ }
+
+ is Type.Tip -> {
+ Timber.d("tipcard for ${type.username} on ${type.platform}")
+ DeeplinkResult(
+ type,
+ listOf(HomeScreen(request = DeepLinkRequest.fromTipCardUsername(type.platform, type.username))),
)
}
@@ -72,20 +83,29 @@ class DeeplinkHandler @Inject constructor() {
}
private val Uri.deeplinkType: Type
- get() = when (val segment = lastPathSegment) {
- "login" -> {
- var entropy = fragments[Key.entropy]
- if (entropy == null) {
- entropy = this.getQueryParameter("data")
+ get() {
+ // check for tipcard URLs
+ val components = pathSegments
+ if (components.count() == 2 && components[0] == "x" && components[1].isNotEmpty()) {
+ return Type.Tip(components[0], components[1])
+ }
+
+ return when (val segment = lastPathSegment) {
+ "login" -> {
+ var entropy = fragments[Key.entropy]
+ if (entropy == null) {
+ entropy = this.getQueryParameter("data")
+ }
+
+ Type.Login(entropy.also { Timber.d("entropy=$it") })
}
- Type.Login(entropy.also { Timber.d("entropy=$it") })
- }
+ "cash", "c" -> Type.Cash(fragments[Key.entropy])
- "cash", "c" -> Type.Cash(fragments[Key.entropy])
- // support all variations of SDK request triggers
- in Type.Sdk.regex -> Type.Sdk(fragments[Key.payload]?.urlDecode())
- else -> Type.Unknown(path = segment)
+ // support all variations of SDK request triggers
+ in Type.Sdk.regex -> Type.Sdk(fragments[Key.payload]?.urlDecode())
+ else -> Type.Unknown(path = segment)
+ }
}
private val Uri.fragments: Map
@@ -104,6 +124,7 @@ class DeeplinkHandler @Inject constructor() {
sealed interface Type {
data class Login(val link: String?) : Type
data class Cash(val link: String?) : Type
+ data class Tip(val platform: String, val username: String): Type
data class Sdk(val payload: String?) : Type {
companion object {
val regex = Regex("^(login|payment|tip)?-?request-(modal|page)-(mobile|desktop)\$")
diff --git a/app/src/main/java/com/getcode/view/main/home/HomeScan.kt b/app/src/main/java/com/getcode/view/main/home/HomeScan.kt
index 455cc3c9b..89d20399d 100644
--- a/app/src/main/java/com/getcode/view/main/home/HomeScan.kt
+++ b/app/src/main/java/com/getcode/view/main/home/HomeScan.kt
@@ -31,18 +31,17 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
-import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment.Companion.BottomCenter
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
-import cafe.adriel.voyager.core.stack.StackEvent
import com.getcode.LocalBiometricsState
import com.getcode.R
import com.getcode.manager.TopBarManager
import com.getcode.models.Bill
+import com.getcode.models.DeepLinkRequest
import com.getcode.navigation.core.CodeNavigator
import com.getcode.navigation.core.LocalCodeNavigator
import com.getcode.navigation.screens.AccountModal
@@ -69,7 +68,6 @@ import com.getcode.view.main.home.components.PermissionsBlockingView
import com.getcode.view.main.home.components.ReceivedKinConfirmation
import com.getcode.view.main.home.components.TipConfirmation
import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@@ -89,8 +87,8 @@ enum class HomeBottomSheet {
@Composable
fun HomeScreen(
homeViewModel: HomeViewModel,
- deepLink: String? = null,
- requestPayload: String? = null,
+ cashLink: String? = null,
+ request: DeepLinkRequest? = null,
) {
val navigator = LocalCodeNavigator.current
val dataState by homeViewModel.uiFlow.collectAsState()
@@ -108,8 +106,8 @@ fun HomeScreen(
HomeScan(
homeViewModel = homeViewModel,
dataState = dataState,
- deepLink = deepLink,
- requestPayload = requestPayload,
+ cashLink = cashLink,
+ request = request,
)
val context = LocalContext.current
@@ -136,8 +134,8 @@ fun HomeScreen(
private fun HomeScan(
homeViewModel: HomeViewModel,
dataState: HomeUiModel,
- deepLink: String?,
- requestPayload: String?,
+ cashLink: String?,
+ request: DeepLinkRequest?,
) {
val navigator = LocalCodeNavigator.current
val scope = rememberCoroutineScope()
@@ -159,28 +157,28 @@ private fun HomeScan(
val focusManager = LocalFocusManager.current
- var deepLinkSaved by remember(deepLink) {
- mutableStateOf(deepLink)
+ var cashLinkSaved by remember(cashLink) {
+ mutableStateOf(cashLink)
}
- var requestPayloadSaved by remember(requestPayload) {
- mutableStateOf(requestPayload)
+ var requestPayloadSaved by remember(request) {
+ mutableStateOf(request)
}
val biometricsState = LocalBiometricsState.current
- LaunchedEffect(biometricsState, previewing, dataState.balance, deepLinkSaved, requestPayloadSaved) {
+ LaunchedEffect(biometricsState, previewing, dataState.balance, cashLinkSaved, requestPayloadSaved) {
if (previewing) {
focusManager.clearFocus()
}
- if (biometricsState.passed && !deepLinkSaved.isNullOrBlank()) {
- homeViewModel.openCashLink(deepLink)
- deepLinkSaved = null
+ if (biometricsState.passed && !cashLinkSaved.isNullOrBlank()) {
+ homeViewModel.openCashLink(cashLink)
+ cashLinkSaved = null
}
- if (biometricsState.passed && !requestPayloadSaved.isNullOrBlank() && dataState.balance != null) {
+ if (biometricsState.passed && requestPayloadSaved != null && dataState.balance != null) {
delay(500.milliseconds)
- homeViewModel.handleRequest(requestPayload)
+ homeViewModel.handleRequest(request)
requestPayloadSaved = null
}
}
diff --git a/app/src/main/java/com/getcode/view/main/home/HomeViewModel.kt b/app/src/main/java/com/getcode/view/main/home/HomeViewModel.kt
index 9d13dfb94..0586737b0 100644
--- a/app/src/main/java/com/getcode/view/main/home/HomeViewModel.kt
+++ b/app/src/main/java/com/getcode/view/main/home/HomeViewModel.kt
@@ -1430,9 +1430,7 @@ class HomeViewModel @Inject constructor(
}
}
- fun handleRequest(bytes: String?) {
- val data = bytes?.base64EncodedData() ?: return
- val request = DeepLinkRequest.from(data)
+ fun handleRequest(request: DeepLinkRequest?) {
if (request != null) {
if (request.paymentRequest != null) {
viewModelScope.launch {