refactor: hoist state out of comments page

This commit is contained in:
Harsh Shandilya 2025-05-26 19:14:11 +05:30
parent 0d3c08c10a
commit bcac86d187
2 changed files with 68 additions and 14 deletions

View file

@ -10,13 +10,12 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState import androidx.compose.runtime.produceState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.github.michaelbull.result.coroutines.runSuspendCatching import com.deliveryhero.whetstone.compose.injectedViewModel
import com.github.michaelbull.result.fold
import dev.msfjarvis.claw.common.NetworkState
import dev.msfjarvis.claw.common.NetworkState.Error import dev.msfjarvis.claw.common.NetworkState.Error
import dev.msfjarvis.claw.common.NetworkState.Loading import dev.msfjarvis.claw.common.NetworkState.Loading
import dev.msfjarvis.claw.common.NetworkState.Success import dev.msfjarvis.claw.common.NetworkState.Success
@ -35,23 +34,17 @@ fun CommentsPage(
getSeenComments: suspend (String) -> PostComments?, getSeenComments: suspend (String) -> PostComments?,
markSeenComments: (String, List<Comment>) -> Unit, markSeenComments: (String, List<Comment>) -> Unit,
contentPadding: PaddingValues, contentPadding: PaddingValues,
modifier: Modifier = Modifier,
openUserProfile: (String) -> Unit, openUserProfile: (String) -> Unit,
modifier: Modifier = Modifier,
viewModel: CommentsViewModel = injectedViewModel(),
) { ) {
val postDetails by LaunchedEffect(postId) { viewModel.loadPostDetails(postId) }
produceState<NetworkState>(Loading, key1 = postId) {
runSuspendCatching { postActions.getComments(postId) }
.fold(
success = { details -> value = Success(details) },
failure = { value = Error(error = it, description = "Failed to load comments") },
)
}
val commentState by val commentState by
produceState<PostComments?>(initialValue = null, key1 = postId) { produceState<PostComments?>(initialValue = null, key1 = postId) {
value = getSeenComments(postId) value = getSeenComments(postId)
} }
when (postDetails) { when (val postDetails = viewModel.postDetails) {
is Success<*> -> { is Success<*> -> {
CommentsPageInternal( CommentsPageInternal(
details = (postDetails as Success<UIPost>).data, details = (postDetails as Success<UIPost>).data,
@ -64,7 +57,7 @@ fun CommentsPage(
) )
} }
is Error -> { is Error -> {
val error = postDetails as Error val error = postDetails
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
NetworkError( NetworkError(
label = error.description, label = error.description,

View file

@ -0,0 +1,61 @@
/*
* 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.comments
import android.app.Application
import android.content.Context
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.AndroidViewModel
import com.deliveryhero.whetstone.app.ApplicationScope
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 com.squareup.anvil.annotations.optional.ForScope
import dev.msfjarvis.claw.api.LobstersApi
import dev.msfjarvis.claw.api.toError
import dev.msfjarvis.claw.common.NetworkState
import dev.msfjarvis.claw.core.injection.IODispatcher
import dev.msfjarvis.claw.model.UIPost
import dev.msfjarvis.claw.model.toUIPost
import java.io.IOException
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
@ContributesViewModel
class CommentsViewModel
@Inject
constructor(
private val api: LobstersApi,
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
@ForScope(ApplicationScope::class) context: Context,
) : AndroidViewModel(context as Application) {
var postDetails by mutableStateOf<NetworkState>(NetworkState.Loading)
suspend fun loadPostDetails(postId: String) {
postDetails =
runSuspendCatching<UIPost> {
withContext(ioDispatcher) {
when (val result = api.getPostDetails(postId)) {
is Success -> result.value.toUIPost()
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") },
)
}
}