diff --git a/SodaLive/Sources/App/AppStep.swift b/SodaLive/Sources/App/AppStep.swift index 9012cda..2e4fa05 100644 --- a/SodaLive/Sources/App/AppStep.swift +++ b/SodaLive/Sources/App/AppStep.swift @@ -101,4 +101,6 @@ enum AppStep { case changeNickname case profileUpdate(refresh: () -> Void) + + case followingList } diff --git a/SodaLive/Sources/ContentView.swift b/SodaLive/Sources/ContentView.swift index 17ac89d..9ca024d 100644 --- a/SodaLive/Sources/ContentView.swift +++ b/SodaLive/Sources/ContentView.swift @@ -142,6 +142,9 @@ struct ContentView: View { case .profileUpdate(let refresh): ProfileUpdateView(refresh: refresh) + case .followingList: + FollowCreatorView() + default: EmptyView() .frame(width: 0, height: 0, alignment: .topLeading) diff --git a/SodaLive/Sources/Follow/FollowCreatorItemView.swift b/SodaLive/Sources/Follow/FollowCreatorItemView.swift new file mode 100644 index 0000000..2339e64 --- /dev/null +++ b/SodaLive/Sources/Follow/FollowCreatorItemView.swift @@ -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 + } + } +} diff --git a/SodaLive/Sources/Follow/FollowCreatorRepository.swift b/SodaLive/Sources/Follow/FollowCreatorRepository.swift new file mode 100644 index 0000000..3e9da30 --- /dev/null +++ b/SodaLive/Sources/Follow/FollowCreatorRepository.swift @@ -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() + + func getFollowedCreatorAllList(page: Int, size: Int) -> AnyPublisher { + return api.requestPublisher(.getFollowedCreatorAllList(page: page, size: size)) + } +} diff --git a/SodaLive/Sources/Follow/FollowCreatorView.swift b/SodaLive/Sources/Follow/FollowCreatorView.swift new file mode 100644 index 0000000..8ed39fe --- /dev/null +++ b/SodaLive/Sources/Follow/FollowCreatorView.swift @@ -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..() + + @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.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) + } +} diff --git a/SodaLive/Sources/Follow/GetCreatorFollowingAllListResponse.swift b/SodaLive/Sources/Follow/GetCreatorFollowingAllListResponse.swift new file mode 100644 index 0000000..759def7 --- /dev/null +++ b/SodaLive/Sources/Follow/GetCreatorFollowingAllListResponse.swift @@ -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 +} diff --git a/SodaLive/Sources/Live/Recommend/LiveRecommendApi.swift b/SodaLive/Sources/Live/Recommend/LiveRecommendApi.swift index 82eabad..47c1103 100644 --- a/SodaLive/Sources/Live/Recommend/LiveRecommendApi.swift +++ b/SodaLive/Sources/Live/Recommend/LiveRecommendApi.swift @@ -9,6 +9,7 @@ import Foundation import Moya enum LiveRecommendApi { + case getFollowedCreatorAllList(page: Int, size: Int) case getFollowedChannelList case getRecommendChannelList case getRecommendLive @@ -21,6 +22,9 @@ extension LiveRecommendApi: TargetType { var path: String { switch self { + case .getFollowedCreatorAllList: + return "/live/recommend/following/channel/all/list" + case .getFollowedChannelList: return "/live/recommend/following/channel/list" @@ -34,7 +38,8 @@ extension LiveRecommendApi: TargetType { var method: Moya.Method { switch self { - case .getFollowedChannelList, + case .getFollowedCreatorAllList, + .getFollowedChannelList, .getRecommendChannelList, .getRecommendLive: return .get @@ -47,6 +52,15 @@ extension LiveRecommendApi: TargetType { .getRecommendChannelList, .getRecommendLive: 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) } } diff --git a/SodaLive/Sources/Live/RecommendChannel/SectionRecommendChannelView.swift b/SodaLive/Sources/Live/RecommendChannel/SectionRecommendChannelView.swift index 7d85ba8..63a08c3 100644 --- a/SodaLive/Sources/Live/RecommendChannel/SectionRecommendChannelView.swift +++ b/SodaLive/Sources/Live/RecommendChannel/SectionRecommendChannelView.swift @@ -105,6 +105,7 @@ struct SectionRecommendChannelView: View { .lineLimit(1) } .onTapGesture { + AppState.shared.setAppStep(step: .followingList) } } }