차단 유저 리스트 페이지 추가

This commit is contained in:
Yu Sung 2024-09-04 18:00:11 +09:00
parent 2bea2365a0
commit 7408288d85
9 changed files with 342 additions and 4 deletions

View File

@ -129,4 +129,6 @@ enum AppStep {
case seriesContentAll(seriesId: Int, seriesTitle: String)
case tempCanPayment(orderType: OrderType, contentId: Int, title: String, can: Int)
case blockList
}

View File

@ -190,6 +190,8 @@ struct ContentView: View {
case .tempCanPayment(let orderType, let contentId, let title, let can):
CanPaymentTempView(orderType: orderType, contentId: contentId, title: title, can: can)
case .blockList:
BlockMemberListView()
default:
EmptyView()

View File

@ -0,0 +1,89 @@
//
// BlockMemberListView.swift
// SodaLive
//
// Created by klaus on 9/4/24.
//
import SwiftUI
struct BlockMemberListView: View {
@StateObject var viewModel = BlockMemberListViewModel()
var body: some View {
BaseView(isLoading: $viewModel.isLoading) {
VStack(spacing: 0) {
DetailNavigationBar(title: "차단 리스트")
HStack(spacing: 0) {
Text("")
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color.grayee)
Text("\(viewModel.totalCount)")
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color.mainRed3)
Text("")
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color.grayee)
Spacer()
}
.padding(.horizontal, 13.3)
.padding(.top, 6.7)
if viewModel.totalCount > 0 {
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 13.3) {
ForEach(0..<viewModel.blockedMemberList.count, id: \.self) { index in
let item = viewModel.blockedMemberList[index]
BlockedMemberListItemView(
item: item,
blockMember: { viewModel.blockMember(userId: $0) },
unBlockMember: { viewModel.unBlockMember(userId: $0) }
)
.padding(.horizontal, 13.3)
.onAppear {
if index == viewModel.blockedMemberList.count - 1 {
viewModel.getBlockedMemberList()
}
}
}
}
.padding(.top, 26.7)
}
} else {
Text("차단한 유저가 없습니다.")
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color.grayee)
.frame(maxHeight: .infinity)
}
}
.onAppear {
viewModel.getBlockedMemberList()
}
.popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .bottom, autohideIn: 2) {
HStack {
Spacer()
Text(viewModel.errorMessage)
.padding(.vertical, 13.3)
.frame(width: screenSize().width - 66.7, alignment: .center)
.font(.custom(Font.medium.rawValue, size: 12))
.background(Color.button)
.foregroundColor(Color.white)
.multilineTextAlignment(.leading)
.cornerRadius(20)
.padding(.bottom, 66.7)
Spacer()
}
}
}
}
}
#Preview {
BlockMemberListView()
}

View File

@ -0,0 +1,107 @@
//
// BlockMemberListViewModel.swift
// SodaLive
//
// Created by klaus on 9/4/24.
//
import Foundation
import Moya
import Combine
final class BlockMemberListViewModel: ObservableObject {
private var userRepository = UserRepository()
private var subscription = Set<AnyCancellable>()
@Published var isLoading = false
@Published var errorMessage = ""
@Published var isShowPopup = false
@Published var blockedMemberList = [GetBlockedMemberListItem]()
@Published var totalCount = 0
var page = 1
var isLast = false
private let pageSize = 10
func getBlockedMemberList() {
if (!isLast && !isLoading) {
isLoading = true
userRepository.getBlockedMemberList(page: page, size: pageSize)
.sink { result in
switch result {
case .finished:
DEBUG_LOG("finish")
case .failure(let error):
ERROR_LOG(error.localizedDescription)
}
} receiveValue: { [unowned self] response in
self.isLoading = false
let responseData = response.data
do {
let jsonDecoder = JSONDecoder()
let decoded = try jsonDecoder.decode(ApiResponse<GetBlockedMemberListResponse>.self, from: responseData)
self.isLoading = false
if let data = decoded.data, decoded.success {
if page == 1 {
blockedMemberList.removeAll()
}
self.totalCount = data.totalCount
if !data.items.isEmpty {
page += 1
self.blockedMemberList.append(contentsOf: data.items)
} else {
isLast = true
}
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
}
self.isShowPopup = true
}
} catch {
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
self.isShowPopup = true
self.isLoading = false
}
}
.store(in: &subscription)
} else {
isLoading = false
}
}
func blockMember(userId: Int) {
userRepository.memberBlock(userId: userId)
.sink { result in
switch result {
case .finished:
DEBUG_LOG("finish")
case .failure(let error):
ERROR_LOG(error.localizedDescription)
}
} receiveValue: { _ in }
.store(in: &subscription)
}
func unBlockMember(userId: Int) {
userRepository.memberUnBlock(userId: userId)
.sink { result in
switch result {
case .finished:
DEBUG_LOG("finish")
case .failure(let error):
ERROR_LOG(error.localizedDescription)
}
} receiveValue: { _ in }
.store(in: &subscription)
}
}

View File

@ -0,0 +1,92 @@
//
// BlockedMemberListItemView.swift
// SodaLive
//
// Created by klaus on 9/4/24.
//
import SwiftUI
import Kingfisher
struct BlockedMemberListItemView: View {
let item: GetBlockedMemberListItem
let blockMember: (Int) -> Void
let unBlockMember: (Int) -> Void
@State private var isBlocked = true
var body: some View {
VStack(spacing: 13.3) {
HStack(spacing: 0) {
KFImage(URL(string: item.profileImageUrl))
.resizable()
.frame(width: 60, height: 60)
.clipShape(Circle())
Text(item.nickname)
.font(.custom(Font.medium.rawValue, size: 16.7))
.foregroundColor(Color.grayee)
.padding(.leading, 13.3)
Spacer()
Text(isBlocked ? "차단해제" : "차단")
.font(.custom(Font.medium.rawValue, size: 12))
.foregroundColor(Color.button)
.frame(minWidth: 83)
.padding(.vertical, 7)
.background(Color.button.opacity(isBlocked ? 0.2 : 0.0))
.cornerRadius(13.3)
.overlay(
RoundedRectangle(cornerRadius: 13.3)
.stroke(lineWidth: 1)
.foregroundColor(Color.button)
)
.onTapGesture {
if isBlocked {
unBlockMember(item.memberId)
} else {
blockMember(item.memberId)
}
isBlocked = !isBlocked
}
}
Rectangle()
.foregroundColor(Color.gray59)
.frame(height: 0.5)
}
.onAppear {
isBlocked = item.isBlocked
}
}
}
#Preview("차단됨") {
BlockedMemberListItemView(
item: GetBlockedMemberListItem(
memberId: 1,
nickname: "유저1",
profileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
isBlocked: true
),
blockMember: { _ in },
unBlockMember: { _ in }
)
}
#Preview("차단해제됨") {
BlockedMemberListItemView(
item: GetBlockedMemberListItem(
memberId: 1,
nickname: "유저1",
profileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
isBlocked: false
),
blockMember: { _ in },
unBlockMember: { _ in }
)
}

View File

@ -0,0 +1,18 @@
//
// GetBlockedMemberListResponse.swift
// SodaLive
//
// Created by klaus on 9/4/24.
//
struct GetBlockedMemberListResponse: Decodable {
let totalCount: Int
let items: [GetBlockedMemberListItem]
}
struct GetBlockedMemberListItem: Decodable {
let memberId: Int
let nickname: String
let profileImageUrl: String
let isBlocked: Bool
}

View File

@ -87,11 +87,30 @@ struct MyPageView: View {
.padding(.vertical, 13.3)
.font(.custom(Font.bold.rawValue, size: 15.3))
.foregroundColor(Color.grayee)
.background(Color.button)
.cornerRadius(6.7)
.overlay(
RoundedRectangle(cornerRadius: 6.7)
.stroke(Color.button, lineWidth: 1)
)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .followingList)
}
Text("차단 리스트")
.frame(maxWidth: .infinity)
.padding(.vertical, 13.3)
.font(.custom(Font.bold.rawValue, size: 15.3))
.foregroundColor(Color.grayee)
.cornerRadius(6.7)
.overlay(
RoundedRectangle(cornerRadius: 6.7)
.stroke(Color.button, lineWidth: 1)
)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared.setAppStep(step: .blockList)
}
}
.padding(.top, 26.7)
.padding(.horizontal, 13.3)

View File

@ -22,6 +22,7 @@ enum UserApi {
case updatePushToken(request: PushTokenUpdateRequest)
case creatorFollow(request: CreatorFollowRequest)
case creatorUnFollow(request: CreatorFollowRequest)
case getBlockedMemberList(page: Int, size: Int)
case memberBlock(request: MemberBlockRequest)
case memberUnBlock(request: MemberBlockRequest)
case getMyProfile
@ -79,7 +80,7 @@ extension UserApi: TargetType {
case .creatorUnFollow:
return "/member/creator/unfollow"
case .memberBlock:
case .getBlockedMemberList, .memberBlock:
return "/member/block"
case .memberUnBlock:
@ -111,7 +112,7 @@ extension UserApi: TargetType {
.profileImageUpdate:
return .post
case .searchUser, .getMypage, .getMemberInfo, .getMyProfile, .getChangeNicknamePrice, .checkNickname:
case .searchUser, .getMypage, .getMemberInfo, .getMyProfile, .getChangeNicknamePrice, .checkNickname, .getBlockedMemberList:
return .get
case .updatePushToken, .profileUpdate, .changeNickname, .updateIdfa:
@ -154,6 +155,10 @@ extension UserApi: TargetType {
case .creatorUnFollow(let request):
return .requestJSONEncodable(request)
case .getBlockedMemberList(let page, let size):
let parameters = ["page": page - 1, "size": size] as [String : Any]
return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
case .memberBlock(let request):
return .requestJSONEncodable(request)

View File

@ -77,6 +77,10 @@ final class UserRepository {
return api.requestPublisher(.creatorUnFollow(request: CreatorFollowRequest(creatorId: creatorId)))
}
func getBlockedMemberList(page: Int, size: Int) -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(.getBlockedMemberList(page: page, size: size))
}
func memberBlock(userId: Int) -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(.memberBlock(request: MemberBlockRequest(blockMemberId: userId)))
}