Skip to content

Commit

Permalink
Merge pull request #3790 from CruGlobal/circuitDashboardHome
Browse files Browse the repository at this point in the history
GT-2314 use Circuit to render the Dashboard Home
  • Loading branch information
frett authored Nov 26, 2024
2 parents 1ed32f3 + b2ca1c8 commit 5d79459
Show file tree
Hide file tree
Showing 29 changed files with 674 additions and 225 deletions.
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

0 comments on commit 5d79459

Please sign in to comment.