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

GT-2314 use Circuit to render the Dashboard Home #3790

Merged
merged 7 commits into from
Nov 26, 2024
Merged
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 @@ -57,8 +57,7 @@ import org.cru.godtools.base.ui.theme.GodToolsTheme
import org.cru.godtools.model.Tool
import org.cru.godtools.shared.analytics.AnalyticsScreenNames
import org.cru.godtools.ui.dashboard.home.AllFavoritesScreen
import org.cru.godtools.ui.dashboard.home.DashboardHomeEvent
import org.cru.godtools.ui.dashboard.home.HomeContent
import org.cru.godtools.ui.dashboard.home.HomeScreen
import org.cru.godtools.ui.dashboard.lessons.DashboardLessonsEvent
import org.cru.godtools.ui.dashboard.lessons.LessonsLayout
import org.cru.godtools.ui.dashboard.tools.ToolsScreen
Expand Down Expand Up @@ -130,33 +129,23 @@ internal fun DashboardLayout(onEvent: (DashboardEvent) -> Unit, viewModel: Dashb
},
)

Page.HOME -> HomeContent(
onEvent = {
when (it) {
DashboardHomeEvent.ViewAllFavorites -> {
saveableStateHolder.removeState(Page.FAVORITE_TOOLS)
viewModel.updateCurrentPage(Page.FAVORITE_TOOLS, false)
}
DashboardHomeEvent.ViewAllTools -> viewModel.updateCurrentPage(Page.ALL_TOOLS)
is DashboardHomeEvent.OpenTool ->
onEvent(DashboardEvent.OpenTool(it.tool, it.type, it.lang1, it.lang2))
is DashboardHomeEvent.OpenToolDetails ->
onEvent(DashboardEvent.OpenToolDetails(it.tool))
}
}
)

Page.HOME,
Page.FAVORITE_TOOLS,
Page.ALL_TOOLS -> {
CircuitContent(
screen = when (page) {
Page.HOME -> HomeScreen
Page.FAVORITE_TOOLS -> AllFavoritesScreen
Page.ALL_TOOLS -> ToolsScreen
else -> error("Page $page is not converted to Circuit yet")
},
onNavEvent = {
when (it) {
is NavEvent.GoTo -> when (val screen = it.screen) {
AllFavoritesScreen -> {
saveableStateHolder.removeState(Page.FAVORITE_TOOLS)
viewModel.updateCurrentPage(Page.FAVORITE_TOOLS, false)
}
is IntentScreen -> onEvent(DashboardEvent.OpenIntent(screen.intent))
is ToolDetailsScreen -> onEvent(
DashboardEvent.OpenToolDetails(
Expand All @@ -165,6 +154,9 @@ internal fun DashboardLayout(onEvent: (DashboardEvent) -> Unit, viewModel: Dashb
)
)
}
is NavEvent.ResetRoot -> when (it.newRoot) {
ToolsScreen -> viewModel.updateCurrentPage(Page.ALL_TOOLS)
}
else -> Unit
}
},
Expand Down
145 changes: 44 additions & 101 deletions app/src/main/kotlin/org/cru/godtools/ui/dashboard/home/HomeLayout.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,60 +21,37 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import java.util.Locale
import org.cru.godtools.BuildConfig
import com.slack.circuit.codegen.annotations.CircuitInject
import dagger.hilt.components.SingletonComponent
import org.cru.godtools.R
import org.cru.godtools.analytics.model.OpenAnalyticsActionEvent.Companion.ACTION_OPEN_LESSON
import org.cru.godtools.analytics.model.OpenAnalyticsActionEvent.Companion.ACTION_OPEN_TOOL
import org.cru.godtools.analytics.model.OpenAnalyticsActionEvent.Companion.ACTION_OPEN_TOOL_DETAILS
import org.cru.godtools.analytics.model.OpenAnalyticsActionEvent.Companion.SOURCE_FAVORITE
import org.cru.godtools.analytics.model.OpenAnalyticsActionEvent.Companion.SOURCE_FEATURED
import org.cru.godtools.model.Tool
import org.cru.godtools.ui.banner.Banners
import org.cru.godtools.ui.dashboard.home.HomeScreen.UiEvent
import org.cru.godtools.ui.dashboard.home.HomeScreen.UiState
import org.cru.godtools.ui.tools.LessonToolCard
import org.cru.godtools.ui.tools.PreloadTool
import org.cru.godtools.ui.tools.SquareToolCard
import org.cru.godtools.ui.tools.ToolCardEvent

private val PADDING_HORIZONTAL = 16.dp

internal sealed interface DashboardHomeEvent {
open class OpenTool(val tool: String?, val type: Tool.Type?, val lang1: Locale?, val lang2: Locale? = null) :
DashboardHomeEvent {
constructor(event: ToolCardEvent) : this(event.tool, event.toolType, event.lang1, event.lang2)
}
open class OpenToolDetails(val tool: String?) : DashboardHomeEvent {
constructor(event: ToolCardEvent.OpenToolDetails) : this(event.tool)
}
class OpenLesson(event: ToolCardEvent) : OpenTool(event.tool, Tool.Type.LESSON, event.lang1)
data object ViewAllFavorites : DashboardHomeEvent
data object ViewAllTools : DashboardHomeEvent
}

@Composable
internal fun HomeContent(onEvent: (DashboardHomeEvent) -> Unit, viewModel: HomeViewModel = viewModel()) {
val favoriteTools by viewModel.favoriteTools.collectAsState()
val spotlightLessons by viewModel.spotlightLessons.collectAsState()
val favoriteToolsLoaded by remember { derivedStateOf { favoriteTools != null } }
val hasFavoriteTools by remember { derivedStateOf { !favoriteTools.isNullOrEmpty() } }
@CircuitInject(HomeScreen::class, SingletonComponent::class)
internal fun HomeLayout(state: UiState, modifier: Modifier = Modifier) {
val banner by rememberUpdatedState(state.banner)
val favoriteToolsLoaded by rememberUpdatedState(state.favoriteToolsLoaded)

val hasFavoriteTools by rememberUpdatedState(state.favoriteTools.isNotEmpty())

val columnState = rememberLazyListState()
val banner by viewModel.banner.collectAsState()
LaunchedEffect(banner) { if (banner != null) columnState.animateScrollToItem(0) }

LazyColumn(state = columnState, contentPadding = PaddingValues(bottom = 16.dp)) {
LazyColumn(state = columnState, contentPadding = PaddingValues(bottom = 16.dp), modifier = modifier) {
item("banners", "banners") {
Banners(
{ banner },
Expand All @@ -94,7 +71,7 @@ internal fun HomeContent(onEvent: (DashboardHomeEvent) -> Unit, viewModel: HomeV
}

// featured lessons
if (spotlightLessons.isNotEmpty()) {
if (state.spotlightLessons.isNotEmpty()) {
item("lesson-header", "lesson-header") {
FeaturedLessonsHeader(
modifier = Modifier
Expand All @@ -104,20 +81,13 @@ internal fun HomeContent(onEvent: (DashboardHomeEvent) -> Unit, viewModel: HomeV
)
}

items(spotlightLessons, key = { it }, contentType = { "lesson-tool-card" }) { lesson ->
items(
state.spotlightLessons,
key = { it.toolCode.orEmpty() },
contentType = { "lesson-tool-card" }
) { lessonState ->
LessonToolCard(
lesson,
onEvent = {
when (it) {
is ToolCardEvent.Click, is ToolCardEvent.OpenTool -> {
viewModel.recordOpenClickInAnalytics(ACTION_OPEN_LESSON, it.tool, SOURCE_FEATURED)
onEvent(DashboardHomeEvent.OpenLesson(it))
}
is ToolCardEvent.OpenToolDetails -> {
if (BuildConfig.DEBUG) error("$it is currently unsupported for Lesson Cards")
}
}
},
lessonState,
modifier = Modifier
.animateItem()
.padding(horizontal = PADDING_HORIZONTAL)
Expand All @@ -130,8 +100,7 @@ internal fun HomeContent(onEvent: (DashboardHomeEvent) -> Unit, viewModel: HomeV
if (favoriteToolsLoaded) {
item("favorites-header") {
FavoritesHeader(
showViewAll = { hasFavoriteTools },
onEvent = onEvent,
state = state,
modifier = Modifier
.animateItem()
.padding(horizontal = PADDING_HORIZONTAL)
Expand All @@ -142,22 +111,7 @@ internal fun HomeContent(onEvent: (DashboardHomeEvent) -> Unit, viewModel: HomeV
if (hasFavoriteTools) {
item("favorites", "favorites") {
HorizontalFavoriteTools(
{ favoriteTools.orEmpty().take(5) },
onEvent = {
when {
it is DashboardHomeEvent.OpenTool -> viewModel.recordOpenClickInAnalytics(
ACTION_OPEN_TOOL,
it.tool,
SOURCE_FAVORITE
)
it is DashboardHomeEvent.OpenToolDetails -> viewModel.recordOpenClickInAnalytics(
ACTION_OPEN_TOOL_DETAILS,
it.tool,
SOURCE_FAVORITE
)
}
onEvent(it)
},
state,
modifier = Modifier
.animateItem()
.fillMaxWidth()
Expand All @@ -166,7 +120,7 @@ internal fun HomeContent(onEvent: (DashboardHomeEvent) -> Unit, viewModel: HomeV
} else {
item("favorites-empty", "favorites-empty") {
NoFavoriteTools(
onEvent = onEvent,
state = state,
modifier = Modifier
.animateItem()
.padding(horizontal = PADDING_HORIZONTAL)
Expand All @@ -192,11 +146,9 @@ private fun FeaturedLessonsHeader(modifier: Modifier = Modifier) = Text(
)

@Composable
private fun FavoritesHeader(
showViewAll: () -> Boolean,
onEvent: (DashboardHomeEvent) -> Unit,
modifier: Modifier = Modifier,
) = Row(modifier = modifier.fillMaxWidth()) {
private fun FavoritesHeader(state: UiState, modifier: Modifier = Modifier) = Row(modifier = modifier.fillMaxWidth()) {
val eventSink by rememberUpdatedState(state.eventSink)

Text(
stringResource(R.string.dashboard_home_section_favorites_title),
style = MaterialTheme.typography.titleLarge,
Expand All @@ -206,7 +158,7 @@ private fun FavoritesHeader(
)

AnimatedVisibility(
showViewAll(),
state.favoriteTools.isNotEmpty(),
enter = fadeIn(),
exit = fadeOut(),
modifier = Modifier.alignByBaseline()
Expand All @@ -215,47 +167,38 @@ private fun FavoritesHeader(
stringResource(R.string.dashboard_home_section_favorites_action_view_all),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.clickable { onEvent(DashboardHomeEvent.ViewAllFavorites) }
modifier = Modifier.clickable { eventSink(UiEvent.ViewAllFavorites) }
)
}
}

@Composable
private fun HorizontalFavoriteTools(
tools: () -> List<Tool>,
onEvent: (DashboardHomeEvent) -> Unit,
modifier: Modifier = Modifier,
) = LazyRow(
contentPadding = PaddingValues(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier
) {
items(tools(), key = { it.code.orEmpty() }) {
PreloadTool(it)

SquareToolCard(
toolCode = it.code.orEmpty(),
confirmRemovalFromFavorites = true,
onEvent = {
when (it) {
is ToolCardEvent.Click, is ToolCardEvent.OpenTool -> onEvent(DashboardHomeEvent.OpenTool(it))
is ToolCardEvent.OpenToolDetails -> onEvent(DashboardHomeEvent.OpenToolDetails(it))
}
},
modifier = Modifier.animateItem()
)
private fun HorizontalFavoriteTools(state: UiState, modifier: Modifier = Modifier) {
LazyRow(
contentPadding = PaddingValues(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier
) {
items(state.favoriteTools, key = { it.toolCode.orEmpty() }) { toolState ->
SquareToolCard(
state = toolState,
confirmRemovalFromFavorites = true,
modifier = Modifier.animateItem()
)
}
}
}

@Preview
@Composable
private fun NoFavoriteTools(modifier: Modifier = Modifier, onEvent: (DashboardHomeEvent) -> Unit = {}) = Surface(
private fun NoFavoriteTools(state: UiState, modifier: Modifier = Modifier) = Surface(
color = MaterialTheme.colorScheme.surfaceVariant,
shape = RectangleShape,
modifier = modifier
.fillMaxWidth()
.heightIn(min = 215.dp)
) {
val eventSink by rememberUpdatedState(state.eventSink)

Column(verticalArrangement = Arrangement.Center, modifier = Modifier.padding(16.dp)) {
Text(
stringResource(R.string.dashboard_home_section_favorites_no_tools_title),
Expand All @@ -270,7 +213,7 @@ private fun NoFavoriteTools(modifier: Modifier = Modifier, onEvent: (DashboardHo
modifier = Modifier.fillMaxWidth()
)
Button(
onClick = { onEvent(DashboardHomeEvent.ViewAllTools) },
onClick = { eventSink(UiEvent.ViewAllTools) },
modifier = Modifier
.padding(top = 8.dp)
.align(Alignment.CenterHorizontally)
Expand Down
Loading
Loading