mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-15 04:07:03 +05:30
common: add TagList
This commit is contained in:
parent
a0f044786f
commit
a746734ad8
10 changed files with 248 additions and 0 deletions
|
@ -43,6 +43,7 @@ constructor(
|
|||
@ForScope(ApplicationScope::class) context: Context,
|
||||
) : AndroidViewModel(context as Application) {
|
||||
var postDetails by mutableStateOf<NetworkState>(NetworkState.Loading)
|
||||
private set
|
||||
|
||||
suspend fun loadPostDetails(postId: String) {
|
||||
if (postDetails is NetworkState.Error) {
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright © Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.tags
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.stringSetPreferencesKey
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class TagFilterRepository @Inject constructor(private val preferences: DataStore<Preferences>) {
|
||||
private val tagsKey = stringSetPreferencesKey("tags")
|
||||
|
||||
fun getSavedTags(): Flow<Set<String>> {
|
||||
return preferences.data.map { prefs -> prefs[tagsKey] ?: emptySet() }
|
||||
}
|
||||
|
||||
suspend fun saveTags(tags: Set<String>) {
|
||||
preferences.edit { prefs -> prefs[tagsKey] = tags }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright © Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.tags
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.deliveryhero.whetstone.viewmodel.ContributesViewModel
|
||||
import com.github.michaelbull.result.coroutines.runSuspendCatching
|
||||
import com.github.michaelbull.result.fold
|
||||
import com.slack.eithernet.ApiResult.Failure
|
||||
import com.slack.eithernet.ApiResult.Success
|
||||
import dev.msfjarvis.claw.api.LobstersApi
|
||||
import dev.msfjarvis.claw.api.toError
|
||||
import dev.msfjarvis.claw.common.NetworkState
|
||||
import dev.msfjarvis.claw.core.coroutines.IODispatcher
|
||||
import dev.msfjarvis.claw.model.Tag
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@ContributesViewModel
|
||||
class TagFilterViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
private val api: LobstersApi,
|
||||
private val tagFilterRepository: TagFilterRepository,
|
||||
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||
) : ViewModel() {
|
||||
|
||||
var filteredTags by mutableStateOf<ImmutableList<String>>(persistentListOf())
|
||||
private set
|
||||
|
||||
var allTags by mutableStateOf<NetworkState>(NetworkState.Loading)
|
||||
private set
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
tagFilterRepository.getSavedTags().collectLatest { filteredTags = it.toImmutableList() }
|
||||
}
|
||||
viewModelScope.launch {
|
||||
allTags =
|
||||
runSuspendCatching<ImmutableList<Tag>> {
|
||||
withContext(ioDispatcher) {
|
||||
when (val result = api.getTags()) {
|
||||
is Success -> result.value.toImmutableList()
|
||||
is Failure.NetworkFailure -> throw result.error
|
||||
is Failure.UnknownFailure -> throw result.error
|
||||
is Failure.HttpFailure -> throw result.toError()
|
||||
is Failure.ApiFailure -> throw IOException("API returned an invalid response")
|
||||
}
|
||||
}
|
||||
}
|
||||
.fold(
|
||||
success = { details -> NetworkState.Success(details) },
|
||||
failure = { NetworkState.Error(error = it, description = "Failed to load comments") },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun saveTags(tags: Set<String>) {
|
||||
viewModelScope.launch { tagFilterRepository.saveTags(tags) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright © Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.tags
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Remove
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.deliveryhero.whetstone.compose.injectedViewModel
|
||||
import dev.msfjarvis.claw.common.NetworkState
|
||||
import dev.msfjarvis.claw.model.Tag
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun TagList(
|
||||
contentPadding: PaddingValues,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: TagFilterViewModel = injectedViewModel(key = "tag_filter"),
|
||||
) {
|
||||
val allTagsState = viewModel.allTags
|
||||
val filteredTags = viewModel.filteredTags
|
||||
|
||||
LazyColumn(modifier = modifier.fillMaxWidth().padding(contentPadding)) {
|
||||
when (allTagsState) {
|
||||
is NetworkState.Loading -> {
|
||||
item { Text("Loading tags...") }
|
||||
}
|
||||
is NetworkState.Error -> {
|
||||
item { Text("Failed to load tags") }
|
||||
}
|
||||
is NetworkState.Success<*> -> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val allTags = (allTagsState as NetworkState.Success<ImmutableList<Tag>>).data
|
||||
items(allTags) { tag ->
|
||||
val isSelected = filteredTags.contains(tag.tag)
|
||||
ListItem(
|
||||
headlineContent = { Text(tag.tag) },
|
||||
supportingContent = { Text(tag.description) },
|
||||
trailingContent = {
|
||||
Button(
|
||||
onClick = {
|
||||
val updatedTags =
|
||||
if (isSelected) {
|
||||
filteredTags - tag.tag
|
||||
} else {
|
||||
filteredTags + tag.tag
|
||||
}
|
||||
viewModel.saveTags(updatedTags.toSet())
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (isSelected) Icons.Default.Remove else Icons.Default.Add,
|
||||
contentDescription = if (isSelected) "Remove" else "Add",
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue