팔로잉 채널 전체 리스트 페이지 추가

This commit is contained in:
Yu Sung 2023-08-19 23:14:54 +09:00
parent 633e1bfd92
commit 78f80cebd5
9 changed files with 303 additions and 1 deletions

View File

@ -101,4 +101,6 @@ enum AppStep {
case changeNickname case changeNickname
case profileUpdate(refresh: () -> Void) case profileUpdate(refresh: () -> Void)
case followingList
} }

View File

@ -142,6 +142,9 @@ struct ContentView: View {
case .profileUpdate(let refresh): case .profileUpdate(let refresh):
ProfileUpdateView(refresh: refresh) ProfileUpdateView(refresh: refresh)
case .followingList:
FollowCreatorView()
default: default:
EmptyView() EmptyView()
.frame(width: 0, height: 0, alignment: .topLeading) .frame(width: 0, height: 0, alignment: .topLeading)

View File

@ -0,0 +1,54 @@
//
// FollowCreatorItemView.swift
// SodaLive
//
// Created by klaus on 2023/08/19.
//
import SwiftUI
import Kingfisher
struct FollowCreatorItemView: View {
let creator: GetCreatorFollowingAllListItem
let onClickFollow: (Int) -> Void
let onClickUnFollow: (Int) -> Void
@State private var isFollow = true
var body: some View {
VStack(spacing: 13.3) {
HStack(spacing: 0) {
KFImage(URL(string: creator.profileImageUrl))
.resizable()
.frame(width: 60, height: 60)
.clipShape(Circle())
Text(creator.nickname)
.font(.custom(Font.bold.rawValue, size: 16.7))
.foregroundColor(Color(hex: "eeeeee"))
.padding(.leading, 13.3)
Spacer()
Image(isFollow ? "btn_notification_selected" : "btn_notification")
.onTapGesture {
if isFollow {
onClickUnFollow(creator.creatorId)
} else {
onClickFollow(creator.creatorId)
}
isFollow = !isFollow
}
}
Rectangle()
.foregroundColor(Color(hex: "595959"))
.frame(height: 0.5)
}
.onAppear {
isFollow = creator.isFollow
}
}
}

View File

@ -0,0 +1,19 @@
//
// FollowCreatorRepository.swift
// SodaLive
//
// Created by klaus on 2023/08/19.
//
import Foundation
import CombineMoya
import Combine
import Moya
final class FollowCreatorRepository {
private let api = MoyaProvider<LiveRecommendApi>()
func getFollowedCreatorAllList(page: Int, size: Int) -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(.getFollowedCreatorAllList(page: page, size: size))
}
}

View File

@ -0,0 +1,81 @@
//
// FollowCreatorView.swift
// SodaLive
//
// Created by klaus on 2023/08/19.
//
import SwiftUI
struct FollowCreatorView: View {
@StateObject var viewModel = FollowCreatorViewModel()
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(hex: "eeeeee"))
Text("\(viewModel.totalCount)")
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color(hex: "dd4500"))
Text("")
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color(hex: "eeeeee"))
Spacer()
}
.padding(.horizontal, 13.3)
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 13.3) {
ForEach(0..<viewModel.creatorList.count, id: \.self) { index in
let creator = viewModel.creatorList[index]
FollowCreatorItemView(
creator: creator,
onClickFollow: { viewModel.creatorFollow(userId: $0) },
onClickUnFollow: { viewModel.creatorUnFollow(userId: $0) }
)
.padding(.horizontal, 20)
.onTapGesture {
AppState.shared
.setAppStep(step: .creatorDetail(userId: creator.creatorId))
}
.onAppear {
if index == viewModel.creatorList.count - 1 {
viewModel.getFollowedCreatorAllList()
}
}
}
}
.padding(.top, 13.3)
}
}
.onAppear {
viewModel.getFollowedCreatorAllList()
}
.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(hex: "9970ff"))
.foregroundColor(Color.white)
.multilineTextAlignment(.leading)
.cornerRadius(20)
.padding(.bottom, 66.7)
Spacer()
}
}
}
}
}

View File

@ -0,0 +1,108 @@
//
// FollowCreatorViewModel.swift
// SodaLive
//
// Created by klaus on 2023/08/19.
//
import Foundation
import Moya
import Combine
final class FollowCreatorViewModel: ObservableObject {
private let repository = FollowCreatorRepository()
private var userRepository = UserRepository()
private var subscription = Set<AnyCancellable>()
@Published var isLoading = false
@Published var errorMessage = ""
@Published var isShowPopup = false
@Published var creatorList = [GetCreatorFollowingAllListItem]()
@Published var totalCount = 0
var page = 1
var isLast = false
private let pageSize = 10
func getFollowedCreatorAllList() {
if (!isLast && !isLoading) {
isLoading = true
repository.getFollowedCreatorAllList(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<GetCreatorFollowingAllListResponse>.self, from: responseData)
self.isLoading = false
if let data = decoded.data, decoded.success {
if page == 1 {
creatorList.removeAll()
}
self.totalCount = data.totalCount
if !data.items.isEmpty {
page += 1
self.creatorList.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 creatorFollow(userId: Int) {
userRepository.creatorFollow(creatorId: 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 creatorUnFollow(userId: Int) {
userRepository.creatorUnFollow(creatorId: 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,20 @@
//
// GetCreatorFollowingAllListResponse.swift
// SodaLive
//
// Created by klaus on 2023/08/19.
//
import Foundation
struct GetCreatorFollowingAllListResponse: Decodable {
let totalCount: Int
let items: [GetCreatorFollowingAllListItem]
}
struct GetCreatorFollowingAllListItem: Decodable {
let creatorId: Int
let nickname: String
let profileImageUrl: String
let isFollow: Bool
}

View File

@ -9,6 +9,7 @@ import Foundation
import Moya import Moya
enum LiveRecommendApi { enum LiveRecommendApi {
case getFollowedCreatorAllList(page: Int, size: Int)
case getFollowedChannelList case getFollowedChannelList
case getRecommendChannelList case getRecommendChannelList
case getRecommendLive case getRecommendLive
@ -21,6 +22,9 @@ extension LiveRecommendApi: TargetType {
var path: String { var path: String {
switch self { switch self {
case .getFollowedCreatorAllList:
return "/live/recommend/following/channel/all/list"
case .getFollowedChannelList: case .getFollowedChannelList:
return "/live/recommend/following/channel/list" return "/live/recommend/following/channel/list"
@ -34,7 +38,8 @@ extension LiveRecommendApi: TargetType {
var method: Moya.Method { var method: Moya.Method {
switch self { switch self {
case .getFollowedChannelList, case .getFollowedCreatorAllList,
.getFollowedChannelList,
.getRecommendChannelList, .getRecommendChannelList,
.getRecommendLive: .getRecommendLive:
return .get return .get
@ -47,6 +52,15 @@ extension LiveRecommendApi: TargetType {
.getRecommendChannelList, .getRecommendChannelList,
.getRecommendLive: .getRecommendLive:
return .requestPlain return .requestPlain
case .getFollowedCreatorAllList(let page, let size):
let parameters = [
"page": page - 1,
"size": size,
"timezone": TimeZone.current.identifier,
] as [String : Any]
return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
} }
} }

View File

@ -105,6 +105,7 @@ struct SectionRecommendChannelView: View {
.lineLimit(1) .lineLimit(1)
} }
.onTapGesture { .onTapGesture {
AppState.shared.setAppStep(step: .followingList)
} }
} }
} }