Skip to content
This repository has been archived by the owner on Mar 8, 2024. It is now read-only.

๐Ÿ”€ :: (#148) ๋ฒ„๊ทธ ์ œ๋ณด ๊ธฐ๋Šฅ ๊ตฌํ˜„ #149

Merged
6 changes: 5 additions & 1 deletion data/src/main/kotlin/com/signal/data/api/ApiProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,16 @@ object ApiProvider {
fun getRecommendApi(tokenInterceptor: TokenInterceptor): RecommendApi {
return getRetrofit(tokenInterceptor).create(RecommendApi::class.java)
}

fun getReservationApi(tokenInterceptor: TokenInterceptor): ReservationApi {
return getRetrofit(tokenInterceptor).create(ReservationApi::class.java)
}

fun getCoinApi(tokenInterceptor: TokenInterceptor): CoinApi {
return getRetrofit(tokenInterceptor).create(CoinApi::class.java)
}

fun getReportApi(tokenInterceptor: TokenInterceptor): ReportApi {
return getRetrofit(tokenInterceptor).create(ReportApi::class.java)
}
}
12 changes: 12 additions & 0 deletions data/src/main/kotlin/com/signal/data/api/ReportApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.signal.data.api

import com.signal.data.model.report.ReportBugRequest
import retrofit2.http.Body
import retrofit2.http.POST

interface ReportApi {
@POST(SignalUrl.Report.ReportBug)
suspend fun reportBug(
@Body reportBugRequest: ReportBugRequest,
)
}
9 changes: 7 additions & 2 deletions data/src/main/kotlin/com/signal/data/api/SignalUrl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ object SignalUrl {
private const val reservation = "/reservation"
private const val admin = "/admin"
private const val coin = "/coin"
private const val report = "/report"

object Users {
const val SignIn = "$users/signin"
Expand Down Expand Up @@ -39,11 +40,11 @@ object SignalUrl {
const val DeleteDiary = "$diary/{diary_id}"
}

object Recommend{
object Recommend {
const val FetchRecommends = "$recommend/list"
const val RecommendId = "$recommend/{recommend_id}"
}

object Reservation {
const val FetchHospitals = "$admin/hospital/list"
const val FetchDayReservation = "$reservation/user"
Expand All @@ -55,4 +56,8 @@ object SignalUrl {
const val CreateCoin = coin
const val FetchCoins = "$coin/list"
}

object Report {
const val ReportBug = report
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.signal.data.datasource.report

import com.signal.data.model.report.ReportBugRequest

interface ReportDataSource {
suspend fun reportBug(reportBugRequest: ReportBugRequest)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.signal.data.datasource.report

import com.signal.data.api.ReportApi
import com.signal.data.model.report.ReportBugRequest
import com.signal.data.util.ExceptionHandler

class ReportDataSourceImpl(
private val reportApi: ReportApi,
) : ReportDataSource {
override suspend fun reportBug(reportBugRequest: ReportBugRequest) =
ExceptionHandler<Unit>().httpRequest {
reportApi.reportBug(reportBugRequest = reportBugRequest)
}.sendRequest()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.signal.data.model.report

import com.google.gson.annotations.SerializedName

class ReportBugRequest(
@SerializedName("content") val content: String,
@SerializedName("image") val image: String?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.signal.data.repository

import com.signal.data.datasource.report.ReportDataSource
import com.signal.data.model.report.ReportBugRequest
import com.signal.domain.repository.ReportRepository

class ReportRepositoryImpl(
private val reportDataSource: ReportDataSource
) : ReportRepository {
override suspend fun reportBug(
content: String,
image: String?,
) = kotlin.runCatching {
reportDataSource.reportBug(
reportBugRequest = ReportBugRequest(
content = content,
image = image,
),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.signal.domain.repository

interface ReportRepository {
suspend fun reportBug(
content: String,
image: String?,
): Result<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ internal fun SignalApp() {
moveToEditProfile = {
navController.navigate(NavigationRoute.Main.EditProfile)
},
moveToReportBug = {
navController.navigate(NavigationRoute.User.ReportBug)
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import com.signal.data.datasource.file.AttachmentDataSource
import com.signal.data.datasource.file.AttachmentDataSourceImpl
import com.signal.data.datasource.recommend.RecommendDataSource
import com.signal.data.datasource.recommend.RecommendDataSourceImpl
import com.signal.data.datasource.report.ReportDataSource
import com.signal.data.datasource.report.ReportDataSourceImpl
import com.signal.data.datasource.reservation.ReservationDataSource
import com.signal.data.datasource.reservation.ReservationDataSourceImpl
import com.signal.data.datasource.user.local.LocalUserDataSource
Expand All @@ -28,6 +30,7 @@ import com.signal.data.repository.DiagnosisRepositoryImpl
import com.signal.data.repository.DiaryRepositoryImpl
import com.signal.data.repository.FeedRepositoryImpl
import com.signal.data.repository.RecommendRepositoryImpl
import com.signal.data.repository.ReportRepositoryImpl
import com.signal.data.repository.ReservationRepositoryImpl
import com.signal.data.repository.UserRepositoryImpl
import com.signal.data.util.PreferenceManager
Expand All @@ -38,6 +41,7 @@ import com.signal.domain.repository.DiagnosisRepository
import com.signal.domain.repository.DiaryRepository
import com.signal.domain.repository.FeedRepository
import com.signal.domain.repository.RecommendRepository
import com.signal.domain.repository.ReportRepository
import com.signal.domain.repository.ReservationRepository
import com.signal.domain.repository.UserRepository
import com.signal.domain.usecase.users.AddFamousSayingUseCase
Expand All @@ -54,6 +58,7 @@ import com.signal.domain.usecase.users.SignInUseCase
import com.signal.domain.usecase.users.SignOutUseCase
import com.signal.domain.usecase.users.SignUpUseCase
import com.signal.domain.usecase.users.UpdateUserInformationUseCase
import com.signal.signal_android.feature.bug.BugViewModel
import com.signal.signal_android.feature.coin.CoinViewModel
import com.signal.signal_android.feature.diagnosis.DiagnosisViewModel
import com.signal.signal_android.feature.file.AttachmentViewModel
Expand Down Expand Up @@ -104,6 +109,7 @@ val apiModule: Module
single { ApiProvider.getRecommendApi(tokenInterceptor = get()) }
single { ApiProvider.getReservationApi(tokenInterceptor = get()) }
single { ApiProvider.getCoinApi(tokenInterceptor = get()) }
single { ApiProvider.getReportApi(tokenInterceptor = get()) }
}

val daoModule: Module
Expand Down Expand Up @@ -149,6 +155,7 @@ val dataSourceModule: Module
single<RecommendDataSource> { RecommendDataSourceImpl(recommendApi = get()) }
single<ReservationDataSource> { ReservationDataSourceImpl(reservationApi = get()) }
single<CoinDataSource> { CoinDataSourceImpl(coinApi = get()) }
single<ReportDataSource> { ReportDataSourceImpl(reportApi = get()) }
}

val repositoryModule: Module
Expand Down Expand Up @@ -180,6 +187,9 @@ val repositoryModule: Module
single<CoinRepository> {
CoinRepositoryImpl(coinDataSource = get())
}
single<ReportRepository> {
ReportRepositoryImpl(reportDataSource = get())
}
}

val useCaseModule: Module
Expand Down Expand Up @@ -242,4 +252,5 @@ val viewModelModule: Module
viewModel { RecommendViewModel(recommendRepository = get()) }
viewModel { ReservationViewModel(reservationRepository = get()) }
viewModel { CoinViewModel(coinRepository = get()) }
viewModel { BugViewModel(reportRepository = get()) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.signal.signal_android.feature.bug

sealed interface BugSideEffect {
object Success: BugSideEffect
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.signal.signal_android.feature.bug

internal data class BugState(
val content: String,
val image: String,
) {
companion object {
fun getDefaultState() = BugState(
content = "",
image = "",
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.signal.signal_android.feature.bug

import androidx.lifecycle.viewModelScope
import com.signal.domain.repository.ReportRepository
import com.signal.signal_android.BaseViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

internal class BugViewModel(
private val reportRepository: ReportRepository,
) : BaseViewModel<BugState, BugSideEffect>(BugState.getDefaultState()) {

internal fun reportBug() {
with(state.value) {
viewModelScope.launch(Dispatchers.IO) {
reportRepository.reportBug(
content = content,
image = image,
).onSuccess {
postSideEffect(BugSideEffect.Success)
}.onFailure {

}
}
}
}

internal fun setContent(content: String) {
setState(state.value.copy(content = content))
}

internal fun setImage(image: String){
setState(state.value.copy(image = image))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.signal.signal_android.feature.bug

import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
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.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.signal.data.util.FileUtil
import com.signal.signal_android.R
import com.signal.signal_android.designsystem.button.SignalFilledButton
import com.signal.signal_android.designsystem.component.Header
import com.signal.signal_android.designsystem.textfield.SignalTextField
import com.signal.signal_android.feature.file.AttachmentViewModel
import com.signal.signal_android.feature.main.feed.PostImage
import org.koin.androidx.compose.koinViewModel

@Composable
internal fun ReportBug(
moveToBack: () -> Unit,
bugViewModel: BugViewModel = koinViewModel(),
attachmentViewModel: AttachmentViewModel = koinViewModel(),
) {
val state by bugViewModel.state.collectAsState()
val attachmentState by attachmentViewModel.state.collectAsState()

LaunchedEffect(Unit) {
bugViewModel.sideEffect.collect {
when (it) {
is BugSideEffect.Success -> moveToBack()
}
}
}

val context = LocalContext.current

var imagePreview: Uri? by remember { mutableStateOf(null) }
val imageUrl by remember { mutableStateOf("") }

val focusManager = LocalFocusManager.current

val launcher = rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) {
it?.run {
imagePreview = it
with(attachmentViewModel) {
setFile(
FileUtil.toFile(
context = context,
uri = this@run,
)
)
uploadFile()
}
}
}

var reason by remember { mutableStateOf("") }

Column(
modifier = Modifier
.fillMaxSize()
.padding(
start = 16.dp,
end = 16.dp,
bottom = 24.dp,
)
) {
Header(
title = stringResource(id = R.string.report_bug_header_title),
onLeadingClicked = moveToBack,
)
Spacer(modifier = Modifier.height(12.dp))
SignalTextField(
modifier = Modifier
.fillMaxHeight(0.5f)
.padding(bottom = 28.dp),
value = state.content,
onValueChange = bugViewModel::setContent,
showLength = true,
maxLength = 1000,
hint = stringResource(id = R.string.report_bug_hint_reason),
alignment = Alignment.Top,
)
PostImage(
uri = { imagePreview },
imageUrl = { imageUrl },
) {
focusManager.clearFocus()
launcher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
}
Spacer(modifier = Modifier.weight(1f))
SignalFilledButton(
text = stringResource(id = R.string.my_page_secession_check),
onClick = {
bugViewModel.setImage(image = attachmentState.imageUrl)
bugViewModel.reportBug()
},
enabled = state.content.isNotEmpty(),
)
}
}
Loading