234 lines
6.9 KiB
Swift
234 lines
6.9 KiB
Swift
import Foundation
|
|
import Combine
|
|
|
|
final class MainContentAllViewModel: ObservableObject {
|
|
private let repository = MainContentAllRepository()
|
|
private var subscription = Set<AnyCancellable>()
|
|
private let pageSize = 20
|
|
private var latestRequestId = 0
|
|
|
|
@Published var selectedType: MainContentAllType = .audio
|
|
@Published var selectedSort: ContentSort = .latest
|
|
@Published var selectedDayOfWeek: SeriesPublishedDaysOfWeek = .currentDeviceDay
|
|
@Published var response: MainContentAllTabResponse?
|
|
@Published var audios = [MainContentAudioResponse]()
|
|
@Published var series = [MainContentSeriesResponse]()
|
|
@Published var page = 0
|
|
@Published var size = 20
|
|
@Published var hasNext = false
|
|
@Published var hasLoaded = false
|
|
@Published var isLoading = false
|
|
@Published var isLoadingNextPage = false
|
|
@Published var errorMessage = ""
|
|
@Published var isShowPopup = false
|
|
|
|
var allowedSorts: [ContentSort] {
|
|
selectedType == .free ? [.latest, .popular] : ContentSort.allCases
|
|
}
|
|
|
|
var shouldShowEmptyState: Bool {
|
|
guard hasLoaded, isLoading == false else { return false }
|
|
return selectedType.usesAudioItems ? audios.isEmpty : series.isEmpty
|
|
}
|
|
|
|
func fetchFirstPageIfNeeded() {
|
|
guard hasLoaded == false else { return }
|
|
fetchFirstPage()
|
|
}
|
|
|
|
func fetchFirstPage() {
|
|
fetchContents(page: 0, isNextPage: false)
|
|
}
|
|
|
|
func selectType(_ type: MainContentAllType) {
|
|
guard selectedType != type else { return }
|
|
|
|
selectedType = type
|
|
if allowedSorts.contains(selectedSort) == false {
|
|
selectedSort = .latest
|
|
}
|
|
clearItems()
|
|
fetchFirstPage()
|
|
}
|
|
|
|
func selectSort(_ sort: ContentSort) {
|
|
guard allowedSorts.contains(sort), selectedSort != sort else { return }
|
|
|
|
selectedSort = sort
|
|
clearItems()
|
|
fetchFirstPage()
|
|
}
|
|
|
|
func selectDayOfWeek(_ dayOfWeek: SeriesPublishedDaysOfWeek) {
|
|
guard selectedType == .series, selectedDayOfWeek != dayOfWeek else { return }
|
|
|
|
selectedDayOfWeek = dayOfWeek
|
|
clearItems()
|
|
fetchFirstPage()
|
|
}
|
|
|
|
func fetchNextPageIfNeeded(currentAudio: MainContentAudioResponse) {
|
|
guard audios.last?.audioContentId == currentAudio.audioContentId else { return }
|
|
fetchNextPageIfPossible()
|
|
}
|
|
|
|
func fetchNextPageIfNeeded(currentSeries: MainContentSeriesResponse) {
|
|
guard series.last?.seriesId == currentSeries.seriesId else { return }
|
|
fetchNextPageIfPossible()
|
|
}
|
|
|
|
private func fetchNextPageIfPossible() {
|
|
guard hasNext, isLoading == false, isLoadingNextPage == false else { return }
|
|
fetchContents(page: page + 1, isNextPage: true)
|
|
}
|
|
|
|
private func fetchContents(page requestPage: Int, isNextPage: Bool) {
|
|
latestRequestId += 1
|
|
let requestId = latestRequestId
|
|
|
|
if isNextPage {
|
|
isLoadingNextPage = true
|
|
} else {
|
|
isLoading = true
|
|
}
|
|
|
|
repository.getContents(
|
|
type: selectedType,
|
|
sort: selectedSort,
|
|
page: requestPage,
|
|
size: pageSize,
|
|
dayOfWeek: selectedDayOfWeek
|
|
)
|
|
.sink { [weak self] result in
|
|
guard let self else { return }
|
|
guard requestId == self.latestRequestId else { return }
|
|
|
|
switch result {
|
|
case .finished:
|
|
DEBUG_LOG("finish")
|
|
case .failure(let error):
|
|
ERROR_LOG(error.localizedDescription)
|
|
self.applyFailureState()
|
|
}
|
|
} receiveValue: { [weak self] response in
|
|
guard let self else { return }
|
|
guard requestId == self.latestRequestId else { return }
|
|
|
|
do {
|
|
let jsonDecoder = JSONDecoder()
|
|
let decoded = try jsonDecoder.decode(ApiResponse<MainContentAllTabResponse>.self, from: response.data)
|
|
|
|
if let data = decoded.data, decoded.success {
|
|
self.applySuccessState(data, isNextPage: isNextPage)
|
|
} else {
|
|
self.errorMessage = decoded.message ?? I18n.MainContentAll.loadFailedMessage
|
|
self.isShowPopup = true
|
|
}
|
|
} catch {
|
|
ERROR_LOG(error.localizedDescription)
|
|
self.errorMessage = I18n.MainContentAll.loadFailedMessage
|
|
self.isShowPopup = true
|
|
}
|
|
|
|
self.hasLoaded = true
|
|
self.isLoading = false
|
|
self.isLoadingNextPage = false
|
|
}
|
|
.store(in: &subscription)
|
|
}
|
|
|
|
private func applySuccessState(_ data: MainContentAllTabResponse, isNextPage: Bool) {
|
|
response = data
|
|
selectedType = data.type
|
|
selectedSort = data.sort
|
|
if let dayOfWeek = data.dayOfWeek {
|
|
selectedDayOfWeek = dayOfWeek
|
|
}
|
|
page = data.page
|
|
size = data.size
|
|
hasNext = data.hasNext
|
|
|
|
if data.type.usesAudioItems {
|
|
if isNextPage {
|
|
audios.append(contentsOf: data.audios)
|
|
} else {
|
|
audios = data.audios
|
|
series = []
|
|
}
|
|
} else {
|
|
if isNextPage {
|
|
series.append(contentsOf: data.series)
|
|
} else {
|
|
series = data.series
|
|
audios = []
|
|
}
|
|
}
|
|
}
|
|
|
|
private func applyFailureState() {
|
|
errorMessage = I18n.MainContentAll.loadFailedMessage
|
|
isShowPopup = true
|
|
hasLoaded = true
|
|
isLoading = false
|
|
isLoadingNextPage = false
|
|
}
|
|
|
|
private func clearItems() {
|
|
audios = []
|
|
series = []
|
|
response = nil
|
|
page = 0
|
|
hasNext = false
|
|
}
|
|
}
|
|
|
|
|
|
extension SeriesPublishedDaysOfWeek {
|
|
static var currentDeviceDay: SeriesPublishedDaysOfWeek {
|
|
switch Calendar.current.component(.weekday, from: Date()) {
|
|
case 1:
|
|
return .SUN
|
|
case 2:
|
|
return .MON
|
|
case 3:
|
|
return .TUE
|
|
case 4:
|
|
return .WED
|
|
case 5:
|
|
return .THU
|
|
case 6:
|
|
return .FRI
|
|
case 7:
|
|
return .SAT
|
|
default:
|
|
return .SUN
|
|
}
|
|
}
|
|
|
|
static var allFilterCases: [SeriesPublishedDaysOfWeek] {
|
|
[.MON, .TUE, .WED, .THU, .FRI, .SAT, .SUN, .RANDOM]
|
|
}
|
|
|
|
var shortTitle: String {
|
|
switch self {
|
|
case .SUN:
|
|
return I18n.MainContentAll.DayOfWeek.sun
|
|
case .MON:
|
|
return I18n.MainContentAll.DayOfWeek.mon
|
|
case .TUE:
|
|
return I18n.MainContentAll.DayOfWeek.tue
|
|
case .WED:
|
|
return I18n.MainContentAll.DayOfWeek.wed
|
|
case .THU:
|
|
return I18n.MainContentAll.DayOfWeek.thu
|
|
case .FRI:
|
|
return I18n.MainContentAll.DayOfWeek.fri
|
|
case .SAT:
|
|
return I18n.MainContentAll.DayOfWeek.sat
|
|
case .RANDOM:
|
|
return I18n.MainContentAll.DayOfWeek.random
|
|
}
|
|
}
|
|
}
|
|
|