팔로잉 채널 전체 리스트 페이지 추가
This commit is contained in:
		| @@ -101,4 +101,6 @@ enum AppStep { | ||||
|     case changeNickname | ||||
|      | ||||
|     case profileUpdate(refresh: () -> Void) | ||||
|      | ||||
|     case followingList | ||||
| } | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
							
								
								
									
										54
									
								
								SodaLive/Sources/Follow/FollowCreatorItemView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								SodaLive/Sources/Follow/FollowCreatorItemView.swift
									
									
									
									
									
										Normal 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 | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										19
									
								
								SodaLive/Sources/Follow/FollowCreatorRepository.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								SodaLive/Sources/Follow/FollowCreatorRepository.swift
									
									
									
									
									
										Normal 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)) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										81
									
								
								SodaLive/Sources/Follow/FollowCreatorView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								SodaLive/Sources/Follow/FollowCreatorView.swift
									
									
									
									
									
										Normal 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() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										108
									
								
								SodaLive/Sources/Follow/FollowCreatorViewModel.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								SodaLive/Sources/Follow/FollowCreatorViewModel.swift
									
									
									
									
									
										Normal 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) | ||||
|     } | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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) | ||||
|         } | ||||
|     } | ||||
|      | ||||
|   | ||||
| @@ -105,6 +105,7 @@ struct SectionRecommendChannelView: View { | ||||
|                                 .lineLimit(1) | ||||
|                         } | ||||
|                         .onTapGesture { | ||||
|                             AppState.shared.setAppStep(step: .followingList) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Yu Sung
					Yu Sung