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

Sequence Units level to Course home view as nested list #192

Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a6e019f
feat: add expand section in detail course
eyatsenkoperpetio Nov 9, 2023
b010fd0
refactor: add click animation and gray arrow
eyatsenkoperpetio Nov 9, 2023
f1093dd
refactor: add new paddings
eyatsenkoperpetio Nov 9, 2023
bbcb5c4
fix: new ids for expand sections
eyatsenkoperpetio Nov 10, 2023
5e3b5a0
chore: add counter and remove banner
eyatsenkoperpetio Nov 20, 2023
da65af1
fix: top offset
eyatsenkoperpetio Nov 21, 2023
01a9ade
Merge branch 'develop' into feat/expandable_sections_detail_course
eyatsenkoperpetio Nov 23, 2023
fb4e218
Merge branch 'develop' into feat/expandable_sections_detail_course
eyatsenkoperpetio Nov 23, 2023
464b2a3
chore: add course expandable sections enabled and course banner ena…
eyatsenkoperpetio Dec 7, 2023
ad11d14
Merge branch 'develop' into feat/expandable_sections_detail_course
eyatsenkoperpetio Dec 7, 2023
74241f8
fix: tests
eyatsenkoperpetio Dec 7, 2023
4bfc46d
Merge branch 'develop' into feat/expandable_sections_detail_course
eyatsenkoperpetio Dec 8, 2023
667cf78
chore: move flags
eyatsenkoperpetio Dec 8, 2023
84bb1d0
chore: add new flag course nested list
eyatsenkoperpetio Dec 8, 2023
7bc3ed8
fix: animation
eyatsenkoperpetio Dec 8, 2023
120a46a
chore: remove extra code
eyatsenkoperpetio Dec 11, 2023
c896ede
Merge branch 'develop' into feat/expandable_sections_detail_course
eyatsenkoperpetio Dec 11, 2023
bc6dcd1
chore: change key name and merge with develop
eyatsenkoperpetio Dec 11, 2023
3ed826a
chore: add arrow to header section
eyatsenkoperpetio Dec 11, 2023
7a8f9c6
chore: add flags
eyatsenkoperpetio Dec 12, 2023
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
4 changes: 4 additions & 0 deletions Core/Core.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
0770DE5F28D0B22C006D8A5D /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0770DE5E28D0B22C006D8A5D /* Strings.swift */; };
0770DE6128D0B2CB006D8A5D /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0770DE6028D0B2CB006D8A5D /* Assets.swift */; };
07DDFCBD29A780BB00572595 /* UINavigationController+Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07DDFCBC29A780BB00572595 /* UINavigationController+Animation.swift */; };
BAAD62C62AFCF00B000E6103 /* CustomDisclosureGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAAD62C52AFCF00B000E6103 /* CustomDisclosureGroup.swift */; };
BA30427F2B20B320009B64B7 /* SocialAuthError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA30427D2B20B299009B64B7 /* SocialAuthError.swift */; };
BA76135C2B21BC7300B599B7 /* SocialAuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA76135B2B21BC7300B599B7 /* SocialAuthResponse.swift */; };
BA8B3A2F2AD546A700D25EF5 /* DebugLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA8B3A2E2AD546A700D25EF5 /* DebugLog.swift */; };
Expand Down Expand Up @@ -288,6 +289,7 @@
3B74C6685E416657F3C5F5A8 /* Pods-App-Core.releaseprod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.releaseprod.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.releaseprod.xcconfig"; sourceTree = "<group>"; };
60153262DBC2F9E660D7E11B /* Pods-App-Core.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.release.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.release.xcconfig"; sourceTree = "<group>"; };
9D5B06CAA99EA5CD49CBE2BB /* Pods-App-Core.debugdev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.debugdev.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.debugdev.xcconfig"; sourceTree = "<group>"; };
BAAD62C52AFCF00B000E6103 /* CustomDisclosureGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDisclosureGroup.swift; sourceTree = "<group>"; };
BA30427D2B20B299009B64B7 /* SocialAuthError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocialAuthError.swift; sourceTree = "<group>"; };
BA76135B2B21BC7300B599B7 /* SocialAuthResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialAuthResponse.swift; sourceTree = "<group>"; };
BA8B3A2E2AD546A700D25EF5 /* DebugLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugLog.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -628,6 +630,7 @@
027BD3C42909707700392132 /* Shake.swift */,
023A1135291432B200D0D354 /* RegistrationTextField.swift */,
023A1137291432FD00D0D354 /* FieldConfiguration.swift */,
BAAD62C52AFCF00B000E6103 /* CustomDisclosureGroup.swift */,
BA8FA6672AD59A5700EA029A /* SocialAuthButton.swift */,
02E93F862AEBAED4006C4750 /* AppReview */,
);
Expand Down Expand Up @@ -1015,6 +1018,7 @@
DBF6F24A2B0380E00098414B /* FeaturesConfig.swift in Sources */,
02F164372902A9EB0090DDEF /* StringExtension.swift in Sources */,
0231CDBE2922422D00032416 /* CSSInjector.swift in Sources */,
BAAD62C62AFCF00B000E6103 /* CustomDisclosureGroup.swift in Sources */,
BADB3F5B2AD6EC56004D5CFA /* ResultExtension.swift in Sources */,
0236961928F9A26900EEF206 /* AuthRepository.swift in Sources */,
023A1136291432B200D0D354 /* RegistrationTextField.swift in Sources */,
Expand Down
11 changes: 7 additions & 4 deletions Core/Core/Configuration/Config/UIComponentsConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@
import Foundation

private enum Keys: String {
case isVerticalsMenuEnabled = "VERTICALS_MENU_ENABLED"
case courseNestedListEnabled = "COURSE_NESTED_LIST_ENABLED"
case courseBannerEnabled = "COURSE_BANNER_ENABLED"
}

public class UIComponentsConfig: NSObject {
public var isVerticalsMenuEnabled: Bool = false

public var courseNestedListEnabled: Bool = false
public var courseBannerEnabled: Bool

init(dictionary: [String: Any]) {
isVerticalsMenuEnabled = dictionary[Keys.isVerticalsMenuEnabled.rawValue] as? Bool ?? false
courseNestedListEnabled = dictionary[Keys.courseNestedListEnabled.rawValue] as? Bool ?? false
courseBannerEnabled = dictionary[Keys.courseBannerEnabled.rawValue] as? Bool ?? false
super.init()
}
}
Expand Down
8 changes: 4 additions & 4 deletions Core/Core/Domain/Model/CourseBlockModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ public struct CourseStructure: Equatable {

}

public struct CourseChapter {
public struct CourseChapter: Identifiable {

public let blockId: String
public let id: String
public let displayName: String
Expand All @@ -72,8 +72,8 @@ public struct CourseChapter {
}
}

public struct CourseSequential {
public struct CourseSequential: Identifiable {

public let blockId: String
public let id: String
public let displayName: String
Expand Down
2 changes: 2 additions & 0 deletions Core/Core/SwiftGen/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public enum CoreLocalization {
public static let nextSectionDescriptionLast = CoreLocalization.tr("Localizable", "COURSEWARE.NEXT_SECTION_DESCRIPTION_LAST", fallback: "” press “Next section”.")
/// Prev
public static let previous = CoreLocalization.tr("Localizable", "COURSEWARE.PREVIOUS", fallback: "Prev")
/// Resume
public static let resume = CoreLocalization.tr("Localizable", "COURSEWARE.RESUME", fallback: "Resume")
/// Section “
public static let section = CoreLocalization.tr("Localizable", "COURSEWARE.SECTION", fallback: "Section “")
}
Expand Down
49 changes: 49 additions & 0 deletions Core/Core/View/Base/CustomDisclosureGroup.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// CustomDisclosureGroup.swift
// Core
//
// Created by Eugene Yatsenko on 09.11.2023.
//

import SwiftUI

public struct CustomDisclosureGroup<Header: View, Content: View>: View {

@Binding var isExpanded: Bool

private var onClick: () -> Void
private var animation: Animation?
private let header: Header
private let content: Content

public init(
animation: Animation?,
isExpanded: Binding<Bool>,
onClick: @escaping () -> Void,
header: (_ isExpanded: Bool) -> Header,
content: () -> Content
) {
self.onClick = onClick
self._isExpanded = isExpanded
self.animation = animation
self.header = header(isExpanded.wrappedValue)
self.content = content()
}

public var body: some View {
VStack(spacing: 0) {
Button {
withAnimation(animation) {
onClick()
}
} label: {
header
.contentShape(Rectangle())
}
if isExpanded {
content
}
}
.clipped()
}
}
2 changes: 1 addition & 1 deletion Core/Core/View/Base/UnitButtonView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public enum UnitButtonType: Equatable {
case .reload:
return CoreLocalization.Error.reload
case .continueLesson:
return CoreLocalization.Courseware.continue
return CoreLocalization.Courseware.resume
case .nextSection:
return CoreLocalization.Courseware.nextSection
case let .custom(text):
Expand Down
1 change: 1 addition & 0 deletions Core/Core/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"COURSEWARE.SECTION" = "Section “";
"COURSEWARE.IS_FINISHED" = "“ is finished.";
"COURSEWARE.CONTINUE" = "Continue";
"COURSEWARE.RESUME" = "Resume";
"COURSEWARE.CONTINUE_WITH" = "Continue with:";
"COURSEWARE.NEXT_SECTION" = "Next section";

Expand Down
1 change: 1 addition & 0 deletions Core/Core/uk.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"COURSEWARE.SECTION" = "Секція “";
"COURSEWARE.IS_FINISHED" = "“ завершена.";
"COURSEWARE.CONTINUE" = "Продовжити";
"COURSEWARE.RESUME" = "Resume";
"COURSEWARE.CONTINUE_WITH" = "Продовжити далі:";
"COURSEWARE.NEXT_SECTION" = "Наступний розділ";

Expand Down
4 changes: 4 additions & 0 deletions Course/Course.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
0766DFD0299AB29000EBEF6A /* PlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0766DFCF299AB29000EBEF6A /* PlayerViewController.swift */; };
197FB8EA8F92F00A8F383D82 /* Pods_App_Course.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E795BD160CDA7D9C120DE6 /* Pods_App_Course.framework */; };
B8F50317B6B830A0E520C954 /* Pods_App_Course_CourseTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50E59D2B81E12610964282C5 /* Pods_App_Course_CourseTests.framework */; };
BAAD62C82AFD00EE000E6103 /* CourseExpandableContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAAD62C72AFD00EE000E6103 /* CourseExpandableContentView.swift */; };
DB205BFB2AE81B1200136EC2 /* CourseDateViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB205BFA2AE81B1200136EC2 /* CourseDateViewModelTests.swift */; };
DB7D6EAC2ADFCAC50036BB13 /* CourseDatesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7D6EAB2ADFCAC40036BB13 /* CourseDatesView.swift */; };
DB7D6EAE2ADFCB4A0036BB13 /* CourseDatesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7D6EAD2ADFCB4A0036BB13 /* CourseDatesViewModel.swift */; };
Expand Down Expand Up @@ -167,6 +168,7 @@
A47C63D9EB0D866F303D4588 /* Pods-App-Course.releasestage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Course.releasestage.xcconfig"; path = "Target Support Files/Pods-App-Course/Pods-App-Course.releasestage.xcconfig"; sourceTree = "<group>"; };
ADC2A1B8183A674705F5F7E2 /* Pods-App-Course.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Course.debug.xcconfig"; path = "Target Support Files/Pods-App-Course/Pods-App-Course.debug.xcconfig"; sourceTree = "<group>"; };
B196A14555D0E006995A5683 /* Pods-App-CourseDetails.releaseprod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-CourseDetails.releaseprod.xcconfig"; path = "Target Support Files/Pods-App-CourseDetails/Pods-App-CourseDetails.releaseprod.xcconfig"; sourceTree = "<group>"; };
BAAD62C72AFD00EE000E6103 /* CourseExpandableContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseExpandableContentView.swift; sourceTree = "<group>"; };
DB205BFA2AE81B1200136EC2 /* CourseDateViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDateViewModelTests.swift; sourceTree = "<group>"; };
DB7D6EAB2ADFCAC40036BB13 /* CourseDatesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseDatesView.swift; sourceTree = "<group>"; };
DB7D6EAD2ADFCB4A0036BB13 /* CourseDatesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDatesViewModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -395,6 +397,7 @@
0270210128E736E700F54332 /* CourseOutlineView.swift */,
02A8076729474831007F53AB /* CourseVerticalView.swift */,
0248C92629C097EB00DC8402 /* CourseVerticalViewModel.swift */,
BAAD62C72AFD00EE000E6103 /* CourseExpandableContentView.swift */,
06FD7EDE2B1F29F3008D632B /* CourseVerticalImageView.swift */,
);
path = Outline;
Expand Down Expand Up @@ -753,6 +756,7 @@
073512E229C0E400005CFA41 /* BaseCourseViewModel.swift in Sources */,
0231124F28EDA811002588FB /* CourseUnitViewModel.swift in Sources */,
02F0144F28F46474002E513D /* CourseContainerView.swift in Sources */,
BAAD62C82AFD00EE000E6103 /* CourseExpandableContentView.swift in Sources */,
02A8076829474831007F53AB /* CourseVerticalView.swift in Sources */,
0231124D28EDA804002588FB /* CourseUnitView.swift in Sources */,
027020FC28E7362100F54332 /* Data_CourseOutlineResponse.swift in Sources */,
Expand Down
75 changes: 56 additions & 19 deletions Course/Course/Presentation/Container/CourseContainerViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public class CourseContainerViewModel: BaseCourseViewModel {
@Published var courseVideosStructure: CourseStructure?
@Published private(set) var isShowProgress = false
@Published var showError: Bool = false
@Published var downloadState: [String: DownloadViewState] = [:]
@Published var sequentialsDownloadState: [String: DownloadViewState] = [:]
@Published var verticalsDownloadState: [String: DownloadViewState] = [:]
@Published var continueWith: ContinueWith?

var errorMessage: String? {
Expand Down Expand Up @@ -129,7 +130,16 @@ public class CourseContainerViewModel: BaseCourseViewModel {
courseStructure: courseStructure
)
}


func verticalsBlocksDownloadable(by courseSequential: CourseSequential) -> [String: DownloadViewState] {
verticalsDownloadState.filter { dict in
courseSequential.childs.contains(where: { item in
let state = verticalsDownloadState[dict.key]
return (state == .available || state == .downloading) && dict.key == item.id
})
}
}

func onDownloadViewTap(chapter: CourseChapter, blockId: String, state: DownloadViewState) {
let blocks = chapter.childs
.first(where: { $0.id == blockId })?.childs
Expand All @@ -140,21 +150,21 @@ public class CourseContainerViewModel: BaseCourseViewModel {
switch state {
case .available:
try manager.addToDownloadQueue(blocks: blocks)
downloadState[blockId] = .downloading
sequentialsDownloadState[blockId] = .downloading
case .downloading:
try manager.cancelDownloading(courseId: courseStructure?.id ?? "", blocks: blocks)
downloadState[blockId] = .available
sequentialsDownloadState[blockId] = .available
case .finished:
manager.deleteFile(blocks: blocks)
downloadState[blockId] = .available
sequentialsDownloadState[blockId] = .available
}
} catch let error {
if error is NoWiFiError {
errorMessage = CoreLocalization.Error.wifi
}
}
}

func trackSelectedTab(
selection: CourseContainerView.CourseTab,
courseId: String,
Expand All @@ -173,7 +183,20 @@ public class CourseContainerViewModel: BaseCourseViewModel {
analytics.courseOutlineHandoutsTabClicked(courseId: courseId, courseName: courseName)
}
}


func trackVerticalClicked(
courseId: String,
courseName: String,
vertical: CourseVertical
) {
analytics.verticalClicked(
courseId: courseId,
courseName: courseName,
blockId: vertical.blockId,
blockName: vertical.displayName
)
}

func trackSequentialClicked(_ sequential: CourseSequential) {
guard let course = courseStructure else { return }
analytics.sequentialClicked(
Expand All @@ -197,35 +220,49 @@ public class CourseContainerViewModel: BaseCourseViewModel {
private func setDownloadsStates() {
guard let course = courseStructure else { return }
let downloads = manager.getDownloadsForCourse(course.id)
var states: [String: DownloadViewState] = [:]
var sequentialsStates: [String: DownloadViewState] = [:]
var verticalsStates: [String: DownloadViewState] = [:]
for chapter in course.childs {
for sequential in chapter.childs where sequential.isDownloadable {
var childs: [DownloadViewState] = []
var sequentialsChilds: [DownloadViewState] = []
for vertical in sequential.childs where vertical.isDownloadable {
var verticalsChilds: [DownloadViewState] = []
for block in vertical.childs where block.isDownloadable {
if let download = downloads.first(where: { $0.id == block.id }) {
switch download.state {
case .waiting, .inProgress:
childs.append(.downloading)
sequentialsChilds.append(.downloading)
verticalsChilds.append(.downloading)
case .paused:
childs.append(.available)
sequentialsChilds.append(.available)
verticalsChilds.append(.available)
case .finished:
childs.append(.finished)
sequentialsChilds.append(.finished)
verticalsChilds.append(.finished)
}
} else {
childs.append(.available)
sequentialsChilds.append(.available)
verticalsChilds.append(.available)
}
}
if verticalsChilds.first(where: { $0 == .downloading }) != nil {
verticalsStates[vertical.id] = .downloading
} else if verticalsChilds.allSatisfy({ $0 == .finished }) {
verticalsStates[vertical.id] = .finished
} else {
verticalsStates[vertical.id] = .available
}
}
if childs.first(where: { $0 == .downloading }) != nil {
states[sequential.id] = .downloading
} else if childs.allSatisfy({ $0 == .finished }) {
states[sequential.id] = .finished
if sequentialsChilds.first(where: { $0 == .downloading }) != nil {
sequentialsStates[sequential.id] = .downloading
} else if sequentialsChilds.allSatisfy({ $0 == .finished }) {
sequentialsStates[sequential.id] = .finished
} else {
states[sequential.id] = .available
sequentialsStates[sequential.id] = .available
}
}
self.downloadState = states
self.sequentialsDownloadState = sequentialsStates
self.verticalsDownloadState = verticalsStates
}
}

Expand Down
Loading