재생목록 만들기 페이지 추가

This commit is contained in:
Yu Sung 2024-12-09 18:11:28 +09:00
parent abc4a4f39d
commit 0fdb9edd23
20 changed files with 757 additions and 45 deletions

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_check_blue.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ic_playlist_add.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 962 B

View File

@ -11,44 +11,67 @@ struct ContentBoxView: View {
@StateObject var viewModel = ContentBoxViewModel()
@State private var isShowCreatePlaylist = false
@State private var isReloadData = false
var body: some View {
NavigationView {
VStack(spacing: 13.3) {
DetailNavigationBar(title: "내 보관함")
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 12) {
ContentBoxTabView(
title: "구매목록",
isSelected: viewModel.currentTab == .orderlist
)
.onTapGesture {
if viewModel.currentTab != .orderlist {
viewModel.currentTab = .orderlist
ZStack {
NavigationView {
VStack(spacing: 13.3) {
DetailNavigationBar(title: "내 보관함")
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 12) {
ContentBoxTabView(
title: "구매목록",
isSelected: viewModel.currentTab == .orderlist
)
.onTapGesture {
if viewModel.currentTab != .orderlist {
viewModel.currentTab = .orderlist
}
}
ContentBoxTabView(
title: "재생목록",
isSelected: viewModel.currentTab == .playlist
)
.onTapGesture {
if viewModel.currentTab != .playlist {
viewModel.currentTab = .playlist
}
}
}
ContentBoxTabView(
title: "재생목록",
isSelected: viewModel.currentTab == .playlist
)
.onTapGesture {
if viewModel.currentTab != .playlist {
viewModel.currentTab = .playlist
}
}
}
.padding(.horizontal, 13.3)
}
if viewModel.currentTab == .playlist {
ContentPlaylistListView()
.padding(.bottom, 13.3)
.padding(.horizontal, 13.3)
} else {
OrderListAllInnerView()
}
if viewModel.currentTab == .playlist {
if isReloadData {
Color.clear
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
isReloadData = false
}
}
} else {
ContentPlaylistListView(
onClickCreate: { isShowCreatePlaylist = true }
)
.padding(.bottom, 13.3)
.padding(.horizontal, 13.3)
}
} else {
OrderListAllInnerView()
}
}
}
if isShowCreatePlaylist {
ContentPlaylistCreateView(
isShowing: $isShowCreatePlaylist,
reloadData: $isReloadData
)
}
}
}
}

View File

@ -14,7 +14,7 @@ enum ContentApi {
case likeContent(request: PutAudioContentLikeRequest)
case registerComment(request: RegisterAudioContentCommentRequest)
case orderAudioContent(request: OrderRequest)
case getOrderList(page: Int, size: Int)
case getOrderList(orderType: OrderType?, page: Int, size: Int)
case addAllPlaybackTracking(request: AddAllPlaybackTrackingRequest)
case getAudioContentThemeList
case uploadAudioContent(parameters: [MultipartFormData])
@ -180,12 +180,16 @@ extension ContentApi: TargetType {
case .orderAudioContent(let request):
return .requestJSONEncodable(request)
case .getOrderList(let page, let size):
let parameters = [
case .getOrderList(let orderType, let page, let size):
var parameters = [
"page": page - 1,
"size": size
] as [String : Any]
if let orderType = orderType {
parameters["orderType"] = orderType
}
return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
case .addAllPlaybackTracking(let request):

View File

@ -34,8 +34,8 @@ final class ContentRepository {
return api.requestPublisher(.orderAudioContent(request: OrderRequest(contentId: contentId, orderType: orderType)))
}
func getOrderList(page: Int, size: Int) -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(.getOrderList(page: page, size: size))
func getOrderList(orderType: OrderType?, page: Int, size: Int) -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(.getOrderList(orderType: orderType, page: page, size: size))
}
func addAllPlaybackTracking(request: AddAllPlaybackTrackingRequest) -> AnyPublisher<Response, MoyaError> {

View File

@ -15,4 +15,8 @@ class ContentPlaylistListRepository {
func getPlaylistList() -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(.getPlaylistList)
}
func createPlaylist(request: CreatePlaylistRequest) -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(.createPlaylist(request: request))
}
}

View File

@ -11,6 +11,8 @@ struct ContentPlaylistListView: View {
@ObservedObject var viewModel = ContentPlaylistListViewModel()
let onClickCreate: () -> Void
var body: some View {
BaseView(isLoading: $viewModel.isLoading) {
VStack(spacing: 13.3) {
@ -21,7 +23,9 @@ struct ContentPlaylistListView: View {
.frame(maxWidth: .infinity)
.background(Color.button)
.cornerRadius(5.3)
.contentShape(Rectangle())
.onTapGesture {
onClickCreate()
}
if viewModel.playlists.isEmpty {
@ -52,13 +56,31 @@ struct ContentPlaylistListView: View {
}
.frame(maxWidth: .infinity)
ForEach(0..<viewModel.playlists.count, id: \.self) { index in
let playlist = viewModel.playlists[index]
ContentPlaylistItemView(item: playlist)
ScrollView(.vertical) {
LazyVStack(spacing: 11) {
ForEach(0..<viewModel.playlists.count, id: \.self) { index in
let playlist = viewModel.playlists[index]
ContentPlaylistItemView(item: playlist)
}
}
}
}
}
.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()
}
}
.onAppear {
viewModel.getPlaylistList()
}
@ -67,5 +89,5 @@ struct ContentPlaylistListView: View {
}
#Preview {
ContentPlaylistListView()
ContentPlaylistListView(onClickCreate: {})
}

View File

@ -0,0 +1,172 @@
//
// ContentPlaylistCreateView.swift
// SodaLive
//
// Created by klaus on 12/7/24.
//
import SwiftUI
struct ContentPlaylistCreateView: View {
@StateObject var viewModel = ContentPlaylistCreateViewModel()
@Binding var isShowing: Bool
@Binding var reloadData: Bool
@State private var isShowAddContentView = false
var body: some View {
BaseView(isLoading: $viewModel.isLoading) {
VStack(alignment: .leading, spacing: 0) {
HStack(spacing: 0) {
Button {
isShowing = false
} label: {
Image("ic_back")
.resizable()
.frame(width: 20, height: 20)
Text("새 재생목록 만들기")
.font(.custom(Font.bold.rawValue, size: 18.3))
.foregroundColor(Color.grayee)
}
Spacer()
Text("저장")
.font(.custom(Font.medium.rawValue, size: 14.7))
.foregroundColor(Color.grayee)
.frame(minHeight: 48)
.onTapGesture {
viewModel.savePlaylist {
reloadData = true
isShowing = false
}
}
}
.frame(height: 50)
.padding(.horizontal, 13.3)
.frame(maxWidth: .infinity)
.background(Color.black)
HStack(spacing: 0) {
Text("재생목록 제목")
.font(.custom(Font.bold.rawValue, size: 16.7))
.foregroundColor(Color.grayee)
Spacer()
Text("\(viewModel.title.count)/30")
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color.gray77)
.onChange(of: viewModel.title) { newValue in
if newValue.count > 30 {
viewModel.title = String(newValue.prefix(30))
}
}
}
.padding(.top, 26.7)
.padding(.horizontal, 13.3)
TextField("", text: $viewModel.title)
.autocapitalization(.none)
.disableAutocorrection(true)
.font(.custom(Font.bold.rawValue, size: 13.3))
.foregroundColor(Color.grayee)
.keyboardType(.webSearch)
.frame(maxWidth: .infinity)
.padding(.horizontal, 13.3)
.padding(.vertical, 17)
.background(Color.gray22)
.cornerRadius(6.7)
.padding(.top, 13.3)
.padding(.horizontal, 13.3)
HStack(spacing: 0) {
Text("재생목록 설명을 입력해 주세요")
.font(.custom(Font.bold.rawValue, size: 16.7))
.foregroundColor(Color.grayee)
Spacer()
Text("\(viewModel.desc.count)/40")
.font(.custom(Font.medium.rawValue, size: 13.3))
.foregroundColor(Color.gray77)
.onChange(of: viewModel.desc) { newValue in
if newValue.count > 40 {
viewModel.desc = String(newValue.prefix(40))
}
}
}
.padding(.top, 26.7)
.padding(.horizontal, 13.3)
TextField("", text: $viewModel.desc)
.autocapitalization(.none)
.disableAutocorrection(true)
.font(.custom(Font.bold.rawValue, size: 13.3))
.foregroundColor(Color.grayee)
.keyboardType(.webSearch)
.frame(maxWidth: .infinity)
.padding(.horizontal, 13.3)
.padding(.vertical, 17)
.background(Color.gray22)
.cornerRadius(6.7)
.padding(.top, 13.3)
.padding(.horizontal, 13.3)
HStack(spacing: 8) {
Image("btn_plus_round")
Text("새로운 콘텐츠 추가/제거")
.font(.custom(Font.bold.rawValue, size: 14.7))
.foregroundColor(Color.button)
}
.padding(.top, 26.7)
.padding(.horizontal, 13.3)
.onTapGesture {
isShowAddContentView = true
}
ScrollView(.vertical, showsIndicators: false) {
LazyVStack(alignment: .leading, spacing: 13.3) {
ForEach(0..<viewModel.contentList.count, id: \.self) { index in
PlaylistCreateContentView(content: viewModel.contentList[index])
}
}
.padding(.horizontal, 13.3)
}
.padding(.vertical, 13.3)
}
.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()
}
}
if isShowAddContentView {
PlaylistAddContentView(
isShowing: $isShowAddContentView,
contentList: $viewModel.contentList
)
}
}
}
}
#Preview {
ContentPlaylistCreateView(
isShowing: .constant(true),
reloadData: .constant(false)
)
}

View File

@ -0,0 +1,90 @@
//
// ContentPlaylistCreateViewModel.swift
// SodaLive
//
// Created by klaus on 12/7/24.
//
import Foundation
import Combine
final class ContentPlaylistCreateViewModel: ObservableObject {
private let repository = ContentPlaylistListRepository()
private var subscription = Set<AnyCancellable>()
@Published var isLoading = false
@Published var errorMessage = ""
@Published var isShowPopup = false
@Published var title: String = ""
@Published var desc: String = ""
var contentList = [AudioContentPlaylistContent]()
func savePlaylist(onSuccess: @escaping () -> Void) {
if (validate()) {
isLoading = true
let contentIdAndOrderList = contentList.mapIndexed { index, item in
PlaylistContentIdAndOrder(contentId: item.id, order: index + 1)
}
repository.createPlaylist(
request: CreatePlaylistRequest(
title: title,
desc: desc,
contentIdAndOrderList: contentIdAndOrderList
)
)
.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(ApiResponseWithoutData.self, from: responseData)
self.isLoading = false
if decoded.success {
onSuccess()
} 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)
}
}
private func validate() -> Bool {
if (title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || title.count < 3) {
errorMessage = "제목을 3자 이상 입력하세요"
isShowPopup = true
return false
}
if (contentList.isEmpty) {
errorMessage = "콘텐츠를 1개 이상 추가하세요"
isShowPopup = true
return false
}
return true
}
}

View File

@ -0,0 +1,12 @@
//
// CreatePlaylistRequest.swift
// SodaLive
//
// Created by klaus on 12/8/24.
//
struct CreatePlaylistRequest: Encodable {
let title: String
let desc: String?
let contentIdAndOrderList: [PlaylistContentIdAndOrder]
}

View File

@ -0,0 +1,89 @@
//
// PlaylistAddContentItemView.swift
// SodaLive
//
// Created by klaus on 12/9/24.
//
import SwiftUI
import Kingfisher
struct PlaylistAddContentItemView: View {
let item: GetAudioContentOrderListItem
let onClick: () -> Void
@State private var isChecked: Bool
init(item: GetAudioContentOrderListItem, onClick: @escaping () -> Void, isChecked: Bool) {
self.item = item
self.onClick = onClick
self._isChecked = State(initialValue: isChecked)
}
var body: some View {
HStack(spacing: 13.3) {
KFImage(URL(string: item.coverImageUrl))
.cancelOnDisappear(true)
.downsampling(size: CGSize(width: 40, height: 40))
.resizable()
.scaledToFill()
.frame(width: 40, height: 40, alignment: .center)
.cornerRadius(5.3)
.clipped()
VStack(alignment: .leading, spacing: 2.6) {
HStack(spacing: 8) {
Text(item.themeStr)
.font(.custom(Font.medium.rawValue, size: 10))
.foregroundColor(Color(hex: "3bac6a"))
.padding(2.6)
.background(Color(hex: "28312b"))
.cornerRadius(2.6)
if let duration = item.duration {
Text(duration)
.font(.custom(Font.medium.rawValue, size: 10))
.foregroundColor(Color.gray77)
.padding(2.6)
.background(Color.gray22)
.cornerRadius(2.6)
}
}
Text(item.title)
.font(.custom(Font.medium.rawValue, size: 12))
.foregroundColor(Color.grayd2)
.lineLimit(2)
.truncationMode(.tail)
}
Spacer()
Image(isChecked ? "ic_check_blue" : "ic_playlist_add")
.padding(8)
.onTapGesture {
onClick()
isChecked.toggle()
}
}
}
}
#Preview {
PlaylistAddContentItemView(
item: GetAudioContentOrderListItem(
contentId: 1,
coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
creatorNickname: "유저1",
title: "안녕하세요 오늘은 커버곡을 들려드릴께요....안녕하세요 오늘은 커버곡을 들려드릴께요....",
themeStr: "커버곡",
duration: "00:30:20",
isAdult: false,
orderType: .KEEP,
likeCount: 0,
commentCount: 0
),
onClick: {},
isChecked: true
)
}

View File

@ -0,0 +1,128 @@
//
// PlaylistAddContentView.swift
// SodaLive
//
// Created by klaus on 12/8/24.
//
import SwiftUI
struct PlaylistAddContentView: View {
@StateObject var viewModel = OrderListAllViewModel()
@Binding var isShowing: Bool
@Binding var contentList: [AudioContentPlaylistContent]
var body: some View {
VStack(alignment: .leading, spacing: 13.3) {
ZStack {
Text("새로운 콘텐츠 추가/제거")
.font(.custom(Font.bold.rawValue, size: 18.3))
.foregroundColor(Color.grayee)
HStack(spacing: 0) {
Spacer()
Text("닫기")
.font(.custom(Font.medium.rawValue, size: 14.7))
.foregroundColor(Color.grayee)
.frame(minHeight: 48)
.onTapGesture { isShowing = false }
}
}
.frame(height: 50)
.frame(maxWidth: .infinity)
.background(Color.black)
HStack(alignment: .center, spacing: 5.3) {
Text("전체")
.font(.custom(Font.medium.rawValue, size: 14.7))
.foregroundColor(Color.white)
Text("\(viewModel.totalCount)")
.font(.custom(Font.medium.rawValue, size: 12))
.foregroundColor(Color.gray90)
}
ScrollView(.vertical, showsIndicators: false) {
LazyVStack(spacing: 10.7) {
ForEach(0..<viewModel.orderList.count, id: \.self) { index in
let item = viewModel.orderList[index]
PlaylistAddContentItemView(
item: item,
onClick: {
let isChecked = contentList.contains(where: {
$0.id == item.contentId
})
if isChecked {
contentList.removeAll(where: {
$0.id == item.contentId
})
} else {
contentList.append(
AudioContentPlaylistContent(
id: item.contentId,
title: item.title,
category: item.themeStr,
coverUrl: item.coverImageUrl,
duration: item.duration ?? "00:00:00",
creatorNickname: item.creatorNickname,
creatorProfileUrl: ""
)
)
}
},
isChecked: contentList.contains(where: {
$0.id == item.contentId
})
)
.contentShape(Rectangle())
.padding(.horizontal, 13.3)
.padding(.top, 6.7)
.onAppear {
if index == viewModel.orderList.count - 1 {
viewModel.getOrderList(orderType: .KEEP)
}
}
}
}
}
.onAppear {
viewModel.getOrderList(orderType: .KEEP)
}
}
.padding(.horizontal, 13.3)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.black)
}
}
#Preview {
PlaylistAddContentView(
isShowing: .constant(true),
contentList: .constant(
[
AudioContentPlaylistContent(
id: 1,
title: "안녕하세요 오늘은 커버곡을 들려드리려고 해요안녕하세요 오늘은 커버곡을 들려드리려고 해요안녕하세요 오늘은 커버곡을 들려드리려고 해요안녕하세요 오늘은 커버곡을 들려드리려고 해요안녕하세요 오늘은 커버곡을 들려드리려고 해요안녕하세요 오늘은 커버곡을 들려드리려고 해요",
category: "커버곡",
coverUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
duration: "00:30:20",
creatorNickname: "유저1",
creatorProfileUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
),
AudioContentPlaylistContent(
id: 2,
title: "안녕하세요 오늘은 커버곡을 들려드리려고 해요",
category: "커버곡",
coverUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
duration: "00:30:20",
creatorNickname: "유저2",
creatorProfileUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
)
]
)
)
}

View File

@ -0,0 +1,11 @@
//
// PlaylistContentIdAndOrder.swift
// SodaLive
//
// Created by klaus on 12/8/24.
//
struct PlaylistContentIdAndOrder: Encodable {
let contentId: Int
let order: Int
}

View File

@ -0,0 +1,66 @@
//
// PlaylistCreateContentView.swift
// SodaLive
//
// Created by klaus on 12/8/24.
//
import SwiftUI
import Kingfisher
struct PlaylistCreateContentView: View {
let content: AudioContentPlaylistContent
var body: some View {
HStack(spacing: 13.3) {
KFImage(URL(string: content.coverUrl))
.cancelOnDisappear(true)
.downsampling(size: CGSize(width: 40, height: 40))
.resizable()
.scaledToFill()
.frame(width: 40, height: 40, alignment: .center)
.cornerRadius(5.3)
VStack(alignment: .leading, spacing: 6.7) {
HStack(spacing: 8) {
Text(content.category)
.font(.custom(Font.medium.rawValue, size: 8))
.foregroundColor(Color(hex: "3bac6a"))
.padding(2.6)
.background(Color(hex: "28312b"))
.cornerRadius(2.6)
Text(content.duration)
.font(.custom(Font.medium.rawValue, size: 8))
.foregroundColor(Color.gray77)
.padding(2.6)
.background(Color.gray22)
.cornerRadius(2.6)
}
Text(content.title)
.font(.custom(Font.medium.rawValue, size: 12))
.foregroundColor(Color.grayd2)
.multilineTextAlignment(.leading)
.lineLimit(3)
.truncationMode(.tail)
.fixedSize(horizontal: false, vertical: true)
}
}
}
}
#Preview {
PlaylistCreateContentView(
content: AudioContentPlaylistContent(
id: 1,
title: "안녕하세요 오늘은 커버곡을 들려드리려고 해요안녕하세요 오늘은 커버곡을 들려드리려고 해요안녕하세요 오늘은 커버곡을 들려드리려고 해요안녕하세요 오늘은 커버곡을 들려드리려고 해요안녕하세요 오늘은 커버곡을 들려드리려고 해요안녕하세요 오늘은 커버곡을 들려드리려고 해요",
category: "커버곡",
coverUrl: "https://test-cf.sodalive.net/profile/default-profile.png",
duration: "00:30:20",
creatorNickname: "유저1",
creatorProfileUrl: "https://test-cf.sodalive.net/profile/default-profile.png"
)
)
}

View File

@ -0,0 +1,28 @@
//
// GetPlaylistDetailResponse.swift
// SodaLive
//
// Created by klaus on 12/8/24.
//
import Foundation
struct GetPlaylistDetailResponse: Decodable {
let playlistId: Int
let title: String
let desc: String
let createdDate: String
let contentCount: Int
let playlistCoverImageList: [String]
let contentList: [AudioContentPlaylistContent]
}
struct AudioContentPlaylistContent: Decodable {
let id: Int
let title: String
let category: String
let coverUrl: String
let duration: String
let creatorNickname: String
let creatorProfileUrl: String
}

View File

@ -10,6 +10,7 @@ import Moya
enum PlaylistApi {
case getPlaylistList
case createPlaylist(request: CreatePlaylistRequest)
}
extension PlaylistApi: TargetType {
@ -19,7 +20,7 @@ extension PlaylistApi: TargetType {
var path: String {
switch self {
case .getPlaylistList:
case .getPlaylistList, .createPlaylist:
return "/audio-content/playlist"
}
}
@ -28,6 +29,9 @@ extension PlaylistApi: TargetType {
switch self {
case .getPlaylistList:
return .get
case .createPlaylist:
return .post
}
}
@ -35,6 +39,9 @@ extension PlaylistApi: TargetType {
switch self {
case .getPlaylistList:
return .requestPlain
case .createPlaylist(let request):
return .requestJSONEncodable(request)
}
}

View File

@ -0,0 +1,14 @@
//
// CollectionExtension.swift
// SodaLive
//
// Created by klaus on 12/8/24.
//
extension Collection {
func mapIndexed<T>(_ transform: (Index, Element) -> T) -> [T] {
return self.enumerated().map { index, element in
transform(self.index(startIndex, offsetBy: index), element)
}
}
}

View File

@ -25,11 +25,11 @@ final class OrderListAllViewModel: ObservableObject {
var isLast = false
private let pageSize = 10
func getOrderList() {
func getOrderList(orderType: OrderType? = nil) {
if (!isLast && !isLoading) {
isLoading = true
repository.getOrderList(page: page, size: pageSize)
repository.getOrderList(orderType: orderType, page: page, size: pageSize)
.sink { result in
switch result {
case .finished: