Skip to content

Commit 2fff9ef

Browse files
committed
fix(send): sync contacts on in-list grant and fix on-Flipcash scroll offset
- Keep the contacts permission gate visible until the full sync completes (via ContactSyncComplete) instead of advancing the moment device contacts land. Advancing early rendered the list before on-Flipcash contacts were fetched, then prepended them above the viewport once they arrived — pushing them offscreen so the user had to scroll up. - Dispatch ContactsGranted when the user allows access from the in-list rationale card so the contact sync fires, mirroring the gate. - Hoist ContactAccessHandle out of ContactList; the screen now owns the single handle and wires both Granted (sync) and Picked (add contacts). Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 4de0738 commit 2fff9ef

3 files changed

Lines changed: 25 additions & 8 deletions

File tree

apps/flipcash/features/direct-send/src/main/kotlin/com/flipcash/app/directsend/internal/screens/ContactListScreen.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import androidx.compose.ui.Modifier
2020
import androidx.compose.ui.platform.testTag
2121
import androidx.compose.ui.res.stringResource
2222
import androidx.lifecycle.compose.collectAsStateWithLifecycle
23+
import com.flipcash.app.analytics.Button
2324
import com.flipcash.app.core.AppRoute
2425
import com.flipcash.app.core.send.SendResult
2526
import com.flipcash.app.core.send.SendStep
@@ -28,6 +29,7 @@ import com.flipcash.app.directsend.internal.screens.components.ContactList
2829
import com.flipcash.app.permissions.ContactAccessResult
2930
import com.flipcash.app.permissions.rememberContactAccessHandle
3031
import com.flipcash.features.directsend.R
32+
import com.getcode.libs.analytics.LocalAnalytics
3133
import com.getcode.navigation.flow.LocalOuterCodeNavigator
3234
import com.getcode.navigation.flow.flowSharedViewModel
3335
import com.getcode.navigation.flow.rememberFlowNavigator
@@ -47,6 +49,7 @@ internal fun ContactListScreen() {
4749
val flowNavigator = rememberFlowNavigator<SendStep, SendResult>()
4850
val viewModel = flowSharedViewModel<SendFlowViewModel>()
4951
val navigator = LocalOuterCodeNavigator.current
52+
val analytics = LocalAnalytics.current
5053

5154
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
5255

@@ -72,6 +75,14 @@ internal fun ContactListScreen() {
7275
isPickerMode = state.isPickerMode,
7376
) { result ->
7477
when (result) {
78+
ContactAccessResult.Granted -> {
79+
// Granting via the in-list rationale card kicks off the same contact
80+
// sync the permission gate does; otherwise the newly-allowed contacts
81+
// are never fetched.
82+
analytics.action(Button.AllowContacts)
83+
viewModel.dispatchEvent(SendFlowViewModel.Event.ContactsGranted)
84+
}
85+
7586
is ContactAccessResult.Picked -> {
7687
viewModel.dispatchEvent(SendFlowViewModel.Event.ContactsPicked(result.contacts))
7788
}
@@ -161,8 +172,8 @@ internal fun ContactListScreen() {
161172
items = state.listItems,
162173
searchState = state.searchState,
163174
listState = listState,
175+
accessHandle = accessHandle,
164176
isPickerMode = state.isPickerMode,
165-
onAddMoreContacts = { accessHandle.launch() },
166177
onItemClick = { contact ->
167178
viewModel.dispatchEvent(SendFlowViewModel.Event.OnContactClicked(contact))
168179
},

apps/flipcash/features/direct-send/src/main/kotlin/com/flipcash/app/directsend/internal/screens/ContactsPermissionGateScreen.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,16 @@ internal fun ContactsPermissionGateScreen() {
7878
// If this step has been removed from the flow (e.g. chats arrived while we're
7979
// on this screen, or on re-open when chats already exist), advance immediately.
8080
// FlowNavigator.proceed() handles removed steps by replacing with the first remaining step.
81+
//
82+
// Exception: while a sync is actively running (the user just granted access),
83+
// the device contacts land first and remove this gate from the steps, but the
84+
// on-Flipcash contacts are fetched in a second pass. If we advanced now, the list
85+
// would render with only the "not on Flipcash" section, then prepend the
86+
// on-Flipcash contacts above the viewport once they arrive — pushing them
87+
// offscreen. Let ContactSyncComplete drive navigation instead so the list is
88+
// fully formed before it's shown.
8189
val gateStillInSteps = state.steps.any { it is SendStep.ContactsGate }
82-
if (!gateStillInSteps) {
90+
if (!gateStillInSteps && !state.contactSyncState.loading) {
8391
LaunchedEffect(Unit) { flowNavigator.proceed() }
8492
return
8593
}

apps/flipcash/features/direct-send/src/main/kotlin/com/flipcash/app/directsend/internal/screens/components/ContactList.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import com.flipcash.app.contacts.ui.ContactAvatar
4848
import com.flipcash.app.core.android.extensions.launchAppSettings
4949
import com.flipcash.app.core.contacts.DeviceContact
5050
import com.flipcash.app.directsend.internal.ContactListItem
51-
import com.flipcash.app.permissions.rememberContactAccessHandle
51+
import com.flipcash.app.permissions.ContactAccessHandle
5252
import com.flipcash.app.theme.FlipcashThemeWrapper
5353
import com.flipcash.features.directsend.R
5454
import com.flipcash.shared.chat.ui.AnimatedConversationPaymentsPreview
@@ -65,16 +65,13 @@ internal fun ContactList(
6565
items: List<ContactListItem>,
6666
searchState: TextFieldState,
6767
listState: LazyListState,
68+
accessHandle: ContactAccessHandle,
6869
modifier: Modifier = Modifier,
6970
isPickerMode: Boolean = false,
70-
onAddMoreContacts: () -> Unit = {},
7171
onItemClick: (ContactListItem.ContactRow) -> Unit = {},
7272
onItemDismissed: (ContactListItem.ContactRow) -> Unit = {},
7373
) {
7474
val context = LocalContext.current
75-
val accessHandle = rememberContactAccessHandle(
76-
isPickerMode = isPickerMode,
77-
)
7875

7976
LazyColumn(
8077
modifier = Modifier
@@ -188,7 +185,7 @@ internal fun ContactList(
188185
if (isPickerMode) {
189186
item {
190187
PickerModeHeader(
191-
onAddMoreContacts = onAddMoreContacts,
188+
onAddMoreContacts = { accessHandle.launch() },
192189
)
193190
}
194191
}
@@ -563,5 +560,6 @@ private fun ContactListPreview() {
563560
items = items,
564561
searchState = TextFieldState(),
565562
listState = rememberLazyListState(),
563+
accessHandle = ContactAccessHandle(launch = {}),
566564
)
567565
}

0 commit comments

Comments
 (0)