mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-08-01 09:09:29 +00:00
Refine iOS sheets and ratings
This commit is contained in:
parent
f9b5ef8aef
commit
1dbbbdecbc
7 changed files with 310 additions and 46 deletions
|
|
@ -0,0 +1,23 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
internal actual val usesNativeNuvioBottomSheet: Boolean = false
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal actual fun NuvioNativeModalBottomSheet(
|
||||
onDismissRequest: () -> Unit,
|
||||
modifier: Modifier,
|
||||
containerColor: Color,
|
||||
contentColor: Color,
|
||||
showDragHandle: Boolean,
|
||||
fullHeight: Boolean,
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
) = Unit
|
||||
|
||||
internal actual fun dismissNativeNuvioBottomSheet() = Unit
|
||||
|
|
@ -37,22 +37,35 @@ fun NuvioModalBottomSheet(
|
|||
contentColor: Color = MaterialTheme.nuvio.colors.textPrimary,
|
||||
shape: Shape = RoundedCornerShape(topStart = NuvioTokens.Space.s28, topEnd = NuvioTokens.Space.s28),
|
||||
showDragHandle: Boolean = true,
|
||||
fullHeight: Boolean = false,
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
) {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
sheetState = sheetState,
|
||||
modifier = modifier,
|
||||
containerColor = containerColor,
|
||||
contentColor = contentColor,
|
||||
shape = shape,
|
||||
dragHandle = if (showDragHandle) {
|
||||
{ NuvioBottomSheetDragHandle() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
content = content,
|
||||
)
|
||||
if (usesNativeNuvioBottomSheet) {
|
||||
NuvioNativeModalBottomSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
modifier = modifier,
|
||||
containerColor = containerColor,
|
||||
contentColor = contentColor,
|
||||
showDragHandle = showDragHandle,
|
||||
fullHeight = fullHeight,
|
||||
content = content,
|
||||
)
|
||||
} else {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
sheetState = sheetState,
|
||||
modifier = modifier,
|
||||
containerColor = containerColor,
|
||||
contentColor = contentColor,
|
||||
shape = shape,
|
||||
dragHandle = if (showDragHandle) {
|
||||
{ NuvioBottomSheetDragHandle() }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -107,7 +120,9 @@ suspend fun dismissNuvioBottomSheet(
|
|||
sheetState: SheetState,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
if (sheetState.isVisible) {
|
||||
if (usesNativeNuvioBottomSheet) {
|
||||
dismissNativeNuvioBottomSheet()
|
||||
} else if (sheetState.isVisible) {
|
||||
sheetState.hide()
|
||||
}
|
||||
onDismiss()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal expect fun NuvioNativeModalBottomSheet(
|
||||
onDismissRequest: () -> Unit,
|
||||
modifier: Modifier,
|
||||
containerColor: Color,
|
||||
contentColor: Color,
|
||||
showDragHandle: Boolean,
|
||||
fullHeight: Boolean,
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
)
|
||||
|
||||
internal expect val usesNativeNuvioBottomSheet: Boolean
|
||||
|
||||
internal expect fun dismissNativeNuvioBottomSheet()
|
||||
|
|
@ -151,17 +151,16 @@ fun CommentDetailSheet(
|
|||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
if (comment.review || comment.hasSpoilerContent) {
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
if (comment.review) {
|
||||
CommentDetailChip(text = stringResource(Res.string.detail_comments_badge_review))
|
||||
}
|
||||
if (comment.hasSpoilerContent) {
|
||||
CommentDetailChip(text = stringResource(Res.string.detail_comments_badge_spoiler))
|
||||
}
|
||||
comment.rating?.let { rating ->
|
||||
CommentDetailChip(text = stringResource(Res.string.detail_comments_badge_rating, rating))
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
if (comment.review) {
|
||||
CommentDetailChip(text = stringResource(Res.string.detail_comments_badge_review))
|
||||
}
|
||||
if (comment.hasSpoilerContent) {
|
||||
CommentDetailChip(text = stringResource(Res.string.detail_comments_badge_spoiler))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -190,11 +189,20 @@ fun CommentDetailSheet(
|
|||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(Res.string.detail_comments_likes, comment.likes),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
comment.rating?.let { rating ->
|
||||
Text(
|
||||
text = stringResource(Res.string.detail_comments_badge_rating, rating),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(Res.string.detail_comments_likes, comment.likes),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
comment.createdAt?.take(10)?.let { date ->
|
||||
Text(
|
||||
text = date,
|
||||
|
|
|
|||
|
|
@ -214,15 +214,14 @@ private fun CommentCard(
|
|||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
if (review.review) {
|
||||
CommentChip(text = stringResource(Res.string.detail_comments_badge_review))
|
||||
}
|
||||
if (review.hasSpoilerContent) {
|
||||
CommentChip(text = stringResource(Res.string.detail_comments_badge_spoiler))
|
||||
}
|
||||
review.rating?.let { rating ->
|
||||
CommentChip(text = stringResource(Res.string.detail_comments_badge_rating, rating))
|
||||
if (review.review || review.hasSpoilerContent) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
if (review.review) {
|
||||
CommentChip(text = stringResource(Res.string.detail_comments_badge_review))
|
||||
}
|
||||
if (review.hasSpoilerContent) {
|
||||
CommentChip(text = stringResource(Res.string.detail_comments_badge_spoiler))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -235,13 +234,23 @@ private fun CommentCard(
|
|||
modifier = Modifier.weight(1f, fill = false),
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(Res.string.detail_comments_likes, review.likes),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
review.rating?.let { rating ->
|
||||
Text(
|
||||
text = stringResource(Res.string.detail_comments_badge_rating, rating),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(Res.string.detail_comments_likes, review.likes),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -315,6 +315,7 @@ private fun CardDepthFineTuneSheet(
|
|||
onDismiss()
|
||||
},
|
||||
sheetState = sheetState,
|
||||
fullHeight = true,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -0,0 +1,185 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.LocalRippleConfiguration
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.uikit.LocalUIViewController
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.window.ComposeUIViewController
|
||||
import platform.UIKit.UIAdaptivePresentationControllerDelegateProtocol
|
||||
import platform.UIKit.UIColor
|
||||
import platform.UIKit.UIModalPresentationPageSheet
|
||||
import platform.UIKit.UIPresentationController
|
||||
import platform.UIKit.UISheetPresentationController
|
||||
import platform.UIKit.UISheetPresentationControllerDetent
|
||||
import platform.UIKit.UISheetPresentationControllerDetentIdentifierMedium
|
||||
import platform.UIKit.UIViewController
|
||||
import platform.UIKit.presentationController
|
||||
import platform.darwin.NSObject
|
||||
|
||||
internal actual val usesNativeNuvioBottomSheet: Boolean = true
|
||||
|
||||
private var activeNativeBottomSheet: UIViewController? = null
|
||||
private var activeNativeBottomSheetDelegate: NuvioNativeBottomSheetDelegate? = null
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal actual fun NuvioNativeModalBottomSheet(
|
||||
onDismissRequest: () -> Unit,
|
||||
modifier: Modifier,
|
||||
containerColor: Color,
|
||||
contentColor: Color,
|
||||
showDragHandle: Boolean,
|
||||
fullHeight: Boolean,
|
||||
content: @Composable androidx.compose.foundation.layout.ColumnScope.() -> Unit,
|
||||
) {
|
||||
val parentViewController = LocalUIViewController.current
|
||||
val latestContent = rememberUpdatedState(content)
|
||||
val latestOnDismissRequest = rememberUpdatedState(onDismissRequest)
|
||||
val latestContainerColor = rememberUpdatedState(containerColor)
|
||||
val latestContentColor = rememberUpdatedState(contentColor)
|
||||
val latestModifier = rememberUpdatedState(modifier)
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
val typography = MaterialTheme.typography
|
||||
val shapes = MaterialTheme.shapes
|
||||
val themeTokens = LocalNuvioThemeTokens.current
|
||||
val typeScale = LocalNuvioTypeScale.current
|
||||
val appTheme = LocalAppTheme.current
|
||||
val density = LocalDensity.current
|
||||
val rippleConfiguration = LocalRippleConfiguration.current
|
||||
|
||||
val latestColorScheme = rememberUpdatedState(colorScheme)
|
||||
val latestTypography = rememberUpdatedState(typography)
|
||||
val latestShapes = rememberUpdatedState(shapes)
|
||||
val latestThemeTokens = rememberUpdatedState(themeTokens)
|
||||
val latestTypeScale = rememberUpdatedState(typeScale)
|
||||
val latestAppTheme = rememberUpdatedState(appTheme)
|
||||
val latestDensity = rememberUpdatedState(density)
|
||||
val latestRippleConfiguration = rememberUpdatedState(rippleConfiguration)
|
||||
|
||||
DisposableEffect(parentViewController) {
|
||||
val contentController = ComposeUIViewController {
|
||||
CompositionLocalProvider(
|
||||
LocalDensity provides Density(
|
||||
density = latestDensity.value.density,
|
||||
fontScale = latestDensity.value.fontScale,
|
||||
),
|
||||
LocalNuvioThemeTokens provides latestThemeTokens.value,
|
||||
LocalNuvioTypeScale provides latestTypeScale.value,
|
||||
LocalAppTheme provides latestAppTheme.value,
|
||||
LocalRippleConfiguration provides latestRippleConfiguration.value,
|
||||
) {
|
||||
MaterialTheme(
|
||||
colorScheme = latestColorScheme.value,
|
||||
typography = latestTypography.value,
|
||||
shapes = latestShapes.value,
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides latestContentColor.value,
|
||||
) {
|
||||
Column(
|
||||
modifier = latestModifier.value
|
||||
.fillMaxSize()
|
||||
.background(latestContainerColor.value)
|
||||
.padding(top = NuvioTokens.Space.s20),
|
||||
) {
|
||||
latestContent.value(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val sheetController = contentController
|
||||
sheetController.modalPresentationStyle = UIModalPresentationPageSheet
|
||||
sheetController.view.backgroundColor = latestContainerColor.value.toUIColor()
|
||||
(sheetController.presentationController() as? UISheetPresentationController)?.apply {
|
||||
if (fullHeight) {
|
||||
detents = listOf(UISheetPresentationControllerDetent.largeDetent())
|
||||
} else {
|
||||
detents = listOf(
|
||||
UISheetPresentationControllerDetent.mediumDetent(),
|
||||
UISheetPresentationControllerDetent.largeDetent(),
|
||||
)
|
||||
selectedDetentIdentifier = UISheetPresentationControllerDetentIdentifierMedium
|
||||
}
|
||||
prefersGrabberVisible = showDragHandle
|
||||
prefersScrollingExpandsWhenScrolledToEdge = false
|
||||
}
|
||||
|
||||
val dismissalDelegate = NuvioNativeBottomSheetDelegate {
|
||||
if (activeNativeBottomSheet === sheetController) {
|
||||
activeNativeBottomSheet = null
|
||||
activeNativeBottomSheetDelegate = null
|
||||
}
|
||||
latestOnDismissRequest.value()
|
||||
}
|
||||
sheetController.presentationController()?.delegate = dismissalDelegate
|
||||
|
||||
activeNativeBottomSheet?.dismissViewControllerAnimated(
|
||||
flag = false,
|
||||
completion = null,
|
||||
)
|
||||
activeNativeBottomSheet = sheetController
|
||||
activeNativeBottomSheetDelegate = dismissalDelegate
|
||||
parentViewController.presentViewController(
|
||||
viewControllerToPresent = sheetController,
|
||||
animated = true,
|
||||
completion = null,
|
||||
)
|
||||
|
||||
onDispose {
|
||||
if (activeNativeBottomSheet === sheetController) {
|
||||
activeNativeBottomSheet = null
|
||||
activeNativeBottomSheetDelegate = null
|
||||
}
|
||||
if (sheetController.presentingViewController != null) {
|
||||
sheetController.dismissViewControllerAnimated(
|
||||
flag = true,
|
||||
completion = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal actual fun dismissNativeNuvioBottomSheet() {
|
||||
activeNativeBottomSheet?.dismissViewControllerAnimated(
|
||||
flag = true,
|
||||
completion = null,
|
||||
)
|
||||
}
|
||||
|
||||
private class NuvioNativeBottomSheetDelegate(
|
||||
private val onDismissRequest: () -> Unit,
|
||||
) : NSObject(), UIAdaptivePresentationControllerDelegateProtocol {
|
||||
private var didNotifyDismissal = false
|
||||
|
||||
override fun presentationControllerDidDismiss(presentationController: UIPresentationController) {
|
||||
notifyDismissed()
|
||||
}
|
||||
|
||||
private fun notifyDismissed() {
|
||||
if (didNotifyDismissal) return
|
||||
didNotifyDismissal = true
|
||||
onDismissRequest()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Color.toUIColor(): UIColor = UIColor(
|
||||
red = red.toDouble(),
|
||||
green = green.toDouble(),
|
||||
blue = blue.toDouble(),
|
||||
alpha = alpha.toDouble(),
|
||||
)
|
||||
Loading…
Reference in a new issue