Skip to content

Commit c2e2d11

Browse files
author
1주차 미션 완료
committed
week6 commit
1 parent e1a4487 commit c2e2d11

File tree

7 files changed

+66
-19
lines changed

7 files changed

+66
-19
lines changed

app/src/main/java/com/example/kuit6_android_api/MainActivity.kt

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
package com.example.kuit6_android_api
22

33
import android.Manifest
4+
import android.annotation.SuppressLint
45
import android.content.pm.PackageManager
56
import android.os.Build
67
import android.os.Bundle
78
import android.widget.Toast
89
import androidx.activity.ComponentActivity
910
import androidx.activity.compose.setContent
1011
import androidx.activity.result.contract.ActivityResultContracts
12+
import androidx.compose.foundation.background
1113
import androidx.compose.foundation.layout.fillMaxSize
1214
import androidx.compose.material3.MaterialTheme
15+
import androidx.compose.material3.Scaffold
16+
import androidx.compose.material3.SnackbarHost
17+
import androidx.compose.material3.SnackbarHostState
1318
import androidx.compose.material3.Surface
19+
import androidx.compose.runtime.remember
1420
import androidx.compose.ui.Modifier
1521
import androidx.core.content.ContextCompat
1622
import androidx.navigation.compose.rememberNavController
@@ -32,6 +38,7 @@ class MainActivity : ComponentActivity() {
3238
}
3339
}
3440

41+
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
3542
override fun onCreate(savedInstanceState: Bundle?) {
3643
super.onCreate(savedInstanceState)
3744

@@ -40,15 +47,23 @@ class MainActivity : ComponentActivity() {
4047

4148
setContent {
4249
KUIT6_Android_APITheme {
43-
Surface(
44-
modifier = Modifier.fillMaxSize(),
45-
color = MaterialTheme.colorScheme.background
50+
51+
val snackBarState = remember { SnackbarHostState() }
52+
53+
Scaffold(
54+
modifier = Modifier
55+
.fillMaxSize()
56+
.background(MaterialTheme.colorScheme.background),
57+
snackbarHost = {
58+
SnackbarHost(snackBarState)
59+
}
4660
) {
4761
val navController = rememberNavController()
4862

4963
NavGraph(
5064
navController = navController,
51-
startDestination = PostListRoute
65+
startDestination = PostListRoute,
66+
snackBarState = snackBarState
5267
)
5368
}
5469
}

app/src/main/java/com/example/kuit6_android_api/data/api/ApiService.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.example.kuit6_android_api.data.api
33
import com.example.kuit6_android_api.data.model.response.BaseResponse
44
import com.example.kuit6_android_api.data.model.response.PostResponse
55
import com.example.kuit6_android_api.data.model.request.PostCreateRequest
6+
import okhttp3.MultipartBody
67
import retrofit2.http.*
78

89
interface ApiService {
@@ -31,4 +32,10 @@ interface ApiService {
3132
@Path("id") id: Long,
3233
@Body request: PostCreateRequest // 수정된 내용 전달
3334
): BaseResponse<PostResponse>
35+
36+
@Multipart
37+
@POST("/api/images/upload")
38+
suspend fun uploadImage(
39+
@Part file: MultipartBody.Part
40+
): BaseResponse<Map<String, String>>
3441
}

app/src/main/java/com/example/kuit6_android_api/ui/navigation/NavGraph.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.example.kuit6_android_api.ui.navigation
22

3+
import androidx.compose.material3.SnackbarHostState
34
import androidx.compose.runtime.Composable
5+
import androidx.compose.runtime.MutableState
46
import androidx.navigation.NavHostController
57
import androidx.navigation.compose.NavHost
68
import androidx.navigation.compose.composable
@@ -13,7 +15,8 @@ import com.example.kuit6_android_api.ui.post.screen.PostListScreen
1315
@Composable
1416
fun NavGraph(
1517
navController: NavHostController,
16-
startDestination: Any = PostListRoute
18+
startDestination: Any = PostListRoute,
19+
snackBarState: SnackbarHostState
1720
) {
1821
NavHost(
1922
navController = navController,
@@ -40,7 +43,8 @@ fun NavGraph(
4043
},
4144
onEditClick = { postId ->
4245
navController.navigate(PostEditRoute(postId))
43-
}
46+
},
47+
snackBarState = snackBarState
4448
)
4549
}
4650

@@ -51,7 +55,8 @@ fun NavGraph(
5155
},
5256
onPostCreated = {
5357
navController.popBackStack()
54-
}
58+
},
59+
snackBarState = snackBarState
5560
)
5661
}
5762

@@ -65,7 +70,8 @@ fun NavGraph(
6570
},
6671
onPostUpdated = {
6772
navController.popBackStack()
68-
}
73+
},
74+
snackBarState = snackBarState
6975
)
7076
}
7177
}

app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostCreateScreen.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,33 +29,41 @@ import androidx.compose.material3.MaterialTheme
2929
import androidx.compose.material3.OutlinedTextField
3030
import androidx.compose.material3.OutlinedTextFieldDefaults
3131
import androidx.compose.material3.Scaffold
32+
import androidx.compose.material3.SnackbarHostState
3233
import androidx.compose.material3.Text
3334
import androidx.compose.material3.TopAppBar
3435
import androidx.compose.material3.TopAppBarDefaults
3536
import androidx.compose.runtime.Composable
37+
import androidx.compose.runtime.State
3638
import androidx.compose.runtime.getValue
3739
import androidx.compose.runtime.mutableStateOf
3840
import androidx.compose.runtime.remember
41+
import androidx.compose.runtime.rememberCoroutineScope
3942
import androidx.compose.runtime.setValue
4043
import androidx.compose.ui.Alignment
4144
import androidx.compose.ui.Modifier
45+
import androidx.compose.ui.platform.LocalContext
4246
import androidx.compose.ui.text.font.FontWeight
4347
import androidx.compose.ui.tooling.preview.Preview
4448
import androidx.compose.ui.unit.dp
4549
import androidx.lifecycle.viewmodel.compose.viewModel
4650
import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel
51+
import kotlinx.coroutines.launch
4752

4853
@OptIn(ExperimentalMaterial3Api::class)
4954
@Composable
5055
fun PostCreateScreen(
5156
onNavigateBack: () -> Unit,
5257
onPostCreated: () -> Unit,
58+
snackBarState: SnackbarHostState,
5359
viewModel: PostViewModel = viewModel()
5460
) {
61+
val context = LocalContext.current
5562
var author by remember { mutableStateOf("") }
5663
var title by remember { mutableStateOf("") }
5764
var content by remember { mutableStateOf("") }
5865
var selectedImageUri by remember { mutableStateOf<Uri?>(null) }
66+
val scope = rememberCoroutineScope()
5967

6068
val imagePickerLauncher = rememberLauncherForActivityResult(
6169
contract = ActivityResultContracts.GetContent()
@@ -190,6 +198,7 @@ fun PostCreateScreen(
190198
val finalAuthor = author
191199
viewModel.createPost(finalAuthor, title, content, null) {
192200
onPostCreated()
201+
scope.launch { snackBarState.showSnackbar("게시글이 작성되었습니다.") }
193202
}
194203
},
195204
modifier = Modifier
@@ -226,7 +235,8 @@ fun PostCreateScreenPreview() {
226235
MaterialTheme {
227236
PostCreateScreen(
228237
onNavigateBack = {},
229-
onPostCreated = {}
238+
onPostCreated = {},
239+
snackBarState = remember { SnackbarHostState() }
230240
)
231241
}
232242
}

app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostDetailScreen.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import androidx.compose.material3.Icon
2525
import androidx.compose.material3.IconButton
2626
import androidx.compose.material3.MaterialTheme
2727
import androidx.compose.material3.Scaffold
28+
import androidx.compose.material3.SnackbarHostState
2829
import androidx.compose.material3.Surface
2930
import androidx.compose.material3.Text
3031
import androidx.compose.material3.TextButton
@@ -34,6 +35,7 @@ import androidx.compose.runtime.LaunchedEffect
3435
import androidx.compose.runtime.getValue
3536
import androidx.compose.runtime.mutableStateOf
3637
import androidx.compose.runtime.remember
38+
import androidx.compose.runtime.rememberCoroutineScope
3739
import androidx.compose.runtime.setValue
3840
import androidx.compose.ui.Alignment
3941
import androidx.compose.ui.Modifier
@@ -46,17 +48,20 @@ import androidx.lifecycle.viewmodel.compose.viewModel
4648
import coil.compose.AsyncImage
4749
import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel
4850
import com.example.kuit6_android_api.util.formatDateTime
51+
import kotlinx.coroutines.launch
4952

5053
@OptIn(ExperimentalMaterial3Api::class)
5154
@Composable
5255
fun PostDetailScreen(
5356
postId: Long,
5457
onNavigateBack: () -> Unit,
5558
onEditClick: (Long) -> Unit = {},
56-
viewModel: PostViewModel = viewModel()
59+
viewModel: PostViewModel = viewModel(),
60+
snackBarState: SnackbarHostState
5761
) {
5862
val post = viewModel.postDetail
5963
var showDeleteDialog by remember { mutableStateOf(false) }
64+
val scope = rememberCoroutineScope()
6065

6166
LaunchedEffect(postId) {
6267
viewModel.getPostDetail(postId)
@@ -197,6 +202,7 @@ fun PostDetailScreen(
197202
viewModel.deletePost(postId) {
198203
showDeleteDialog = false
199204
onNavigateBack()
205+
scope.launch { snackBarState.showSnackbar("게시글이 삭제되었습니다.") }
200206
}
201207
}) {
202208
Text("삭제")
@@ -219,7 +225,8 @@ fun PostDetailScreenPreview() {
219225
PostDetailScreen(
220226
postId = 1L,
221227
onNavigateBack = {},
222-
onEditClick = {}
228+
onEditClick = {},
229+
snackBarState = remember { SnackbarHostState() }
223230
)
224231
}
225232
}

app/src/main/java/com/example/kuit6_android_api/ui/post/screen/PostEditScreen.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import androidx.compose.material3.OutlinedButton
2828
import androidx.compose.material3.OutlinedTextField
2929
import androidx.compose.material3.OutlinedTextFieldDefaults
3030
import androidx.compose.material3.Scaffold
31+
import androidx.compose.material3.SnackbarHostState
3132
import androidx.compose.material3.Text
3233
import androidx.compose.material3.TopAppBar
3334
import androidx.compose.ui.tooling.preview.Preview
@@ -36,6 +37,7 @@ import androidx.compose.runtime.LaunchedEffect
3637
import androidx.compose.runtime.getValue
3738
import androidx.compose.runtime.mutableStateOf
3839
import androidx.compose.runtime.remember
40+
import androidx.compose.runtime.rememberCoroutineScope
3941
import androidx.compose.runtime.setValue
4042
import androidx.compose.ui.Alignment
4143
import androidx.compose.ui.Modifier
@@ -45,21 +47,24 @@ import androidx.compose.ui.unit.dp
4547
import androidx.lifecycle.viewmodel.compose.viewModel
4648
import coil.compose.AsyncImage
4749
import com.example.kuit6_android_api.ui.post.viewmodel.PostViewModel
50+
import kotlinx.coroutines.launch
4851

4952
@OptIn(ExperimentalMaterial3Api::class)
5053
@Composable
5154
fun PostEditScreen(
5255
postId: Long,
5356
onNavigateBack: () -> Unit,
5457
onPostUpdated: () -> Unit,
55-
viewModel: PostViewModel = viewModel()
58+
viewModel: PostViewModel = viewModel(),
59+
snackBarState: SnackbarHostState
5660
) {
5761
val post = viewModel.postDetail
5862

5963
var title by remember { mutableStateOf("") }
6064
var content by remember { mutableStateOf("") }
6165
var selectedImageUri by remember { mutableStateOf<Uri?>(null) }
6266
var isLoaded by remember { mutableStateOf(false) }
67+
val scope = rememberCoroutineScope()
6368

6469
val imagePickerLauncher = rememberLauncherForActivityResult(
6570
contract = ActivityResultContracts.GetContent()
@@ -204,6 +209,7 @@ fun PostEditScreen(
204209
onClick = {
205210
viewModel.updatePost(postId, title, content, null) {
206211
onPostUpdated()
212+
scope.launch { snackBarState.showSnackbar("게시글이 수정되었습니다.") }
207213
}
208214
},
209215
modifier = Modifier
@@ -235,7 +241,8 @@ fun PostEditScreenPreview() {
235241
PostEditScreen(
236242
postId = 1L,
237243
onNavigateBack = {},
238-
onPostUpdated = {}
244+
onPostUpdated = {},
245+
snackBarState = remember { SnackbarHostState() }
239246
)
240247
}
241248
}

app/src/main/java/com/example/kuit6_android_api/ui/post/viewmodel/PostViewModel.kt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.example.kuit6_android_api.ui.post.viewmodel
22

33
import androidx.compose.runtime.getValue
4-
import androidx.compose.runtime.mutableStateListOf
54
import androidx.compose.runtime.mutableStateOf
65
import androidx.compose.runtime.setValue
76
import androidx.lifecycle.ViewModel
@@ -84,9 +83,7 @@ class PostViewModel : ViewModel() {
8483
apiService.updatePost(postId, request)
8584
}.onSuccess { response ->
8685
if(response.success && response.data != null){
87-
// 변경된 새 리스트를 posts에 대입
8886
posts = posts.map{
89-
// 순회 중인 원소의 id == postId면 갱신된 객체 response.data로 교체
9087
if(it.id == postId) response.data else it
9188
}
9289
postDetail = response.data // postDetail을 갱신된 객체로 바꾸기
@@ -98,16 +95,14 @@ class PostViewModel : ViewModel() {
9895

9996
fun deletePost(postId: Long, onSuccess: () -> Unit = {}) {
10097
viewModelScope.launch {
101-
viewModelScope.launch {
10298
runCatching {
10399
apiService.deletePost(postId)
104100
}.onSuccess { response ->
105-
if(response.success){
101+
if (response.success) {
106102
posts = posts.filterNot { it.id == postId }
107103
onSuccess()
108104
}
109105
}
110-
}
111106
}
112107
}
113108

0 commit comments

Comments
 (0)