Skip to content

Commit

Permalink
switch to bottom sheet for character
Browse files Browse the repository at this point in the history
  • Loading branch information
DatL4g committed Apr 24, 2024
1 parent 77a9b8e commit c08cab2
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 131 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ class MainActivity : AppCompatActivity() {

setContent {
CompositionLocalProvider(
LocalLifecycleOwner provides lifecycleOwner
LocalLifecycleOwner provides lifecycleOwner,
LocalEdgeToEdge provides true
) {
App(
di = di
Expand Down
1 change: 1 addition & 0 deletions composeApp/src/commonMain/kotlin/dev/datlag/aniflow/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import dev.icerock.moko.resources.compose.asFont
import org.kodein.di.DI

val LocalDarkMode = compositionLocalOf<Boolean> { error("No dark mode state provided") }
val LocalEdgeToEdge = staticCompositionLocalOf<Boolean> { false }
val LocalDI = compositionLocalOf<DI> { error("No dependency injection provided") }
val LocalHaze = compositionLocalOf<HazeState> { error("No Haze state provided") }
val LocalPaddingValues = compositionLocalOf<PaddingValues?> { null }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SheetState
import androidx.compose.material3.SheetValue
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
Expand Down Expand Up @@ -168,4 +171,21 @@ fun LazyListState.isScrollingUp(): Boolean {
}
}
}.value
}

/**
* Checks if the modal is currently expanded or a swipe action is in progress to be expanded.
*/
@OptIn(ExperimentalMaterial3Api::class)
fun SheetState.isFullyExpandedOrTargeted(forceFullExpand: Boolean = false): Boolean {
val checkState = if (this.hasExpandedState) {
SheetValue.Expanded
} else {
if (forceFullExpand) {
return false
}
SheetValue.PartiallyExpanded
}

return this.targetValue == checkState
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
package dev.datlag.aniflow.ui.navigation.screen.medium.dialog.character

import androidx.compose.foundation.Image
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Bloodtype
import androidx.compose.material.icons.filled.Cake
import androidx.compose.material.icons.filled.Man4
import androidx.compose.material.icons.filled.Translate
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
Expand All @@ -23,25 +20,44 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import coil3.compose.rememberAsyncImagePainter
import dev.datlag.aniflow.LocalEdgeToEdge
import dev.datlag.aniflow.SharedRes
import dev.datlag.aniflow.anilist.CharacterStateMachine
import dev.datlag.aniflow.common.htmlToAnnotatedString
import dev.datlag.aniflow.common.isFullyExpandedOrTargeted
import dev.datlag.aniflow.common.preferred
import dev.datlag.aniflow.common.preferredName
import dev.datlag.aniflow.ui.navigation.screen.medium.component.TranslateButton
import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle
import dev.icerock.moko.resources.compose.stringResource

@OptIn(ExperimentalLayoutApi::class)
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
@Composable
fun CharacterDialog(component: CharacterComponent) {
AlertDialog(
val sheetState = rememberModalBottomSheetState()
val insets = if (LocalEdgeToEdge.current) {
BottomSheetDefaults.windowInsets.only(WindowInsetsSides.Bottom)
} else {
BottomSheetDefaults.windowInsets
}

ModalBottomSheet(
onDismissRequest = component::dismiss,
icon = {
windowInsets = insets,
sheetState = sheetState
) {
val name by component.name.collectAsStateWithLifecycle()

Box(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
contentAlignment = Alignment.Center
) {
val image by component.image.collectAsStateWithLifecycle()

AsyncImage(
modifier = Modifier.size(80.dp).clip(CircleShape),
modifier = Modifier
.size(80.dp)
.clip(CircleShape),
model = image.large,
error = rememberAsyncImagePainter(
model = image.medium,
Expand All @@ -51,142 +67,135 @@ fun CharacterDialog(component: CharacterComponent) {
alignment = Alignment.Center,
contentDescription = component.initialChar.preferredName()
)
},
title = {
val name by component.name.collectAsStateWithLifecycle()

Text(
text = name.preferred(),
style = MaterialTheme.typography.headlineMedium,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
fontWeight = FontWeight.SemiBold,
softWrap = true
)
},
text = {
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterVertically),
horizontalAlignment = Alignment.CenterHorizontally
this@ModalBottomSheet.AnimatedVisibility(
modifier = Modifier.align(Alignment.CenterStart),
visible = sheetState.isFullyExpandedOrTargeted(forceFullExpand = true),
enter = fadeIn(),
exit = fadeOut()
) {
val description by component.description.collectAsStateWithLifecycle()
val translatedDescription by component.translatedDescription.collectAsStateWithLifecycle()

FlowRow(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically),
horizontalArrangement = Arrangement.SpaceAround
IconButton(
onClick = component::dismiss
) {
val gender by component.gender.collectAsStateWithLifecycle()
val bloodType by component.bloodType.collectAsStateWithLifecycle()
val birthDate by component.birthDate.collectAsStateWithLifecycle()
Icon(
imageVector = Icons.Default.ArrowBackIosNew,
contentDescription = stringResource(SharedRes.strings.close)
)
}
}
}

bloodType?.let {
Column(
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
imageVector = Icons.Filled.Bloodtype,
contentDescription = null
)
Text(
text = stringResource(SharedRes.strings.blood_type),
style = MaterialTheme.typography.labelSmall,
)
Text(
text = it,
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold
)
}
}
Text(
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(vertical = 8.dp),
text = name.preferred(),
style = MaterialTheme.typography.headlineMedium,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
fontWeight = FontWeight.SemiBold,
softWrap = true
)

gender?.let {
Column(
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
imageVector = Icons.Filled.Man4,
contentDescription = null
)
Text(
text = stringResource(SharedRes.strings.gender),
style = MaterialTheme.typography.labelSmall,
)
Text(
text = it,
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold
)
}
}
Column(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically),
horizontalAlignment = Alignment.CenterHorizontally
) {
val description by component.description.collectAsStateWithLifecycle()
val translatedDescription by component.translatedDescription.collectAsStateWithLifecycle()

birthDate?.let {
Column(
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
imageVector = Icons.Filled.Cake,
contentDescription = null
)
Text(
text = stringResource(SharedRes.strings.birth_date),
style = MaterialTheme.typography.labelSmall,
)
Text(
text = it.format(),
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold
)
}
FlowRow(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically),
horizontalArrangement = Arrangement.SpaceAround
) {
val gender by component.gender.collectAsStateWithLifecycle()
val bloodType by component.bloodType.collectAsStateWithLifecycle()
val birthDate by component.birthDate.collectAsStateWithLifecycle()

bloodType?.let {
Column(
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
imageVector = Icons.Filled.Bloodtype,
contentDescription = null
)
Text(
text = stringResource(SharedRes.strings.blood_type),
style = MaterialTheme.typography.labelSmall,
)
Text(
text = it,
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold
)
}
}

(translatedDescription ?: description)?.let {
Text(
modifier = Modifier.verticalScroll(rememberScrollState()),
text = it.htmlToAnnotatedString()
)
} ?: run {
val state by component.state.collectAsStateWithLifecycle()
gender?.let {
Column(
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
imageVector = Icons.Filled.Man4,
contentDescription = null
)
Text(
text = stringResource(SharedRes.strings.gender),
style = MaterialTheme.typography.labelSmall,
)
Text(
text = it,
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold
)
}
}

if (state is CharacterStateMachine.State.Loading) {
CircularProgressIndicator()
birthDate?.let {
Column(
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
imageVector = Icons.Filled.Cake,
contentDescription = null
)
Text(
text = stringResource(SharedRes.strings.birth_date),
style = MaterialTheme.typography.labelSmall,
)
Text(
text = it.format(),
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold
)
}
// ToDo("Display something went wrong")
}
}
},
dismissButton = {
val state by component.state.collectAsStateWithLifecycle()
val description by component.description.collectAsStateWithLifecycle()

description?.let {
TranslateButton(
text = it,
) { text ->
(translatedDescription ?: description)?.let {
TranslateButton(description ?: "") { text ->
component.descriptionTranslation(text)
}
Text(
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(bottom = 16.dp),
text = it.htmlToAnnotatedString()
)
} ?: run {
if (state is CharacterStateMachine.State.Error) {
TextButton(
onClick = component::retry
) {
Text(text = stringResource(SharedRes.strings.retry))
}
val state by component.state.collectAsStateWithLifecycle()

if (state is CharacterStateMachine.State.Loading) {
CircularProgressIndicator()
}
}
},
confirmButton = {
TextButton(
onClick = component::dismiss
) {
Text(text = stringResource(SharedRes.strings.close))
// ToDo("Display something went wrong")
}
}
)
}
}

0 comments on commit c08cab2

Please sign in to comment.