팔로잉/팔로워 리스트 - 팔로우와 알림설정

- 팔로잉 상태에서 알림 켜기/끄기 상태 추가
This commit is contained in:
klaus 2024-09-20 01:57:27 +09:00
parent 5063ce815d
commit 5b2c5a6e2f
14 changed files with 130 additions and 127 deletions

View File

@ -53,5 +53,7 @@ data class AudioContentCreator(
@SerializedName("creatorId") val creatorId: Long, @SerializedName("creatorId") val creatorId: Long,
@SerializedName("nickname") val nickname: String, @SerializedName("nickname") val nickname: String,
@SerializedName("profileImageUrl") val profileImageUrl: String, @SerializedName("profileImageUrl") val profileImageUrl: String,
@SerializedName("isFollowing") val isFollowing: Boolean @SerializedName("isFollowing") val isFollowing: Boolean,
@SerializedName("isFollow") var isFollow: Boolean,
@SerializedName("isNotify") var isNotify: Boolean
) )

View File

@ -34,6 +34,7 @@ data class GetSeriesDetailResponse(
@SerializedName("creatorId") val creatorId: Long, @SerializedName("creatorId") val creatorId: Long,
@SerializedName("nickname") val nickname: String, @SerializedName("nickname") val nickname: String,
@SerializedName("profileImage") val profileImage: String, @SerializedName("profileImage") val profileImage: String,
@SerializedName("isFollow") var isFollow: Boolean @SerializedName("isFollow") var isFollow: Boolean,
@SerializedName("isNotify") var isNotify: Boolean
) : Parcelable ) : Parcelable
} }

View File

@ -240,7 +240,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { AudioContentDetailViewModel(get(), get(), get(), get()) } viewModel { AudioContentDetailViewModel(get(), get(), get(), get()) }
viewModel { AudioContentCommentListViewModel(get()) } viewModel { AudioContentCommentListViewModel(get()) }
viewModel { AudioContentCommentReplyViewModel(get()) } viewModel { AudioContentCommentReplyViewModel(get()) }
viewModel { FollowingCreatorViewModel(get()) } viewModel { FollowingCreatorViewModel(get(), get()) }
viewModel { ServiceCenterViewModel(get()) } viewModel { ServiceCenterViewModel(get()) }
viewModel { ProfileUpdateViewModel(get()) } viewModel { ProfileUpdateViewModel(get()) }
viewModel { NicknameUpdateViewModel(get()) } viewModel { NicknameUpdateViewModel(get()) }
@ -280,7 +280,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
factory { AudioContentRepository(get(), get(), get()) } factory { AudioContentRepository(get(), get(), get()) }
factory { AudioContentCommentRepository(get()) } factory { AudioContentCommentRepository(get()) }
factory { PlaybackTrackingRepository(get()) } factory { PlaybackTrackingRepository(get()) }
factory { FollowingCreatorRepository(get(), get()) } factory { FollowingCreatorRepository(get()) }
factory { FaqRepository(get()) } factory { FaqRepository(get()) }
factory { MemberTagRepository(get()) } factory { MemberTagRepository(get()) }
factory { UserProfileFantalkAllViewModel(get(), get()) } factory { UserProfileFantalkAllViewModel(get(), get()) }

View File

@ -14,5 +14,6 @@ data class GetFollowerListResponseItem(
@SerializedName("userId") val userId: Long, @SerializedName("userId") val userId: Long,
@SerializedName("profileImage") val profileImage: String, @SerializedName("profileImage") val profileImage: String,
@SerializedName("nickname") val nickname: String, @SerializedName("nickname") val nickname: String,
@SerializedName("isFollow") val isFollow: Boolean? @SerializedName("isFollow") val isFollow: Boolean?,
@SerializedName("isNotify") val isNotify: Boolean?
) )

View File

@ -10,6 +10,7 @@ import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityUserFollowerListBinding import kr.co.vividnext.sodalive.databinding.ActivityUserFollowerListBinding
import kr.co.vividnext.sodalive.explorer.profile.CreatorFollowNotifyFragment
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat import kr.co.vividnext.sodalive.extensions.moneyFormat
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@ -50,8 +51,26 @@ class UserFollowerListActivity : BaseActivity<ActivityUserFollowerListBinding>(
binding.toolbar.tvBack.setOnClickListener { finish() } binding.toolbar.tvBack.setOnClickListener { finish() }
adapter = UserFollowerListAdapter( adapter = UserFollowerListAdapter(
onClickRegisterNotification = { viewModel.registerNotification(it) }, onClickFollow = { creatorId, isFollow ->
onClickUnRegisterNotification = { viewModel.unRegisterNotification(it) } if (isFollow) {
val notifyFragment = CreatorFollowNotifyFragment(
onClickNotifyAll = {
viewModel.follow(creatorId, follow = true, notify = true)
},
onClickNotifyNone = {
viewModel.follow(creatorId, follow = true, notify = false)
},
onClickUnFollow = {
viewModel.follow(creatorId, follow = false, notify = false)
}
)
if (notifyFragment.isAdded) return@UserFollowerListAdapter
notifyFragment.show(supportFragmentManager, notifyFragment.tag)
} else {
viewModel.follow(creatorId)
}
}
) )
binding.rvFollowerList.layoutManager = LinearLayoutManager( binding.rvFollowerList.layoutManager = LinearLayoutManager(
@ -94,6 +113,7 @@ class UserFollowerListActivity : BaseActivity<ActivityUserFollowerListBinding>(
private fun bindData() { private fun bindData() {
viewModel.userId = userId viewModel.userId = userId
viewModel.followerListItemsLiveData.observe(this) { viewModel.followerListItemsLiveData.observe(this) {
if (viewModel.page == 2) adapter.clear()
adapter.addAll(it) adapter.addAll(it)
} }

View File

@ -11,8 +11,7 @@ import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemFollowerListBinding import kr.co.vividnext.sodalive.databinding.ItemFollowerListBinding
class UserFollowerListAdapter( class UserFollowerListAdapter(
private val onClickRegisterNotification: (Long) -> Unit, private val onClickFollow: (Long, Boolean) -> Unit
private val onClickUnRegisterNotification: (Long) -> Unit,
) : RecyclerView.Adapter<UserFollowerListAdapter.ViewHolder>() { ) : RecyclerView.Adapter<UserFollowerListAdapter.ViewHolder>() {
private val items = mutableListOf<GetFollowerListResponseItem>() private val items = mutableListOf<GetFollowerListResponseItem>()
@ -28,23 +27,25 @@ class UserFollowerListAdapter(
crossfade(true) crossfade(true)
} }
if (item.isFollow != null) { if (item.isFollow != null && item.isNotify != null) {
binding.ivNotification.visibility = View.VISIBLE
if (item.isFollow) { if (item.isFollow) {
binding.ivNotification.setImageResource(R.drawable.btn_following_big) binding.ivFollow.setImageResource(
binding.ivNotification.setOnClickListener { if (item.isNotify) {
onClickUnRegisterNotification(item.userId) R.drawable.btn_following_big
clear() } else {
R.drawable.btn_following_no_alarm_big
}
)
} else {
binding.ivFollow.setImageResource(R.drawable.btn_follow_big)
}
binding.ivFollow.visibility = View.VISIBLE
binding.ivFollow.setOnClickListener {
onClickFollow(item.userId, item.isFollow)
} }
} else { } else {
binding.ivNotification.setImageResource(R.drawable.btn_follow_big) binding.ivFollow.visibility = View.GONE
binding.ivNotification.setOnClickListener {
onClickRegisterNotification(item.userId)
clear()
}
}
} else {
binding.ivNotification.visibility = View.GONE
} }
} }
} }

View File

@ -34,7 +34,7 @@ class UserFollowerListViewModel(
var userId: Long = 0 var userId: Long = 0
var page = 1 var page = 1
private var isLast = false private var isLast = false
private val pageSize = 10 private var pageSize = 10
fun getFollowerList() { fun getFollowerList() {
if (!isLast && !_isLoading.value!!) { if (!isLast && !_isLoading.value!!) {
@ -52,6 +52,7 @@ class UserFollowerListViewModel(
.subscribe( .subscribe(
{ {
_isLoading.value = false _isLoading.value = false
pageSize = 10
if (it.success && it.data != null) { if (it.success && it.data != null) {
val data = it.data val data = it.data
_totalCountLiveData.value = data.totalCount _totalCountLiveData.value = data.totalCount
@ -84,11 +85,13 @@ class UserFollowerListViewModel(
} }
} }
fun registerNotification(creatorId: Long) { fun follow(creatorId: Long, follow: Boolean = true, notify: Boolean = true) {
_isLoading.value = true _isLoading.value = true
compositeDisposable.add( compositeDisposable.add(
userRepository.creatorFollow( userRepository.creatorFollow(
creatorId = creatorId, creatorId = creatorId,
follow,
notify,
token = "Bearer ${SharedPreferenceManager.token}" token = "Bearer ${SharedPreferenceManager.token}"
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
@ -97,43 +100,9 @@ class UserFollowerListViewModel(
{ {
_isLoading.value = false _isLoading.value = false
if (it.success && it.data != null) { if (it.success && it.data != null) {
page = 1
isLast = false isLast = false
getFollowerList() pageSize *= page
} 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 unRegisterNotification(creatorId: Long) {
_isLoading.value = true
compositeDisposable.add(
userRepository.creatorUnFollow(
creatorId,
"Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success && it.data != null) {
page = 1 page = 1
isLast = false
getFollowerList() getFollowerList()
} else { } else {
if (it.message != null) { if (it.message != null) {

View File

@ -12,6 +12,7 @@ import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityFollowingCreatorBinding import kr.co.vividnext.sodalive.databinding.ActivityFollowingCreatorBinding
import kr.co.vividnext.sodalive.explorer.profile.CreatorFollowNotifyFragment
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@ -23,6 +24,7 @@ class FollowingCreatorActivity : BaseActivity<ActivityFollowingCreatorBinding>(
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
private lateinit var adapter: FollowingCreatorAdapter private lateinit var adapter: FollowingCreatorAdapter
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -43,8 +45,26 @@ class FollowingCreatorActivity : BaseActivity<ActivityFollowingCreatorBinding>(
} }
) )
}, },
onClickRegisterNotification = { viewModel.registerNotification(it) }, onClickFollow = { creatorId, isFollow ->
onClickUnRegisterNotification = { viewModel.unRegisterNotification(it) } if (isFollow) {
val notifyFragment = CreatorFollowNotifyFragment(
onClickNotifyAll = {
viewModel.follow(creatorId, follow = true, notify = true)
},
onClickNotifyNone = {
viewModel.follow(creatorId, follow = true, notify = false)
},
onClickUnFollow = {
viewModel.follow(creatorId, follow = false, notify = false)
}
)
if (notifyFragment.isAdded) return@FollowingCreatorAdapter
notifyFragment.show(supportFragmentManager, notifyFragment.tag)
} else {
viewModel.follow(creatorId)
}
},
) )
binding.rvFollowingCreator.layoutManager = LinearLayoutManager( binding.rvFollowingCreator.layoutManager = LinearLayoutManager(
@ -99,6 +119,7 @@ class FollowingCreatorActivity : BaseActivity<ActivityFollowingCreatorBinding>(
} }
viewModel.creatorListLiveData.observe(this) { viewModel.creatorListLiveData.observe(this) {
if (viewModel.page == 2) adapter.clear()
adapter.addAll(it) adapter.addAll(it)
} }

View File

@ -12,8 +12,7 @@ import kr.co.vividnext.sodalive.databinding.ItemFollowerListBinding
class FollowingCreatorAdapter( class FollowingCreatorAdapter(
private val onClickItem: (Long) -> Unit, private val onClickItem: (Long) -> Unit,
private val onClickRegisterNotification: (Long) -> Unit, private val onClickFollow: (Long, Boolean) -> Unit
private val onClickUnRegisterNotification: (Long) -> Unit,
) : RecyclerView.Adapter<FollowingCreatorAdapter.ViewHolder>() { ) : RecyclerView.Adapter<FollowingCreatorAdapter.ViewHolder>() {
private val items = mutableListOf<GetCreatorFollowingAllListItem>() private val items = mutableListOf<GetCreatorFollowingAllListItem>()
@ -30,29 +29,22 @@ class FollowingCreatorAdapter(
crossfade(true) crossfade(true)
} }
binding.ivNotification.visibility = View.VISIBLE binding.ivFollow.visibility = View.VISIBLE
if (item.isFollow) { if (item.isFollow) {
binding.ivNotification.setImageResource(R.drawable.btn_following_big) binding.ivFollow.setImageResource(
binding.ivNotification.setOnClickListener { if (item.isNotify) {
val index = items.indexOf(item) R.drawable.btn_following_big
val copyItem = item.copy(isFollow = false)
items.add(index, copyItem)
items.removeAt(index + 1)
notifyDataSetChanged()
onClickUnRegisterNotification(item.creatorId)
}
} else { } else {
binding.ivNotification.setImageResource(R.drawable.btn_follow_big) R.drawable.btn_following_no_alarm_big
binding.ivNotification.setOnClickListener {
val index = items.indexOf(item)
val copyItem = item.copy(isFollow = true)
items.add(index, copyItem)
items.removeAt(index + 1)
notifyDataSetChanged()
onClickRegisterNotification(item.creatorId)
} }
)
} else {
binding.ivFollow.setImageResource(R.drawable.btn_follow_big)
} }
binding.ivFollow.setOnClickListener {
onClickFollow(item.creatorId, item.isFollow)
}
binding.root.setOnClickListener { onClickItem(item.creatorId) } binding.root.setOnClickListener { onClickItem(item.creatorId) }
} }
} }

View File

@ -4,29 +4,10 @@ import kr.co.vividnext.sodalive.live.recommend.LiveRecommendApi
import kr.co.vividnext.sodalive.user.CreatorFollowRequestRequest import kr.co.vividnext.sodalive.user.CreatorFollowRequestRequest
import kr.co.vividnext.sodalive.user.UserApi import kr.co.vividnext.sodalive.user.UserApi
class FollowingCreatorRepository( class FollowingCreatorRepository(private val api: LiveRecommendApi) {
private val api: LiveRecommendApi,
private val userApi: UserApi
) {
fun getFollowedCreatorAllList( fun getFollowedCreatorAllList(
page: Int, page: Int,
size: Int, size: Int,
token: String token: String
) = api.getCreatorFollowingAllList(page = page - 1, size = size, authHeader = token) ) = api.getCreatorFollowingAllList(page = page - 1, size = size, authHeader = token)
fun registerNotification(
creatorId: Long,
token: String
) = userApi.creatorFollow(
request = CreatorFollowRequestRequest(creatorId = creatorId),
authHeader = token
)
fun unRegisterNotification(
creatorId: Long,
token: String
) = userApi.creatorUnFollow(
request = CreatorFollowRequestRequest(creatorId = creatorId),
authHeader = token
)
} }

View File

@ -7,9 +7,11 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseViewModel import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.user.UserRepository
class FollowingCreatorViewModel( class FollowingCreatorViewModel(
private val repository: FollowingCreatorRepository private val repository: FollowingCreatorRepository,
private val userRepository: UserRepository
) : BaseViewModel() { ) : BaseViewModel() {
private val _creatorListLiveData = MutableLiveData<List<GetCreatorFollowingAllListItem>>() private val _creatorListLiveData = MutableLiveData<List<GetCreatorFollowingAllListItem>>()
@ -30,7 +32,7 @@ class FollowingCreatorViewModel(
var page = 1 var page = 1
var isLast = false var isLast = false
private val pageSize = 10 private var pageSize = 10
fun getFollowedCreatorAllList() { fun getFollowedCreatorAllList() {
if (!isLast && !_isLoading.value!!) { if (!isLast && !_isLoading.value!!) {
@ -47,6 +49,7 @@ class FollowingCreatorViewModel(
.subscribe( .subscribe(
{ {
_isLoading.value = false _isLoading.value = false
pageSize = 10
if (it.success && it.data != null) { if (it.success && it.data != null) {
val data = it.data val data = it.data
if (data.items.isEmpty()) { if (data.items.isEmpty()) {
@ -78,29 +81,40 @@ class FollowingCreatorViewModel(
} }
} }
fun registerNotification(creatorId: Long) { fun follow(creatorId: Long, follow: Boolean = true, notify: Boolean = true) {
_creatorListTotalCountLiveData.value = _creatorListTotalCountLiveData.value!! + 1
compositeDisposable.add( compositeDisposable.add(
repository.registerNotification( userRepository.creatorFollow(
creatorId, creatorId = creatorId,
"Bearer ${SharedPreferenceManager.token}" follow,
notify,
token = "Bearer ${SharedPreferenceManager.token}"
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe({}, {}) .subscribe(
) {
} _isLoading.value = false
if (it.success && it.data != null) {
fun unRegisterNotification(creatorId: Long) { isLast = false
_creatorListTotalCountLiveData.value = _creatorListTotalCountLiveData.value!! - 1 pageSize *= page
compositeDisposable.add( page = 1
repository.unRegisterNotification( getFollowedCreatorAllList()
creatorId, } else {
"Bearer ${SharedPreferenceManager.token}" if (it.message != null) {
) _toastLiveData.postValue(it.message)
.subscribeOn(Schedulers.io()) } else {
.observeOn(AndroidSchedulers.mainThread()) _toastLiveData.postValue(
.subscribe({}, {}) "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
) )
} }
} }

View File

@ -14,5 +14,6 @@ data class GetCreatorFollowingAllListItem(
@SerializedName("creatorId") val creatorId: Long, @SerializedName("creatorId") val creatorId: Long,
@SerializedName("nickname") val nickname: String, @SerializedName("nickname") val nickname: String,
@SerializedName("profileImageUrl") val profileImageUrl: String, @SerializedName("profileImageUrl") val profileImageUrl: String,
@SerializedName("isFollow") val isFollow: Boolean @SerializedName("isFollow") var isFollow: Boolean,
@SerializedName("isNotify") var isNotify: Boolean
) )

View File

@ -29,7 +29,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="4dp" android:layout_marginStart="4dp"
android:fontFamily="@font/gmarket_sans_medium" android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_9970ff" android:textColor="@color/color_3bb9f1"
android:textSize="18.3sp" android:textSize="18.3sp"
app:layout_constraintStart_toEndOf="@+id/tv_title" app:layout_constraintStart_toEndOf="@+id/tv_title"
app:layout_constraintTop_toTopOf="@+id/tv_title" app:layout_constraintTop_toTopOf="@+id/tv_title"

View File

@ -17,14 +17,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginStart="13.3dp" android:layout_marginStart="13.3dp"
android:layout_toStartOf="@+id/iv_notification" android:layout_toStartOf="@+id/iv_follow"
android:layout_toEndOf="@+id/iv_profile" android:layout_toEndOf="@+id/iv_profile"
android:fontFamily="@font/gmarket_sans_medium" android:fontFamily="@font/gmarket_sans_medium"
android:textSize="16.7sp" android:textSize="16.7sp"
tools:text="상남자" /> tools:text="상남자" />
<ImageView <ImageView
android:id="@+id/iv_notification" android:id="@+id/iv_follow"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"