프로필 수정 페이지 추가
This commit is contained in:
@@ -55,6 +55,11 @@ import kr.co.vividnext.sodalive.mypage.can.CanRepository
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeViewModel
|
||||
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentViewModel
|
||||
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusViewModel
|
||||
import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateViewModel
|
||||
import kr.co.vividnext.sodalive.mypage.profile.nickname.NicknameUpdateViewModel
|
||||
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagApi
|
||||
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagRepository
|
||||
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagViewModel
|
||||
import kr.co.vividnext.sodalive.mypage.service_center.FaqApi
|
||||
import kr.co.vividnext.sodalive.mypage.service_center.FaqRepository
|
||||
import kr.co.vividnext.sodalive.mypage.service_center.ServiceCenterViewModel
|
||||
@@ -134,6 +139,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
||||
single { ApiBuilder().build(get(), NoticeApi::class.java) }
|
||||
single { ApiBuilder().build(get(), AudioContentApi::class.java) }
|
||||
single { ApiBuilder().build(get(), FaqApi::class.java) }
|
||||
single { ApiBuilder().build(get(), MemberTagApi::class.java) }
|
||||
}
|
||||
|
||||
private val viewModelModule = module {
|
||||
@@ -179,6 +185,9 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
||||
viewModel { AudioContentCommentReplyViewModel(get()) }
|
||||
viewModel { FollowingCreatorViewModel(get()) }
|
||||
viewModel { ServiceCenterViewModel(get()) }
|
||||
viewModel { ProfileUpdateViewModel(get()) }
|
||||
viewModel { NicknameUpdateViewModel(get()) }
|
||||
viewModel { MemberTagViewModel(get()) }
|
||||
}
|
||||
|
||||
private val repositoryModule = module {
|
||||
@@ -199,6 +208,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
||||
factory { PlaybackTrackingRepository(get()) }
|
||||
factory { FollowingCreatorRepository(get(), get()) }
|
||||
factory { FaqRepository(get()) }
|
||||
factory { MemberTagRepository(get()) }
|
||||
}
|
||||
|
||||
private val moduleList = listOf(
|
||||
|
@@ -19,12 +19,13 @@ import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.databinding.FragmentMyBinding
|
||||
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
|
||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
import kr.co.vividnext.sodalive.live.reservation_status.LiveReservationStatusActivity
|
||||
import kr.co.vividnext.sodalive.mypage.auth.Auth
|
||||
import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest
|
||||
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
|
||||
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusActivity
|
||||
import kr.co.vividnext.sodalive.live.reservation_status.LiveReservationStatusActivity
|
||||
import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateActivity
|
||||
import kr.co.vividnext.sodalive.mypage.service_center.ServiceCenterActivity
|
||||
import kr.co.vividnext.sodalive.settings.SettingsActivity
|
||||
import kr.co.vividnext.sodalive.settings.notification.MemberRole
|
||||
@@ -59,7 +60,14 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
||||
)
|
||||
}
|
||||
|
||||
binding.ivEdit.setOnClickListener {}
|
||||
binding.ivEdit.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireActivity(),
|
||||
ProfileUpdateActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
binding.llTotalCan.setOnClickListener {
|
||||
startActivity(
|
||||
|
@@ -0,0 +1,20 @@
|
||||
package kr.co.vividnext.sodalive.mypage.profile
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kr.co.vividnext.sodalive.user.Gender
|
||||
|
||||
data class ProfileResponse(
|
||||
@SerializedName("userId") val userId: Long,
|
||||
@SerializedName("email") val email: String,
|
||||
@SerializedName("nickname") val nickname: String,
|
||||
@SerializedName("gender") val gender: Gender,
|
||||
@SerializedName("profileUrl") val profileUrl: String,
|
||||
@SerializedName("chargeCoin") val chargeCoin: Int,
|
||||
@SerializedName("rewardCoin") val rewardCoin: Int,
|
||||
@SerializedName("youtubeUrl") val youtubeUrl: String?,
|
||||
@SerializedName("instagramUrl") val instagramUrl: String?,
|
||||
@SerializedName("blogUrl") val blogUrl: String?,
|
||||
@SerializedName("websiteUrl") val websiteUrl: String?,
|
||||
@SerializedName("introduce") val introduce: String,
|
||||
@SerializedName("tags") val tags: List<String>
|
||||
)
|
@@ -0,0 +1,277 @@
|
||||
package kr.co.vividnext.sodalive.mypage.profile
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import coil.load
|
||||
import coil.transform.CircleCropTransformation
|
||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
||||
import com.jakewharton.rxbinding4.widget.textChanges
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.common.RealPathUtil
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityProfileUpdateBinding
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveTagSelectedBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.mypage.profile.nickname.NicknameUpdateActivity
|
||||
import kr.co.vividnext.sodalive.mypage.profile.password.ModifyPasswordActivity
|
||||
import kr.co.vividnext.sodalive.mypage.profile.tag.MemberTagFragment
|
||||
import kr.co.vividnext.sodalive.user.Gender
|
||||
import org.koin.android.ext.android.inject
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class ProfileUpdateActivity : BaseActivity<ActivityProfileUpdateBinding>(
|
||||
ActivityProfileUpdateBinding::inflate
|
||||
) {
|
||||
|
||||
private val viewModel: ProfileUpdateViewModel by inject()
|
||||
|
||||
private val imageResult =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
val resultCode = result.resultCode
|
||||
val data = result.data
|
||||
|
||||
if (resultCode == RESULT_OK) {
|
||||
// Image Uri will not be null for RESULT_OK
|
||||
val fileUri = data?.data!!
|
||||
binding.ivProfile.background = null
|
||||
viewModel.updateProfileImage(fileUri) {
|
||||
binding.ivProfile.load(it) {
|
||||
crossfade(true)
|
||||
transformations(CircleCropTransformation())
|
||||
}
|
||||
}
|
||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
||||
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private val tagFragment: MemberTagFragment by lazy {
|
||||
MemberTagFragment(viewModel.tags) { tag, isChecked ->
|
||||
when {
|
||||
isChecked -> {
|
||||
viewModel.addTag(tag)
|
||||
return@MemberTagFragment true
|
||||
}
|
||||
|
||||
!isChecked -> {
|
||||
viewModel.removeTag(tag)
|
||||
return@MemberTagFragment true
|
||||
}
|
||||
|
||||
else -> {
|
||||
return@MemberTagFragment false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
viewModel.getRealPathFromURI = {
|
||||
RealPathUtil.getRealPath(applicationContext, it)
|
||||
}
|
||||
|
||||
bindData()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
viewModel.getUserInfo()
|
||||
}
|
||||
|
||||
private fun bindData() {
|
||||
compositeDisposable.add(
|
||||
binding.etBlog.textChanges().skip(1)
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe {
|
||||
viewModel.blogUrl = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
binding.etWebsite.textChanges().skip(1)
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe {
|
||||
viewModel.websiteUrl = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
binding.etYoutube.textChanges().skip(1)
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe {
|
||||
viewModel.youtubeUrl = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
binding.etInstagram.textChanges().skip(1)
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe {
|
||||
viewModel.instagramUrl = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
viewModel.genderLiveData.observe(this) {
|
||||
binding.tvMale.isSelected = false
|
||||
binding.tvFemale.isSelected = false
|
||||
binding.tvNone.isSelected = false
|
||||
|
||||
when (it) {
|
||||
Gender.MALE -> binding.tvMale.isSelected = true
|
||||
Gender.FEMALE -> binding.tvFemale.isSelected = true
|
||||
Gender.NONE -> binding.tvNone.isSelected = true
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.userInfoLiveData.observe(this) {
|
||||
binding.ivProfile.load(it.profileUrl) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(CircleCropTransformation())
|
||||
}
|
||||
|
||||
binding.tvEmail.text = it.email
|
||||
binding.tvNickname.text = it.nickname
|
||||
it.youtubeUrl?.let { url -> binding.etYoutube.setText(url) }
|
||||
it.instagramUrl?.let { url -> binding.etInstagram.setText(url) }
|
||||
it.websiteUrl?.let { url -> binding.etWebsite.setText(url) }
|
||||
it.blogUrl?.let { url -> binding.etBlog.setText(url) }
|
||||
binding.etIntroduce.setText(it.introduce)
|
||||
|
||||
SharedPreferenceManager.nickname = it.nickname
|
||||
}
|
||||
|
||||
viewModel.toastLiveData.observe(this) {
|
||||
it?.let {
|
||||
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.isLoading.observe(this) {
|
||||
if (it) {
|
||||
loadingDialog.show(screenWidth)
|
||||
} else {
|
||||
loadingDialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.selectedTagLiveData.observe(this) {
|
||||
binding.llSelectTags.removeAllViews()
|
||||
binding.llSelectTags.visibility = if (it.isNotEmpty()) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
View.GONE
|
||||
}
|
||||
for (index in it.indices) {
|
||||
val tag = it[index]
|
||||
val itemView = ItemLiveTagSelectedBinding.inflate(layoutInflater)
|
||||
itemView.tvTag.text = tag
|
||||
itemView.ivRemove.setOnClickListener {
|
||||
viewModel.removeTag(tag)
|
||||
}
|
||||
binding.llSelectTags.addView(itemView.root)
|
||||
|
||||
if (index > 0) {
|
||||
val layoutParams = itemView.root.layoutParams as LinearLayout.LayoutParams
|
||||
layoutParams.marginStart = 10.dpToPx().toInt()
|
||||
itemView.root.layoutParams = layoutParams
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setupView() {
|
||||
binding.toolbar.tvBack.text = "프로필 수정"
|
||||
binding.toolbar.tvBack.setOnClickListener { finish() }
|
||||
|
||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||
|
||||
compositeDisposable.add(
|
||||
binding.etIntroduce.textChanges()
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe {
|
||||
viewModel.introduce = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
binding.tvMale.setOnClickListener {
|
||||
viewModel.changeGender(Gender.MALE)
|
||||
}
|
||||
|
||||
binding.tvFemale.setOnClickListener {
|
||||
viewModel.changeGender(Gender.FEMALE)
|
||||
}
|
||||
|
||||
binding.tvNone.setOnClickListener {
|
||||
viewModel.changeGender(Gender.NONE)
|
||||
}
|
||||
|
||||
binding.ivPhotoPicker.setOnClickListener {
|
||||
ImagePicker.with(this)
|
||||
.crop()
|
||||
.galleryOnly()
|
||||
.galleryMimeTypes( // Exclude gif images
|
||||
mimeTypes = arrayOf(
|
||||
"image/png",
|
||||
"image/jpg",
|
||||
"image/jpeg"
|
||||
)
|
||||
)
|
||||
.createIntent { imageResult.launch(it) }
|
||||
}
|
||||
|
||||
binding.tvSelectTag.setOnClickListener {
|
||||
if (tagFragment.isAdded) return@setOnClickListener
|
||||
|
||||
tagFragment.show(supportFragmentManager, tagFragment.tag)
|
||||
}
|
||||
|
||||
binding.tvModifyPassword.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(
|
||||
applicationContext,
|
||||
ModifyPasswordActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
binding.tvSave.setOnClickListener {
|
||||
viewModel.updateProfile {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
binding.tvChangeNickname.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(
|
||||
applicationContext,
|
||||
NicknameUpdateActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package kr.co.vividnext.sodalive.mypage.profile
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kr.co.vividnext.sodalive.user.Gender
|
||||
|
||||
data class ProfileUpdateRequest(
|
||||
@SerializedName("email") val email: String,
|
||||
@SerializedName("password") val password: String? = null,
|
||||
@SerializedName("modifyPassword") val modifyPassword: String? = null,
|
||||
@SerializedName("nickname") val nickname: String? = null,
|
||||
@SerializedName("gender") val gender: Gender? = null,
|
||||
@SerializedName("insertTags") val insertTags: List<String>? = null,
|
||||
@SerializedName("removeTags") val removeTags: List<String>? = null,
|
||||
@SerializedName("introduce") val introduce: String? = null,
|
||||
@SerializedName("youtubeUrl") val youtubeUrl: String? = null,
|
||||
@SerializedName("instagramUrl") val instagramUrl: String? = null,
|
||||
@SerializedName("websiteUrl") val websiteUrl: String? = null,
|
||||
@SerializedName("blogUrl") val blogUrl: String? = null,
|
||||
@SerializedName("container") val container: String = "aos"
|
||||
)
|
@@ -0,0 +1,290 @@
|
||||
package kr.co.vividnext.sodalive.mypage.profile
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.orhanobut.logger.Logger
|
||||
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.user.Gender
|
||||
import kr.co.vividnext.sodalive.user.UserRepository
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import java.io.File
|
||||
|
||||
class ProfileUpdateViewModel(private val repository: UserRepository) : BaseViewModel() {
|
||||
|
||||
var youtubeUrl = ""
|
||||
var instagramUrl = ""
|
||||
var websiteUrl = ""
|
||||
var blogUrl = ""
|
||||
var introduce = ""
|
||||
|
||||
var currentPassword = ""
|
||||
var newPassword = ""
|
||||
var newPasswordConfirm = ""
|
||||
|
||||
val tags = mutableSetOf<String>()
|
||||
private val insertTags = mutableListOf<String>()
|
||||
private val removeTags = mutableListOf<String>()
|
||||
|
||||
private lateinit var profileResponse: ProfileResponse
|
||||
|
||||
private val _userInfoLiveData = MutableLiveData<ProfileResponse>()
|
||||
val userInfoLiveData: LiveData<ProfileResponse>
|
||||
get() = _userInfoLiveData
|
||||
|
||||
private val _genderLiveData = MutableLiveData<Gender>()
|
||||
val genderLiveData: LiveData<Gender>
|
||||
get() = _genderLiveData
|
||||
|
||||
private val _toastLiveData = MutableLiveData<String?>()
|
||||
val toastLiveData: LiveData<String?>
|
||||
get() = _toastLiveData
|
||||
|
||||
private val _selectedTagLiveData = MutableLiveData<List<String>>()
|
||||
val selectedTagLiveData: LiveData<List<String>>
|
||||
get() = _selectedTagLiveData
|
||||
|
||||
private var _isLoading = MutableLiveData(false)
|
||||
val isLoading: LiveData<Boolean>
|
||||
get() = _isLoading
|
||||
|
||||
lateinit var getRealPathFromURI: (Uri) -> String?
|
||||
|
||||
fun getUserInfo() {
|
||||
compositeDisposable.add(
|
||||
repository.getProfile("Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success && it.data != null) {
|
||||
profileResponse = it.data
|
||||
tags.addAll(profileResponse.tags)
|
||||
_selectedTagLiveData.postValue(profileResponse.tags)
|
||||
_genderLiveData.postValue(profileResponse.gender)
|
||||
_userInfoLiveData.postValue(profileResponse)
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun changeGender(gender: Gender) {
|
||||
_genderLiveData.postValue(gender)
|
||||
}
|
||||
|
||||
fun updateProfileImage(uri: Uri, onSuccess: (String) -> Unit) {
|
||||
val file = File(getRealPathFromURI(uri))
|
||||
val image = MultipartBody.Part.createFormData(
|
||||
"image",
|
||||
file.name,
|
||||
file.asRequestBody("image/*".toMediaType())
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
repository.updateProfileImage(image, "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success && it.data != null) {
|
||||
onSuccess(it.data)
|
||||
_toastLiveData.postValue("프로필 이미지가 변경되었습니다.")
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun updateProfile(onSuccess: () -> Unit) {
|
||||
if (
|
||||
profileResponse.youtubeUrl != youtubeUrl ||
|
||||
profileResponse.instagramUrl != instagramUrl ||
|
||||
profileResponse.blogUrl != blogUrl ||
|
||||
profileResponse.websiteUrl != websiteUrl ||
|
||||
profileResponse.gender != _genderLiveData.value ||
|
||||
insertTags.isNotEmpty() ||
|
||||
removeTags.isNotEmpty() ||
|
||||
profileResponse.introduce != introduce
|
||||
) {
|
||||
val request = ProfileUpdateRequest(
|
||||
email = profileResponse.email,
|
||||
nickname = null,
|
||||
youtubeUrl = if (profileResponse.youtubeUrl != youtubeUrl) {
|
||||
youtubeUrl
|
||||
} else {
|
||||
null
|
||||
},
|
||||
instagramUrl = if (profileResponse.instagramUrl != instagramUrl) {
|
||||
instagramUrl
|
||||
} else {
|
||||
null
|
||||
},
|
||||
blogUrl = if (profileResponse.blogUrl != blogUrl) {
|
||||
blogUrl
|
||||
} else {
|
||||
null
|
||||
},
|
||||
websiteUrl = if (profileResponse.websiteUrl != websiteUrl) {
|
||||
websiteUrl
|
||||
} else {
|
||||
null
|
||||
},
|
||||
gender = if (profileResponse.gender != _genderLiveData.value) {
|
||||
_genderLiveData.value
|
||||
} else {
|
||||
null
|
||||
},
|
||||
introduce = if (profileResponse.introduce != introduce) {
|
||||
introduce
|
||||
} else {
|
||||
null
|
||||
},
|
||||
insertTags = insertTags.ifEmpty { null },
|
||||
removeTags = removeTags.ifEmpty { null }
|
||||
)
|
||||
|
||||
_isLoading.value = true
|
||||
|
||||
compositeDisposable.add(
|
||||
repository.updateProfile(request, "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success) {
|
||||
_toastLiveData.postValue(
|
||||
"프로필이 변경되었습니다."
|
||||
)
|
||||
onSuccess()
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
_isLoading.value = false
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
} else run {
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
fun updatePassword(onSuccess: () -> Unit) {
|
||||
val email = SharedPreferenceManager.email
|
||||
|
||||
if (currentPassword.isBlank()) {
|
||||
_toastLiveData.postValue("현재 비밀번호를 입력하세요")
|
||||
return
|
||||
}
|
||||
|
||||
if (newPassword.isBlank()) {
|
||||
_toastLiveData.postValue("변경할 비밀번호를 입력하세요")
|
||||
return
|
||||
}
|
||||
|
||||
if (newPasswordConfirm != newPassword) {
|
||||
_toastLiveData.postValue("비밀번호가 일치하지 않습니다.")
|
||||
return
|
||||
}
|
||||
|
||||
val request = ProfileUpdateRequest(
|
||||
email = email,
|
||||
password = currentPassword,
|
||||
modifyPassword = newPassword
|
||||
)
|
||||
|
||||
_isLoading.value = true
|
||||
|
||||
compositeDisposable.add(
|
||||
repository.updateProfile(request, "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success) {
|
||||
_toastLiveData.postValue(
|
||||
"비밀번호가 변경되었습니다."
|
||||
)
|
||||
onSuccess()
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
_isLoading.value = false
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun removeTag(tag: String) {
|
||||
tags.remove(tag)
|
||||
if (insertTags.contains(tag)) {
|
||||
insertTags.remove(tag)
|
||||
} else {
|
||||
removeTags.add(tag)
|
||||
}
|
||||
_selectedTagLiveData.postValue(tags.toList())
|
||||
}
|
||||
|
||||
fun addTag(tag: String) {
|
||||
tags.add(tag)
|
||||
if (removeTags.contains(tag)) {
|
||||
removeTags.remove(tag)
|
||||
} else {
|
||||
insertTags.add(tag)
|
||||
}
|
||||
_selectedTagLiveData.postValue(tags.toList())
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package kr.co.vividnext.sodalive.mypage.profile.nickname
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class GetChangeNicknamePriceResponse(@SerializedName("price") val price: Int)
|
@@ -0,0 +1,83 @@
|
||||
package kr.co.vividnext.sodalive.mypage.profile.nickname
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import com.jakewharton.rxbinding4.widget.textChanges
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityNicknameUpdateBinding
|
||||
import org.koin.android.ext.android.inject
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class NicknameUpdateActivity : BaseActivity<ActivityNicknameUpdateBinding>(
|
||||
ActivityNicknameUpdateBinding::inflate
|
||||
) {
|
||||
|
||||
private val viewModel: NicknameUpdateViewModel by inject()
|
||||
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
bindData()
|
||||
viewModel.getChangeNicknamePrice()
|
||||
|
||||
binding.etNickname.setText(SharedPreferenceManager.nickname)
|
||||
}
|
||||
|
||||
override fun setupView() {
|
||||
binding.toolbar.tvBack.text = "닉네임 변경"
|
||||
binding.toolbar.tvBack.setOnClickListener { finish() }
|
||||
|
||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||
|
||||
binding.tvCheckNickname.setOnClickListener {
|
||||
viewModel.checkNickname()
|
||||
}
|
||||
|
||||
binding.tvChangeNickname.setOnClickListener {
|
||||
viewModel.changeNickname { finish() }
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun bindData() {
|
||||
compositeDisposable.add(
|
||||
binding.etNickname.textChanges().skip(1)
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe {
|
||||
viewModel.isCheckedNickname = false
|
||||
viewModel.nickname = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
viewModel.toastLiveData.observe(this) {
|
||||
it?.let {
|
||||
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.isLoading.observe(this) {
|
||||
if (it) {
|
||||
loadingDialog.show(screenWidth)
|
||||
} else {
|
||||
loadingDialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.priceLiveData.observe(this) {
|
||||
if (it > 0) {
|
||||
binding.tvChangeNickname.text = "${it}코인으로 닉네임 변경하기"
|
||||
} else {
|
||||
binding.tvChangeNickname.text = "닉네임 변경하기"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,142 @@
|
||||
package kr.co.vividnext.sodalive.mypage.profile.nickname
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.orhanobut.logger.Logger
|
||||
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.mypage.profile.ProfileUpdateRequest
|
||||
import kr.co.vividnext.sodalive.user.UserRepository
|
||||
|
||||
class NicknameUpdateViewModel(private val repository: UserRepository) : BaseViewModel() {
|
||||
var nickname = ""
|
||||
|
||||
private val _priceLiveData = MutableLiveData(0)
|
||||
val priceLiveData: LiveData<Int>
|
||||
get() = _priceLiveData
|
||||
|
||||
private var _isLoading = MutableLiveData(false)
|
||||
val isLoading: LiveData<Boolean>
|
||||
get() = _isLoading
|
||||
|
||||
private val _toastLiveData = MutableLiveData<String?>()
|
||||
val toastLiveData: LiveData<String?>
|
||||
get() = _toastLiveData
|
||||
|
||||
var isCheckedNickname = false
|
||||
|
||||
fun getChangeNicknamePrice() {
|
||||
_isLoading.value = true
|
||||
|
||||
compositeDisposable.add(
|
||||
repository.getChangeNicknamePrice(token = "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
if (it.success && it.data != null) {
|
||||
_priceLiveData.value = it.data.price
|
||||
} 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 checkNickname() {
|
||||
if (nickname.isNotBlank()) {
|
||||
_isLoading.value = true
|
||||
|
||||
compositeDisposable.add(
|
||||
repository.checkNickname(nickname)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
if (it.success) {
|
||||
isCheckedNickname = true
|
||||
_toastLiveData.postValue("사용가능한 닉네임 입니다.")
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
} else {
|
||||
_toastLiveData.postValue("닉네임을 입력하세요.")
|
||||
}
|
||||
}
|
||||
|
||||
fun changeNickname(onSuccess: () -> Unit) {
|
||||
if (isCheckedNickname) {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
repository.changeNickname(
|
||||
request = ProfileUpdateRequest(
|
||||
email = SharedPreferenceManager.email,
|
||||
nickname = nickname
|
||||
),
|
||||
token = "Bearer ${SharedPreferenceManager.token}"
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
if (it.success) {
|
||||
_toastLiveData.postValue("닉네임이 변경되었습니다.")
|
||||
SharedPreferenceManager.nickname = nickname
|
||||
onSuccess()
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
} else {
|
||||
_toastLiveData.postValue("닉네임 중복체크를 해주세요.")
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
package kr.co.vividnext.sodalive.mypage.profile.password
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import com.jakewharton.rxbinding4.widget.textChanges
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityModifyPasswordBinding
|
||||
import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateViewModel
|
||||
import org.koin.android.ext.android.inject
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class ModifyPasswordActivity : BaseActivity<ActivityModifyPasswordBinding>(
|
||||
ActivityModifyPasswordBinding::inflate
|
||||
) {
|
||||
|
||||
private val viewModel: ProfileUpdateViewModel by inject()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
bindData()
|
||||
}
|
||||
|
||||
private fun bindData() {
|
||||
compositeDisposable.add(
|
||||
binding.etCurrentPassword.textChanges().skip(1)
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe {
|
||||
viewModel.currentPassword = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
binding.etNewPassword.textChanges().skip(1)
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe {
|
||||
viewModel.newPassword = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
binding.etNewPasswordConfirm.textChanges().skip(1)
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe {
|
||||
viewModel.newPasswordConfirm = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
viewModel.toastLiveData.observe(this) {
|
||||
it?.let {
|
||||
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setupView() {
|
||||
binding.toolbar.tvBack.text = "비밀번호 변경"
|
||||
binding.toolbar.tvBack.setOnClickListener { finish() }
|
||||
|
||||
binding.tvModifyPassword.setOnClickListener {
|
||||
viewModel.updatePassword { finish() }
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
package kr.co.vividnext.sodalive.mypage.profile.tag
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.databinding.ItemLiveTagBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
|
||||
class MemberTagAdapter(
|
||||
private val selectedTags: Set<String>,
|
||||
private val onItemClick: (String, Boolean) -> Boolean
|
||||
) : RecyclerView.Adapter<MemberTagAdapter.ViewHolder>() {
|
||||
inner class ViewHolder(
|
||||
private val binding: ItemLiveTagBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
private var isChecked = false
|
||||
|
||||
fun bind(item: MemberTagResponse) {
|
||||
if (selectedTags.contains(item.tag)) {
|
||||
binding.ivTagChecked.visibility = View.VISIBLE
|
||||
binding.ivTag.setBackgroundResource(R.drawable.bg_round_corner_30_9970ff)
|
||||
isChecked = true
|
||||
} else {
|
||||
binding.ivTagChecked.visibility = View.GONE
|
||||
binding.ivTag.background = null
|
||||
isChecked = false
|
||||
}
|
||||
|
||||
binding.ivTag.load(item.image) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(RoundedCornersTransformation(30f.dpToPx()))
|
||||
}
|
||||
binding.tvTag.text = item.tag
|
||||
|
||||
binding.root.setOnClickListener {
|
||||
isChecked = !isChecked
|
||||
|
||||
if (onItemClick(item.tag, isChecked)) {
|
||||
if (isChecked) {
|
||||
binding.ivTagChecked.visibility = View.VISIBLE
|
||||
binding.ivTag.setBackgroundResource(R.drawable.bg_round_corner_30_9970ff)
|
||||
} else {
|
||||
binding.ivTagChecked.visibility = View.GONE
|
||||
binding.ivTag.background = null
|
||||
}
|
||||
} else {
|
||||
isChecked = !isChecked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val items = mutableSetOf<MemberTagResponse>()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(
|
||||
ItemLiveTagBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(items.toList()[position])
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package kr.co.vividnext.sodalive.mypage.profile.tag
|
||||
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
|
||||
interface MemberTagApi {
|
||||
@GET("/member/tag")
|
||||
fun getTags(
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<List<MemberTagResponse>>>
|
||||
}
|
@@ -0,0 +1,117 @@
|
||||
package kr.co.vividnext.sodalive.mypage.profile.tag
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.graphics.Rect
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class MemberTagFragment(
|
||||
private val selectedTags: Set<String>,
|
||||
private val onItemClick: (String, Boolean) -> Boolean
|
||||
) : BottomSheetDialogFragment() {
|
||||
private val viewModel: MemberTagViewModel by inject()
|
||||
|
||||
private lateinit var adapter: MemberTagAdapter
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialog = super.onCreateDialog(savedInstanceState)
|
||||
|
||||
dialog.setOnShowListener {
|
||||
val d = it as BottomSheetDialog
|
||||
val bottomSheet = d.findViewById<FrameLayout>(
|
||||
com.google.android.material.R.id.design_bottom_sheet
|
||||
)
|
||||
if (bottomSheet != null) {
|
||||
BottomSheetBehavior.from(bottomSheet).state = BottomSheetBehavior.STATE_EXPANDED
|
||||
}
|
||||
}
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View = inflater.inflate(R.layout.fragment_creator_tag, container, false)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
view.findViewById<ImageView>(R.id.iv_close).setOnClickListener {
|
||||
dialog?.dismiss()
|
||||
}
|
||||
|
||||
view.findViewById<TextView>(R.id.tv_select).setOnClickListener {
|
||||
dialog?.dismiss()
|
||||
}
|
||||
|
||||
loadingDialog = LoadingDialog(requireActivity(), layoutInflater)
|
||||
|
||||
setupAdapter(view)
|
||||
bindData()
|
||||
|
||||
viewModel.getTags()
|
||||
}
|
||||
|
||||
private fun setupAdapter(view: View) {
|
||||
val recyclerView = view.findViewById<RecyclerView>(R.id.rv_tags)
|
||||
adapter = MemberTagAdapter(selectedTags) { tag, isChecked ->
|
||||
return@MemberTagAdapter onItemClick(tag, isChecked)
|
||||
}
|
||||
|
||||
recyclerView.setHasFixedSize(true)
|
||||
recyclerView.layoutManager = GridLayoutManager(requireContext(), 4)
|
||||
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
super.getItemOffsets(outRect, view, parent, state)
|
||||
|
||||
outRect.top = 13.3f.dpToPx().toInt()
|
||||
outRect.bottom = 13.3f.dpToPx().toInt()
|
||||
outRect.left = 13.3f.dpToPx().toInt()
|
||||
outRect.right = 13.3f.dpToPx().toInt()
|
||||
}
|
||||
})
|
||||
recyclerView.adapter = adapter
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun bindData() {
|
||||
viewModel.toastLiveData.observe(viewLifecycleOwner) {
|
||||
it?.let { Toast.makeText(requireActivity(), it, Toast.LENGTH_LONG).show() }
|
||||
}
|
||||
|
||||
viewModel.tagLiveData.observe(viewLifecycleOwner) {
|
||||
adapter.items.addAll(it)
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
viewModel.isLoading.observe(viewLifecycleOwner) {
|
||||
if (it) {
|
||||
loadingDialog.show(resources.displayMetrics.widthPixels)
|
||||
} else {
|
||||
loadingDialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package kr.co.vividnext.sodalive.mypage.profile.tag
|
||||
|
||||
class MemberTagRepository(private val api: MemberTagApi) {
|
||||
fun getTags(token: String) = api.getTags(authHeader = token)
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package kr.co.vividnext.sodalive.mypage.profile.tag
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class MemberTagResponse(
|
||||
@SerializedName("id") val id: Long,
|
||||
@SerializedName("tag") val tag: String,
|
||||
@SerializedName("image") val image: String
|
||||
)
|
@@ -0,0 +1,53 @@
|
||||
package kr.co.vividnext.sodalive.mypage.profile.tag
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.orhanobut.logger.Logger
|
||||
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
|
||||
|
||||
class MemberTagViewModel(private val repository: MemberTagRepository) : BaseViewModel() {
|
||||
private val _toastLiveData = MutableLiveData<String?>()
|
||||
val toastLiveData: LiveData<String?>
|
||||
get() = _toastLiveData
|
||||
|
||||
private val _tagLiveData = MutableLiveData<List<MemberTagResponse>>()
|
||||
val tagLiveData: LiveData<List<MemberTagResponse>>
|
||||
get() = _tagLiveData
|
||||
|
||||
private var _isLoading = MutableLiveData(false)
|
||||
val isLoading: LiveData<Boolean>
|
||||
get() = _isLoading
|
||||
|
||||
fun getTags() {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
repository.getTags("Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success && it.data != null) {
|
||||
_tagLiveData.postValue(it.data!!)
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
_isLoading.value = false
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@@ -6,6 +6,9 @@ import kr.co.vividnext.sodalive.explorer.profile.MemberBlockRequest
|
||||
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
|
||||
import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest
|
||||
import kr.co.vividnext.sodalive.mypage.MyPageResponse
|
||||
import kr.co.vividnext.sodalive.mypage.profile.ProfileResponse
|
||||
import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateRequest
|
||||
import kr.co.vividnext.sodalive.mypage.profile.nickname.GetChangeNicknamePriceResponse
|
||||
import kr.co.vividnext.sodalive.settings.notification.GetMemberInfoResponse
|
||||
import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest
|
||||
import kr.co.vividnext.sodalive.settings.signout.SignOutRequest
|
||||
@@ -101,4 +104,37 @@ interface UserApi {
|
||||
|
||||
@POST("/member/logout/all")
|
||||
fun logoutAll(@Header("Authorization") authHeader: String): Single<ApiResponse<Any>>
|
||||
|
||||
@GET("/member/change/nickname/price")
|
||||
fun getChangeNicknamePrice(
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<GetChangeNicknamePriceResponse>>
|
||||
|
||||
@GET("/member/check/nickname")
|
||||
fun checkNickname(@Query("nickname") nickname: String): Single<ApiResponse<Any>>
|
||||
|
||||
@PUT("/member/change/nickname")
|
||||
fun changeNickname(
|
||||
@Body request: ProfileUpdateRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@GET("/member")
|
||||
fun getMyProfile(
|
||||
@Query("container") container: String = "aos",
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<ProfileResponse>>
|
||||
|
||||
@PUT("/member")
|
||||
fun updateProfile(
|
||||
@Body request: ProfileUpdateRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<ProfileResponse>>
|
||||
|
||||
@Multipart
|
||||
@POST("/member/image")
|
||||
fun updateProfileImage(
|
||||
@Part multipartFile: MultipartBody.Part,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<String>>
|
||||
}
|
||||
|
@@ -6,6 +6,8 @@ import kr.co.vividnext.sodalive.explorer.profile.MemberBlockRequest
|
||||
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
|
||||
import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest
|
||||
import kr.co.vividnext.sodalive.mypage.MyPageResponse
|
||||
import kr.co.vividnext.sodalive.mypage.profile.ProfileResponse
|
||||
import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateRequest
|
||||
import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest
|
||||
import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest
|
||||
import kr.co.vividnext.sodalive.user.login.LoginRequest
|
||||
@@ -79,4 +81,31 @@ class UserRepository(private val userApi: UserApi) {
|
||||
|
||||
fun logout(token: String) = userApi.logout(authHeader = token)
|
||||
fun logoutAllDevice(token: String) = userApi.logoutAll(authHeader = token)
|
||||
|
||||
fun getChangeNicknamePrice(token: String) = userApi.getChangeNicknamePrice(authHeader = token)
|
||||
|
||||
fun checkNickname(nickname: String) = userApi.checkNickname(nickname)
|
||||
|
||||
fun changeNickname(
|
||||
request: ProfileUpdateRequest,
|
||||
token: String
|
||||
) = userApi.changeNickname(request = request, authHeader = token)
|
||||
|
||||
fun updateProfileImage(
|
||||
multipartFile: MultipartBody.Part,
|
||||
token: String
|
||||
): Single<ApiResponse<String>> {
|
||||
return userApi.updateProfileImage(multipartFile, authHeader = token)
|
||||
}
|
||||
|
||||
fun updateProfile(
|
||||
request: ProfileUpdateRequest,
|
||||
token: String
|
||||
): Single<ApiResponse<ProfileResponse>> {
|
||||
return userApi.updateProfile(request, authHeader = token)
|
||||
}
|
||||
|
||||
fun getProfile(token: String): Single<ApiResponse<ProfileResponse>> {
|
||||
return userApi.getMyProfile(authHeader = token)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user