Skip to content

Commit

Permalink
Fix launchOrThrow crashes (#2044)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbanes authored Sep 15, 2024
1 parent da11c2f commit 7860661
Show file tree
Hide file tree
Showing 30 changed files with 157 additions and 114 deletions.
25 changes: 25 additions & 0 deletions common/ui/circuit/src/commonMain/kotlin/app/tivi/EventSink.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2024, Christopher Banes and the Tivi project contributors
// SPDX-License-Identifier: Apache-2.0

package app.tivi

import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import co.touchlab.kermit.Logger
import com.slack.circuit.runtime.CircuitUiEvent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.isActive

@Composable
inline fun <E : CircuitUiEvent> wrapEventSink(
crossinline eventSink: CoroutineScope.(E) -> Unit,
coroutineScope: CoroutineScope = rememberCoroutineScope(),
): (E) -> Unit = { event ->
if (coroutineScope.isActive) {
coroutineScope.eventSink(event)
} else {
Logger.i(IllegalStateException()) {
"Received event, but CoroutineScope is no longer active. See stack trace for caller."
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package app.tivi.account
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import app.tivi.data.traktauth.TraktAuthState
import app.tivi.domain.interactors.LoginTrakt
import app.tivi.domain.interactors.LogoutTrakt
Expand All @@ -16,11 +15,13 @@ import app.tivi.domain.observers.ObserveUserDetails
import app.tivi.screens.AccountScreen
import app.tivi.screens.SettingsScreen
import app.tivi.util.launchOrThrow
import app.tivi.wrapEventSink
import com.slack.circuit.retained.collectAsRetainedState
import com.slack.circuit.runtime.CircuitContext
import com.slack.circuit.runtime.Navigator
import com.slack.circuit.runtime.presenter.Presenter
import com.slack.circuit.runtime.screen.Screen
import kotlinx.coroutines.CoroutineScope
import me.tatarka.inject.annotations.Assisted
import me.tatarka.inject.annotations.Inject

Expand Down Expand Up @@ -50,25 +51,27 @@ class AccountPresenter(
val user by observeUserDetails.value.flow.collectAsRetainedState(null)
val authState by observeTraktAuthState.value.flow
.collectAsRetainedState(TraktAuthState.LOGGED_OUT)
val scope = rememberCoroutineScope()

LaunchedEffect(Unit) {
observeTraktAuthState.value.invoke(Unit)
observeUserDetails.value.invoke(ObserveUserDetails.Params("me"))
}

return AccountUiState(
user = user,
authState = authState,
) { event ->
val eventSink: CoroutineScope.(AccountUiEvent) -> Unit = { event ->
when (event) {
AccountUiEvent.NavigateToSettings -> {
navigator.pop() // dismiss ourselves
navigator.goTo(SettingsScreen)
}
AccountUiEvent.Login -> scope.launchOrThrow { loginTrakt.value.invoke() }
AccountUiEvent.Logout -> scope.launchOrThrow { logoutTrakt.value.invoke() }
AccountUiEvent.Login -> launchOrThrow { loginTrakt.value.invoke() }
AccountUiEvent.Logout -> launchOrThrow { logoutTrakt.value.invoke() }
}
}

return AccountUiState(
user = user,
authState = authState,
eventSink = wrapEventSink(eventSink),
)
}
}
1 change: 1 addition & 0 deletions ui/anticipated/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ kotlin {
implementation(projects.core.base)
implementation(projects.domain)
implementation(projects.common.ui.compose)
implementation(projects.common.ui.circuit)

api(projects.common.ui.screens)
api(libs.circuit.foundation)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import app.tivi.common.compose.rememberRetainedCachedPagingFlow
import app.tivi.domain.observers.ObservePagedAnticipatedShows
import app.tivi.screens.AnticipatedShowsScreen
import app.tivi.screens.ShowDetailsScreen
import app.tivi.wrapEventSink
import com.slack.circuit.retained.rememberRetained
import com.slack.circuit.runtime.CircuitContext
import com.slack.circuit.runtime.Navigator
import com.slack.circuit.runtime.presenter.Presenter
import com.slack.circuit.runtime.screen.Screen
import kotlinx.coroutines.CoroutineScope
import me.tatarka.inject.annotations.Assisted
import me.tatarka.inject.annotations.Inject

Expand Down Expand Up @@ -54,7 +56,7 @@ class AnticipatedShowsPresenter(
retainedPagingInteractor(ObservePagedAnticipatedShows.Params(PAGING_CONFIG))
}

val eventSink: (AnticipatedShowsUiEvent) -> Unit = { event ->
val eventSink: CoroutineScope.(AnticipatedShowsUiEvent) -> Unit = { event ->
when (event) {
AnticipatedShowsUiEvent.NavigateUp -> navigator.pop()
is AnticipatedShowsUiEvent.OpenShowDetails -> {
Expand All @@ -65,7 +67,7 @@ class AnticipatedShowsPresenter(

return AnticipatedShowsUiState(
items = items,
eventSink = eventSink,
eventSink = wrapEventSink(eventSink),
)
}

Expand Down
1 change: 1 addition & 0 deletions ui/developer/log/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ kotlin {
dependencies {
implementation(projects.core.base)
implementation(projects.common.ui.compose)
implementation(projects.common.ui.circuit)
implementation(projects.core.logging)

api(projects.common.ui.screens)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import app.tivi.screens.DevLogScreen
import app.tivi.util.RecordingLoggerWriter
import app.tivi.wrapEventSink
import com.slack.circuit.runtime.CircuitContext
import com.slack.circuit.runtime.Navigator
import com.slack.circuit.runtime.presenter.Presenter
import com.slack.circuit.runtime.screen.Screen
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.map
import me.tatarka.inject.annotations.Assisted
import me.tatarka.inject.annotations.Inject
Expand Down Expand Up @@ -42,15 +44,15 @@ class DevLogPresenter(
RecordingLoggerWriter.buffer.map { it.asReversed() }
}.collectAsState(emptyList())

val eventSink: (DevLogUiEvent) -> Unit = { event ->
val eventSink: CoroutineScope.(DevLogUiEvent) -> Unit = { event ->
when (event) {
DevLogUiEvent.NavigateUp -> navigator.pop()
}
}

return DevLogUiState(
logs = logs,
eventSink = eventSink,
eventSink = wrapEventSink(eventSink),
)
}
}
1 change: 1 addition & 0 deletions ui/developer/notifications/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ kotlin {
api(projects.domain)

implementation(projects.common.ui.compose)
implementation(projects.common.ui.circuit)

api(projects.common.ui.screens)
api(libs.circuit.foundation)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import app.tivi.core.notifications.NotificationManager
import app.tivi.data.models.Notification
import app.tivi.data.models.NotificationChannel
import app.tivi.domain.interactors.ScheduleDebugEpisodeNotification
import app.tivi.screens.DevNotificationsScreen
import app.tivi.util.launchOrThrow
import app.tivi.wrapEventSink
import com.slack.circuit.runtime.CircuitContext
import com.slack.circuit.runtime.Navigator
import com.slack.circuit.runtime.presenter.Presenter
import com.slack.circuit.runtime.screen.Screen
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.datetime.Clock
Expand Down Expand Up @@ -51,7 +52,6 @@ class DevNotificationsPresenter(

@Composable
override fun present(): DevNotificationsUiState {
val coroutineScope = rememberCoroutineScope()
var pending by remember { mutableStateOf(emptyList<Notification>()) }

LaunchedEffect(notificationsManager) {
Expand All @@ -61,18 +61,18 @@ class DevNotificationsPresenter(
}
}

val eventSink: (DevNotificationsUiEvent) -> Unit = { event ->
val eventSink: CoroutineScope.(DevNotificationsUiEvent) -> Unit = { event ->
when (event) {
DevNotificationsUiEvent.NavigateUp -> navigator.pop()
DevNotificationsUiEvent.ShowEpisodeAiringNotification -> {
coroutineScope.launchOrThrow {
launchOrThrow {
scheduleDebugEpisodeNotification(
ScheduleDebugEpisodeNotification.Params(3.seconds),
)
}
}
DevNotificationsUiEvent.ScheduleNotification -> {
coroutineScope.launchOrThrow {
launchOrThrow {
notificationsManager.schedule(
Notification(
id = "scheduled_test",
Expand All @@ -86,7 +86,7 @@ class DevNotificationsPresenter(
}

DevNotificationsUiEvent.ShowNotification -> {
coroutineScope.launchOrThrow {
launchOrThrow {
notificationsManager.schedule(
Notification(
id = "immediate_test",
Expand All @@ -103,7 +103,7 @@ class DevNotificationsPresenter(

return DevNotificationsUiState(
pendingNotifications = pending,
eventSink = eventSink,
eventSink = wrapEventSink(eventSink),
)
}
}
1 change: 1 addition & 0 deletions ui/developer/settings/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ kotlin {
implementation(projects.core.preferences)
implementation(projects.domain)
implementation(projects.common.ui.compose)
implementation(projects.common.ui.circuit)

api(projects.common.ui.screens)
api(libs.circuit.foundation)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ package app.tivi.settings.developer

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import app.tivi.common.compose.collectAsState
import app.tivi.screens.DevLogScreen
import app.tivi.screens.DevNotificationsScreen
import app.tivi.screens.DevSettingsScreen
import app.tivi.settings.TiviPreferences
import app.tivi.settings.toggle
import app.tivi.util.launchOrThrow
import app.tivi.wrapEventSink
import com.slack.circuit.runtime.CircuitContext
import com.slack.circuit.runtime.Navigator
import com.slack.circuit.runtime.presenter.Presenter
import com.slack.circuit.runtime.screen.Screen
import kotlinx.coroutines.CoroutineScope
import me.tatarka.inject.annotations.Assisted
import me.tatarka.inject.annotations.Inject

Expand Down Expand Up @@ -44,22 +45,20 @@ class DevSettingsPresenter(
override fun present(): DevSettingsUiState {
val hideArtwork by preferences.value.developerHideArtwork.collectAsState()

val coroutineScope = rememberCoroutineScope()

val eventSink: (DevSettingsUiEvent) -> Unit = { event ->
val eventSink: CoroutineScope.(DevSettingsUiEvent) -> Unit = { event ->
when (event) {
DevSettingsUiEvent.NavigateUp -> navigator.pop()
DevSettingsUiEvent.NavigateLog -> navigator.goTo(DevLogScreen)
DevSettingsUiEvent.NavigateNotifications -> navigator.goTo(DevNotificationsScreen)
DevSettingsUiEvent.ToggleHideArtwork -> {
coroutineScope.launchOrThrow { preferences.value.developerHideArtwork.toggle() }
launchOrThrow { preferences.value.developerHideArtwork.toggle() }
}
}
}

return DevSettingsUiState(
hideArtwork = hideArtwork,
eventSink = eventSink,
eventSink = wrapEventSink(eventSink),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import app.tivi.common.compose.UiMessage
import app.tivi.common.compose.UiMessageManager
import app.tivi.data.traktauth.TraktAuthState
Expand All @@ -32,12 +31,14 @@ import app.tivi.screens.RecommendedShowsScreen
import app.tivi.screens.ShowDetailsScreen
import app.tivi.screens.TrendingShowsScreen
import app.tivi.util.launchOrThrow
import app.tivi.wrapEventSink
import co.touchlab.kermit.Logger
import com.slack.circuit.retained.collectAsRetainedState
import com.slack.circuit.runtime.CircuitContext
import com.slack.circuit.runtime.Navigator
import com.slack.circuit.runtime.presenter.Presenter
import com.slack.circuit.runtime.screen.Screen
import kotlinx.coroutines.CoroutineScope
import me.tatarka.inject.annotations.Assisted
import me.tatarka.inject.annotations.Inject

Expand Down Expand Up @@ -74,7 +75,6 @@ class DiscoverPresenter(

@Composable
override fun present(): DiscoverUiState {
val scope = rememberCoroutineScope()
val uiMessageManager = remember { UiMessageManager() }

val trendingItems by observeTrendingShows.value.flow.collectAsRetainedState(emptyList())
Expand All @@ -95,16 +95,16 @@ class DiscoverPresenter(

val message by uiMessageManager.message.collectAsState(null)

val eventSink: (DiscoverUiEvent) -> Unit = { event ->
val eventSink: CoroutineScope.(DiscoverUiEvent) -> Unit = { event ->
when (event) {
is DiscoverUiEvent.ClearMessage -> {
scope.launchOrThrow {
launchOrThrow {
uiMessageManager.clearMessage(event.id)
}
}

is DiscoverUiEvent.Refresh -> {
scope.launchOrThrow {
launchOrThrow {
updatePopularShows.value.invoke(
UpdatePopularShows.Params(
page = UpdatePopularShows.Page.REFRESH,
Expand All @@ -115,7 +115,7 @@ class DiscoverPresenter(
uiMessageManager.emitMessage(UiMessage(e))
}
}
scope.launchOrThrow {
launchOrThrow {
updateTrendingShows.value.invoke(
UpdateTrendingShows.Params(
page = UpdateTrendingShows.Page.REFRESH,
Expand All @@ -126,7 +126,7 @@ class DiscoverPresenter(
uiMessageManager.emitMessage(UiMessage(e))
}
}
scope.launchOrThrow {
launchOrThrow {
updateAntipicatedShows.value.invoke(
UpdateAnticipatedShows.Params(
page = UpdateAnticipatedShows.Page.REFRESH,
Expand All @@ -138,7 +138,7 @@ class DiscoverPresenter(
}
}
if (authState == TraktAuthState.LOGGED_IN) {
scope.launchOrThrow {
launchOrThrow {
updateRecommendedShows.value.invoke(
UpdateRecommendedShows.Params(isUserInitiated = event.fromUser),
).onFailure { e ->
Expand Down Expand Up @@ -190,7 +190,7 @@ class DiscoverPresenter(
anticipatedRefreshing = anticipatedLoading,
nextEpisodesToWatch = nextEpisodesToWatch,
message = message,
eventSink = eventSink,
eventSink = wrapEventSink(eventSink),
)
}
}
Loading

0 comments on commit 7860661

Please sign in to comment.