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

Convert HomeScreen to interface pattern #1168

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
8 changes: 3 additions & 5 deletions app-scaffold/src/main/kotlin/catchup/app/home/HomeList.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import catchup.app.home.HomeScreen.Event.Selected
import catchup.app.home.HomeScreen.Event.ShowChangelog
import catchup.app.home.HomeScreen.State
import catchup.compose.dynamicAwareColor
import dev.zacsweers.catchup.app.scaffold.R
Expand All @@ -48,7 +46,7 @@ fun HomeList(state: State, modifier: Modifier = Modifier) {
modifier = Modifier.padding(bottom = 32.dp, start = 12.dp)
)
if (state.changelogAvailable) {
ChangelogButton(Modifier.align(Alignment.TopEnd)) { state.eventSink(ShowChangelog) }
ChangelogButton(Modifier.align(Alignment.TopEnd), state::showChangelog)
}
}
}
Expand All @@ -61,7 +59,7 @@ fun HomeList(state: State, modifier: Modifier = Modifier) {
serviceTint = colorResource(meta.themeColor),
description = "",
isSelected = state.selectedIndex == index,
onClick = { state.eventSink(Selected(index)) }
onClick = { state.selected(index) }
)
}
}
Expand All @@ -75,7 +73,7 @@ fun HomeList(state: State, modifier: Modifier = Modifier) {
serviceTint = MaterialTheme.colorScheme.primary,
description = "Miscellaneous CatchUp settings",
isSelected = state.selectedIndex == index,
onClick = { state.eventSink(Selected(index)) }
onClick = { state.selected(index) }
)
}
}
Expand Down
98 changes: 46 additions & 52 deletions app-scaffold/src/main/kotlin/catchup/app/home/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,6 @@ import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.window.layout.FoldingFeature
import catchup.app.CatchUpPreferences
import catchup.app.changes.ChangelogHelper
import catchup.app.home.HomeScreen.Event.NestedNavEvent
import catchup.app.home.HomeScreen.Event.OpenBookmarks
import catchup.app.home.HomeScreen.Event.OpenSettings
import catchup.app.home.HomeScreen.Event.Selected
import catchup.app.home.HomeScreen.Event.ShowChangelog
import catchup.app.home.HomeScreen.State
import catchup.app.service.ServiceScreen
import catchup.app.service.bookmarks.Bookmark
Expand All @@ -91,7 +86,6 @@ import com.slack.circuit.foundation.CircuitContent
import com.slack.circuit.foundation.NavEvent
import com.slack.circuit.foundation.onNavEvent
import com.slack.circuit.overlay.LocalOverlayHost
import com.slack.circuit.runtime.CircuitUiEvent
import com.slack.circuit.runtime.CircuitUiState
import com.slack.circuit.runtime.Navigator
import com.slack.circuit.runtime.presenter.Presenter
Expand Down Expand Up @@ -126,24 +120,21 @@ import kotlinx.parcelize.Parcelize
object HomeScreen : Screen, DeepLinkable {
override fun createScreen(queryParams: ImmutableMap<String, List<String?>>): Screen = HomeScreen

data class State(
val serviceMetas: ImmutableList<ServiceMeta>,
val changelogAvailable: Boolean,
val selectedIndex: Int,
val bookmarksCount: Long,
val eventSink: (Event) -> Unit = {}
) : CircuitUiState
interface State : CircuitUiState {
val serviceMetas: ImmutableList<ServiceMeta>
val changelogAvailable: Boolean
val selectedIndex: Int
val bookmarksCount: Long

sealed interface Event : CircuitUiEvent {
data object OpenSettings : Event
fun openSettings()

data object OpenBookmarks : Event
fun openBookmarks()

data object ShowChangelog : Event
fun showChangelog()

data class NestedNavEvent(val navEvent: NavEvent) : Event
fun nestedNavEvent(navEvent: NavEvent)

data class Selected(val index: Int) : Event
fun selected(index: Int)
}
}

Expand All @@ -166,7 +157,7 @@ constructor(
@Composable
override fun present(): State {
val currentOrder by
remember { catchUpPreferences.servicesOrder }.collectAsState(initial = persistentListOf())
remember(catchUpPreferences::servicesOrder).collectAsState(initial = persistentListOf())
var selectedIndex by remember(currentOrder) { mutableIntStateOf(0) }
val serviceMetas by
produceState(initialValue = persistentListOf(), currentOrder) {
Expand All @@ -186,43 +177,47 @@ constructor(
val context = LocalContext.current
val changelogAvailable by changelogHelper.changelogAvailable(context).collectAsState(false)

val countFlow = remember { bookmarkRepository.bookmarksCountFlow() }
val countFlow = remember(bookmarkRepository::bookmarksCountFlow)
val bookmarksCount by countFlow.collectAsState(0L)

val scope = rememberStableCoroutineScope()
val overlayHost = LocalOverlayHost.current
return State(
serviceMetas = serviceMetas,
changelogAvailable = changelogAvailable,
selectedIndex = selectedIndex,
bookmarksCount = bookmarksCount
) { event ->
when (event) {
OpenSettings -> {
return remember(scope, overlayHost) {
object : State {
override val serviceMetas = serviceMetas
override val changelogAvailable = changelogAvailable
override val selectedIndex = selectedIndex
override val bookmarksCount = bookmarksCount

override fun openSettings() {
navigator.goTo(SettingsScreen)
}
OpenBookmarks -> {

override fun openBookmarks() {
navigator.goTo(BookmarksScreen)
}
is NestedNavEvent -> {
navigator.onNavEvent(event.navEvent)
}
is Selected -> {
selectedIndex = event.index
// TODO only do this if we make a TwoPane nav-aware
// navigator.goTo(ServiceScreen(serviceMetas[event.index].id))
}
ShowChangelog -> {

override fun showChangelog() {
scope.launch {
overlayHost.show(
BottomSheetOverlay(
model = Unit,
content = { _, _ -> changelogHelper.Content() },
onDismiss = { Unit }
onDismiss = { }
)
)
}
}

override fun nestedNavEvent(navEvent: NavEvent) {
navigator.onNavEvent(navEvent)
}

override fun selected(index: Int) {
selectedIndex = index
// TODO only do this if we make a TwoPane nav-aware
// navigator.goTo(ServiceScreen(serviceMetas[event.index].id))
}
}
}
}
Expand All @@ -237,7 +232,7 @@ fun Home(state: State, modifier: Modifier = Modifier) {
// TODO movable for current content? How can we better save the state of the current detail
val displayFeatures = LocalDisplayFeatures.current
val foldingFeature =
remember(displayFeatures) { displayFeatures.filterIsInstance<FoldingFeature>().firstOrNull() }
remember(displayFeatures, displayFeatures.filterIsInstance<FoldingFeature>()::firstOrNull)

if (foldingFeature != null) {
// TODO
Expand Down Expand Up @@ -310,12 +305,12 @@ fun HomePager(state: State, modifier: Modifier = Modifier) {
if (state.serviceMetas.isEmpty()) return // Not loaded yet

val pagerState =
key(state.serviceMetas) { rememberPagerState(state.selectedIndex) { state.serviceMetas.size } }
key(state.serviceMetas) { rememberPagerState(state.selectedIndex, pageCount = state.serviceMetas::size) }

LaunchedEffect(pagerState) {
snapshotFlow { pagerState.settledPage }
snapshotFlow(pagerState::settledPage)
.distinctUntilChanged()
.collect { state.eventSink(Selected(it)) }
.collect(state::selected)
}

val currentServiceMeta = state.serviceMetas[pagerState.currentPage]
Expand All @@ -333,7 +328,7 @@ fun HomePager(state: State, modifier: Modifier = Modifier) {
if (!dynamicTheme) {
// Set the status bar color to match the top app bar when it's collapsed
LaunchedEffect(scrollBehavior) {
snapshotFlow { scrollBehavior.state.collapsedFraction }
snapshotFlow(scrollBehavior.state::collapsedFraction)
.collect { fraction ->
scrimColor = lerp(surfaceColor, tabLayoutColor.value, fraction)
if (fraction == 1.0f) {
Expand All @@ -346,7 +341,7 @@ fun HomePager(state: State, modifier: Modifier = Modifier) {

// Transition the color from one
LaunchedEffect(pagerState) {
snapshotFlow { pagerState.currentPageOffsetFraction }
snapshotFlow(pagerState::currentPageOffsetFraction)
.filterNot { isAnimatingColor }
.collect { offset ->
val position = pagerState.currentPage
Expand Down Expand Up @@ -379,7 +374,6 @@ fun HomePager(state: State, modifier: Modifier = Modifier) {
modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
}
val serviceMetas by rememberUpdatedState(state.serviceMetas)
val eventSink by rememberUpdatedState(state.eventSink)
HazeScaffold(
modifier = nestedScrollModifier,
contentWindowInsets = WindowInsets(0, 0, 0, 0),
Expand All @@ -395,7 +389,7 @@ fun HomePager(state: State, modifier: Modifier = Modifier) {
actions = {
// TODO wire with Syllabus
if (state.changelogAvailable) {
ChangelogButton { eventSink(ShowChangelog) }
ChangelogButton(onClick = state::showChangelog)
}

AnimatedVisibility(
Expand All @@ -408,7 +402,7 @@ fun HomePager(state: State, modifier: Modifier = Modifier) {
shouldWiggle = { old, new -> new > old },
) {
IconButton(
onClick = { eventSink(OpenBookmarks) },
onClick = state::openBookmarks,
) {
Icon(
imageVector = Icons.Filled.Bookmark,
Expand All @@ -418,7 +412,7 @@ fun HomePager(state: State, modifier: Modifier = Modifier) {
}
}
IconButton(
onClick = { eventSink(OpenSettings) },
onClick = state::openSettings,
) {
Icon(
imageVector = Icons.Default.Settings,
Expand All @@ -432,7 +426,7 @@ fun HomePager(state: State, modifier: Modifier = Modifier) {
Column(
modifier = Modifier.fillMaxSize().padding(innerPadding).consumeWindowInsets(innerPadding)
) {
val scrollToTop = remember { MutableScrollToTop() }
val scrollToTop = remember(::MutableScrollToTop)
val contentColor =
if (dynamicTheme) MaterialTheme.colorScheme.onPrimaryContainer else Color.White
val containerColor =
Expand Down Expand Up @@ -498,7 +492,7 @@ fun HomePager(state: State, modifier: Modifier = Modifier) {
) {
CircuitContent(
screen = ServiceScreen(serviceMetas[page].id),
onNavEvent = { eventSink(NestedNavEvent(it)) }
onNavEvent = state::nestedNavEvent
)
}
}
Expand Down
Loading