diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 3d9c95a..f535598 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -42,8 +42,8 @@ jobs:
- name: Run unit tests
run: ./gradlew test
env:
- BASE_URL: ${{ secrets.BASE_URL }}
- KAKAO_NATIVE_APP_KEY: ${{ secrets.KAKAO_NATIVE_APP_KEY }}
+ BASE_URL: ${{ secrets.BASE_URL }}
+ KAKAO_NATIVE_APP_KEY: ${{ secrets.KAKAO_NATIVE_APP_KEY }}
- name: Upload test reports
if: always()
@@ -96,4 +96,4 @@ jobs:
run: bundle exec fastlane distribute
env:
BASE_URL: ${{ secrets.BASE_URL }}
- KAKAO_NATIVE_APP_KEY: ${{ secrets.KAKAO_NATIVE_APP_KEY }}
+ KAKAO_NATIVE_APP_KEY: ${{ secrets.KAKAO_NATIVE_APP_KEY }}
\ No newline at end of file
diff --git a/.idea/caches/deviceStreaming.xml b/.idea/caches/deviceStreaming.xml
index 37bf1cf..39f0210 100644
--- a/.idea/caches/deviceStreaming.xml
+++ b/.idea/caches/deviceStreaming.xml
@@ -51,6 +51,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -160,6 +172,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -753,74 +777,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -943,146 +899,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1171,40 +987,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1217,76 +999,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/request/auth/LogoutRequestDto.kt b/app/src/main/java/com/hsLink/hslink/data/dto/request/auth/LogoutRequestDto.kt
new file mode 100644
index 0000000..3e22059
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/data/dto/request/auth/LogoutRequestDto.kt
@@ -0,0 +1,9 @@
+// data/dto/request/auth/LogoutRequestDto.kt
+package com.hsLink.hslink.data.dto.request.auth
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class LogoutRequestDto(
+ val refreshToken: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/request/auth/WithdrawRequestDto.kt b/app/src/main/java/com/hsLink/hslink/data/dto/request/auth/WithdrawRequestDto.kt
new file mode 100644
index 0000000..83fa837
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/data/dto/request/auth/WithdrawRequestDto.kt
@@ -0,0 +1,9 @@
+// data/dto/request/auth/WithdrawRequestDto.kt
+package com.hsLink.hslink.data.dto.request.auth
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class WithdrawRequestDto(
+ val refreshToken: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/dto/response/auth/WithdrawResponseDto.kt b/app/src/main/java/com/hsLink/hslink/data/dto/response/auth/WithdrawResponseDto.kt
new file mode 100644
index 0000000..90a567d
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/data/dto/response/auth/WithdrawResponseDto.kt
@@ -0,0 +1,10 @@
+// data/dto/response/auth/WithdrawResponseDto.kt
+package com.hsLink.hslink.data.dto.response.auth
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class WithdrawResponseDto(
+ val userId: Long,
+ val deletedAt: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/AuthRepositoryImpl.kt b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/AuthRepositoryImpl.kt
index 308347e..3cb6c31 100644
--- a/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/AuthRepositoryImpl.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/repositoryimpl/AuthRepositoryImpl.kt
@@ -1,10 +1,14 @@
package com.hsLink.hslink.data.repositoryimpl
+import com.hsLink.hslink.data.dto.request.auth.LogoutRequestDto
import com.hsLink.hslink.data.dto.request.auth.SocialLoginRequestDto
+import com.hsLink.hslink.data.dto.request.auth.WithdrawRequestDto
import com.hsLink.hslink.data.dto.response.auth.SocialLoginResponseDto
+import com.hsLink.hslink.data.dto.response.auth.WithdrawResponseDto
import com.hsLink.hslink.data.local.TokenDataStore
import com.hsLink.hslink.data.service.login.AuthService
import com.hsLink.hslink.domain.repository.AuthRepository
+import kotlinx.coroutines.flow.first
import javax.inject.Inject
class AuthRepositoryImpl @Inject constructor(
@@ -32,4 +36,47 @@ class AuthRepositoryImpl @Inject constructor(
Result.failure(e)
}
}
+
+ // ← 새로 추가: 로그아웃
+ override suspend fun logout(): Result {
+ return try {
+ val refreshToken = tokenDataStore.refreshToken.first()
+ if (refreshToken.isNullOrEmpty()) {
+ return Result.failure(Exception("토큰이 존재하지 않습니다"))
+ }
+
+ val request = LogoutRequestDto(refreshToken)
+ val response = authService.logout(request) // <- 이제 Response
+
+ if (response.isSuccessful) {
+ tokenDataStore.clearTokens()
+ Result.success(Unit)
+ } else {
+ Result.failure(Exception("로그아웃에 실패했습니다: ${response.code()}"))
+ }
+ } catch (e: Exception) {
+ Result.failure(e)
+ }
+ }
+
+ override suspend fun withdraw(): Result {
+ return try {
+ val refreshToken = tokenDataStore.refreshToken.first() // ← 수정
+ if (refreshToken.isNullOrEmpty()) {
+ return Result.failure(Exception("토큰이 존재하지 않습니다"))
+ }
+
+ val request = WithdrawRequestDto(refreshToken)
+ val response = authService.withdraw(request)
+
+ if (response.isSuccessful && response.body()?.isSuccess == true) {
+ tokenDataStore.clearTokens()
+ Result.success(response.body()!!.result)
+ } else {
+ Result.failure(Exception(response.body()?.message ?: "계정 탈퇴에 실패했습니다"))
+ }
+ } catch (e: Exception) {
+ Result.failure(e)
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/data/service/login/AuthService.kt b/app/src/main/java/com/hsLink/hslink/data/service/login/AuthService.kt
index 84be672..9790696 100644
--- a/app/src/main/java/com/hsLink/hslink/data/service/login/AuthService.kt
+++ b/app/src/main/java/com/hsLink/hslink/data/service/login/AuthService.kt
@@ -1,12 +1,23 @@
package com.hsLink.hslink.data.service.login
+import com.hsLink.hslink.core.network.BaseResponse
+import com.hsLink.hslink.data.dto.request.auth.LogoutRequestDto
import com.hsLink.hslink.data.dto.request.auth.SocialLoginRequestDto
+import com.hsLink.hslink.data.dto.request.auth.WithdrawRequestDto
import com.hsLink.hslink.data.dto.response.auth.SocialLoginResponseDto
+import com.hsLink.hslink.data.dto.response.auth.WithdrawResponseDto
import retrofit2.Response
import retrofit2.http.Body
+import retrofit2.http.DELETE
import retrofit2.http.POST
interface AuthService {
@POST("auth/login")
suspend fun socialLogin(@Body request: SocialLoginRequestDto): Response
+
+ @POST("auth/logout")
+ suspend fun logout(@Body request: LogoutRequestDto): Response
+
+ @DELETE("auth/withdraw")
+ suspend fun withdraw(@Body request: WithdrawRequestDto): Response>
}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/domain/repository/AuthRepository.kt b/app/src/main/java/com/hsLink/hslink/domain/repository/AuthRepository.kt
index b32597a..28a404d 100644
--- a/app/src/main/java/com/hsLink/hslink/domain/repository/AuthRepository.kt
+++ b/app/src/main/java/com/hsLink/hslink/domain/repository/AuthRepository.kt
@@ -1,10 +1,14 @@
package com.hsLink.hslink.domain.repository
import com.hsLink.hslink.data.dto.response.auth.SocialLoginResponseDto
+import com.hsLink.hslink.data.dto.response.auth.WithdrawResponseDto
interface AuthRepository {
suspend fun loginWithSocialToken(
provider: String,
accessToken: String
): Result
+
+ suspend fun logout(): Result
+ suspend fun withdraw(): Result
}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/component/career/ConfirmDialog.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/component/career/ConfirmDialog.kt
new file mode 100644
index 0000000..51e65e0
--- /dev/null
+++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/component/career/ConfirmDialog.kt
@@ -0,0 +1,106 @@
+// presentation/mypage/component/common/ConfirmDialog.kt (이름 변경)
+package com.hsLink.hslink.presentation.mypage.component.career
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.hsLink.hslink.core.designsystem.component.HsLinkActionButton
+import com.hsLink.hslink.core.designsystem.component.HsLinkActionButtonSize
+import com.hsLink.hslink.core.designsystem.component.HsLinkButtonSize
+import com.hsLink.hslink.core.designsystem.component.HsLinkSelectButton
+import com.hsLink.hslink.core.designsystem.theme.HsLinkTheme
+
+
+@Preview(showBackground = true)
+@Composable
+private fun ConfirmDialogLogoutPreview() {
+ HsLinkTheme {
+ ConfirmDialog(
+ title = "로그아웃을\n하시겠습니까?",
+ message = null,
+ cancelText = "취소",
+ confirmText = "확인",
+ onDismiss = {},
+ onConfirm = {}
+ )
+ }
+}
+
+@Composable
+fun ConfirmDialog(
+ modifier: Modifier = Modifier,
+ title: String,
+ message: String? = null,
+ cancelText: String = "취소",
+ confirmText: String = "확인",
+ onDismiss: () -> Unit,
+ onConfirm: () -> Unit,
+) {
+ AlertDialog(
+ onDismissRequest = onDismiss,
+ containerColor = Color.White, // ← 완전 하얀색 배경
+ title = {
+ Text(
+ text = title,
+ style = HsLinkTheme.typography.title_20Strong,
+ textAlign = TextAlign.Center,
+ modifier = Modifier.fillMaxWidth()
+ )
+ },
+ text = message?.let {
+ {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 12.dp), // ← 전체 여백
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text(
+ text = it,
+ style = HsLinkTheme.typography.body_14Normal,
+ color = HsLinkTheme.colors.Grey600,
+ textAlign = TextAlign.Center,
+ lineHeight = 20.sp, // ← 줄 간격 고정값
+ modifier = Modifier.fillMaxWidth()
+ )
+ }
+ }
+ },
+ confirmButton = {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ // ← 취소하기 (회색 배경)
+ HsLinkActionButton(
+ label = cancelText, // "취소하기"
+ onClick = onDismiss,
+ size = HsLinkActionButtonSize.Small, // ← 회색 배경
+ modifier = Modifier.weight(1f)
+ )
+
+ // ← 확인 버튼 (파란색 배경)
+ HsLinkActionButton(
+ label = confirmText, // "로그아웃" 또는 "삭제하기"
+ onClick = onConfirm,
+ size = HsLinkActionButtonSize.Large, // ← 파란색 배경
+ modifier = Modifier.weight(1f)
+ )
+ }
+ },
+ dismissButton = null,
+ modifier = modifier
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt
index 4fa3729..9e45971 100644
--- a/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt
+++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/screen/main/MypageScreen.kt
@@ -13,6 +13,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
@@ -20,6 +23,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
+import androidx.navigation.NavOptions
import androidx.navigation.compose.currentBackStackEntryAsState
import com.hsLink.hslink.R
import com.hsLink.hslink.core.designsystem.component.HsLinkTopBar
@@ -27,6 +31,8 @@ import com.hsLink.hslink.core.designsystem.theme.HsLinkTheme
import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto
import com.hsLink.hslink.data.dto.response.mypage.MyPageUserSummaryDto
import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto
+import com.hsLink.hslink.presentation.login.navigation.navigateToLogin
+import com.hsLink.hslink.presentation.mypage.component.career.ConfirmDialog
import com.hsLink.hslink.presentation.mypage.component.main.MyPageCardItemContainer
import com.hsLink.hslink.presentation.mypage.component.main.MyPageDetailItemContent
import com.hsLink.hslink.presentation.mypage.component.main.MyPageItemData
@@ -61,7 +67,12 @@ fun MypageRoute(
) {
val userSummary by viewModel.userSummary.collectAsState() // ← 변경
val isLoading by viewModel.isLoading.collectAsState()
+ val isAuthLoading by viewModel.isAuthLoading.collectAsState()
val error by viewModel.error.collectAsState()
+ val logoutSuccess by viewModel.logoutSuccess.collectAsState() // ← 추가
+ val withdrawSuccess by viewModel.withdrawSuccess.collectAsState() // ← 추가
+
+
LaunchedEffect(Unit) {
viewModel.loadUserSummary() // 또는 loadMypage()
@@ -76,14 +87,37 @@ fun MypageRoute(
}
}
+ // 로그아웃/탈퇴 성공 시 처리
+ LaunchedEffect(error) {
+ error?.let { errorMessage ->
+ if (errorMessage.contains("성공")) {
+ // 로그인 화면으로 이동 (추후 구현)
+ // navController.navigateToLogin()
+ }
+ }
+ }
+
+
+ LaunchedEffect(logoutSuccess, withdrawSuccess) {
+ if (logoutSuccess || withdrawSuccess) {
+ navController.navigateToLogin(
+ navOptions = NavOptions.Builder()
+ .setPopUpTo(0, inclusive = true) // ← 모든 백스택 클리어
+ .build()
+ )
+ }
+ }
MypageScreen(
paddingValues = paddingValues,
- userSummary = userSummary, // ← 변경
+ userSummary = userSummary,
isLoading = isLoading,
+ isAuthLoading = isAuthLoading, // ← 추가
error = error,
onNavigateToProfile = {
navController.navigateToProfileEdit()
- }
+ },
+ onLogout = viewModel::logout, // ← 추가
+ onWithdraw = viewModel::withdraw // ← 추가
)
}
@@ -94,12 +128,17 @@ fun MypageScreen(
userSummary: MyPageUserSummaryDto? = null, // ← 변경
isLoading: Boolean = false,
error: String? = null,
+ isAuthLoading: Boolean = false, // ← 추가
onNavigateToProfile: () -> Unit = {},
onNavigateToPosts: () -> Unit = {},
onNavigateToSettings: () -> Unit = {},
- onLogout: () -> Unit = {},
- onQuit: () -> Unit = {},
+ onLogout: () -> Unit = {}, // ← 추가
+ onWithdraw: () -> Unit = {}, // ← 추가
) {
+
+ // ← 다이얼로그 상태 관리
+ var showLogoutDialog by remember { mutableStateOf(false) }
+ var showWithdrawDialog by remember { mutableStateOf(false) }
LazyColumn(
modifier = modifier
.fillMaxSize()
@@ -151,9 +190,42 @@ fun MypageScreen(
MyPageItemData(id = "4", title = "탈퇴하기", route = "/quit")
),
onItemClick = { item ->
- // 클릭 처리
+ when (item.id) {
+ "3" -> showLogoutDialog = true // ← 로그아웃 다이얼로그 표시
+ "4" -> showWithdrawDialog = true // ← 탈퇴 다이얼로그 표시
+ }
}
)
}
}
- }
\ No newline at end of file
+
+ // ← 로그아웃 확인 다이얼로그
+ if (showLogoutDialog) {
+ ConfirmDialog(
+ title = "로그아웃을\n하시겠습니까?",
+ message = null,
+ cancelText = "취소",
+ confirmText = "확인",
+ onDismiss = { showLogoutDialog = false },
+ onConfirm = {
+ showLogoutDialog = false
+ onLogout() // ← 실제 로그아웃 실행
+ }
+ )
+ }
+
+ // ← 계정 삭제 확인 다이얼로그
+ if (showWithdrawDialog) {
+ ConfirmDialog(
+ title = "계정을 삭제하시겠습니까?",
+ message = "데이터가 복구되지 않는데 괜찮으신가요?",
+ cancelText = "취소",
+ confirmText = "확인",
+ onDismiss = { showWithdrawDialog = false },
+ onConfirm = {
+ showWithdrawDialog = false
+ onWithdraw() // ← 실제 계정 탈퇴 실행
+ }
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt b/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt
index fbef525..dd93e1c 100644
--- a/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt
+++ b/app/src/main/java/com/hsLink/hslink/presentation/mypage/viewmodel/MypageViewModel.kt
@@ -9,6 +9,7 @@ import com.hsLink.hslink.data.dto.request.mypage.UpdateProfileRequestDto
import com.hsLink.hslink.data.dto.response.mypage.MyPageUserProfileDto
import com.hsLink.hslink.data.dto.response.mypage.MyPageUserSummaryDto
import com.hsLink.hslink.data.dto.response.mypage.UserProfileDto
+import com.hsLink.hslink.domain.repository.AuthRepository
import com.hsLink.hslink.domain.repository.mypage.MypageRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -19,7 +20,8 @@ import javax.inject.Inject
@HiltViewModel
class MypageViewModel @Inject constructor(
- private val mypageRepository: MypageRepository
+ private val mypageRepository: MypageRepository,
+ private val authRepository: AuthRepository
) : ViewModel() {
private val _userProfile = MutableStateFlow(null)
@@ -31,6 +33,19 @@ class MypageViewModel @Inject constructor(
private val _error = MutableStateFlow(null)
val error: StateFlow = _error.asStateFlow()
+ // ← 로그아웃 성공 이벤트 추가
+ private val _logoutSuccess = MutableStateFlow(false)
+ val logoutSuccess: StateFlow = _logoutSuccess.asStateFlow()
+
+ // ← 탈퇴 성공 이벤트 추가
+ private val _withdrawSuccess = MutableStateFlow(false)
+ val withdrawSuccess: StateFlow = _withdrawSuccess.asStateFlow()
+
+
+ // ← 로그아웃/탈퇴 상태 추가
+ private val _isAuthLoading = MutableStateFlow(false)
+ val isAuthLoading: StateFlow = _isAuthLoading.asStateFlow()
+
init {
getUserSummary() // ← getUserProfile() 대신 변경
}
@@ -106,4 +121,44 @@ class MypageViewModel @Inject constructor(
}
}
}
+ // ← 새로 추가: 로그아웃 기능
+ fun logout() {
+ viewModelScope.launch {
+ _isAuthLoading.value = true
+ Log.d("MypageViewModel", "로그아웃 시작")
+
+ authRepository.logout().fold(
+ onSuccess = {
+ Log.d("MypageViewModel", "로그아웃 성공")
+ _error.value = null
+ _logoutSuccess.value = true // ← 성공 이벤트 발생
+ },
+ onFailure = { exception ->
+ Log.e("MypageViewModel", "로그아웃 실패: ${exception.message}")
+ _error.value = exception.message
+ }
+ )
+ _isAuthLoading.value = false
+ }
+ }
+
+ fun withdraw() {
+ viewModelScope.launch {
+ _isAuthLoading.value = true
+ Log.d("MypageViewModel", "계정 탈퇴 시작")
+
+ authRepository.withdraw().fold(
+ onSuccess = { withdrawResponse ->
+ Log.d("MypageViewModel", "계정 탈퇴 성공: userId=${withdrawResponse.userId}")
+ _error.value = null
+ _withdrawSuccess.value = true // ← 성공 이벤트 발생
+ },
+ onFailure = { exception ->
+ Log.e("MypageViewModel", "계정 탈퇴 실패: ${exception.message}")
+ _error.value = exception.message
+ }
+ )
+ _isAuthLoading.value = false
+ }
+ }
}
\ No newline at end of file