시리즈 전체보기 페이지

This commit is contained in:
Yu Sung 2024-04-29 19:36:53 +09:00
parent ffbdbbaa06
commit 101b04b6a9
9 changed files with 324 additions and 9 deletions

View File

@ -0,0 +1,51 @@
//
// SeriesApi.swift
// SodaLive
//
// Created by klaus on 4/29/24.
//
import Foundation
import Moya
enum SeriesApi {
case getSeriesList(creatorId: Int, sortType: SeriesListAllViewModel.SeriesSortType, page: Int, size: Int)
}
extension SeriesApi: TargetType {
var baseURL: URL {
return URL(string: BASE_URL)!
}
var path: String {
switch self {
case .getSeriesList:
return "/audio-content/series"
}
}
var method: Moya.Method {
switch self {
case .getSeriesList:
return .get
}
}
var task: Moya.Task {
switch self {
case .getSeriesList(let creatorId, let sortType, let page, let size):
let parameters = [
"creatorId": creatorId,
"sortType": sortType,
"page": page - 1,
"size": size
] as [String : Any]
return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
}
}
var headers: [String : String]? {
return ["Authorization": "Bearer \(UserDefaults.string(forKey: UserDefaultsKey.token))"]
}
}

View File

@ -0,0 +1,57 @@
//
// SeriesListAllView.swift
// SodaLive
//
// Created by klaus on 4/29/24.
//
import SwiftUI
struct SeriesListAllView: View {
@ObservedObject var viewModel = SeriesListAllViewModel()
let creatorId: Int
let columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]
var body: some View {
BaseView(isLoading: $viewModel.isLoading) {
VStack(spacing: 0) {
DetailNavigationBar(title: "시리즈 전체보기")
ScrollView(.vertical, showsIndicators: false) {
LazyVGrid(columns: columns, spacing: 13.3) {
ForEach(0..<viewModel.seriesList.count, id: \.self) { index in
let item = viewModel.seriesList[index]
SeriesListItemView(item: item)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared
.setAppStep(step: .seriesDetail(seriesId: item.seriesId))
}
.onAppear {
if index == viewModel.seriesList.count - 1 {
viewModel.getSeriesList()
}
}
}
}
.padding(13.3)
}
}
}
.onAppear {
viewModel.creatorId = creatorId
viewModel.getSeriesList()
}
}
}
#Preview {
SeriesListAllView(creatorId: 0)
}

View File

@ -0,0 +1,79 @@
//
// SeriesListAllViewModel.swift
// SodaLive
//
// Created by klaus on 4/29/24.
//
import Foundation
import Combine
final class SeriesListAllViewModel: ObservableObject {
private let repository = SeriesRepository()
private var subscription = Set<AnyCancellable>()
enum SeriesSortType: String {
case NEWEST, POPULAR
}
var creatorId: Int = 0
@Published var isLoading = false
@Published var errorMessage = ""
@Published var isShowPopup = false
@Published var seriesList = [SeriesListItem]()
var page = 1
var isLast = false
private let pageSize = 10
func getSeriesList() {
if !isLoading && !isLast {
repository
.getSeriesList(creatorId: creatorId, sortType: .NEWEST, 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
let responseData = response.data
do {
let jsonDecoder = JSONDecoder()
let decoded = try jsonDecoder.decode(ApiResponse<GetSeriesListResponse>.self, from: responseData)
if let data = decoded.data, decoded.success {
if page == 1 {
self.seriesList.removeAll()
}
if !data.items.isEmpty {
page += 1
self.seriesList.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)
}
}
}

View File

@ -0,0 +1,19 @@
//
// SeriesRepository.swift
// SodaLive
//
// Created by klaus on 4/29/24.
//
import Foundation
import CombineMoya
import Combine
import Moya
class SeriesRepository {
private let api = MoyaProvider<SeriesApi>()
func getSeriesList(creatorId: Int, sortType: SeriesListAllViewModel.SeriesSortType, page: Int, size: Int) -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(.getSeriesList(creatorId: creatorId, sortType: sortType, page: page, size: size))
}
}

View File

@ -178,6 +178,9 @@ struct ContentView: View {
case .contentAllByTheme(let themeId):
ContentAllByThemeView(themeId: themeId)
case .seriesAll(let creatorId):
SeriesListAllView(creatorId: creatorId)
default:
EmptyView()
.frame(width: 0, height: 0, alignment: .topLeading)

View File

@ -23,15 +23,20 @@ struct UserProfileSeriesView: View {
Text("전체보기")
.font(.custom(Font.light.rawValue, size: 11.3))
.foregroundColor(Color.grayee)
.onTapGesture {}
.onTapGesture {
AppState.shared
.setAppStep(step: .seriesAll(creatorId: creatorId))
}
}
HStack(spacing: 13.3) {
ForEach(0..<items.count, id: \.self) {
SeriesListBigItemView(item: items[$0])
let item = items[$0]
SeriesListBigItemView(item: item)
.contentShape(Rectangle())
.onTapGesture {
AppState.shared
.setAppStep(step: .seriesAll(creatorId: creatorId))
.setAppStep(step: .seriesDetail(seriesId: item.seriesId))
}
}
}

View File

@ -54,6 +54,7 @@ struct SeriesListBigItemView: View {
SeriesItemBadgeView(title: "\(item.numberOfContent)", backgroundColor: Color.gray33.opacity(0.7))
}
}
.padding(3.3)
}
.frame(width: 116.7, height: 165, alignment: .center)
@ -79,10 +80,6 @@ struct SeriesListBigItemView: View {
.foregroundColor(Color.gray77)
}
.frame(width: 116.7)
.onTapGesture {
AppState.shared
.setAppStep(step: .seriesDetail(seriesId: item.seriesId))
}
}
}

View File

@ -0,0 +1,104 @@
//
// SeriesListItemView.swift
// SodaLive
//
// Created by klaus on 4/29/24.
//
import SwiftUI
import Kingfisher
struct SeriesListItemView: View {
let item: SeriesListItem
var body: some View {
VStack(alignment: .leading, spacing: 8) {
ZStack {
KFImage(URL(string: item.coverImage))
.resizable()
.scaledToFill()
.frame(width: 102, height: 144, alignment: .center)
.cornerRadius(5)
.clipped()
LinearGradient(
colors: [Color.black.opacity(0.1), Color.black.opacity(0.8)],
startPoint: .top,
endPoint: .bottom
)
VStack(alignment: .leading, spacing: 0) {
HStack(spacing: 3.3) {
if !item.isComplete && item.isNew {
SeriesItemBadgeView(title: "신작", backgroundColor: .button)
}
if item.isComplete {
SeriesItemBadgeView(title: "완결", backgroundColor: Color(hex: "002abd"))
}
if item.isPopular {
SeriesItemBadgeView(title: "인기", backgroundColor: Color(hex: "ec6033"))
}
Spacer()
}
Spacer()
HStack {
Spacer()
SeriesItemBadgeView(title: "\(item.numberOfContent)", backgroundColor: Color.gray33.opacity(0.7))
}
}
.padding(3.3)
}
.frame(width: 102, height: 144, alignment: .center)
Text(item.title)
.font(.custom(Font.medium.rawValue, size: 12))
.foregroundColor(Color.grayee)
.lineLimit(2)
HStack(spacing: 3) {
KFImage(URL(string: item.creator.profileImage))
.resizable()
.scaledToFill()
.frame(width: 16, height: 16, alignment: .center)
.clipShape(Circle())
Text(item.creator.nickname)
.font(.custom(Font.medium.rawValue, size: 10))
.foregroundColor(Color.gray77)
}
Text(item.publishedDaysOfWeek)
.font(.custom(Font.medium.rawValue, size: 11))
.foregroundColor(Color.gray77)
}
.frame(width: 116.7)
}
}
#Preview {
SeriesListItemView(
item: SeriesListItem(
seriesId: 1,
title: "제목, 관심사,프로필+방장, 참여인원(어딘가..)",
coverImage: "https://test-cf.sodalive.net/profile/default-profile.png",
publishedDaysOfWeek: "매주 수, 토요일",
isComplete: true,
creator: SeriesListItemCreator(
creatorId: 1,
nickname: "creator",
profileImage: "https://test-cf.sodalive.net/profile/default-profile.png"
),
numberOfContent: 10,
isNew: true,
isPopular: true
)
)
}

View File

@ -133,10 +133,10 @@ extension UserApi: TargetType {
case .searchUser(let nickname):
return .requestParameters(parameters: ["nickname" : nickname], encoding: URLEncoding.queryString)
case .getMypage, .getMyProfile:
case .getMypage, .getMyProfile, .getMemberInfo:
return .requestParameters(parameters: ["container" : "ios"], encoding: URLEncoding.queryString)
case .getMemberInfo, .logout, .logoutAllDevice, .getChangeNicknamePrice:
case .logout, .logoutAllDevice, .getChangeNicknamePrice:
return .requestPlain
case .notification(let request):