diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/GetNewRouletteResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/GetNewRouletteResponse.kt new file mode 100644 index 0000000..dbb6122 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/GetNewRouletteResponse.kt @@ -0,0 +1,10 @@ +package kr.co.vividnext.sodalive.live.roulette + +import com.google.gson.annotations.SerializedName + +data class GetNewRouletteResponse( + @SerializedName("id") val id: Long, + @SerializedName("can") val can: Int, + @SerializedName("isActive") val isActive: Boolean, + @SerializedName("items") val items: List +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/RouletteRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/RouletteRepository.kt index 07dbd14..ac63f64 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/RouletteRepository.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/RouletteRepository.kt @@ -2,18 +2,32 @@ package kr.co.vividnext.sodalive.live.roulette import io.reactivex.rxjava3.core.Single import kr.co.vividnext.sodalive.common.ApiResponse -import kr.co.vividnext.sodalive.live.roulette.config.CreateOrUpdateRouletteRequest +import kr.co.vividnext.sodalive.live.roulette.config.CreateRouletteRequest import kr.co.vividnext.sodalive.live.roulette.config.RouletteApi +import kr.co.vividnext.sodalive.live.roulette.config.UpdateRouletteRequest class RouletteRepository(private val api: RouletteApi) { - fun createOrUpdateRoulette( - request: CreateOrUpdateRouletteRequest, + fun createRoulette( + request: CreateRouletteRequest, token: String - ) = api.createOrUpdateRoulette( + ) = api.createRoulette( request = request, authHeader = token ) + fun updateRoulette( + request: UpdateRouletteRequest, + token: String + ) = api.updateRoulette( + request = request, + authHeader = token + ) + + fun getAllRoulette(creatorId: Long, token: String) = api.getAllRoulette( + creatorId = creatorId, + authHeader = token + ) + fun getRoulette(creatorId: Long, token: String) = api.getRoulette( creatorId = creatorId, authHeader = token diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/CreateOrUpdateRouletteRequest.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/CreateRouletteRequest.kt similarity index 88% rename from app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/CreateOrUpdateRouletteRequest.kt rename to app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/CreateRouletteRequest.kt index aced332..d6210ac 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/CreateOrUpdateRouletteRequest.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/CreateRouletteRequest.kt @@ -3,7 +3,7 @@ package kr.co.vividnext.sodalive.live.roulette.config import com.google.gson.annotations.SerializedName import kr.co.vividnext.sodalive.live.roulette.RouletteItem -data class CreateOrUpdateRouletteRequest( +data class CreateRouletteRequest( @SerializedName("can") val can: Int, @SerializedName("isActive") val isActive: Boolean, @SerializedName("items") val items: List diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/RouletteApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/RouletteApi.kt index 4b50433..80d2078 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/RouletteApi.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/RouletteApi.kt @@ -2,35 +2,49 @@ package kr.co.vividnext.sodalive.live.roulette.config import io.reactivex.rxjava3.core.Single import kr.co.vividnext.sodalive.common.ApiResponse +import kr.co.vividnext.sodalive.live.roulette.GetNewRouletteResponse import kr.co.vividnext.sodalive.live.roulette.GetRouletteResponse import kr.co.vividnext.sodalive.live.roulette.SpinRouletteRequest import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.Header import retrofit2.http.POST +import retrofit2.http.PUT import retrofit2.http.Path import retrofit2.http.Query interface RouletteApi { - @POST("/roulette") - fun createOrUpdateRoulette( - @Body request: CreateOrUpdateRouletteRequest, + @POST("/new-roulette") + fun createRoulette( + @Body request: CreateRouletteRequest, @Header("Authorization") authHeader: String ): Single> - @GET("/roulette") + @PUT("/new-roulette") + fun updateRoulette( + @Body request: UpdateRouletteRequest, + @Header("Authorization") authHeader: String + ): Single> + + @GET("/new-roulette/creator") + fun getAllRoulette( + @Query("creatorId") creatorId: Long, + @Header("Authorization") authHeader: String + ): Single>> + + @GET("/new-roulette") fun getRoulette( @Query("creatorId") creatorId: Long, @Header("Authorization") authHeader: String ): Single> - @POST("/roulette/spin") + @POST("/new-roulette/spin") fun spinRoulette( @Body request: SpinRouletteRequest, @Header("Authorization") authHeader: String ): Single> - @POST("/roulette/refund/{id}") + @POST("/new-roulette/refund/{id}") fun refundRouletteDonation( @Path("id") id: Long, @Header("Authorization") authHeader: String diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/RouletteSettingsFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/RouletteSettingsFragment.kt index 73fdae1..ff59a44 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/RouletteSettingsFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/RouletteSettingsFragment.kt @@ -13,7 +13,9 @@ import android.view.View import android.view.inputmethod.InputMethodManager import android.widget.EditText import android.widget.ImageView +import android.widget.LinearLayout import android.widget.TextView +import androidx.core.content.ContextCompat import com.jakewharton.rxbinding4.widget.textChanges import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.schedulers.Schedulers @@ -41,7 +43,7 @@ class RouletteSettingsFragment : BaseFragment( setupView() bindData() - viewModel.getRoulette() + viewModel.getAllRoulette() } private fun setupView() { @@ -74,15 +76,57 @@ class RouletteSettingsFragment : BaseFragment( handler.postDelayed({ imm.hideSoftInputFromWindow(view?.windowToken, 0) }, 100) + viewModel.createOrUpdateRoulette { val resultIntent = Intent().apply { putExtra(Constants.EXTRA_RESULT_ROULETTE, it) } requireActivity().setResult(Activity.RESULT_OK, resultIntent) requireActivity().finish() } } + + binding.llSelectRoulette1.setOnClickListener { + viewModel.selectRoulette( + RouletteSettingsViewModel.SelectedRoulette.ROULETTE_1 + ) + } + + binding.llSelectRoulette2.setOnClickListener { + viewModel.selectRoulette( + RouletteSettingsViewModel.SelectedRoulette.ROULETTE_2 + ) + } + + binding.llSelectRoulette3.setOnClickListener { + viewModel.selectRoulette( + RouletteSettingsViewModel.SelectedRoulette.ROULETTE_3 + ) + } } private fun bindData() { + viewModel.selectedRouletteLiveData.observe(viewLifecycleOwner) { + deselectAllRoulette() + when (it) { + RouletteSettingsViewModel.SelectedRoulette.ROULETTE_2 -> selectRouletteButton( + binding.ivSelectRoulette2, + binding.llSelectRoulette2, + binding.tvSelectRoulette2 + ) + + RouletteSettingsViewModel.SelectedRoulette.ROULETTE_3 -> selectRouletteButton( + binding.ivSelectRoulette3, + binding.llSelectRoulette3, + binding.tvSelectRoulette3 + ) + + else -> selectRouletteButton( + binding.ivSelectRoulette1, + binding.llSelectRoulette1, + binding.tvSelectRoulette1 + ) + } + } + viewModel.isActiveLiveData.observe(viewLifecycleOwner) { binding.ivRouletteIsActive.setImageResource( if (it) R.drawable.btn_toggle_on_big else R.drawable.btn_toggle_off_big @@ -185,4 +229,50 @@ class RouletteSettingsFragment : BaseFragment( return optionView } + + private fun deselectAllRoulette() { + binding.ivSelectRoulette1.visibility = View.GONE + binding.ivSelectRoulette2.visibility = View.GONE + binding.ivSelectRoulette3.visibility = View.GONE + + binding.llSelectRoulette1.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) + binding.llSelectRoulette2.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) + binding.llSelectRoulette3.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) + + binding.tvSelectRoulette1.setTextColor( + ContextCompat.getColor( + requireContext(), + R.color.color_3bb9f1 + ) + ) + + binding.tvSelectRoulette2.setTextColor( + ContextCompat.getColor( + requireContext(), + R.color.color_3bb9f1 + ) + ) + + binding.tvSelectRoulette3.setTextColor( + ContextCompat.getColor( + requireContext(), + R.color.color_3bb9f1 + ) + ) + } + + private fun selectRouletteButton( + ivSelectRoulette: ImageView, + llSelectRoulette: LinearLayout, + tvSelectRoulette: TextView + ) { + ivSelectRoulette.visibility = View.VISIBLE + llSelectRoulette.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) + tvSelectRoulette.setTextColor( + ContextCompat.getColor( + requireContext(), + R.color.color_eeeeee + ) + ) + } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/RouletteSettingsViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/RouletteSettingsViewModel.kt index c23fb09..1a94c54 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/RouletteSettingsViewModel.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/RouletteSettingsViewModel.kt @@ -7,6 +7,7 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.schedulers.Schedulers import kr.co.vividnext.sodalive.base.BaseViewModel import kr.co.vividnext.sodalive.common.SharedPreferenceManager +import kr.co.vividnext.sodalive.live.roulette.GetNewRouletteResponse import kr.co.vividnext.sodalive.live.roulette.RouletteItem import kr.co.vividnext.sodalive.live.roulette.RoulettePreview import kr.co.vividnext.sodalive.live.roulette.RoulettePreviewItem @@ -15,6 +16,10 @@ import kotlin.math.floor class RouletteSettingsViewModel(private val repository: RouletteRepository) : BaseViewModel() { + enum class SelectedRoulette { + ROULETTE_1, ROULETTE_2, ROULETTE_3 + } + private var _isLoading = MutableLiveData(false) val isLoading: LiveData get() = _isLoading @@ -39,9 +44,16 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba val roulettePreviewLiveData: LiveData get() = _roulettePreviewLiveData - private val options = mutableListOf() + private val _selectedRouletteLiveData = MutableLiveData() + val selectedRouletteLiveData: LiveData + get() = _selectedRouletteLiveData + var can = 0 var isActive = false + private var rouletteId = 0L + private val options = mutableListOf() + + private val rouletteList = mutableListOf() fun plusWeight(optionIndex: Int) { val currentOption = options[optionIndex] @@ -113,66 +125,130 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba if (!_isLoading.value!!) { _isLoading.value = true - val items = mutableListOf() - for (option in options) { - if (option.title.trim().isEmpty()) { - _toastLiveData.value = "옵션은 빈칸을 할 수 없습니다." - _isLoading.value = false - return - } - - items.add(RouletteItem(title = option.title, weight = option.weight)) + if (rouletteId > 0) { + updateRoulette(onSuccess) + } else { + createRoulette(onSuccess) } - - val request = CreateOrUpdateRouletteRequest( - can = can, - isActive = isActive, - items = items - ) - - compositeDisposable.add( - repository.createOrUpdateRoulette( - request = request, - token = "Bearer ${SharedPreferenceManager.token}" - ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { - _isLoading.value = false - if (it.success && it.data != null && it.data is Boolean) { - val message = if (it.data) { - "룰렛을 활성화 했습니다." - } else { - "룰렛을 비활성화 했습니다." - } - _toastLiveData.postValue(message) - onSuccess(it.data) - } else { - if (it.message != null) { - _toastLiveData.postValue(it.message) - } else { - _toastLiveData.postValue( - "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." - ) - } - } - }, - { - _isLoading.value = false - it.message?.let { message -> Logger.e(message) } - _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") - } - ) - ) } } - fun getRoulette() { + private fun updateRoulette(onSuccess: (Boolean) -> Unit) { + val items = mutableListOf() + for (option in options) { + if (option.title.trim().isEmpty()) { + _toastLiveData.value = "옵션은 빈칸을 할 수 없습니다." + _isLoading.value = false + return + } + + items.add(RouletteItem(title = option.title, weight = option.weight)) + } + + val request = UpdateRouletteRequest( + id = rouletteId, + can = can, + isActive = isActive, + items = items + ) + + compositeDisposable.add( + repository.updateRoulette( + request = request, + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + _isLoading.value = false + if (it.success && it.data != null && it.data is Boolean) { + val message = if (it.data) { + "룰렛을 활성화 했습니다." + } else { + "룰렛을 비활성화 했습니다." + } + _toastLiveData.postValue(message) + onSuccess(it.data) + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + } + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } + + private fun createRoulette(onSuccess: (Boolean) -> Unit) { + val items = mutableListOf() + for (option in options) { + if (option.title.trim().isEmpty()) { + _toastLiveData.value = "옵션은 빈칸을 할 수 없습니다." + _isLoading.value = false + return + } + + items.add(RouletteItem(title = option.title, weight = option.weight)) + } + + val request = CreateRouletteRequest( + can = can, + isActive = isActive, + items = items + ) + + compositeDisposable.add( + repository.createRoulette( + request = request, + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + _isLoading.value = false + if (it.success && it.data != null && it.data is Boolean) { + val message = if (it.data) { + "룰렛을 활성화 했습니다." + } else { + "룰렛을 비활성화 했습니다." + } + _toastLiveData.postValue(message) + onSuccess(it.data) + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + } + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } + + fun getAllRoulette() { if (!_isLoading.value!!) { _isLoading.value = true compositeDisposable.add( - repository.getRoulette( + repository.getAllRoulette( creatorId = SharedPreferenceManager.userId, token = "Bearer ${SharedPreferenceManager.token}" ) @@ -182,30 +258,9 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba { if (it.success) { val data = it.data - - if (data != null && data.items.isNotEmpty()) { - _isActiveLiveData.value = data.isActive - _canLiveData.value = data.can - - isActive = data.isActive - can = data.can - - val options = data.items.asSequence().map { item -> - RouletteOption(title = item.title, weight = item.weight) - }.toList() - removeAllAndAddOptions(options = options) - recalculatePercentages(options) - } else { - _isActiveLiveData.value = false - _canLiveData.value = 0 - - isActive = false - can = 0 - - options.add(RouletteOption(title = "", weight = 1)) - options.add(RouletteOption(title = "", weight = 1)) - recalculatePercentages(options) - } + rouletteList.clear() + rouletteList.addAll(data ?: listOf()) + selectRoulette(SelectedRoulette.ROULETTE_1) } else { if (it.message != null) { _toastLiveData.postValue(it.message) @@ -227,6 +282,56 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba } } + fun selectRoulette(selectedRoulette: SelectedRoulette) { + if ( + rouletteList.isEmpty() && + ( + selectedRoulette == SelectedRoulette.ROULETTE_2 || + selectedRoulette == SelectedRoulette.ROULETTE_3 + ) + ) { + _toastLiveData.value = "룰렛 1만 선택 가능" + return + } + + if (rouletteList.size == 1 && selectedRoulette == SelectedRoulette.ROULETTE_3) { + _toastLiveData.value = "룰렛 1, 룰렛 2만 선택 가능" + return + } + + if (_selectedRouletteLiveData.value != selectedRoulette) { + _selectedRouletteLiveData.value = selectedRoulette + + if (rouletteList.size > selectedRoulette.ordinal) { + val roulette = rouletteList[selectedRoulette.ordinal] + _canLiveData.value = roulette.can + _isActiveLiveData.value = roulette.isActive + + can = roulette.can + rouletteId = roulette.id + isActive = roulette.isActive + + val options = roulette.items.asSequence().map { item -> + RouletteOption(title = item.title, weight = item.weight) + }.toList() + removeAllAndAddOptions(options = options) + recalculatePercentages(options) + } else { + _canLiveData.value = 0 + _isActiveLiveData.value = false + + can = 0 + rouletteId = 0 + isActive = false + + options.clear() + options.add(RouletteOption(title = "", weight = 1)) + options.add(RouletteOption(title = "", weight = 1)) + recalculatePercentages(options) + } + } + } + private fun removeAllAndAddOptions(options: List) { this.options.clear() this.options.addAll(options) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/UpdateRouletteRequest.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/UpdateRouletteRequest.kt new file mode 100644 index 0000000..ccdca9f --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/roulette/config/UpdateRouletteRequest.kt @@ -0,0 +1,11 @@ +package kr.co.vividnext.sodalive.live.roulette.config + +import com.google.gson.annotations.SerializedName +import kr.co.vividnext.sodalive.live.roulette.RouletteItem + +data class UpdateRouletteRequest( + @SerializedName("id") val id: Long, + @SerializedName("can") val can: Int, + @SerializedName("isActive") val isActive: Boolean, + @SerializedName("items") val items: List +) diff --git a/app/src/main/res/layout/fragment_roulette_settings.xml b/app/src/main/res/layout/fragment_roulette_settings.xml index e32cbf8..1e24013 100644 --- a/app/src/main/res/layout/fragment_roulette_settings.xml +++ b/app/src/main/res/layout/fragment_roulette_settings.xml @@ -20,6 +20,99 @@ android:orientation="vertical" android:paddingHorizontal="13.3dp"> + + + + + + + + + + + + + + + + + + + + + + + +