diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/core/ui/SecondaryClick.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/core/ui/SecondaryClick.android.kt new file mode 100644 index 00000000..a01bf27a --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/core/ui/SecondaryClick.android.kt @@ -0,0 +1,5 @@ +package com.nuvio.app.core.ui + +import androidx.compose.ui.Modifier + +internal actual fun Modifier.secondaryClick(onClick: (() -> Unit)?): Modifier = this diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/NuvioShelfComponents.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/NuvioShelfComponents.kt index fafed653..5bf613e9 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/NuvioShelfComponents.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/NuvioShelfComponents.kt @@ -344,6 +344,7 @@ internal fun Modifier.posterCardClickable( onClick = { onClick?.invoke() }, onLongClick = onLongClick, ) + .secondaryClick(onLongClick) } else { this } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/SecondaryClick.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/SecondaryClick.kt new file mode 100644 index 00000000..eea6d19b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/core/ui/SecondaryClick.kt @@ -0,0 +1,5 @@ +package com.nuvio.app.core.ui + +import androidx.compose.ui.Modifier + +internal expect fun Modifier.secondaryClick(onClick: (() -> Unit)?): Modifier diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailActionButtons.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailActionButtons.kt index 7c3324ba..b9bd0b6d 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailActionButtons.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailActionButtons.kt @@ -40,6 +40,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.nuvio.app.core.ui.AppIconResource import com.nuvio.app.core.ui.appIconPainter +import com.nuvio.app.core.ui.secondaryClick import nuvio.composeapp.generated.resources.Res import nuvio.composeapp.generated.resources.action_play import org.jetbrains.compose.resources.stringResource @@ -107,6 +108,7 @@ fun DetailActionButtons( onLongClick = onPlayLongClick, role = Role.Button, ) + .secondaryClick(onPlayLongClick) .height(buttonHeight), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, @@ -249,7 +251,8 @@ private fun DetailIconAction( onClick = onClick, onLongClick = onLongClick, role = Role.Button, - ), + ) + .secondaryClick(onLongClick), contentAlignment = Alignment.Center, ) { Icon( diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailSeriesContent.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailSeriesContent.kt index 3601b1d0..b10aef83 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailSeriesContent.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailSeriesContent.kt @@ -65,6 +65,7 @@ import com.nuvio.app.core.format.formatReleaseDateForDisplay import com.nuvio.app.core.i18n.localizedSeasonEpisodeCode import com.nuvio.app.core.ui.NuvioAnimatedWatchedBadge import com.nuvio.app.core.ui.NuvioProgressBar +import com.nuvio.app.core.ui.secondaryClick import com.nuvio.app.features.details.MetaDetails import com.nuvio.app.features.details.MetaEpisodeCardStyle import com.nuvio.app.features.details.MetaVideo @@ -408,6 +409,7 @@ private fun SeasonTextChipScrollRow( ) { items(seasons, key = { season -> season }) { season -> val isSelected = season == currentSeason + val onSecondaryClick = onLongPress?.let { handler -> { handler(season) } } Box( modifier = Modifier .clip(RoundedCornerShape(sizing.seasonChipRadius)) @@ -420,8 +422,9 @@ private fun SeasonTextChipScrollRow( ) .combinedClickable( onClick = { onSelect(season) }, - onLongClick = onLongPress?.let { handler -> { handler(season) } }, + onLongClick = onSecondaryClick, ) + .secondaryClick(onSecondaryClick) .padding( horizontal = sizing.seasonChipHorizontalPadding, vertical = sizing.seasonChipVerticalPadding, @@ -508,7 +511,8 @@ private fun SeasonPosterButton( .combinedClickable( onClick = onClick, onLongClick = onLongClick, - ), + ) + .secondaryClick(onLongClick), verticalArrangement = Arrangement.spacedBy(8.dp), ) { Box( @@ -677,7 +681,8 @@ private fun EpisodeHorizontalCard( enabled = onClick != null || onLongPress != null, onClick = { onClick?.invoke() }, onLongClick = onLongPress, - ), + ) + .secondaryClick(onLongPress), ) { val imageUrl = video.thumbnail ?: fallbackImage val shouldBlurArtwork = blurUnwatchedEpisodes && !isWatched @@ -1045,7 +1050,8 @@ private fun EpisodeListCard( enabled = onClick != null || onLongPress != null, onClick = { onClick?.invoke() }, onLongClick = onLongPress, - ), + ) + .secondaryClick(onLongPress), ) { Row( modifier = Modifier.fillMaxSize(), diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/components/HomeContinueWatchingSection.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/components/HomeContinueWatchingSection.kt index 99f36172..df2987c4 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/components/HomeContinueWatchingSection.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/components/HomeContinueWatchingSection.kt @@ -53,6 +53,7 @@ import com.nuvio.app.core.ui.landscapePosterHeightForWidth import com.nuvio.app.core.ui.landscapePosterWidth import com.nuvio.app.core.ui.posterCardClickable import com.nuvio.app.core.ui.rememberPosterCardStyleUiState +import com.nuvio.app.core.ui.secondaryClick import com.nuvio.app.features.cloud.CloudLibraryContentType import com.nuvio.app.features.cloud.cloudLibraryDisplayArtworkUrl import com.nuvio.app.features.home.HomeCatalogSettingsRepository @@ -803,7 +804,8 @@ private fun ContinueWatchingWideCard( enabled = onClick != null || onLongClick != null, onClick = { onClick?.invoke() }, onLongClick = onLongClick, - ), + ) + .secondaryClick(onLongClick), ) { val shouldBlurArtwork = blurNextUp && useEpisodeThumbnails && item.isNextUp val artworkUrl = item.continueWatchingArtworkUrl(useEpisodeThumbnails) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt index 0f6281e2..bd9ba6cb 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/streams/StreamsScreen.kt @@ -84,6 +84,7 @@ import com.nuvio.app.core.ui.NuvioBottomSheetDivider import com.nuvio.app.core.ui.NuvioModalBottomSheet import com.nuvio.app.core.ui.NuvioToastController import com.nuvio.app.core.ui.dismissNuvioBottomSheet +import com.nuvio.app.core.ui.secondaryClick import com.nuvio.app.features.downloads.DownloadsRepository import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.compose.material3.ExperimentalMaterial3Api @@ -1039,6 +1040,7 @@ private fun StreamCard( onClick = onClick, onLongClick = onLongClick, ) + .secondaryClick(if (enabled) onLongClick else null) .padding(14.dp), verticalAlignment = Alignment.Top, ) { diff --git a/composeApp/src/desktopMain/kotlin/com/nuvio/app/core/ui/SecondaryClick.desktop.kt b/composeApp/src/desktopMain/kotlin/com/nuvio/app/core/ui/SecondaryClick.desktop.kt new file mode 100644 index 00000000..4b20c306 --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/com/nuvio/app/core/ui/SecondaryClick.desktop.kt @@ -0,0 +1,24 @@ +package com.nuvio.app.core.ui + +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.PointerButton +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.pointerInput + +@OptIn(ExperimentalComposeUiApi::class) +internal actual fun Modifier.secondaryClick(onClick: (() -> Unit)?): Modifier { + if (onClick == null) return this + + return pointerInput(onClick) { + awaitPointerEventScope { + while (true) { + val event = awaitPointerEvent() + if (event.type == PointerEventType.Press && event.button == PointerButton.Secondary) { + event.changes.forEach { change -> change.consume() } + onClick() + } + } + } + } +} diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/core/ui/SecondaryClick.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/core/ui/SecondaryClick.ios.kt new file mode 100644 index 00000000..a01bf27a --- /dev/null +++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/core/ui/SecondaryClick.ios.kt @@ -0,0 +1,5 @@ +package com.nuvio.app.core.ui + +import androidx.compose.ui.Modifier + +internal actual fun Modifier.secondaryClick(onClick: (() -> Unit)?): Modifier = this