From 245293bc997c7d02d12c6cdcf79212dcd390c868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?1=EC=A3=BC=EC=B0=A8=20=EB=AF=B8=EC=85=98=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= Date: Wed, 12 Nov 2025 17:30:44 +0900 Subject: [PATCH 1/2] =?UTF-8?q?week7=20=EC=8B=A4=EC=8A=B5=20commit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 4 +- .../java/com/example/kuit6_android_api/App.kt | 13 +++++ .../kuit6_android_api/data/di/AppContainer.kt | 16 ++++++ .../data/repository/PostRepository.kt | 7 +++ .../data/repository/PostRepositoryImpl.kt | 23 +++++++++ .../ui/navigation/NavGraph.kt | 10 +++- .../ui/post/screen/PostListScreen.kt | 49 ++++++++++++------- .../ui/post/state/PostListUiState.kt | 15 ++++++ .../ui/post/viewmodel/PostListViewModel.kt | 47 ++++++++++++++++++ .../ui/post/viewmodel/PostViewModel.kt | 13 ----- .../ui/post/viewmodel/PostViewModelFactory.kt | 20 ++++++++ 11 files changed, 183 insertions(+), 34 deletions(-) create mode 100644 app/src/main/java/com/example/kuit6_android_api/App.kt create mode 100644 app/src/main/java/com/example/kuit6_android_api/data/di/AppContainer.kt create mode 100644 app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepository.kt create mode 100644 app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepositoryImpl.kt create mode 100644 app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostListUiState.kt create mode 100644 app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt create mode 100644 app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0f991aa..f29b705 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,13 +7,15 @@ - + > +} \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepositoryImpl.kt b/app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepositoryImpl.kt new file mode 100644 index 0000000..d087f97 --- /dev/null +++ b/app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepositoryImpl.kt @@ -0,0 +1,23 @@ +package com.example.kuit6_android_api.data.repository + +import android.util.Log +import com.example.kuit6_android_api.data.api.ApiService +import com.example.kuit6_android_api.data.model.response.PostResponse + +class PostRepositoryImpl( + private val apiService: ApiService +): PostRepository{ + override suspend fun getPosts(): Result> { + return runCatching { + val response = apiService.getPosts() + + if(response.success && response.data != null){ + response.data + }else{ + throw Exception(response.message ?: "게시글 불러오기 실패") + } + }.onFailure { error -> + Log.e("PostRepository", error.message.toString()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt b/app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt index cbcdbd0..c5c1b7e 100644 --- a/app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt +++ b/app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt @@ -3,6 +3,7 @@ package com.example.kuit6_android_api.ui.navigation import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -11,6 +12,9 @@ import com.example.kuit6_android_api.ui.post.screen.PostCreateScreen import com.example.kuit6_android_api.ui.post.screen.PostDetailScreen import com.example.kuit6_android_api.ui.post.screen.PostEditScreen import com.example.kuit6_android_api.ui.post.screen.PostListScreen +import com.example.kuit6_android_api.ui.post.viewmodel.PostListViewModel +import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel +import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModelFactory @Composable fun NavGraph( @@ -23,13 +27,17 @@ fun NavGraph( startDestination = startDestination ) { composable { + val viewModel = viewModel< PostListViewModel>( + factory = PostViewModelFactory.Factory + ) PostListScreen( onPostClick = { postId -> navController.navigate(PostDetailRoute(postId)) }, onCreatePostClick = { navController.navigate(PostCreateRoute) - } + }, + viewModel = viewModel ) } diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostListScreen.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostListScreen.kt index 8522d79..fe31d10 100644 --- a/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostListScreen.kt +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostListScreen.kt @@ -9,6 +9,7 @@ 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.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon @@ -18,10 +19,14 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.example.kuit6_android_api.ui.post.component.PostItem +import com.example.kuit6_android_api.ui.post.state.PostListUiState +import com.example.kuit6_android_api.ui.post.viewmodel.PostListViewModel import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel @OptIn(ExperimentalMaterial3Api::class) @@ -29,13 +34,9 @@ import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel fun PostListScreen( onPostClick: (Long) -> Unit, onCreatePostClick: () -> Unit, - viewModel: PostViewModel = viewModel() + viewModel: PostListViewModel ) { - val posts = viewModel.posts - - LaunchedEffect(Unit) { - viewModel.getPosts() - } + val uiState by viewModel.uiState.collectAsState() Scaffold( topBar = { @@ -49,19 +50,29 @@ fun PostListScreen( } } ) { paddingValues -> - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(paddingValues) - .background(MaterialTheme.colorScheme.background), - contentPadding = PaddingValues(horizontal = 16.dp, vertical = 12.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - items(posts) { post -> - PostItem( - post = post, - onClick = { onPostClick(post.id) } - ) + when (uiState){ + is PostListUiState.Loading -> { + CircularProgressIndicator() + } + is PostListUiState.Success -> { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .background(MaterialTheme.colorScheme.background), + contentPadding = PaddingValues(horizontal = 16.dp, vertical = 12.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + items(items = (uiState as PostListUiState.Success).posts) { post -> + PostItem( + post = post, + onClick = { onPostClick(post.id) } + ) + } + } + } + is PostListUiState.Error -> { + Text(text = "로딩 실패") } } } diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostListUiState.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostListUiState.kt new file mode 100644 index 0000000..94bf3d2 --- /dev/null +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostListUiState.kt @@ -0,0 +1,15 @@ +package com.example.kuit6_android_api.ui.post.state + +import com.example.kuit6_android_api.data.model.response.PostResponse + +sealed class PostListUiState{ + data object Loading : PostListUiState() + + data class Success( + val posts : List + ): PostListUiState() + + data class Error( + val message:String + ): PostListUiState() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt new file mode 100644 index 0000000..5daced6 --- /dev/null +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt @@ -0,0 +1,47 @@ +package com.example.kuit6_android_api.ui.post.viewmodel + +import android.R +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.example.kuit6_android_api.App +import com.example.kuit6_android_api.data.repository.PostRepository +import com.example.kuit6_android_api.ui.post.state.PostListUiState +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import okhttp3.Call + +class PostListViewModel( + private val postRepository: PostRepository +): ViewModel(){ + private val _uiState = MutableStateFlow(PostListUiState.Loading) + val uiState: StateFlow = _uiState.asStateFlow() + + init { + loadPosts() + } + + private fun loadPosts(){ + viewModelScope.launch { + _uiState.value = PostListUiState.Loading + + postRepository.getPosts() + .onSuccess { posts-> + _uiState.value = PostListUiState.Success(posts) + } + .onFailure { error-> + _uiState.value = PostListUiState.Error( + message = error.message ?: "error" + ) + } + } + } + + fun refresh(){ + loadPosts() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModel.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModel.kt index d245bd7..061d083 100644 --- a/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModel.kt +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModel.kt @@ -22,19 +22,6 @@ class PostViewModel : ViewModel() { private set private val apiService = RetrofitClient.apiService - fun getPosts() { - viewModelScope.launch { - runCatching { - apiService.getPosts() - }.onSuccess { response -> - response.data?.let { - if (response.success){ - posts = response.data - } - } - } - } - } fun getPostDetail(postId: Long) { viewModelScope.launch { diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt new file mode 100644 index 0000000..08e65c9 --- /dev/null +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt @@ -0,0 +1,20 @@ +package com.example.kuit6_android_api.ui.post.viewmodel + +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.example.kuit6_android_api.App +import okhttp3.Call + +object PostViewModelFactory { + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val application = this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] + as App + + val postRepository = application.container.postRepository + + PostListViewModel(postRepository) + } + } +} \ No newline at end of file From 5ccb4546087d391c7a44d17f7ae65dd8d87b112e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?1=EC=A3=BC=EC=B0=A8=20=EB=AF=B8=EC=85=98=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= Date: Thu, 13 Nov 2025 15:34:25 +0900 Subject: [PATCH 2/2] =?UTF-8?q?week7=20=EC=8B=A4=EC=8A=B5=20commit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kuit6_android_api/data/api/ApiService.kt | 2 +- .../data/repository/PostRepository.kt | 5 ++ .../data/repository/PostRepositoryImpl.kt | 60 +++++++++++++++++++ .../ui/navigation/NavGraph.kt | 40 ++++++++++--- .../ui/post/screen/PostCreateScreen.kt | 5 +- .../ui/post/screen/PostEditScreen.kt | 6 +- .../ui/post/state/PostUIState.kt | 15 +++++ .../ui/post/viewmodel/PostCreateViewModel.kt | 46 ++++++++++++++ .../ui/post/viewmodel/PostDetailViewModel.kt | 33 ++++++++++ .../ui/post/viewmodel/PostEditViewModel.kt | 52 ++++++++++++++++ .../ui/post/viewmodel/PostListViewModel.kt | 6 -- .../ui/post/viewmodel/PostViewModelFactory.kt | 10 ++-- 12 files changed, 258 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostUIState.kt create mode 100644 app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostCreateViewModel.kt create mode 100644 app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostDetailViewModel.kt create mode 100644 app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostEditViewModel.kt diff --git a/app/src/main/java/com/example/kuit6_android_api/data/api/ApiService.kt b/app/src/main/java/com/example/kuit6_android_api/data/api/ApiService.kt index 1399be5..a9af2df 100644 --- a/app/src/main/java/com/example/kuit6_android_api/data/api/ApiService.kt +++ b/app/src/main/java/com/example/kuit6_android_api/data/api/ApiService.kt @@ -19,7 +19,7 @@ interface ApiService { @DELETE("/api/posts/{id}") suspend fun deletePost( @Path("id") id: Long - ): BaseResponse // data에 빈 객체 반환 + ): BaseResponse // data에 빈 객체 반환 @GET("/api/posts/{id}") suspend fun getPostDetail( diff --git a/app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepository.kt b/app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepository.kt index cf7a9a5..06e4fe8 100644 --- a/app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepository.kt +++ b/app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepository.kt @@ -1,7 +1,12 @@ package com.example.kuit6_android_api.data.repository +import com.example.kuit6_android_api.data.model.request.PostCreateRequest import com.example.kuit6_android_api.data.model.response.PostResponse interface PostRepository{ suspend fun getPosts(): Result> + suspend fun getPostDetail(postId: Long): Result + suspend fun createPost(author: String, request: PostCreateRequest): Result + suspend fun updatePost(postId: Long, request: PostCreateRequest): Result + suspend fun deletePost(postId: Long): Result } \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepositoryImpl.kt b/app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepositoryImpl.kt index d087f97..86f6d1c 100644 --- a/app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepositoryImpl.kt +++ b/app/src/main/java/com/example/kuit6_android_api/data/repository/PostRepositoryImpl.kt @@ -2,6 +2,7 @@ package com.example.kuit6_android_api.data.repository import android.util.Log import com.example.kuit6_android_api.data.api.ApiService +import com.example.kuit6_android_api.data.model.request.PostCreateRequest import com.example.kuit6_android_api.data.model.response.PostResponse class PostRepositoryImpl( @@ -20,4 +21,63 @@ class PostRepositoryImpl( Log.e("PostRepository", error.message.toString()) } } + + override suspend fun getPostDetail(postId: Long): Result { + return runCatching { + val response = apiService.getPostDetail(postId) + + if (response.success && response.data != null){ + response.data + } else{ + throw Exception(response.message ?: "삭제 실패") + } + }.onFailure { error -> + Log.e("PostRepository", error.message.toString()) + } + } + + override suspend fun createPost( + author: String, + request: PostCreateRequest + ): Result { + return runCatching { + val response = apiService.createPost(author, request) + if (response.success && response.data != null){ + response.data + } else{ + throw Exception(response.message ?: "생성 실패") + } + }.onFailure { error -> + Log.e("PostRepository", error.message.toString()) + } + } + + override suspend fun updatePost( + postId: Long, + request: PostCreateRequest + ): Result { + return runCatching { + val response = apiService.updatePost(postId, request) + if (response.success && response.data != null){ + response.data + }else{ + throw Exception(response.message ?: "업데이트 실패") + } + }.onFailure { error -> + Log.e("PostRepository", error.message.toString()) + } + } + + override suspend fun deletePost(postId: Long): Result { + return runCatching { + val response = apiService.deletePost(postId) + if (response.success && response.data != null){ + response.data + }else{ + throw Exception(response.message ?: "삭제 실패") + } + }.onFailure { error -> + Log.e("PostRepository", error.message.toString()) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt b/app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt index c5c1b7e..8556815 100644 --- a/app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt +++ b/app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt @@ -2,7 +2,6 @@ package com.example.kuit6_android_api.ui.navigation import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost @@ -12,8 +11,10 @@ import com.example.kuit6_android_api.ui.post.screen.PostCreateScreen import com.example.kuit6_android_api.ui.post.screen.PostDetailScreen import com.example.kuit6_android_api.ui.post.screen.PostEditScreen import com.example.kuit6_android_api.ui.post.screen.PostListScreen +import com.example.kuit6_android_api.ui.post.viewmodel.PostCreateViewModel +import com.example.kuit6_android_api.ui.post.viewmodel.PostDetailViewModel +import com.example.kuit6_android_api.ui.post.viewmodel.PostEditViewModel import com.example.kuit6_android_api.ui.post.viewmodel.PostListViewModel -import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModelFactory @Composable @@ -27,9 +28,10 @@ fun NavGraph( startDestination = startDestination ) { composable { - val viewModel = viewModel< PostListViewModel>( - factory = PostViewModelFactory.Factory + val listViewModel: PostListViewModel = viewModel( + factory = PostViewModelFactory { PostListViewModel(it) } ) + PostListScreen( onPostClick = { postId -> navController.navigate(PostDetailRoute(postId)) @@ -37,13 +39,19 @@ fun NavGraph( onCreatePostClick = { navController.navigate(PostCreateRoute) }, - viewModel = viewModel + viewModel = listViewModel ) } composable { backStackEntry -> val route = backStackEntry.toRoute() + val detailViewModel: PostDetailViewModel = viewModel( + factory = PostViewModelFactory { PostDetailViewModel(it) } + ) + val editViewModel: PostEditViewModel = viewModel( + factory = PostViewModelFactory { PostEditViewModel(it) } + ) PostDetailScreen( postId = route.postId, onNavigateBack = { @@ -52,11 +60,17 @@ fun NavGraph( onEditClick = { postId -> navController.navigate(PostEditRoute(postId)) }, - snackBarState = snackBarState + snackBarState = snackBarState, + detailViewModel=detailViewModel, + editViewModel=editViewModel ) } composable { + val createViewModel: PostCreateViewModel = viewModel( + factory = PostViewModelFactory { PostCreateViewModel(it) } + ) + PostCreateScreen( onNavigateBack = { navController.popBackStack() @@ -64,12 +78,20 @@ fun NavGraph( onPostCreated = { navController.popBackStack() }, - snackBarState = snackBarState + snackBarState = snackBarState, + postCreateViewModel = createViewModel ) } composable { backStackEntry -> val route = backStackEntry.toRoute() + val detailViewModel: PostDetailViewModel = viewModel( + factory = PostViewModelFactory { PostDetailViewModel(it) } + ) + + val editViewModel: PostEditViewModel = viewModel( + factory = PostViewModelFactory { PostEditViewModel(it) } + ) PostEditScreen( postId = route.postId, @@ -79,7 +101,9 @@ fun NavGraph( onPostUpdated = { navController.popBackStack() }, - snackBarState = snackBarState + snackBarState = snackBarState, + editViewModel = editViewModel, + detailViewModel = detailViewModel ) } } diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt index 07ec04f..28519c4 100644 --- a/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt @@ -47,6 +47,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import com.example.kuit6_android_api.ui.post.viewmodel.PostCreateViewModel import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel import kotlinx.coroutines.launch @@ -56,7 +57,7 @@ fun PostCreateScreen( onNavigateBack: () -> Unit, onPostCreated: () -> Unit, snackBarState: SnackbarHostState, - viewModel: PostViewModel = viewModel() + postCreateViewModel: PostCreateViewModel = viewModel() ) { val context = LocalContext.current var author by remember { mutableStateOf("") } @@ -196,7 +197,7 @@ fun PostCreateScreen( Button( onClick = { val finalAuthor = author - viewModel.createPost(finalAuthor, title, content, null) { + postCreateViewModel.createPost(finalAuthor, title, content, null) { onPostCreated() scope.launch { snackBarState.showSnackbar("게시글이 작성되었습니다.") } } diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt index 23a991c..00a6d5b 100644 --- a/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt @@ -46,6 +46,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import coil.compose.AsyncImage +import com.example.kuit6_android_api.ui.post.viewmodel.PostDetailViewModel +import com.example.kuit6_android_api.ui.post.viewmodel.PostEditViewModel import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel import kotlinx.coroutines.launch @@ -56,7 +58,9 @@ fun PostEditScreen( onNavigateBack: () -> Unit, onPostUpdated: () -> Unit, viewModel: PostViewModel = viewModel(), - snackBarState: SnackbarHostState + snackBarState: SnackbarHostState, + editviewModel: PostEditViewModel = viewModel(), + detailViewModel: PostDetailViewModel = viewModel() ) { val post = viewModel.postDetail diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostUIState.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostUIState.kt new file mode 100644 index 0000000..6bc62fa --- /dev/null +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/state/PostUIState.kt @@ -0,0 +1,15 @@ +package com.example.kuit6_android_api.ui.post.state + +import com.example.kuit6_android_api.data.model.response.PostResponse + +sealed class PostUIState { + data object Loading: PostUIState() + + data class Success( + val post: PostResponse + ): PostUIState() + + data class Error( + val message: String + ): PostUIState() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostCreateViewModel.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostCreateViewModel.kt new file mode 100644 index 0000000..e7f51e6 --- /dev/null +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostCreateViewModel.kt @@ -0,0 +1,46 @@ +package com.example.kuit6_android_api.ui.post.viewmodel + +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.example.kuit6_android_api.data.model.request.PostCreateRequest +import com.example.kuit6_android_api.data.repository.PostRepository +import com.example.kuit6_android_api.ui.post.state.PostUIState +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +class PostCreateViewModel( + private val postRepository: PostRepository +): ViewModel() { + private val _uiState = MutableStateFlow(PostUIState.Loading) + val uiState: StateFlow = _uiState.asStateFlow() + + var uploadedImageUrl by mutableStateOf(null) + private set + + fun createPost(author: String = "anonymous", title: String, content: String, imageUrl: String? = null){ + viewModelScope.launch { + _uiState.value = PostUIState.Loading + val request = PostCreateRequest(title, content, imageUrl) + postRepository.createPost(author, request) + .onSuccess { post -> + _uiState.value = PostUIState.Success(post) + } + .onFailure { error -> + _uiState.value = PostUIState.Error( + message = error.message ?: "error" + ) + } + + clearUploadedImageUrl() + } + } + + fun clearUploadedImageUrl(){ + uploadedImageUrl = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostDetailViewModel.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostDetailViewModel.kt new file mode 100644 index 0000000..23c2466 --- /dev/null +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostDetailViewModel.kt @@ -0,0 +1,33 @@ +package com.example.kuit6_android_api.ui.post.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.kuit6_android_api.data.repository.PostRepository +import com.example.kuit6_android_api.ui.post.state.PostUIState +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +class PostDetailViewModel( + private val postRepository: PostRepository +): ViewModel() { + private val _uiState = MutableStateFlow(PostUIState.Loading) + val uiState: StateFlow = _uiState.asStateFlow() + + fun getPostDetail(postId: Long){ + viewModelScope.launch { + _uiState.value = PostUIState.Loading + + postRepository.getPostDetail(postId) + .onSuccess { post -> + _uiState.value = PostUIState.Success(post) + } + .onFailure { error -> + _uiState.value = PostUIState.Error( + message = error.message ?: "error" + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostEditViewModel.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostEditViewModel.kt new file mode 100644 index 0000000..95c2ae0 --- /dev/null +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostEditViewModel.kt @@ -0,0 +1,52 @@ +package com.example.kuit6_android_api.ui.post.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.kuit6_android_api.data.model.request.PostCreateRequest +import com.example.kuit6_android_api.data.repository.PostRepository +import com.example.kuit6_android_api.ui.post.state.PostUIState +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +class PostEditViewModel ( + private val postRepository: PostRepository + ): ViewModel(){ + private val _uiState = MutableStateFlow(PostUIState.Loading) + val uiState: StateFlow = _uiState.asStateFlow() + + + fun deletePost(postId: Long){ + viewModelScope.launch { + _uiState.value = PostUIState.Loading + + postRepository.deletePost(postId) + .onSuccess { post -> + _uiState.value = PostUIState.Success(post) + } + .onFailure { error -> + _uiState.value = PostUIState.Error( + message = error.message ?: "error" + ) + } + } + } + + fun updatePost(postId: Long, title: String, content: String, imageUrl: String? = null){ + viewModelScope.launch { + _uiState.value = PostUIState.Loading + val request = PostCreateRequest(title, content, imageUrl) + + postRepository.updatePost(postId, request) + .onSuccess { post -> + _uiState.value = PostUIState.Success(post) + } + .onFailure { error -> + _uiState.value = PostUIState.Error( + message = error.message ?: "error" + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt index 5daced6..f93181f 100644 --- a/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostListViewModel.kt @@ -1,19 +1,13 @@ package com.example.kuit6_android_api.ui.post.viewmodel -import android.R import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.initializer -import androidx.lifecycle.viewmodel.viewModelFactory -import com.example.kuit6_android_api.App import com.example.kuit6_android_api.data.repository.PostRepository import com.example.kuit6_android_api.ui.post.state.PostListUiState import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch -import okhttp3.Call class PostListViewModel( private val postRepository: PostRepository diff --git a/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt index 08e65c9..449349c 100644 --- a/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt +++ b/app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModelFactory.kt @@ -1,20 +1,22 @@ package com.example.kuit6_android_api.ui.post.viewmodel +import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import com.example.kuit6_android_api.App +import com.example.kuit6_android_api.data.repository.PostRepository import okhttp3.Call -object PostViewModelFactory { - val Factory: ViewModelProvider.Factory = viewModelFactory { +inline fun PostViewModelFactory( + crossinline create: (PostRepository) -> VM +): ViewModelProvider.Factory = viewModelFactory { initializer { val application = this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as App val postRepository = application.container.postRepository - PostListViewModel(postRepository) + create(postRepository) } } -} \ No newline at end of file