Skip to content

Commit 6284ea4

Browse files
committed
fix(compose): fix stale lambda captures and effect key issues
- Fix stale onStateChanged lambda in TextInput by using rememberUpdatedState, preventing stale closure in LaunchedEffect(Unit) - Fix stale onDelete lambda in SwipeToRevealItem by using rememberUpdatedState for the captured callback - Replace LaunchedEffect(swipeFraction) in SlideToConfirm with snapshotFlow-based collection to avoid cancelling/restarting the effect on every drag frame - Key permission screen LaunchedEffects on permissionState.status instead of Unit so navigation fires when status settles async
1 parent 31b4f2d commit 6284ea4

5 files changed

Lines changed: 33 additions & 21 deletions

File tree

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import androidx.compose.runtime.mutableFloatStateOf
4040
import androidx.compose.runtime.mutableStateOf
4141
import androidx.compose.runtime.remember
4242
import androidx.compose.runtime.rememberCoroutineScope
43+
import androidx.compose.runtime.rememberUpdatedState
4344
import androidx.compose.runtime.saveable.rememberSaveable
4445
import androidx.compose.runtime.setValue
4546
import androidx.compose.ui.Alignment
@@ -315,9 +316,10 @@ private fun SwipeToRevealItem(
315316
}
316317
}
317318

319+
val currentOnDelete by rememberUpdatedState(onDelete)
318320
LaunchedEffect(state.currentValue) {
319321
if (state.currentValue == RevealValue.Dismissed) {
320-
onDelete()
322+
currentOnDelete()
321323
}
322324
}
323325

apps/flipcash/shared/permissions/src/main/kotlin/com/flipcash/app/permissions/ContactPermissionScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ fun ContactPermissionScreen(fromOnboarding: Boolean) {
4141
}
4242
}
4343

44-
LaunchedEffect(Unit) {
44+
LaunchedEffect(permissionState.status) {
4545
when (permissionState.status) {
4646
PermissionResult.Granted -> navigator.navigate(
4747
route = AppRoute.Main.Scanner,

apps/flipcash/shared/permissions/src/main/kotlin/com/flipcash/app/permissions/NotificationPermissionScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ fun NotificationPermissionScreen(fromOnboarding: Boolean = false) {
3939
}
4040
}
4141

42-
LaunchedEffect(Unit) {
42+
LaunchedEffect(permissionState.status) {
4343
when (permissionState.status) {
4444
PermissionResult.Granted -> navigator.navigate(
4545
route = AppRoute.Main.Scanner,

ui/components/src/main/kotlin/com/getcode/ui/components/SlideToConfirm.kt

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import androidx.compose.runtime.remember
3737
import androidx.compose.runtime.rememberCoroutineScope
3838
import androidx.compose.runtime.rememberUpdatedState
3939
import androidx.compose.runtime.setValue
40+
import androidx.compose.runtime.snapshotFlow
4041
import androidx.compose.ui.Alignment
4142
import androidx.compose.ui.Modifier
4243
import androidx.compose.ui.draw.alpha
@@ -63,6 +64,7 @@ import com.getcode.theme.White50
6364
import com.getcode.ui.theme.CodeCircularProgressIndicator
6465
import kotlinx.coroutines.CoroutineScope
6566
import kotlinx.coroutines.delay
67+
import kotlinx.coroutines.flow.filter
6668
import kotlinx.coroutines.launch
6769
import java.util.Timer
6870
import java.util.TimerTask
@@ -190,26 +192,32 @@ fun SlideToConfirm(
190192
}
191193
}
192194

193-
LaunchedEffect(swipeFraction, enabled) {
194-
when {
195-
!enabled -> hintState.cancelTimer()
196-
swipeFraction == 0f -> hintState.startTimer()
197-
swipeFraction in 0.1f .. 0.99f -> hintState.cancelTimer()
195+
LaunchedEffect(enabled) {
196+
if (!enabled) {
197+
hintState.cancelTimer()
198+
return@LaunchedEffect
198199
}
200+
snapshotFlow { swipeFraction }
201+
.collect { fraction ->
202+
when {
203+
fraction == 0f -> hintState.startTimer()
204+
fraction in 0.1f .. 0.99f -> hintState.cancelTimer()
205+
}
206+
}
199207
}
200-
LaunchedEffect(swipeFraction) {
201-
if (swipeFraction == 1f) {
202-
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
203-
onConfirm()
204-
// Give the caller a moment to set isLoading = true.
205-
// If they don't, the confirmation was rejected — reset.
206-
// Launch in composeScope so the animation isn't cancelled when
207-
// swipeFraction changes (which would cancel this LaunchedEffect).
208-
delay(200)
209-
if (!currentIsLoading) {
210-
composeScope.launch { swipeState.animateTo(Anchor.Start) }
208+
LaunchedEffect(Unit) {
209+
snapshotFlow { swipeFraction }
210+
.filter { it == 1f }
211+
.collect {
212+
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
213+
onConfirm()
214+
// Give the caller a moment to set isLoading = true.
215+
// If they don't, the confirmation was rejected — reset.
216+
delay(200)
217+
if (!currentIsLoading) {
218+
composeScope.launch { swipeState.animateTo(Anchor.Start) }
219+
}
211220
}
212-
}
213221
}
214222

215223
// Handle the loading → idle transition (e.g. after a network call completes)

ui/components/src/main/kotlin/com/getcode/ui/components/TextInput.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import androidx.compose.runtime.LaunchedEffect
2929
import androidx.compose.runtime.getValue
3030
import androidx.compose.runtime.mutableStateOf
3131
import androidx.compose.runtime.remember
32+
import androidx.compose.runtime.rememberUpdatedState
3233
import androidx.compose.runtime.setValue
3334
import androidx.compose.runtime.snapshotFlow
3435
import androidx.compose.ui.Alignment
@@ -141,9 +142,10 @@ fun TextInput(
141142
)
142143
}
143144

145+
val currentOnStateChanged by rememberUpdatedState(onStateChanged)
144146
LaunchedEffect(Unit) {
145147
snapshotFlow { state.text }
146-
.onEach { onStateChanged() }
148+
.onEach { currentOnStateChanged() }
147149
.launchIn(this)
148150
}
149151

0 commit comments

Comments
 (0)