From 3e49c78de7cb41843279f88c395abb3d28dc4961 Mon Sep 17 00:00:00 2001 From: tapframe <85391825+tapframe@users.noreply.github.com> Date: Sun, 21 Jun 2026 16:45:56 +0530 Subject: [PATCH] fix: multiple ios crashes --- .../commonMain/kotlin/com/nuvio/app/App.kt | 152 +++++++++--------- .../features/player/skip/SubmitIntroDialog.kt | 8 +- 2 files changed, 80 insertions(+), 80 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt index 42fb5aafa..eecbc6584 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt @@ -47,12 +47,14 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveableStateHolder import androidx.compose.runtime.setValue +import androidx.compose.runtime.withFrameNanos import androidx.compose.ui.Alignment import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -111,7 +113,6 @@ import com.nuvio.app.features.addons.AddonRepository import com.nuvio.app.features.catalog.CatalogRepository import com.nuvio.app.features.catalog.CatalogScreen import com.nuvio.app.features.catalog.CatalogTarget -import com.nuvio.app.features.catalog.CatalogTargetKind import com.nuvio.app.features.cloud.CloudLibraryContentType import com.nuvio.app.features.cloud.CloudLibraryFile import com.nuvio.app.features.cloud.CloudLibraryItem @@ -303,65 +304,30 @@ data class StreamRoute( @Serializable data class CatalogRoute( + val launchId: Long, +) + +private data class CatalogLaunch( val title: String, val subtitle: String, - val targetKind: String, - val contentType: String, - val supportsPagination: Boolean = false, - val manifestUrl: String? = null, - val addonCatalogId: String? = null, - val genre: String? = null, - val librarySectionType: String? = null, - val collectionId: String? = null, - val folderId: String? = null, - val sourceKey: String? = null, -) { - constructor( - title: String, - subtitle: String, - target: CatalogTarget, - ) : this( - title = title, - subtitle = subtitle, - targetKind = when (target) { - is CatalogTarget.Addon -> CatalogTargetKind.ADDON - is CatalogTarget.Library -> CatalogTargetKind.LIBRARY - is CatalogTarget.CollectionSource -> CatalogTargetKind.COLLECTION_SOURCE - }.name, - contentType = target.contentType, - supportsPagination = target.supportsPagination, - manifestUrl = (target as? CatalogTarget.Addon)?.manifestUrl, - addonCatalogId = (target as? CatalogTarget.Addon)?.catalogId, - genre = (target as? CatalogTarget.Addon)?.genre, - librarySectionType = (target as? CatalogTarget.Library)?.sectionType, - collectionId = (target as? CatalogTarget.CollectionSource)?.collectionId, - folderId = (target as? CatalogTarget.CollectionSource)?.folderId, - sourceKey = (target as? CatalogTarget.CollectionSource)?.sourceKey, - ) + val target: CatalogTarget, +) - fun toCatalogTarget(): CatalogTarget = - when (CatalogTargetKind.valueOf(targetKind)) { - CatalogTargetKind.ADDON -> CatalogTarget.Addon( - manifestUrl = requireNotNull(manifestUrl), - contentType = contentType, - catalogId = requireNotNull(addonCatalogId), - genre = genre, - supportsPagination = supportsPagination, - ) +private object CatalogLaunchStore { + private var nextLaunchId = 1L + private val launches = mutableMapOf() - CatalogTargetKind.LIBRARY -> CatalogTarget.Library( - contentType = contentType, - sectionType = requireNotNull(librarySectionType), - ) + fun put(launch: CatalogLaunch): Long { + val launchId = nextLaunchId++ + launches[launchId] = launch + return launchId + } - CatalogTargetKind.COLLECTION_SOURCE -> CatalogTarget.CollectionSource( - collectionId = requireNotNull(collectionId), - folderId = requireNotNull(folderId), - sourceKey = requireNotNull(sourceKey), - contentType = contentType, - supportsPagination = supportsPagination, - ) - } + fun get(launchId: Long): CatalogLaunch? = launches[launchId] + + fun remove(launchId: Long) { + launches.remove(launchId) + } } private data class PosterActionTarget( @@ -726,6 +692,7 @@ private fun MainAppContent( ProfileSettingsSync.startObserving() } val hapticFeedback = LocalHapticFeedback.current + val focusManager = LocalFocusManager.current val coroutineScope = rememberCoroutineScope() var selectedTab by rememberSaveable { mutableStateOf(AppScreenTab.Home) } var searchFocusRequestCount by remember { mutableStateOf(0) } @@ -758,6 +725,14 @@ private fun MainAppContent( LibraryRepository.uiState }.collectAsStateWithLifecycle() val authState by AuthRepository.state.collectAsStateWithLifecycle() + val openPosterActions: (PosterActionTarget) -> Unit = { target -> + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + focusManager.clearFocus(force = true) + coroutineScope.launch { + withFrameNanos { } + selectedPosterActionTarget = target + } + } val profileState by ProfileRepository.state.collectAsStateWithLifecycle() val playerSettingsUiState by remember { PlayerSettingsRepository.ensureLoaded() @@ -1297,13 +1272,18 @@ private fun MainAppContent( } val onCatalogClick: (HomeCatalogSection) -> Unit = { section -> - navController.navigate( - CatalogRoute( + val launchId = CatalogLaunchStore.put( + CatalogLaunch( title = section.title, subtitle = section.subtitle, target = section.target, ), ) + navController.navigate( + CatalogRoute( + launchId = launchId, + ), + ) } val librarySectionSubtitle = if (libraryUiState.sourceMode == LibrarySourceMode.TRAKT) { @@ -1313,8 +1293,8 @@ private fun MainAppContent( } val onLibrarySectionViewAllClick: (LibrarySection) -> Unit = { section -> - navController.navigate( - CatalogRoute( + val launchId = CatalogLaunchStore.put( + CatalogLaunch( title = section.displayTitle, subtitle = librarySectionSubtitle, target = CatalogTarget.Library( @@ -1323,6 +1303,11 @@ private fun MainAppContent( ), ), ) + navController.navigate( + CatalogRoute( + launchId = launchId, + ), + ) } val openContinueWatching: (ContinueWatchingItem, Boolean, Boolean) -> Unit = { item, manualSelection, startFromBeginning -> @@ -1505,18 +1490,18 @@ private fun MainAppContent( navController.navigate(DetailRoute(type = meta.type, id = meta.id)) }, onPosterLongClick = { meta -> - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - selectedPosterActionTarget = PosterActionTarget(preview = meta) + openPosterActions(PosterActionTarget(preview = meta)) }, onLibraryPosterClick = { item -> navController.navigate(DetailRoute(type = item.type, id = item.id)) }, onLibraryPosterLongClick = { item, section -> - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - selectedPosterActionTarget = PosterActionTarget( - preview = item.toMetaPreview(), - libraryItem = item, - libraryListKey = section.type, + openPosterActions( + PosterActionTarget( + preview = item.toMetaPreview(), + libraryItem = item, + libraryListKey = section.type, + ), ) }, onLibrarySectionViewAllClick = onLibrarySectionViewAllClick, @@ -2487,29 +2472,38 @@ private fun MainAppContent( } composable { backStackEntry -> val route = backStackEntry.toRoute() - val target = route.toCatalogTarget() + val launch = remember(route.launchId) { CatalogLaunchStore.get(route.launchId) } + if (launch == null) { + LaunchedEffect(route.launchId) { + navController.popBackStack() + } + return@composable + } + val target = launch.target CatalogScreen( - title = route.title, - subtitle = route.subtitle, + title = launch.title, + subtitle = launch.subtitle, target = target, onBack = { CatalogRepository.clear() + CatalogLaunchStore.remove(route.launchId) navController.popBackStack() }, onPosterClick = { meta -> navController.navigate(DetailRoute(type = meta.type, id = meta.id)) }, onPosterLongClick = { meta -> - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - selectedPosterActionTarget = if (target is CatalogTarget.Library) { - PosterActionTarget( - preview = meta, - libraryItem = meta.toLibraryItem(savedAtEpochMs = 0L), - libraryListKey = target.sectionType, - ) - } else { - PosterActionTarget(preview = meta) - } + openPosterActions( + if (target is CatalogTarget.Library) { + PosterActionTarget( + preview = meta, + libraryItem = meta.toLibraryItem(savedAtEpochMs = 0L), + libraryListKey = target.sectionType, + ) + } else { + PosterActionTarget(preview = meta) + }, + ) }, modifier = Modifier.fillMaxSize(), ) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/SubmitIntroDialog.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/SubmitIntroDialog.kt index 6f3e80691..8be4314e4 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/SubmitIntroDialog.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/SubmitIntroDialog.kt @@ -10,9 +10,11 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField @@ -86,7 +88,10 @@ fun SubmitIntroDialog( BasicAlertDialog(onDismissRequest = onDismiss) { Surface( - modifier = Modifier.padding(horizontal = 16.dp, vertical = 24.dp), + modifier = Modifier + .padding(horizontal = 16.dp, vertical = 24.dp) + .widthIn(max = 420.dp) + .heightIn(max = 560.dp), shape = RoundedCornerShape(24.dp), color = MaterialTheme.colorScheme.surface, tonalElevation = 8.dp, @@ -94,6 +99,7 @@ fun SubmitIntroDialog( Column( modifier = Modifier .padding(24.dp) + .heightIn(max = 512.dp) .verticalScroll(scrollState), verticalArrangement = Arrangement.spacedBy(16.dp), ) {