Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User courses tab downloaded #1004

Merged
merged 3 commits into from
Jul 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Stepic/Legacy/Analytics/Events/AmplitudeAnalyticsEvents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,19 @@ extension AnalyticsEvent {
)
}

// MARK: - UserCourses -

static let myCoursesScreenOpened = AmplitudeAnalyticsEvent(name: "My courses screen opened")

static func myCoursesScreenTabOpened(tab: UserCourses.Tab) -> AmplitudeAnalyticsEvent {
AmplitudeAnalyticsEvent(
name: "My courses screen tab opened",
parameters: [
"tab": tab.rawValue
]
)
}

// MARK: - Wishlist -

static func wishlistCourseAdded(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ extension CourseListDataBackUpdateService: DataBackUpdateServiceDelegate {

if update.contains(.enrollment) {
self.handleCourse(course, didUpdateEnrollment: update)

if self.courseListType is DownloadedCourseListType {
self.delegate?.courseListDataBackUpdateServiceDidUpdateCourseList(self)
}
}

// If isArchived or isFavorite state was updated then handle specified update and refresh course list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,31 +373,52 @@ final class RecommendationsCourseListNetworkService: BaseCourseListNetworkServic
}
}

final class WishlistCourseListNetworkService: BaseCourseListNetworkService, CourseListNetworkServiceProtocol {
let type: WishlistCourseListType
class BaseCacheCoursesIDsSourceCourseListNetworkService: BaseCourseListNetworkService,
CourseListNetworkServiceProtocol {
func getCoursesIDs() -> Promise<[Course.IdType]> { .value([]) }

func fetch(page: Int, filterQuery: CourseListFilterQuery?) -> Promise<([Course], Meta)> {
Promise { seal in
self.getCoursesIDs().then { coursesIDs -> Promise<([Course.IdType], [Course])> in
self.coursesAPI.retrieve(ids: coursesIDs).map { (coursesIDs, $0) }
}.done { coursesIDs, courses in
let result = courses.reordered(order: coursesIDs, transform: { $0.id })
seal.fulfill((result, .oneAndOnlyPage))
}.catch { _ in
seal.reject(Error.fetchFailed)
}
}
}
}

final class WishlistCourseListNetworkService: BaseCacheCoursesIDsSourceCourseListNetworkService {
private let wishlistStorageManager: WishlistStorageManagerProtocol

init(
type: WishlistCourseListType,
coursesAPI: CoursesAPI,
wishlistStorageManager: WishlistStorageManagerProtocol
) {
self.type = type
self.wishlistStorageManager = wishlistStorageManager
super.init(coursesAPI: coursesAPI)
}

func fetch(page: Int, filterQuery: CourseListFilterQuery?) -> Promise<([Course], Meta)> {
let coursesIDs = self.wishlistStorageManager.coursesIDs
let finalMeta = Meta.oneAndOnlyPage
override func getCoursesIDs() -> Promise<[Course.IdType]> {
.value(self.wishlistStorageManager.coursesIDs)
}
}

return Promise { seal in
self.coursesAPI.retrieve(ids: coursesIDs).done { courses in
let courses = courses.reordered(order: coursesIDs, transform: { $0.id })
seal.fulfill((courses, finalMeta))
}.catch { _ in
seal.reject(Error.fetchFailed)
}
}
final class DownloadedCourseListNetworkService: BaseCacheCoursesIDsSourceCourseListNetworkService {
private let downloadedCourseListPersistenceService: DownloadedCourseListPersistenceService

init(
coursesAPI: CoursesAPI,
downloadedCourseListPersistenceService: DownloadedCourseListPersistenceService
) {
self.downloadedCourseListPersistenceService = downloadedCourseListPersistenceService
super.init(coursesAPI: coursesAPI)
}

override func getCoursesIDs() -> Promise<[Course.IdType]> {
self.downloadedCourseListPersistenceService.fetch().mapValues(\.id)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,23 @@ extension VisitedCourseListPersistenceService: VisitedCourseListPersistenceServi
self.updateStorageUsingCurrentData()
}
}

// MARK: - DownloadedCourseListPersistenceService: CourseListPersistenceService -

final class DownloadedCourseListPersistenceService: CourseListPersistenceService {
private let downloadsProvider: DownloadsProviderProtocol

init(downloadsProvider: DownloadsProviderProtocol = DownloadsProvider.default) {
self.downloadsProvider = downloadsProvider
super.init(storage: PassiveCourseListPersistenceStorage(cachedList: []))
}

override func fetch() -> Promise<[Course]> {
self.downloadsProvider.fetchCachedCourses().then { courses -> Promise<[Course]> in
let resultCourses = courses.filter(\.enrolled).sorted { $0.id < $1.id }
return .value(resultCourses)
}
}

override func update(newCachedList: [Course]) {}
}
14 changes: 12 additions & 2 deletions Stepic/Sources/Modules/CourseList/Provider/CourseListTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ struct FavoriteCourseListType: CourseListType {
var analyticName: String { "favorite_course_list" }
}

struct DownloadedCourseListType: CourseListType {
var analyticName: String { "downloaded_course_list" }
}

struct ArchivedCourseListType: CourseListType {
var analyticName: String { "archived_course_list" }
}
Expand Down Expand Up @@ -123,6 +127,8 @@ final class CourseListServicesFactory {
cacheID: "MyCoursesInfoFavorite"
)
)
} else if self.type is DownloadedCourseListType {
return DownloadedCourseListPersistenceService()
} else if self.type is ArchivedCourseListType {
return CourseListPersistenceService(
storage: DefaultsCourseListPersistenceStorage(
Expand Down Expand Up @@ -224,12 +230,16 @@ final class CourseListServicesFactory {
courseRecommendationsAPI: self.courseRecommendationsAPI
)
)
} else if let type = self.type as? WishlistCourseListType {
} else if type is WishlistCourseListType {
return WishlistCourseListNetworkService(
type: type,
coursesAPI: self.coursesAPI,
wishlistStorageManager: WishlistStorageManager()
)
} else if type is DownloadedCourseListType {
return DownloadedCourseListNetworkService(
coursesAPI: self.coursesAPI,
downloadedCourseListPersistenceService: DownloadedCourseListPersistenceService()
)
} else {
fatalError("Unsupported course list type")
}
Expand Down
15 changes: 15 additions & 0 deletions Stepic/Sources/Modules/Downloads/DownloadsProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,18 @@ final class DownloadsProvider: DownloadsProviderProtocol {
return resultSteps
}
}

extension DownloadsProvider {
static var `default`: DownloadsProvider {
DownloadsProvider(
coursesPersistenceService: CoursesPersistenceService(),
adaptiveStorageManager: AdaptiveStorageManager.shared,
videoFileManager: VideoStoredFileManager(fileManager: .default),
imageFileManager: ImageStoredFileManager(fileManager: .default),
storageUsageService: StorageUsageService(
videoFileManager: VideoStoredFileManager(fileManager: .default),
imageFileManager: ImageStoredFileManager(fileManager: .default)
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ final class UserCoursesAssembly: Assembly {
let viewController = UserCoursesViewController(
interactor: interactor,
availableTabs: UserCourses.Tab.allCases,
initialTab: .allCourses
initialTab: .allCourses,
analytics: StepikAnalytics.shared
)

presenter.viewController = viewController
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import Foundation

enum UserCourses {
enum Tab: CaseIterable {
case allCourses
enum Tab: String, CaseIterable {
case allCourses = "all"
case favorites
case archived
case downloaded
case archived = "archive"

var title: String {
switch self {
case .allCourses:
return NSLocalizedString("UserCoursesTabAllCoursesTitle", comment: "")
case .favorites:
return NSLocalizedString("UserCoursesTabFavoritesTitle", comment: "")
case .downloaded:
return NSLocalizedString("UserCoursesTabDownloadedTitle", comment: "")
case .archived:
return NSLocalizedString("UserCoursesTabArchivedTitle", comment: "")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ final class UserCoursesViewController: TabmanViewController {
static let barTintColor = UIColor.stepikAccent
static let barBackgroundColor = UIColor.stepikNavigationBarBackground
static let barSeparatorColor = UIColor.stepikOpaqueSeparator
static let barInterButtonSpacing: CGFloat = 16
static let barContentInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)

static var navigationBarAppearance: StyledNavigationController.NavigationBarAppearanceState {
.init(shadowViewAlpha: 0.0)
Expand All @@ -27,8 +29,10 @@ final class UserCoursesViewController: TabmanViewController {
bar.backgroundView.style = .flat(color: Appearance.barBackgroundColor)
bar.indicator.tintColor = Appearance.barTintColor
bar.indicator.weight = .light
bar.layout.interButtonSpacing = 0
bar.layout.contentMode = .fit
bar.layout.interButtonSpacing = Appearance.barInterButtonSpacing
bar.layout.contentInset = Appearance.barContentInset
bar.layout.alignment = .leading
bar.layout.contentMode = .intrinsic

let separatorView = UIView()
separatorView.backgroundColor = Appearance.barSeparatorColor
Expand All @@ -48,10 +52,13 @@ final class UserCoursesViewController: TabmanViewController {
private let initialTabIndex: Int
private var tabViewControllers: [UIViewController?] = []

private let analytics: Analytics

init(
interactor: UserCoursesInteractorProtocol,
availableTabs: [UserCourses.Tab],
initialTab: UserCourses.Tab
initialTab: UserCourses.Tab,
analytics: Analytics
) {
self.interactor = interactor

Expand All @@ -64,6 +71,8 @@ final class UserCoursesViewController: TabmanViewController {
self.initialTabIndex = 0
}

self.analytics = analytics

super.init(nibName: nil, bundle: nil)
}

Expand Down Expand Up @@ -93,6 +102,26 @@ final class UserCoursesViewController: TabmanViewController {
sender: self
)
}

self.analytics.send(.myCoursesScreenOpened)
}

override func pageboyViewController(
_ pageboyViewController: PageboyViewController,
didScrollToPageAt index: TabmanViewController.PageIndex,
direction: PageboyViewController.NavigationDirection,
animated: Bool
) {
super.pageboyViewController(
pageboyViewController,
didScrollToPageAt: index,
direction: direction,
animated: animated
)

if let selectedTab = self.availableTabs[safe: index] {
self.analytics.send(.myCoursesScreenTabOpened(tab: selectedTab))
}
}

// MARK: Private API
Expand Down Expand Up @@ -135,6 +164,8 @@ private extension UserCourses.Tab {
return EnrolledCourseListType()
case .favorites:
return FavoriteCourseListType()
case .downloaded:
return DownloadedCourseListType()
case .archived:
return ArchivedCourseListType()
}
Expand Down
1 change: 1 addition & 0 deletions Stepic/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ CourseBenefitDetailAmountTitle = "Your earnings";
UserCoursesTitle = "My Courses";
UserCoursesTabAllCoursesTitle = "All";
UserCoursesTabFavoritesTitle = "Favorite";
UserCoursesTabDownloadedTitle = "Downloaded";
UserCoursesTabArchivedTitle = "Archive";

/* User Courses Reviews */
Expand Down
1 change: 1 addition & 0 deletions Stepic/ru.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ CourseBenefitDetailAmountTitle = "Ваш доход";
UserCoursesTitle = "Мои курсы";
UserCoursesTabAllCoursesTitle = "Все";
UserCoursesTabFavoritesTitle = "Избранные";
UserCoursesTabDownloadedTitle = "Загруженные";
UserCoursesTabArchivedTitle = "Архив";

/* User Courses Reviews */
Expand Down