Skip to content

Commit 50ac9ad

Browse files
committed
feat(contacts): add phone-number send beta flag and gate send feature behind it
Add FeatureFlag.PhoneNumberSend as a persistent beta toggle that combines with the server-side enablePhoneNumberSend user flag. When the effective value is false, the contact permission screen is skipped during onboarding and the Send button is hidden from the scanner nav bar. The user flag is surfaced as read-only in the user flags editor. Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 21bf35c commit 50ac9ad

27 files changed

Lines changed: 127 additions & 14 deletions

File tree

apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/AppScreenContent.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ fun appEntryProvider(
9696

9797
// Sheets (inner content — wrapped in Main.Sheet by navigateTo())
9898
annotatedEntry<AppRoute.Sheets.Give> { key -> CashScreen(key.mint, key.fromTokenInfo) }
99+
annotatedEntry<AppRoute.Sheets.Send> { }
99100
annotatedEntry<AppRoute.Sheets.TokenSelection> { key -> TokenSelectScreen(key.purpose) }
100101
annotatedEntry<AppRoute.Sheets.Wallet> { BalanceScreen() }
101102
annotatedEntry<AppRoute.Sheets.ShareApp> { ShareAppScreen() }

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/AppRoute.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ sealed interface AppRoute : NavKey, Parcelable {
110110
data class TokenSelection(val purpose: TokenPurpose) : Sheets
111111
@Serializable
112112
data class Give(val mint: Mint? = null, val fromTokenInfo: Boolean = false) : Sheets
113+
114+
@Serializable
115+
data object Send: Sheets
113116
@Serializable
114117
data object Wallet : Sheets
115118
@Serializable

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/navigation/NavBarButton.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ enum class NavBarButton {
44
Give,
55
Wallet,
66
Discover,
7+
Send,
78
;
89

910
companion object {
10-
val defaultOrder = listOf(Give, Wallet, Discover)
11+
val defaultOrder = listOf(Discover, Give, Send, Wallet,)
1112
}
1213
}

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/ui/NavigationBar.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,14 @@ fun NavigationBar(
132132
badgeCount = 0,
133133
onClick = { onButtonClick(NavBarButton.Discover) }
134134
)
135+
136+
NavBarButton.Send -> BottomBarAction(
137+
modifier = buttonModifier,
138+
label = stringResource(R.string.action_send),
139+
painter = painterResource(R.drawable.ic_send_outlined),
140+
badgeCount = 0,
141+
onClick = { onButtonClick(NavBarButton.Send) }
142+
)
135143
}
136144
}
137145
}

apps/flipcash/core/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@
376376
<string name="subtitle_youReceive">You Receive</string>
377377

378378
<string name="action_give">Give</string>
379+
<string name="action_send">Send</string>
379380
<string name="title_transactionHistory">Transaction History</string>
380381
<string name="action_viewTransactionHistory">Transaction History</string>
381382
<string name="subtitle_marketcap">Market Cap</string>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
build/
2+
.gradle/
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
plugins {
2+
alias(libs.plugins.flipcash.android.feature)
3+
}
4+
5+
android {
6+
namespace = "${Gradle.flipcashNamespace}.features.directsend"
7+
}
8+
9+
dependencies {
10+
testImplementation(kotlin("test"))
11+
testImplementation(libs.bundles.unit.testing)
12+
testImplementation(libs.mockito.kotlin)
13+
testImplementation(libs.robolectric)
14+
testImplementation(project(":libs:test-utils"))
15+
16+
implementation(libs.kotlin.stdlib)
17+
implementation(project(":apps:flipcash:shared:analytics"))
18+
implementation(project(":apps:flipcash:shared:session"))
19+
implementation(project(":apps:flipcash:shared:tokens"))
20+
implementation(project(":libs:datetime"))
21+
implementation(project(":libs:logging"))
22+
implementation(project(":libs:messaging"))
23+
implementation(project(":libs:permissions:bindings"))
24+
}

apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/accesskey/AccessKeyScreen.kt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,11 @@ import com.flipcash.app.login.internal.AccessKeyScreen
1212
import com.flipcash.features.login.R
1313
import com.getcode.navigation.core.LocalCodeNavigator
1414
import com.getcode.ui.components.AppBarWithTitle
15-
import com.getcode.util.permissions.rememberNotificationPermission
1615

1716
@Composable
1817
fun AccessKeyScreen() {
1918
val viewModel = hiltViewModel<LoginAccessKeyViewModel>()
2019
val navigator = LocalCodeNavigator.current
21-
val notificationPermissions = rememberNotificationPermission()
2220
Column(
2321
modifier = Modifier.fillMaxSize(),
2422
horizontalAlignment = Alignment.CenterHorizontally,
@@ -33,11 +31,21 @@ fun AccessKeyScreen() {
3331
if (requiresIap) {
3432
navigator.push(AppRoute.Onboarding.Purchase())
3533
} else {
36-
if (notificationPermissions.isPermanentlyDenied) {
37-
navigator.push(AppRoute.Onboarding.NotificationPermissionRationale(true))
34+
val target = if (viewModel.isPhoneNumberSendEnabled) {
35+
AppRoute.Onboarding.ContactPermission(postCreate = true)
3836
} else {
39-
navigator.push(AppRoute.Onboarding.NotificationPermission(true))
37+
AppRoute.Onboarding.NotificationPermission(postCreate = true)
4038
}
39+
40+
navigator.push(
41+
AppRoute.Verification(
42+
origin = AppRoute.Onboarding.AccessKey,
43+
includePhone = true,
44+
includeEmail = false,
45+
target = target,
46+
fullScreen = true,
47+
)
48+
)
4149
}
4250
}
4351
}

apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/accesskey/LoginAccessKeyViewModel.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import com.flipcash.app.analytics.Button
66
import com.flipcash.app.analytics.FlipcashAnalyticsService
77
import com.flipcash.app.auth.AuthManager
88
import com.flipcash.app.core.storage.MediaSaver
9+
import com.flipcash.app.featureflags.FeatureFlag
10+
import com.flipcash.app.featureflags.FeatureFlagController
911
import com.flipcash.app.userflags.UserFlagsCoordinator
1012
import com.flipcash.services.user.UserManager
1113
import com.getcode.libs.qr.QRCodeGenerator
@@ -25,10 +27,15 @@ class LoginAccessKeyViewModel @Inject constructor(
2527
mediaSaver: MediaSaver,
2628
userManager: UserManager,
2729
private val userFlags: UserFlagsCoordinator,
30+
private val featureFlags: FeatureFlagController,
2831
private val authManager: AuthManager,
2932
private val analytics: FlipcashAnalyticsService,
3033
): BaseAccessKeyViewModel(resources, mnemonicManager, mediaSaver, userManager, qrCodeGenerator) {
3134

35+
val isPhoneNumberSendEnabled: Boolean
36+
get() = featureFlags.observe(FeatureFlag.PhoneNumberSend).value ||
37+
userFlags.resolvedFlags.value.enablePhoneNumberSend.effectiveValue
38+
3239
suspend fun onWroteDownInstead(): Result<Boolean> {
3340
trackButton(Button.WroteAccessKey)
3441
uiFlow.update { it.copy(skipState = LoadingSuccessState(loading = true)) }

apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/ScannerDecorItem.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ sealed class ScannerDecorItem(val screen: AppRoute) {
1010
data object Menu : ScannerDecorItem(AppRoute.Sheets.Menu)
1111
data object Logo: ScannerDecorItem(AppRoute.Sheets.ShareApp)
1212
data object Discover: ScannerDecorItem(AppRoute.Token.Discovery)
13+
data object Send: ScannerDecorItem(AppRoute.Sheets.Send)
1314
}

0 commit comments

Comments
 (0)