Skip to content

Commit

Permalink
- Update: Improve Otaku List Screen and Recent Fragment
Browse files Browse the repository at this point in the history
This commit introduces several improvements to the Otaku List Screen and Recent Fragment:

- **Otaku List Screen:**
    - Updates animations for the details pane to use `EnterTransition.None` and `ExitTransition.None` when transitioning between items, providing a smoother experience.
    - Changes the `AnimatedPanes` function to be a part of `ThreePaneScaffoldPaneScope`, aligning with the updated API.
    - Introduces a new state variable `viewingItem` in `OtakuListViewModel` to track the currently viewed item, which is used for animations and data updates.
    - Updates the list and search views to use `viewingItem` for displaying data, ensuring consistency.
    - Modifies the saving logic to use `viewingItem` for writing data to the file.
- **Recent Fragment:**
    - Improves the layout for offline states in the Recent Fragment, placing the cloud off icon and offline text in the center of the screen.
    - Adds padding to the shimmer item when the filtered source list is empty, enhancing visual consistency.
    - Encloses the offline state and online content in separate blocks, improving code structure.
- **Dependency Updates:**
    - Updates the Kotlin Symbol Processing (KSP) version to 2.0.21-1.0.28.
    - Updates the Haze library to version 1.0.2.
  • Loading branch information
jacobrein committed Nov 19, 2024
1 parent ab9d185 commit 87009dd
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package com.programmersbox.uiviews.lists
import androidx.activity.compose.BackHandler
import androidx.biometric.BiometricPrompt
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
Expand All @@ -25,11 +27,13 @@ import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.compose.material3.adaptive.allVerticalHingeBounds
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.material3.adaptive.currentWindowSize
import androidx.compose.material3.adaptive.layout.AnimatedPane
import androidx.compose.material3.adaptive.layout.AnimatedPaneScope
import androidx.compose.material3.adaptive.layout.HingePolicy
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.layout.PaneScaffoldDirective
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
import androidx.compose.material3.adaptive.occludingVerticalHingeBounds
import androidx.compose.material3.adaptive.separatingVerticalHingeBounds
Expand Down Expand Up @@ -79,13 +83,16 @@ fun OtakuListScreen(
)
)

val details: @Composable ThreePaneScaffoldScope.() -> Unit = {
val details: @Composable ThreePaneScaffoldPaneScope.() -> Unit = {
AnimatedPanes(modifier = Modifier.fillMaxSize()) {
AnimatedContent(
targetState = viewModel.customItem,
targetState = viewModel.viewingItem,
label = "",
transitionSpec = {
(slideInHorizontally { -it } + fadeIn()) togetherWith (fadeOut() + slideOutHorizontally { -it })
if (initialState != null && targetState != null)
EnterTransition.None togetherWith ExitTransition.None
else
(slideInHorizontally { -it } + fadeIn()) togetherWith (fadeOut() + slideOutHorizontally { -it })
}
) { targetState ->
if (targetState != null) {
Expand Down Expand Up @@ -177,15 +184,14 @@ fun OtakuListScreen(

@ExperimentalMaterial3AdaptiveApi
@Composable
fun ThreePaneScaffoldScope.AnimatedPanes(
fun ThreePaneScaffoldPaneScope.AnimatedPanes(
modifier: Modifier,
content: (@Composable () -> Unit),
content: @Composable AnimatedPaneScope.() -> Unit,
) {
/*AnimatedPane(
AnimatedPane(
modifier = modifier,
content = content
)*/
content()
)
}

@ExperimentalMaterial3AdaptiveApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.programmersbox.favoritesdatabase.CustomList
import com.programmersbox.favoritesdatabase.CustomListInfo
import com.programmersbox.favoritesdatabase.ListDao
import com.programmersbox.gsonutils.toJson
import kotlinx.coroutines.flow.flatMapMerge
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
Expand All @@ -27,24 +30,30 @@ class OtakuListViewModel(

var customItem: CustomList? by mutableStateOf(null)

var viewingItem: CustomList? by mutableStateOf(null)
private set

var searchBarActive by mutableStateOf(false)
var searchQuery by mutableStateOf("")

val listBySource by derivedStateOf {
customItem?.list
viewingItem
?.list
.orEmpty()
.groupBy { it.source }
}

val searchItems by derivedStateOf {
customItem?.list
viewingItem
?.list
.orEmpty()
.distinctBy { it.title }
.filter { it.title.contains(searchQuery, ignoreCase = true) }
}

val items by derivedStateOf {
customItem?.list
viewingItem
?.list
.orEmpty()
.filter { it.title.contains(searchQuery, ignoreCase = true) }
.groupBy { it.title }
Expand All @@ -53,12 +62,21 @@ class OtakuListViewModel(
}

init {
listDao.getAllLists()
listDao
.getAllLists()
.onEach {
customLists.clear()
customLists.addAll(it)
}
.launchIn(viewModelScope)

snapshotFlow { customItem }
.flatMapMerge {
if (it == null) flowOf(null)
else listDao.getCustomListItemFlow(it.item.uuid)
}
.onEach { viewingItem = it }
.launchIn(viewModelScope)
}

fun removeItem(item: CustomListInfo) {
Expand All @@ -70,16 +88,16 @@ class OtakuListViewModel(

suspend fun removeItems(items: List<CustomListInfo>): Result<Boolean> = runCatching {
items.forEach { item -> listDao.removeItem(item) }
customItem?.item?.let { listDao.updateFullList(it) }
viewingItem?.item?.let { listDao.updateFullList(it) }
true
}

fun rename(newName: String) {
viewModelScope.launch { customItem?.item?.copy(name = newName)?.let { listDao.updateFullList(it) } }
viewModelScope.launch { viewingItem?.item?.copy(name = newName)?.let { listDao.updateFullList(it) } }
}

fun deleteAll() {
viewModelScope.launch { customItem?.let { item -> listDao.removeList(item) } }
viewModelScope.launch { viewingItem?.let { item -> listDao.removeList(item) } }
}

fun setQuery(query: String) {
Expand All @@ -92,7 +110,7 @@ class OtakuListViewModel(
try {
context.contentResolver.openFileDescriptor(document, "w")?.use {
FileOutputStream(it.fileDescriptor).use { f ->
f.write(customItem?.toJson()?.toByteArray())
f.write(viewingItem?.toJson()?.toByteArray())
}
}
} catch (e: FileNotFoundException) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,55 +194,57 @@ fun RecentView(
targetState = isConnected,
label = ""
) { connected ->
when (connected) {
false -> {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Image(
Icons.Default.CloudOff,
null,
modifier = Modifier.size(50.dp),
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground)
)
Text(stringResource(R.string.you_re_offline), style = MaterialTheme.typography.titleLarge)
}
if (!connected) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Image(
Icons.Default.CloudOff,
null,
modifier = Modifier.size(50.dp),
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground)
)
Text(stringResource(R.string.you_re_offline), style = MaterialTheme.typography.titleLarge)
}
} else {
OtakuPullToRefreshBox(
isRefreshing = recentVm.isRefreshing,
onRefresh = { recentVm.reset() },
state = pull,
paddingValues = p
) {
when {
sourceList.isEmpty() -> NoSourcesInstalled(Modifier.fillMaxSize())

true -> {
OtakuPullToRefreshBox(
isRefreshing = recentVm.isRefreshing,
onRefresh = { recentVm.reset() },
state = pull,
paddingValues = p
) {
when {
sourceList.isEmpty() -> NoSourcesInstalled(Modifier.fillMaxSize())

recentVm.filteredSourceList.isEmpty() -> Box(Modifier.fillMaxSize()) { info.ComposeShimmerItem() }
recentVm.filteredSourceList.isEmpty() -> {
Box(
Modifier
.fillMaxSize()
.padding(p)
) { info.ComposeShimmerItem() }
}

else -> {
info.ItemListView(
list = recentVm.filteredSourceList,
listState = state,
favorites = recentVm.favoriteList,
paddingValues = p,
onLongPress = { item, c ->
newItemModel(if (c == ComponentState.Pressed) item else null)
showBanner = c == ComponentState.Pressed
},
modifier = Modifier.fillMaxSize()
) { navController.navigateToDetails(it) }
}
else -> {
info.ItemListView(
list = recentVm.filteredSourceList,
listState = state,
favorites = recentVm.favoriteList,
paddingValues = p,
onLongPress = { item, c ->
newItemModel(if (c == ComponentState.Pressed) item else null)
showBanner = c == ComponentState.Pressed
},
modifier = Modifier.fillMaxSize()
) { navController.navigateToDetails(it) }
}
}
}

if (source?.canScroll == true && recentVm.filteredSourceList.isNotEmpty()) {
InfiniteListHandler(listState = state, buffer = info.scrollBuffer) {
recentVm.loadMore()
}
if (source?.canScroll == true && recentVm.filteredSourceList.isNotEmpty()) {
InfiniteListHandler(listState = state, buffer = info.scrollBuffer) {
recentVm.loadMore()
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ generativeai = "0.9.0"
gradle = "8.7.2"

kotlin = "2.0.21"
kspVersion = "2.0.21-1.0.27"
kspVersion = "2.0.21-1.0.28"

androidxBaselineprofileGradlePlugin = "1.3.3"
composeCollapsable = "0.4.0"
dragselect = "2.4.1"
easylauncher = "6.4.0"
firebaseCrashlyticsGradle = "3.0.2"
googleServices = "4.4.2"
haze = "1.0.1"
haze = "1.0.2"
kamelImage = "1.0.1"
latestAboutLibsRelease = "11.2.3"
coroutinesVersion = "1.9.0"
Expand Down

0 comments on commit 87009dd

Please sign in to comment.