Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Toast is persisted through configuration changes #592

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,24 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kiwi.orbit.compose.catalog.AppTheme
import kiwi.orbit.compose.catalog.semantics.SubScreenSemantics
import kiwi.orbit.compose.icons.Icons
import kiwi.orbit.compose.icons.IconName
import kiwi.orbit.compose.ui.controls.Coupon
import kiwi.orbit.compose.ui.controls.Scaffold
import kiwi.orbit.compose.ui.controls.Text
import kiwi.orbit.compose.ui.controls.ToastHostState
import kiwi.orbit.compose.ui.controls.TopAppBar
import kotlinx.coroutines.launch
import kiwi.orbit.compose.ui.controls.rememberToastHostState

@Composable
internal fun CouponScreen(
onNavigateUp: () -> Unit,
) {
val toastHostState = remember { ToastHostState() }
val coroutineScope = rememberCoroutineScope()

val toastHostState = rememberToastHostState()
Scaffold(
modifier = Modifier.testTag(SubScreenSemantics.Tag),
topBar = {
Expand All @@ -50,9 +45,7 @@ internal fun CouponScreen(
) {
CouponScreenInner(
onCouponCopied = {
coroutineScope.launch {
toastHostState.showToast("Copied to clipboard!") { Icons.Copy }
}
toastHostState.showToast("Copied to clipboard!", IconName.Copy)
},
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,25 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kiwi.orbit.compose.catalog.semantics.SubScreenSemantics
import kiwi.orbit.compose.icons.Icons
import kiwi.orbit.compose.icons.IconName
import kiwi.orbit.compose.ui.OrbitTheme
import kiwi.orbit.compose.ui.controls.Scaffold
import kiwi.orbit.compose.ui.controls.Tag
import kiwi.orbit.compose.ui.controls.Text
import kiwi.orbit.compose.ui.controls.ToastHostState
import kiwi.orbit.compose.ui.controls.TopAppBar
import kotlinx.coroutines.launch
import kiwi.orbit.compose.ui.controls.rememberToastHostState

@Composable
internal fun TagScreen(onNavigateUp: () -> Unit) {
val toastHostState = remember { ToastHostState() }
val toastHostState = rememberToastHostState()
Scaffold(
modifier = Modifier.testTag(SubScreenSemantics.Tag),
topBar = {
Expand All @@ -57,11 +55,8 @@ internal fun TagScreen(onNavigateUp: () -> Unit) {
@Composable
private fun TagScreenInner(toastHostState: ToastHostState) {
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
val scope = rememberCoroutineScope()
val onRemove: () -> Unit = {
scope.launch {
toastHostState.showToast("Tag removed.") { Icons.InformationCircle }
}
toastHostState.showToast("Tag removed.", IconName.InformationCircle)
}

Text("Non-interactive", style = OrbitTheme.typography.title4)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,37 @@ import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
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 androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import kiwi.orbit.compose.catalog.semantics.SubScreenSemantics
import kiwi.orbit.compose.catalog.semantics.ToastScreenSemantics
import kiwi.orbit.compose.icons.Icons
import kiwi.orbit.compose.icons.IconName
import kiwi.orbit.compose.ui.controls.ButtonSecondary
import kiwi.orbit.compose.ui.controls.Scaffold
import kiwi.orbit.compose.ui.controls.Text
import kiwi.orbit.compose.ui.controls.ToastHostState
import kiwi.orbit.compose.ui.controls.TopAppBar
import kotlinx.coroutines.launch
import kiwi.orbit.compose.ui.controls.rememberToastHostState
import kotlinx.datetime.Clock
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime

@Composable
internal fun ToastScreen(
onNavigateUp: () -> Unit,
) {
val toastHostState = remember { ToastHostState() }
val scope = rememberCoroutineScope()
var dismissedText by remember { mutableStateOf("") }
val toastHostState = rememberToastHostState(
onDismiss = {
val now = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())
dismissedText = "Last toast dismissed at ${now.hour}:${now.minute}:${now.second}"
},
)
Scaffold(
modifier = Modifier.testTag(SubScreenSemantics.Tag),
topBar = {
Expand All @@ -48,18 +56,17 @@ internal fun ToastScreen(
.fillMaxSize()
.padding(contentPadding),
) {
ToastScreenInner { message, icon ->
scope.launch {
toastHostState.showToast(message, icon)
}
ToastScreenInner(dismissedText) { message, iconName ->
toastHostState.showToast(message, iconName)
}
}
}
}

@Composable
private fun ToastScreenInner(
onToast: (String, @Composable (() -> Painter)?) -> Unit,
dismissedText: String,
onToast: (String, IconName?) -> Unit,
) {
Column(
Modifier
Expand All @@ -71,39 +78,36 @@ private fun ToastScreenInner(
horizontalAlignment = Alignment.CenterHorizontally,
) {
ButtonSecondary(
onClick = {
onToast("You’re signed in as jon.snow@wall.7k.") { Icons.CheckCircle }
},
onClick = { onToast("You’re signed in as jon.snow@wall.7k.", IconName.CheckCircle) },
modifier = Modifier.testTag(ToastScreenSemantics.ToastSignedInButtonTag),
) { Text("Toast – signed in") }

content = { Text("Toast – signed in") },
)
ButtonSecondary(
onClick = {
onToast("Price alert was removed.") { Icons.NotificationOff }
},
) { Text("Toast – price alert") }

onClick = { onToast("Price alert was removed.", IconName.NotificationOff) },
content = { Text("Toast – price alert") },
)
ButtonSecondary(
onClick = { onToast("We’ll notify you when the price changes.", IconName.Notification) },
content = { Text("Toast – price alert created") },
)
ButtonSecondary(
onClick = {
onToast("We’ll notify you when the price changes.") { Icons.Notification }
onToast("On mobile there’s always a fixed width to make the Toast stand out a bit more.", null)
},
) { Text("Toast – price alert created") }

content = { Text("Toast – long message") },
)
ButtonSecondary(
onClick = {
onToast(
"On mobile there’s always a fixed width to make the Toast stand out a bit more.",
null,
IconName.AirplaneLanding,
)
},
) { Text("Toast – long message") }

ButtonSecondary(
onClick = {
onToast("On mobile there’s always a fixed width to make the Toast stand out a bit more.") {
Icons.AirplaneLanding
}
},
) { Text("Toast – long message 2") }
content = { Text("Toast – long message 2") },
)
Text(
text = dismissedText,
modifier = Modifier.padding(top = 24.dp),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public fun Scaffold(
content = action,
)
},
toastHostState: ToastHostState = remember { ToastHostState() },
toastHostState: ToastHostState = rememberToastHostState(),
toastHost: @Composable (ToastHostState) -> Unit = { ToastHost(it) },
contentWindowInsets: WindowInsets = WindowInsets.ime,
content: @Composable (contentPadding: PaddingValues) -> Unit,
Expand Down
20 changes: 10 additions & 10 deletions ui/src/androidMain/kotlin/kiwi/orbit/compose/ui/controls/Toast.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.positionChange
import androidx.compose.ui.input.pointer.util.VelocityTracker
import androidx.compose.ui.platform.AccessibilityManager
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import kiwi.orbit.compose.icons.Icons
import kiwi.orbit.compose.icons.IconName
import kiwi.orbit.compose.icons.painter
import kiwi.orbit.compose.ui.OrbitTheme
import kiwi.orbit.compose.ui.controls.internal.OrbitPreviews
import kiwi.orbit.compose.ui.controls.internal.Preview
Expand All @@ -49,7 +49,7 @@ import kotlinx.coroutines.launch

public interface ToastData {
public val message: String
public val icon: @Composable (() -> Painter)?
public val iconName: IconName?
Copy link
Contributor

@shanio shanio Dec 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would really like the name to be part of "Painter" in icons (e.g. by encapsulating the painter in our own object or similarly...), it would allow to stick with usage of Icons even here, internally it would derive the name and back through your mapping, but that's just a wish I guess.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would you deserialize the name back to the painter - especially in the case of your own set of icons?


public val animationDuration: StateFlow<Duration?>

Expand All @@ -72,7 +72,7 @@ public fun Toast(
key(toastData) {
Toast(
message = toastData.message,
icon = toastData.icon,
iconName = toastData.iconName,
animateDuration = animateDuration,
onPause = toastData::pause,
onResume = toastData::resume,
Expand All @@ -84,7 +84,7 @@ public fun Toast(
@Composable
private fun Toast(
message: String,
icon: @Composable (() -> Painter)?,
iconName: IconName?,
animateDuration: Duration? = Duration.ZERO,
onPause: () -> Unit = {},
onResume: () -> Unit = {},
Expand Down Expand Up @@ -135,9 +135,9 @@ private fun Toast(
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
ProvideMergedTextStyle(OrbitTheme.typography.bodyNormal) {
if (icon != null) {
if (iconName != null) {
Icon(
icon(),
iconName.painter(),
contentDescription = null,
)
}
Expand Down Expand Up @@ -233,15 +233,15 @@ internal fun ToastPreview() {
Preview {
Toast(
message = "Message",
icon = null,
iconName = null,
)
Toast(
message = "Message with icon",
icon = { Icons.CheckCircle },
iconName = IconName.CheckCircle,
)
Toast(
message = "Message with icon and very long message with many words.",
icon = { Icons.CheckCircle },
iconName = IconName.CheckCircle,
)
}
}
Loading