This commit introduces a comprehensive set of features, establishing the core functionality of the application using an MVVM architecture with Hilt for dependency injection.
**Key Changes:**
* **UI & Navigation:**
* Implemented navigation between the main feed, post details, and login screens using Jetpack Navigation Compose.
* Added `TalkScreen` for displaying a feed of posts and `PostDetailScreen` for viewing individual posts and their comments.
* Created a `LoginScreen` with input fields and authentication logic.
* Introduced `PostCardItem` and `CommentItem` Composables for a consistent and reusable UI.
* Added shared element transitions for a smoother user experience when navigating to post details.
* **Architecture & State Management:**
* Integrated Hilt for dependency injection across ViewModels and Repositories.
* Created ViewModels (`TalkViewModel`, `PostDetailViewModel`, `UserViewModel`, `CommentViewModel`, etc.) to manage UI state and business logic.
* Implemented Repository pattern for abstracting data sources from the backend API.
* Defined UI state data classes to ensure a predictable and observable state flow.
* **Data & Models:**
* Introduced data models for `PostCard` and `CommentCard` to aggregate and display complex data structures.
* Added `PostDetailRepository` to orchestrate fetching of post and comment data concurrently.
* Refined DTOs, such as `CreateCommentDto`, for API interactions.
* **Dependencies & Tooling:**
* Added Hilt, Navigation Compose, and Lifecycle ViewModel dependencies.
* Included the `pangu-jvm` library for improved text formatting with spacing between Chinese and English characters.
154 lines
5.8 KiB
Kotlin
154 lines
5.8 KiB
Kotlin
package com.qingshuige.tangyuan.viewmodel
|
|
|
|
import androidx.lifecycle.ViewModel
|
|
import androidx.lifecycle.viewModelScope
|
|
import com.qingshuige.tangyuan.model.Comment
|
|
import com.qingshuige.tangyuan.model.CreateCommentDto
|
|
import com.qingshuige.tangyuan.repository.CommentRepository
|
|
import com.qingshuige.tangyuan.utils.collectFlow
|
|
import com.qingshuige.tangyuan.utils.collectFlowList
|
|
import com.qingshuige.tangyuan.utils.UiState
|
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
import kotlinx.coroutines.flow.MutableStateFlow
|
|
import kotlinx.coroutines.flow.StateFlow
|
|
import kotlinx.coroutines.flow.asStateFlow
|
|
import kotlinx.coroutines.flow.catch
|
|
import kotlinx.coroutines.launch
|
|
import javax.inject.Inject
|
|
|
|
data class CommentUiState(
|
|
val isLoading: Boolean = false,
|
|
val comments: List<Comment> = emptyList(),
|
|
val subComments: Map<Int, List<Comment>> = emptyMap(),
|
|
val error: String? = null,
|
|
val isCreating: Boolean = false,
|
|
val createSuccess: Boolean = false
|
|
)
|
|
|
|
@HiltViewModel
|
|
class CommentViewModel @Inject constructor(
|
|
private val commentRepository: CommentRepository
|
|
) : ViewModel() {
|
|
|
|
private val _commentUiState = MutableStateFlow(CommentUiState())
|
|
val commentUiState: StateFlow<CommentUiState> = _commentUiState.asStateFlow()
|
|
|
|
private val _searchResults = MutableStateFlow<List<Comment>>(emptyList())
|
|
val searchResults: StateFlow<List<Comment>> = _searchResults.asStateFlow()
|
|
|
|
fun getCommentsForPost(postId: Int) {
|
|
viewModelScope.launch {
|
|
_commentUiState.value = _commentUiState.value.copy(isLoading = true, error = null)
|
|
commentRepository.getCommentsForPost(postId)
|
|
.catch { e ->
|
|
_commentUiState.value = _commentUiState.value.copy(
|
|
isLoading = false,
|
|
error = e.message
|
|
)
|
|
}
|
|
.collect { comments ->
|
|
_commentUiState.value = _commentUiState.value.copy(
|
|
isLoading = false,
|
|
comments = comments
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
fun getSubComments(parentCommentId: Int) {
|
|
viewModelScope.launch {
|
|
commentRepository.getSubComments(parentCommentId)
|
|
.catch { e ->
|
|
_commentUiState.value = _commentUiState.value.copy(error = e.message)
|
|
}
|
|
.collect { subComments ->
|
|
val currentSubComments = _commentUiState.value.subComments.toMutableMap()
|
|
currentSubComments[parentCommentId] = subComments
|
|
_commentUiState.value = _commentUiState.value.copy(
|
|
subComments = currentSubComments
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
fun getCommentById(commentId: Int) {
|
|
viewModelScope.launch {
|
|
commentRepository.getCommentById(commentId)
|
|
.catch { e ->
|
|
_commentUiState.value = _commentUiState.value.copy(error = e.message)
|
|
}
|
|
.collect { comment ->
|
|
// Handle single comment result
|
|
}
|
|
}
|
|
}
|
|
|
|
fun createComment(createCommentDto: CreateCommentDto) {
|
|
viewModelScope.launch {
|
|
_commentUiState.value = _commentUiState.value.copy(isCreating = true, error = null)
|
|
commentRepository.createComment(createCommentDto)
|
|
.catch { e ->
|
|
_commentUiState.value = _commentUiState.value.copy(
|
|
isCreating = false,
|
|
error = e.message
|
|
)
|
|
}
|
|
.collect { result ->
|
|
_commentUiState.value = _commentUiState.value.copy(
|
|
isCreating = false,
|
|
createSuccess = true
|
|
)
|
|
// Refresh comments for the post
|
|
getCommentsForPost(createCommentDto.postId.toInt())
|
|
}
|
|
}
|
|
}
|
|
|
|
fun deleteComment(commentId: Int) {
|
|
viewModelScope.launch {
|
|
commentRepository.deleteComment(commentId)
|
|
.catch { e ->
|
|
_commentUiState.value = _commentUiState.value.copy(error = e.message)
|
|
}
|
|
.collect { success ->
|
|
if (success) {
|
|
// Remove from current comments list
|
|
val updatedComments = _commentUiState.value.comments.filter {
|
|
it.commentId != commentId
|
|
}
|
|
_commentUiState.value = _commentUiState.value.copy(comments = updatedComments)
|
|
|
|
// Also remove from sub-comments if exists
|
|
val updatedSubComments = _commentUiState.value.subComments.mapValues { entry ->
|
|
entry.value.filter { it.commentId != commentId }
|
|
}
|
|
_commentUiState.value = _commentUiState.value.copy(subComments = updatedSubComments)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fun searchComments(keyword: String) {
|
|
viewModelScope.launch {
|
|
commentRepository.searchComments(keyword)
|
|
.catch { e ->
|
|
_commentUiState.value = _commentUiState.value.copy(error = e.message)
|
|
}
|
|
.collect { comments ->
|
|
_searchResults.value = comments
|
|
}
|
|
}
|
|
}
|
|
|
|
fun clearError() {
|
|
_commentUiState.value = _commentUiState.value.copy(error = null)
|
|
}
|
|
|
|
fun clearCreateSuccess() {
|
|
_commentUiState.value = _commentUiState.value.copy(createSuccess = false)
|
|
}
|
|
|
|
fun clearComments() {
|
|
_commentUiState.value = CommentUiState()
|
|
}
|
|
} |