From f4b2f2ea389f1c6418b58c28b6b9a1224daac730 Mon Sep 17 00:00:00 2001 From: forgotvas Date: Mon, 4 Dec 2023 17:40:49 +0300 Subject: [PATCH 01/11] feat: added unit popup menu --- Course/Course.xcodeproj/project.pbxproj | 24 +++ .../Presentation/Unit/CourseUnitView.swift | 78 ++++++--- .../Unit/CourseUnitViewModel.swift | 17 ++ .../DropdownList/CourseUnitDropDownCell.swift | 112 +++++++++++++ .../DropdownList/CourseUnitDropDownList.swift | 150 ++++++++++++++++++ .../CourseUnitDropDownTitle.swift | 46 ++++++ .../CourseUnitVerticalsDropdownView.swift | 44 +++++ 7 files changed, 448 insertions(+), 23 deletions(-) create mode 100644 Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift create mode 100644 Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift create mode 100644 Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownTitle.swift create mode 100644 Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitVerticalsDropdownView.swift diff --git a/Course/Course.xcodeproj/project.pbxproj b/Course/Course.xcodeproj/project.pbxproj index 16f57b0fb..4d23f958b 100644 --- a/Course/Course.xcodeproj/project.pbxproj +++ b/Course/Course.xcodeproj/project.pbxproj @@ -58,6 +58,10 @@ 02F78AEB29E6BCA20038DE30 /* VideoPlayerViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02F78AEA29E6BCA20038DE30 /* VideoPlayerViewModelTests.swift */; }; 02F98A8128F8224200DE94C0 /* Discussion.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02F98A8028F8224200DE94C0 /* Discussion.framework */; }; 02FFAD0D29E4347300140E46 /* VideoPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FFAD0C29E4347300140E46 /* VideoPlayerViewModel.swift */; }; + 068DDA5F2B1E198700FF8CCB /* CourseUnitDropDownList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068DDA5B2B1E198700FF8CCB /* CourseUnitDropDownList.swift */; }; + 068DDA602B1E198700FF8CCB /* CourseUnitVerticalsDropdownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068DDA5C2B1E198700FF8CCB /* CourseUnitVerticalsDropdownView.swift */; }; + 068DDA612B1E198700FF8CCB /* CourseUnitDropDownCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068DDA5D2B1E198700FF8CCB /* CourseUnitDropDownCell.swift */; }; + 068DDA622B1E198700FF8CCB /* CourseUnitDropDownTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068DDA5E2B1E198700FF8CCB /* CourseUnitDropDownTitle.swift */; }; 073512E229C0E400005CFA41 /* BaseCourseViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 073512E129C0E400005CFA41 /* BaseCourseViewModel.swift */; }; 0766DFCC299AA7A600EBEF6A /* YouTubeVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0766DFCB299AA7A600EBEF6A /* YouTubeVideoPlayer.swift */; }; 0766DFCE299AB26D00EBEF6A /* EncodedVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0766DFCD299AB26D00EBEF6A /* EncodedVideoPlayer.swift */; }; @@ -135,6 +139,10 @@ 02F78AEA29E6BCA20038DE30 /* VideoPlayerViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = VideoPlayerViewModelTests.swift; path = CourseTests/Presentation/Unit/VideoPlayerViewModelTests.swift; sourceTree = SOURCE_ROOT; }; 02F98A8028F8224200DE94C0 /* Discussion.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Discussion.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 02FFAD0C29E4347300140E46 /* VideoPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewModel.swift; sourceTree = ""; }; + 068DDA5B2B1E198700FF8CCB /* CourseUnitDropDownList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseUnitDropDownList.swift; sourceTree = ""; }; + 068DDA5C2B1E198700FF8CCB /* CourseUnitVerticalsDropdownView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseUnitVerticalsDropdownView.swift; sourceTree = ""; }; + 068DDA5D2B1E198700FF8CCB /* CourseUnitDropDownCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseUnitDropDownCell.swift; sourceTree = ""; }; + 068DDA5E2B1E198700FF8CCB /* CourseUnitDropDownTitle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseUnitDropDownTitle.swift; sourceTree = ""; }; 073512E129C0E400005CFA41 /* BaseCourseViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCourseViewModel.swift; sourceTree = ""; }; 0766DFCB299AA7A600EBEF6A /* YouTubeVideoPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YouTubeVideoPlayer.swift; sourceTree = ""; }; 0766DFCD299AB26D00EBEF6A /* EncodedVideoPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodedVideoPlayer.swift; sourceTree = ""; }; @@ -214,6 +222,7 @@ 02454C9E2A2618D40043052A /* Subviews */ = { isa = PBXGroup; children = ( + 068DDA5A2B1E198700FF8CCB /* DropdownList */, 02454C9F2A2618E70043052A /* YouTubeView.swift */, 02454CA12A26190A0043052A /* EncodedVideoView.swift */, 02454CA32A26193F0043052A /* WebView.swift */, @@ -343,6 +352,17 @@ path = Presentation; sourceTree = ""; }; + 068DDA5A2B1E198700FF8CCB /* DropdownList */ = { + isa = PBXGroup; + children = ( + 068DDA5B2B1E198700FF8CCB /* CourseUnitDropDownList.swift */, + 068DDA5C2B1E198700FF8CCB /* CourseUnitVerticalsDropdownView.swift */, + 068DDA5D2B1E198700FF8CCB /* CourseUnitDropDownCell.swift */, + 068DDA5E2B1E198700FF8CCB /* CourseUnitDropDownTitle.swift */, + ); + path = DropdownList; + sourceTree = ""; + }; 070019A328F6EFC100D5FC78 /* Model */ = { isa = PBXGroup; children = ( @@ -701,11 +721,14 @@ 02454CA42A26193F0043052A /* WebView.swift in Sources */, 022C64DA29ACEC50000F532B /* HandoutsViewModel.swift in Sources */, 02635AC72A24F181008062F2 /* ContinueWithView.swift in Sources */, + 068DDA622B1E198700FF8CCB /* CourseUnitDropDownTitle.swift in Sources */, 022C64DE29AD167A000F532B /* HandoutsUpdatesDetailView.swift in Sources */, 0270210328E736E700F54332 /* CourseOutlineView.swift in Sources */, + 068DDA602B1E198700FF8CCB /* CourseUnitVerticalsDropdownView.swift in Sources */, 022C64E029ADEA9B000F532B /* Data_UpdatesResponse.swift in Sources */, 02454CA02A2618E70043052A /* YouTubeView.swift in Sources */, 02454CA22A26190A0043052A /* EncodedVideoView.swift in Sources */, + 068DDA612B1E198700FF8CCB /* CourseUnitDropDownCell.swift in Sources */, 02B6B3BC28E1D14F00232911 /* CourseRepository.swift in Sources */, 02280F60294B50030032823A /* CoursePersistenceProtocol.swift in Sources */, 02454CAA2A2619B40043052A /* LessonProgressView.swift in Sources */, @@ -715,6 +738,7 @@ 0276D75B29DDA3890004CDF8 /* Data_ResumeBlock.swift in Sources */, 0276D75D29DDA3F80004CDF8 /* ResumeBlock.swift in Sources */, 02F3BFDD29252E900051930C /* CourseRouter.swift in Sources */, + 068DDA5F2B1E198700FF8CCB /* CourseUnitDropDownList.swift in Sources */, 022F8E182A1E2642008EFAB9 /* EncodedVideoPlayerViewModel.swift in Sources */, 0248C92729C097EB00DC8402 /* CourseVerticalViewModel.swift in Sources */, 02F0145728F4A2FF002E513D /* CourseContainerViewModel.swift in Sources */, diff --git a/Course/Course/Presentation/Unit/CourseUnitView.swift b/Course/Course/Presentation/Unit/CourseUnitView.swift index f17fc926b..8a37812c9 100644 --- a/Course/Course/Presentation/Unit/CourseUnitView.swift +++ b/Course/Course/Presentation/Unit/CourseUnitView.swift @@ -26,10 +26,13 @@ public struct CourseUnitView: View { } @State var offsetView: CGFloat = 0 @State var showDiscussion: Bool = false - @Environment(\.presentationMode) private var presentationMode + @State var showDropdown: Bool = false + @Environment(\.isPresented) private var isPresented @Environment(\.isHorizontal) private var isHorizontal private let sectionName: String public let playerStateSubject = CurrentValueSubject(nil) + private let portraitTopSpacing: CGFloat = 60 + private let landscapeTopSpacing: CGFloat = 75 public init(viewModel: CourseUnitViewModel, sectionName: String) { @@ -39,6 +42,23 @@ public struct CourseUnitView: View { viewModel.nextTitles() } + var sequenceTitle: String { + let chapter = viewModel.chapters[viewModel.chapterIndex] + let sequence = chapter.childs[viewModel.sequentialIndex] + return sequence.displayName + } + + var unitTitle: String { + let chapter = viewModel.chapters[viewModel.chapterIndex] + let sequence = chapter.childs[viewModel.sequentialIndex] + let unit = sequence.childs[viewModel.verticalIndex] + return unit.displayName + } + + var isDropdownAvailable: Bool { + viewModel.verticals.count > 1 + } + public var body: some View { ZStack(alignment: .top) { // MARK: - Page Body @@ -51,6 +71,20 @@ public struct CourseUnitView: View { let data = Array(viewModel.verticals[viewModel.verticalIndex].childs.enumerated()) ForEach(data, id: \.offset) { index, block in VStack(spacing: 0) { + HStack { + let block = viewModel.verticals[viewModel.verticalIndex] + .childs[viewModel.index] + if block.type == .video { + let title = block.displayName + Text(title) + .lineLimit(1) + .font(Theme.Fonts.titleLarge) + .foregroundStyle(Theme.Colors.textPrimary) + .padding(.vertical, 10) + .padding(.horizontal, 20) + Spacer() + } + } switch LessonType.from(block) { // MARK: YouTube case let .youtube(url, blockID): @@ -233,34 +267,21 @@ public struct CourseUnitView: View { // MARK: - Course Navigation VStack { ZStack { - GeometryReader { reader in - VStack { - HStack { - let currentBlock = viewModel.verticals[viewModel.verticalIndex] - .childs[viewModel.index] - if currentBlock.type == .video { - let title = currentBlock.displayName - Text(title) - .lineLimit(1) - .font(Theme.Fonts.titleLarge) - .foregroundStyle(Theme.Colors.textPrimary) - .padding(.leading, isHorizontal ? 30 : 42) - .padding(.top, isHorizontal ? 14 : 2) - Spacer() - } - }.frame(maxWidth: isHorizontal ? reader.size.width * 0.5 : nil) - Spacer() - } - } VStack { NavigationBar( - title: "", + title: sequenceTitle, leftButtonAction: { viewModel.router.back() playerStateSubject.send(VideoPlayerState.kill) }).padding(.top, isHorizontal ? 10 : 0) .padding(.leading, isHorizontal ? -16 : 0) - Spacer() + CourseUnitDropDownTitle( + title: unitTitle, + isAvailable: isDropdownAvailable, + showDropdown: $showDropdown) + .padding(.top, 0) + .offset(y: -20) + Spacer() } HStack(alignment: .center) { if isHorizontal { @@ -287,10 +308,20 @@ public struct CourseUnitView: View { }.frame(maxWidth: .infinity) } .onDisappear { - if !presentationMode.wrappedValue.isPresented { + if !isPresented { playerStateSubject.send(VideoPlayerState.kill) } } + if showDropdown { + CourseUnitVerticalsDropdownView( + verticals: viewModel.verticals, + currentIndex: viewModel.verticalIndex, + offsetY: isHorizontal ? landscapeTopSpacing : portraitTopSpacing, + showDropdown: $showDropdown + ) { [weak viewModel] vertical in + viewModel?.route(to: vertical) + } + } } .ignoresSafeArea(.all, edges: .bottom) .onRightSwipeGesture { @@ -309,6 +340,7 @@ public struct CourseUnitView: View { Theme.Colors.background .ignoresSafeArea() ) + .animation(.easeOut(duration: 0.2), value: showDropdown) } } diff --git a/Course/Course/Presentation/Unit/CourseUnitViewModel.swift b/Course/Course/Presentation/Unit/CourseUnitViewModel.swift index e48e3ab32..ca1f260db 100644 --- a/Course/Course/Presentation/Unit/CourseUnitViewModel.swift +++ b/Course/Course/Presentation/Unit/CourseUnitViewModel.swift @@ -180,4 +180,21 @@ public class CourseUnitViewModel: ObservableObject { func trackFinishVerticalBackToOutlineClicked() { analytics.finishVerticalBackToOutlineClicked(courseId: courseID, courseName: courseName) } + + func route(to vertical: CourseVertical) { + if let index = verticals.firstIndex(where: {$0.id == vertical.id}), + let block = vertical.childs.first { + router.replaceCourseUnit( + courseName: courseName, + blockId: block.id, + courseID: courseID, + sectionName: block.displayName, + verticalIndex: index, + chapters: chapters, + chapterIndex: chapterIndex, + sequentialIndex: sequentialIndex, + animated: false + ) + } + } } diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift new file mode 100644 index 000000000..1e86dd53a --- /dev/null +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift @@ -0,0 +1,112 @@ +// +// CourseUnitDropDownCell.swift +// Course +// +// Created by Vadim Kuznetsov on 4.12.23. +// + +import Core +import SwiftUI + +struct CourseUnitDropDownCell: View { + var vertical: CourseVertical + var isLast: Bool = false + var isSelected: Bool = false + var action: () -> Void + private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom } + + var body: some View { + VStack(spacing: 0) { + Button(action: { + action() + }, label: { + HStack { + Group { + VStack { + if vertical.completion == 1 { + CoreAssets.finished.swiftUIImage + .renderingMode(.template) + .foregroundColor(.accentColor) + } + } + .frame(width: 25) + Text(vertical.displayName) + .font(Theme.Fonts.titleSmall) + .lineLimit(1) + .frame(alignment: .leading) + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity, alignment: .leading) + verticalImage(childs: vertical.childs) + + } + .foregroundColor(Theme.Colors.textPrimary) + } + }) + .padding(.horizontal, 20) + .padding(.vertical, 5) + if !isLast { + Divider() + .frame(height: 1) + .overlay(Theme.Colors.cardViewStroke) + .padding(.horizontal, 20) + .padding(.vertical, 0) + } + } + .padding(0) + .background( + isSelected ? Color.secondary.opacity(0.2) : Color.clear + ) + + } + + private func verticalImage(childs: [CourseBlock]) -> Image { + if childs.contains(where: { $0.type == .problem }) { + return CoreAssets.pen.swiftUIImage.renderingMode(.template) + } else if childs.contains(where: { $0.type == .video }) { + return CoreAssets.video.swiftUIImage.renderingMode(.template) + } else if childs.contains(where: { $0.type == .discussion }) { + return CoreAssets.discussion.swiftUIImage.renderingMode(.template) + } else if childs.contains(where: { $0.type == .html }) { + return CoreAssets.extra.swiftUIImage.renderingMode(.template) + } else { + return CoreAssets.extra.swiftUIImage.renderingMode(.template) + } + } +} + +#if DEBUG +struct CourseUnitDropDownCell_Previews: PreviewProvider { + static var previews: some View { + let vertical = CourseVertical( + blockId: "1", + id: "1", + courseId: "123", + displayName: "Lesson 1", + type: .vertical, + completion: 1, + childs: [ + CourseBlock( + blockId: "1", + id: "1", + courseId: "123", + topicId: "1", + graded: false, + completion: 1, + type: .video, + displayName: "Lesson 1", + studentUrl: "", + videoUrl: nil, + youTubeUrl: nil + ) + ] + ) + + CourseUnitDropDownCell( + vertical: vertical, + isLast: false, + isSelected: false, + action: {} + ) + } +} +#endif diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift new file mode 100644 index 000000000..d6f8a4856 --- /dev/null +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift @@ -0,0 +1,150 @@ +// +// CourseUnitDropDownList.swift +// Course +// +// Created by Vadim Kuznetsov on 4.12.23. +// + +import Core +import SwiftUI + +struct CourseUnitDropDownList: View where Content: View { + @ViewBuilder var content: () -> Content + + @State var height: CGFloat = 0 + + var scrollViewHeight: CGFloat { + height > 400 ? 400 : height + } + var body: some View { + VStack { + ScrollView { + VStack(spacing: 0) { + content() + } + .background( + GeometryReader { proxy in + Color.clear + .onAppear { + height = proxy.size.height + } + } + ) + } + } + .background(RoundedRectangle(cornerRadius: 10).foregroundColor(Theme.Colors.background).shadow(radius: 4)) + .frame(height: scrollViewHeight) + } +} + +#if DEBUG +struct CourseUnitDropDownList_Previews: PreviewProvider { + static var previews: some View { + let blocks = [ + CourseBlock( + blockId: "1", + id: "1", + courseId: "123", + topicId: "1", + graded: false, + completion: 0, + type: .video, + displayName: "Lesson 1", + studentUrl: "", + videoUrl: nil, + youTubeUrl: nil + ), + CourseBlock( + blockId: "2", + id: "2", + courseId: "123", + topicId: "2", + graded: false, + completion: 0, + type: .video, + displayName: "Lesson 2", + studentUrl: "2", + videoUrl: nil, + youTubeUrl: nil + ), + CourseBlock( + blockId: "3", + id: "3", + courseId: "123", + topicId: "3", + graded: false, + completion: 0, + type: .unknown, + displayName: "Lesson 3", + studentUrl: "3", + videoUrl: nil, + youTubeUrl: nil + ), + CourseBlock( + blockId: "4", + id: "4", + courseId: "123", + topicId: "4", + graded: false, + completion: 0, + type: .unknown, + displayName: "4", + studentUrl: "4", + videoUrl: nil, + youTubeUrl: nil + ) + ] + + let verticals = [ + CourseVertical( + blockId: "1", + id: "1", + courseId: "123", + displayName: "First Unit", + type: .vertical, + completion: 0, + childs: blocks + ), + CourseVertical( + blockId: "2", + id: "2", + courseId: "123", + displayName: "Second Unit", + type: .vertical, + completion: 1, + childs: blocks + ), + CourseVertical( + blockId: "3", + id: "3", + courseId: "123", + displayName: "Third Unit", + type: .vertical, + completion: 0, + childs: blocks + ), + CourseVertical( + blockId: "4", + id: "4", + courseId: "123", + displayName: "Fourth Unit", + type: .vertical, + completion: 1, + childs: blocks + ) + ] + + CourseUnitDropDownList(content: { + ForEach(verticals, id: \.id) { vertical in + let isLast = verticals.last?.id == vertical.id + CourseUnitDropDownCell( + vertical: vertical, + isLast: isLast, + isSelected: false + ) {} + } + }) + .padding(10) + } +} +#endif diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownTitle.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownTitle.swift new file mode 100644 index 000000000..b0e451866 --- /dev/null +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownTitle.swift @@ -0,0 +1,46 @@ +// +// CourseUnitDropDownTitle.swift +// Course +// +// Created by Vadim Kuznetsov on 4.12.23. +// + +import SwiftUI + +struct CourseUnitDropDownTitle: View { + var title: String + var isAvailable: Bool + @Binding var showDropdown: Bool + + var body: some View { + HStack { + Text(title) + .onTapGesture { + if isAvailable { + showDropdown.toggle() + } + } + .opacity(showDropdown ? 0.7 : 1.0) + if isAvailable { + if showDropdown { + Image(systemName: "chevron.right") + .rotationEffect(.degrees(90)) + } else { + Image(systemName: "chevron.right") + } + } + } + } +} + +#if DEBUG +struct CourseUnitDropDownTitle_Previews: PreviewProvider { + static var previews: some View { + CourseUnitDropDownTitle( + title: "Dropdown title", + isAvailable: true, + showDropdown: .constant(false) + ) + } +} +#endif diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitVerticalsDropdownView.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitVerticalsDropdownView.swift new file mode 100644 index 000000000..cd9956aca --- /dev/null +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitVerticalsDropdownView.swift @@ -0,0 +1,44 @@ +// +// CourseUnitVerticalsDropdownView.swift +// Course +// +// Created by Vadim Kuznetsov on 4.12.23. +// + +import Core +import SwiftUI + +struct CourseUnitVerticalsDropdownView: View { + var verticals: [CourseVertical] + var currentIndex: Int + var offsetY: CGFloat + @Binding var showDropdown: Bool + var action: (CourseVertical) -> Void + + var body: some View { + ZStack(alignment: .top) { + Color.clear + .contentShape(Rectangle()) + .onTapGesture { + showDropdown.toggle() + } + CourseUnitDropDownList(content: { + ForEach(verticals, id: \.id) { vertical in + let isLast = verticals.last?.id == vertical.id + let isSelected = vertical.id == verticals[currentIndex].id + CourseUnitDropDownCell( + vertical: vertical, + isLast: isLast, + isSelected: isSelected + ) { + action(vertical) + } + } + }) + .offset(y: offsetY) + .padding(.horizontal, 20) + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) + .transition(.opacity) + } +} From db3c083377221ab676ff4ee466b90a18385b2105 Mon Sep 17 00:00:00 2001 From: forgotvas Date: Mon, 4 Dec 2023 17:55:09 +0300 Subject: [PATCH 02/11] chore: a little design changes --- Course/Course/Presentation/CourseRouter.swift | 6 ++++-- Course/Course/Presentation/Unit/CourseNavigationView.swift | 4 +++- Course/Course/Presentation/Unit/CourseUnitView.swift | 2 +- .../Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift | 1 + .../Unit/Subviews/DropdownList/CourseUnitDropDownList.swift | 1 + OpenEdX/Router.swift | 5 +++-- default_config/dev/config.yaml | 4 ++-- 7 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Course/Course/Presentation/CourseRouter.swift b/Course/Course/Presentation/CourseRouter.swift index ea454a3ca..3b4e6de7d 100644 --- a/Course/Course/Presentation/CourseRouter.swift +++ b/Course/Course/Presentation/CourseRouter.swift @@ -41,7 +41,8 @@ public protocol CourseRouter: BaseRouter { verticalIndex: Int, chapters: [CourseChapter], chapterIndex: Int, - sequentialIndex: Int + sequentialIndex: Int, + animated: Bool ) func showCourseVerticalView( @@ -98,7 +99,8 @@ public class CourseRouterMock: BaseRouterMock, CourseRouter { verticalIndex: Int, chapters: [CourseChapter], chapterIndex: Int, - sequentialIndex: Int + sequentialIndex: Int, + animated: Bool ) {} public func showCourseVerticalView( diff --git a/Course/Course/Presentation/Unit/CourseNavigationView.swift b/Course/Course/Presentation/Unit/CourseNavigationView.swift index 505d865e0..5778f1fd0 100644 --- a/Course/Course/Presentation/Unit/CourseNavigationView.swift +++ b/Course/Course/Presentation/Unit/CourseNavigationView.swift @@ -119,7 +119,9 @@ struct CourseNavigationView: View { verticalIndex: verticalIndex, chapters: viewModel.chapters, chapterIndex: chapterIndex, - sequentialIndex: sequentialIndex) + sequentialIndex: sequentialIndex, + animated: true + ) } ) playerStateSubject.send(VideoPlayerState.pause) diff --git a/Course/Course/Presentation/Unit/CourseUnitView.swift b/Course/Course/Presentation/Unit/CourseUnitView.swift index 8a37812c9..254aa492f 100644 --- a/Course/Course/Presentation/Unit/CourseUnitView.swift +++ b/Course/Course/Presentation/Unit/CourseUnitView.swift @@ -280,7 +280,7 @@ public struct CourseUnitView: View { isAvailable: isDropdownAvailable, showDropdown: $showDropdown) .padding(.top, 0) - .offset(y: -20) + .offset(y: -25) Spacer() } HStack(alignment: .center) { diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift index 1e86dd53a..a9782c348 100644 --- a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift @@ -7,6 +7,7 @@ import Core import SwiftUI +import Theme struct CourseUnitDropDownCell: View { var vertical: CourseVertical diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift index d6f8a4856..df070f8c6 100644 --- a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift @@ -7,6 +7,7 @@ import Core import SwiftUI +import Theme struct CourseUnitDropDownList: View where Content: View { @ViewBuilder var content: () -> Content diff --git a/OpenEdX/Router.swift b/OpenEdX/Router.swift index 6746ec652..7b0d391fc 100644 --- a/OpenEdX/Router.swift +++ b/OpenEdX/Router.swift @@ -315,7 +315,8 @@ public class Router: AuthorizationRouter, verticalIndex: Int, chapters: [CourseChapter], chapterIndex: Int, - sequentialIndex: Int + sequentialIndex: Int, + animated: Bool ) { let vmVertical = Container.shared.resolve( @@ -348,7 +349,7 @@ public class Router: AuthorizationRouter, var controllers = navigationController.viewControllers controllers.removeLast(2) controllers.append(contentsOf: [controllerVertical, controllerUnit]) - navigationController.setViewControllers(controllers, animated: true) + navigationController.setViewControllers(controllers, animated: animated) } public func showThreads(courseID: String, topics: Topics, title: String, type: ThreadType) { diff --git a/default_config/dev/config.yaml b/default_config/dev/config.yaml index d7e76817e..418a6f4fd 100644 --- a/default_config/dev/config.yaml +++ b/default_config/dev/config.yaml @@ -1,4 +1,4 @@ -API_HOST_URL: 'http://localhost:8000' +API_HOST_URL: 'https://raccoonapis.sandbox.edx.org' ENVIRONMENT_DISPLAY_NAME: 'Localhost' FEEDBACK_EMAIL_ADDRESS: 'support@example.com' -OAUTH_CLIENT_ID: '' +OAUTH_CLIENT_ID: 'rg-edx-oauth-client-id' From 9e466049b53aa7f647c7c81ddb4a151f2411b033 Mon Sep 17 00:00:00 2001 From: forgotvas Date: Mon, 4 Dec 2023 18:28:14 +0300 Subject: [PATCH 03/11] feat: added accessibilty --- .../DropdownList/CourseUnitDropDownCell.swift | 2 ++ .../CourseUnitDropDownTitle.swift | 30 +++++++++++-------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift index a9782c348..585a8dae1 100644 --- a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift @@ -43,6 +43,8 @@ struct CourseUnitDropDownCell: View { .foregroundColor(Theme.Colors.textPrimary) } }) + .accessibilityElement(children: .ignore) + .accessibilityLabel(vertical.displayName) .padding(.horizontal, 20) .padding(.vertical, 5) if !isLast { diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownTitle.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownTitle.swift index b0e451866..f7e40619b 100644 --- a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownTitle.swift +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownTitle.swift @@ -13,23 +13,27 @@ struct CourseUnitDropDownTitle: View { @Binding var showDropdown: Bool var body: some View { - HStack { - Text(title) - .onTapGesture { - if isAvailable { - showDropdown.toggle() - } - } - .opacity(showDropdown ? 0.7 : 1.0) + Button { if isAvailable { - if showDropdown { - Image(systemName: "chevron.right") - .rotationEffect(.degrees(90)) - } else { - Image(systemName: "chevron.right") + showDropdown.toggle() + } + } label: { + HStack { + Text(title) + .opacity(showDropdown ? 0.7 : 1.0) + if isAvailable { + if showDropdown { + Image(systemName: "chevron.right") + .rotationEffect(.degrees(90)) + } else { + Image(systemName: "chevron.right") + } } } } + .buttonStyle(.plain) + .accessibilityElement(children: .ignore) + .accessibilityLabel(title) } } From c7f4b94a633835bf77093fbc2a3ee7460d2eabb6 Mon Sep 17 00:00:00 2001 From: forgotvas Date: Mon, 4 Dec 2023 19:08:55 +0300 Subject: [PATCH 04/11] fix: accessiblity fix --- .../DropdownList/CourseUnitDropDownCell.swift | 2 -- .../CourseUnitDropDownTitle.swift | 34 ++++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift index 585a8dae1..a9782c348 100644 --- a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift @@ -43,8 +43,6 @@ struct CourseUnitDropDownCell: View { .foregroundColor(Theme.Colors.textPrimary) } }) - .accessibilityElement(children: .ignore) - .accessibilityLabel(vertical.displayName) .padding(.horizontal, 20) .padding(.vertical, 5) if !isLast { diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownTitle.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownTitle.swift index f7e40619b..e2d83af16 100644 --- a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownTitle.swift +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownTitle.swift @@ -13,27 +13,29 @@ struct CourseUnitDropDownTitle: View { @Binding var showDropdown: Bool var body: some View { - Button { - if isAvailable { - showDropdown.toggle() - } - } label: { - HStack { - Text(title) - .opacity(showDropdown ? 0.7 : 1.0) + if isAvailable { + Button { if isAvailable { - if showDropdown { - Image(systemName: "chevron.right") - .rotationEffect(.degrees(90)) - } else { - Image(systemName: "chevron.right") + showDropdown.toggle() + } + } label: { + HStack { + Text(title) + .opacity(showDropdown ? 0.7 : 1.0) + if isAvailable { + if showDropdown { + Image(systemName: "chevron.right") + .rotationEffect(.degrees(90)) + } else { + Image(systemName: "chevron.right") + } } } } + .buttonStyle(.plain) + } else { + Text(title) } - .buttonStyle(.plain) - .accessibilityElement(children: .ignore) - .accessibilityLabel(title) } } From b7f7266b578ce38e025f8c3bbc3d8b9053484445 Mon Sep 17 00:00:00 2001 From: forgotvas Date: Mon, 4 Dec 2023 19:15:17 +0300 Subject: [PATCH 05/11] chore: remove config paramaters --- default_config/dev/config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/default_config/dev/config.yaml b/default_config/dev/config.yaml index 418a6f4fd..d7e76817e 100644 --- a/default_config/dev/config.yaml +++ b/default_config/dev/config.yaml @@ -1,4 +1,4 @@ -API_HOST_URL: 'https://raccoonapis.sandbox.edx.org' +API_HOST_URL: 'http://localhost:8000' ENVIRONMENT_DISPLAY_NAME: 'Localhost' FEEDBACK_EMAIL_ADDRESS: 'support@example.com' -OAUTH_CLIENT_ID: 'rg-edx-oauth-client-id' +OAUTH_CLIENT_ID: '' From c33a0d05911a332a4ff93f46189d00d518aa9a8f Mon Sep 17 00:00:00 2001 From: forgotvas Date: Tue, 5 Dec 2023 13:03:01 +0300 Subject: [PATCH 06/11] fix: added flag to hide/show unit dropdown menu --- Course/Course.xcodeproj/project.pbxproj | 4 + .../Outline/CourseVerticalImageView.swift | 116 ++++++++++++++++++ .../Outline/CourseVerticalView.swift | 16 +-- .../Presentation/Unit/CourseUnitView.swift | 108 +++++++++++----- .../DropdownList/CourseUnitDropDownCell.swift | 17 +-- .../DropdownList/CourseUnitDropDownList.swift | 6 +- .../CourseUnitVerticalsDropdownView.swift | 108 ++++++++++++++++ 7 files changed, 313 insertions(+), 62 deletions(-) create mode 100644 Course/Course/Presentation/Outline/CourseVerticalImageView.swift diff --git a/Course/Course.xcodeproj/project.pbxproj b/Course/Course.xcodeproj/project.pbxproj index 4d23f958b..df1a73520 100644 --- a/Course/Course.xcodeproj/project.pbxproj +++ b/Course/Course.xcodeproj/project.pbxproj @@ -62,6 +62,7 @@ 068DDA602B1E198700FF8CCB /* CourseUnitVerticalsDropdownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068DDA5C2B1E198700FF8CCB /* CourseUnitVerticalsDropdownView.swift */; }; 068DDA612B1E198700FF8CCB /* CourseUnitDropDownCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068DDA5D2B1E198700FF8CCB /* CourseUnitDropDownCell.swift */; }; 068DDA622B1E198700FF8CCB /* CourseUnitDropDownTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068DDA5E2B1E198700FF8CCB /* CourseUnitDropDownTitle.swift */; }; + 06FD7EDF2B1F29F3008D632B /* CourseVerticalImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FD7EDE2B1F29F3008D632B /* CourseVerticalImageView.swift */; }; 073512E229C0E400005CFA41 /* BaseCourseViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 073512E129C0E400005CFA41 /* BaseCourseViewModel.swift */; }; 0766DFCC299AA7A600EBEF6A /* YouTubeVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0766DFCB299AA7A600EBEF6A /* YouTubeVideoPlayer.swift */; }; 0766DFCE299AB26D00EBEF6A /* EncodedVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0766DFCD299AB26D00EBEF6A /* EncodedVideoPlayer.swift */; }; @@ -143,6 +144,7 @@ 068DDA5C2B1E198700FF8CCB /* CourseUnitVerticalsDropdownView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseUnitVerticalsDropdownView.swift; sourceTree = ""; }; 068DDA5D2B1E198700FF8CCB /* CourseUnitDropDownCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseUnitDropDownCell.swift; sourceTree = ""; }; 068DDA5E2B1E198700FF8CCB /* CourseUnitDropDownTitle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseUnitDropDownTitle.swift; sourceTree = ""; }; + 06FD7EDE2B1F29F3008D632B /* CourseVerticalImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseVerticalImageView.swift; sourceTree = ""; }; 073512E129C0E400005CFA41 /* BaseCourseViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCourseViewModel.swift; sourceTree = ""; }; 0766DFCB299AA7A600EBEF6A /* YouTubeVideoPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YouTubeVideoPlayer.swift; sourceTree = ""; }; 0766DFCD299AB26D00EBEF6A /* EncodedVideoPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodedVideoPlayer.swift; sourceTree = ""; }; @@ -390,6 +392,7 @@ 0270210128E736E700F54332 /* CourseOutlineView.swift */, 02A8076729474831007F53AB /* CourseVerticalView.swift */, 0248C92629C097EB00DC8402 /* CourseVerticalViewModel.swift */, + 06FD7EDE2B1F29F3008D632B /* CourseVerticalImageView.swift */, ); path = Outline; sourceTree = ""; @@ -752,6 +755,7 @@ DB7D6EB02ADFDA0E0036BB13 /* CourseDates.swift in Sources */, 02E685C028E4B629000AE015 /* CourseDetailsViewModel.swift in Sources */, 0295C889299BBE8200ABE571 /* CourseNavigationView.swift in Sources */, + 06FD7EDF2B1F29F3008D632B /* CourseVerticalImageView.swift in Sources */, DB7D6EAE2ADFCB4A0036BB13 /* CourseDatesViewModel.swift in Sources */, 02F066E829DC71750073E13B /* SubtittlesView.swift in Sources */, 022C64E229ADEB83000F532B /* CourseUpdate.swift in Sources */, diff --git a/Course/Course/Presentation/Outline/CourseVerticalImageView.swift b/Course/Course/Presentation/Outline/CourseVerticalImageView.swift new file mode 100644 index 000000000..9c035fc35 --- /dev/null +++ b/Course/Course/Presentation/Outline/CourseVerticalImageView.swift @@ -0,0 +1,116 @@ +// +// CourseVerticalImageView.swift +// Course +// +// Created by Vadim Kuznetsov on 5.12.23. +// + +import Core +import SwiftUI + +struct CourseVerticalImageView: View { + var blocks: [CourseBlock] + + var body: some View { + if blocks.contains(where: { $0.type == .problem }) { + return CoreAssets.pen.swiftUIImage.renderingMode(.template) + } else if blocks.contains(where: { $0.type == .video }) { + return CoreAssets.video.swiftUIImage.renderingMode(.template) + } else if blocks.contains(where: { $0.type == .discussion }) { + return CoreAssets.discussion.swiftUIImage.renderingMode(.template) + } else if blocks.contains(where: { $0.type == .html }) { + return CoreAssets.extra.swiftUIImage.renderingMode(.template) + } else { + return CoreAssets.extra.swiftUIImage.renderingMode(.template) + } + } +} +#if DEBUG +struct CourseVerticalImageView_Previews: PreviewProvider { + static var previews: some View { + let blocks1 = [ + CourseBlock( + blockId: "1", + id: "1", + courseId: "123", + topicId: "1", + graded: false, + completion: 1, + type: .video, + displayName: "Block 1", + studentUrl: "", + videoUrl: nil, + youTubeUrl: nil + ) + ] + + let blocks2 = [ + CourseBlock( + blockId: "1", + id: "1", + courseId: "123", + topicId: "1", + graded: false, + completion: 1, + type: .problem, + displayName: "Block 1", + studentUrl: "", + videoUrl: nil, + youTubeUrl: nil + ) + ] + let blocks3 = [ + CourseBlock( + blockId: "1", + id: "1", + courseId: "123", + topicId: "1", + graded: false, + completion: 1, + type: .discussion, + displayName: "Block 1", + studentUrl: "", + videoUrl: nil, + youTubeUrl: nil + ) + ] + let blocks4 = [ + CourseBlock( + blockId: "1", + id: "1", + courseId: "123", + topicId: "1", + graded: false, + completion: 1, + type: .html, + displayName: "Block 1", + studentUrl: "", + videoUrl: nil, + youTubeUrl: nil + ) + ] + let blocks5 = [ + CourseBlock( + blockId: "1", + id: "1", + courseId: "123", + topicId: "1", + graded: false, + completion: 1, + type: .unknown, + displayName: "Block 1", + studentUrl: "", + videoUrl: nil, + youTubeUrl: nil + ) + ] + HStack { + CourseVerticalImageView(blocks: blocks1) + CourseVerticalImageView(blocks: blocks2) + CourseVerticalImageView(blocks: blocks3) + CourseVerticalImageView(blocks: blocks4) + CourseVerticalImageView(blocks: blocks5) + } + } +} +#endif diff --git a/Course/Course/Presentation/Outline/CourseVerticalView.swift b/Course/Course/Presentation/Outline/CourseVerticalView.swift index 547423bb3..b5401b4dc 100644 --- a/Course/Course/Presentation/Outline/CourseVerticalView.swift +++ b/Course/Course/Presentation/Outline/CourseVerticalView.swift @@ -32,20 +32,6 @@ public struct CourseVerticalView: View { self.viewModel = viewModel } - private func verticalImage(childs: [CourseBlock]) -> Image { - if childs.contains(where: { $0.type == .problem }) { - return CoreAssets.pen.swiftUIImage.renderingMode(.template) - } else if childs.contains(where: { $0.type == .video }) { - return CoreAssets.video.swiftUIImage.renderingMode(.template) - } else if childs.contains(where: { $0.type == .discussion }) { - return CoreAssets.discussion.swiftUIImage.renderingMode(.template) - } else if childs.contains(where: { $0.type == .html }) { - return CoreAssets.extra.swiftUIImage.renderingMode(.template) - } else { - return CoreAssets.extra.swiftUIImage.renderingMode(.template) - } - } - public var body: some View { ZStack(alignment: .top) { // MARK: - Page Body @@ -82,7 +68,7 @@ public struct CourseVerticalView: View { .renderingMode(.template) .foregroundColor(.accentColor) } else { - verticalImage(childs: vertical.childs) + CourseVerticalImageView(blocks: vertical.childs) } Text(vertical.displayName) .font(Theme.Fonts.titleMedium) diff --git a/Course/Course/Presentation/Unit/CourseUnitView.swift b/Course/Course/Presentation/Unit/CourseUnitView.swift index 254aa492f..22d11c249 100644 --- a/Course/Course/Presentation/Unit/CourseUnitView.swift +++ b/Course/Course/Presentation/Unit/CourseUnitView.swift @@ -26,21 +26,17 @@ public struct CourseUnitView: View { } @State var offsetView: CGFloat = 0 @State var showDiscussion: Bool = false - @State var showDropdown: Bool = false @Environment(\.isPresented) private var isPresented @Environment(\.isHorizontal) private var isHorizontal private let sectionName: String public let playerStateSubject = CurrentValueSubject(nil) + + //Dropdown parameters + @State var showDropdown: Bool = false private let portraitTopSpacing: CGFloat = 60 private let landscapeTopSpacing: CGFloat = 75 - public init(viewModel: CourseUnitViewModel, - sectionName: String) { - self.viewModel = viewModel - self.sectionName = sectionName - viewModel.loadIndex() - viewModel.nextTitles() - } + let isDropdownActive: Bool = false var sequenceTitle: String { let chapter = viewModel.chapters[viewModel.chapterIndex] @@ -58,7 +54,15 @@ public struct CourseUnitView: View { var isDropdownAvailable: Bool { viewModel.verticals.count > 1 } - + + public init(viewModel: CourseUnitViewModel, + sectionName: String) { + self.viewModel = viewModel + self.sectionName = sectionName + viewModel.loadIndex() + viewModel.nextTitles() + } + public var body: some View { ZStack(alignment: .top) { // MARK: - Page Body @@ -71,18 +75,20 @@ public struct CourseUnitView: View { let data = Array(viewModel.verticals[viewModel.verticalIndex].childs.enumerated()) ForEach(data, id: \.offset) { index, block in VStack(spacing: 0) { - HStack { - let block = viewModel.verticals[viewModel.verticalIndex] - .childs[viewModel.index] - if block.type == .video { - let title = block.displayName - Text(title) - .lineLimit(1) - .font(Theme.Fonts.titleLarge) - .foregroundStyle(Theme.Colors.textPrimary) - .padding(.vertical, 10) - .padding(.horizontal, 20) - Spacer() + if isDropdownActive { + HStack { + let block = viewModel.verticals[viewModel.verticalIndex] + .childs[viewModel.index] + if block.type == .video { + let title = block.displayName + Text(title) + .lineLimit(1) + .font(Theme.Fonts.titleLarge) + .foregroundStyle(Theme.Colors.textPrimary) + .padding(.vertical, 10) + .padding(.horizontal, 20) + Spacer() + } } } switch LessonType.from(block) { @@ -267,20 +273,43 @@ public struct CourseUnitView: View { // MARK: - Course Navigation VStack { ZStack { + if !isDropdownActive { + GeometryReader { reader in + VStack { + HStack { + let currentBlock = viewModel.verticals[viewModel.verticalIndex] + .childs[viewModel.index] + if currentBlock.type == .video { + let title = currentBlock.displayName + Text(title) + .lineLimit(1) + .font(Theme.Fonts.titleLarge) + .foregroundStyle(Theme.Colors.textPrimary) + .padding(.leading, isHorizontal ? 30 : 42) + .padding(.top, isHorizontal ? 14 : 2) + Spacer() + } + }.frame(maxWidth: isHorizontal ? reader.size.width * 0.5 : nil) + Spacer() + } + } + } VStack { NavigationBar( - title: sequenceTitle, + title: isDropdownActive ? sequenceTitle : "", leftButtonAction: { viewModel.router.back() playerStateSubject.send(VideoPlayerState.kill) }).padding(.top, isHorizontal ? 10 : 0) .padding(.leading, isHorizontal ? -16 : 0) - CourseUnitDropDownTitle( - title: unitTitle, - isAvailable: isDropdownAvailable, - showDropdown: $showDropdown) - .padding(.top, 0) - .offset(y: -25) + if isDropdownActive { + CourseUnitDropDownTitle( + title: unitTitle, + isAvailable: isDropdownAvailable, + showDropdown: $showDropdown) + .padding(.top, 0) + .offset(y: -25) + } Spacer() } HStack(alignment: .center) { @@ -312,7 +341,7 @@ public struct CourseUnitView: View { playerStateSubject.send(VideoPlayerState.kill) } } - if showDropdown { + if isDropdownActive && showDropdown { CourseUnitVerticalsDropdownView( verticals: viewModel.verticals, currentIndex: viewModel.verticalIndex, @@ -340,7 +369,26 @@ public struct CourseUnitView: View { Theme.Colors.background .ignoresSafeArea() ) - .animation(.easeOut(duration: 0.2), value: showDropdown) + .dropdownAnimation(isActive: isDropdownActive, value: showDropdown) + } +} + +struct DropdownAnimationModifier: ViewModifier where V: Equatable { + var isActive: Bool + var value: V + func body(content: Content) -> some View { + if isActive { + content + .animation(.easeOut(duration: 0.2), value: value) + } else { + content + } + } +} + +extension View { + func dropdownAnimation(isActive: Bool, value: V) -> some View where V: Equatable { + modifier(DropdownAnimationModifier(isActive: isActive, value: value)) } } diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift index a9782c348..8ce21dc08 100644 --- a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownCell.swift @@ -37,8 +37,7 @@ struct CourseUnitDropDownCell: View { .frame(alignment: .leading) .multilineTextAlignment(.leading) .frame(maxWidth: .infinity, alignment: .leading) - verticalImage(childs: vertical.childs) - + CourseVerticalImageView(blocks: vertical.childs) } .foregroundColor(Theme.Colors.textPrimary) } @@ -59,20 +58,6 @@ struct CourseUnitDropDownCell: View { ) } - - private func verticalImage(childs: [CourseBlock]) -> Image { - if childs.contains(where: { $0.type == .problem }) { - return CoreAssets.pen.swiftUIImage.renderingMode(.template) - } else if childs.contains(where: { $0.type == .video }) { - return CoreAssets.video.swiftUIImage.renderingMode(.template) - } else if childs.contains(where: { $0.type == .discussion }) { - return CoreAssets.discussion.swiftUIImage.renderingMode(.template) - } else if childs.contains(where: { $0.type == .html }) { - return CoreAssets.extra.swiftUIImage.renderingMode(.template) - } else { - return CoreAssets.extra.swiftUIImage.renderingMode(.template) - } - } } #if DEBUG diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift index df070f8c6..27ec20446 100644 --- a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift @@ -33,7 +33,11 @@ struct CourseUnitDropDownList: View where Content: View { ) } } - .background(RoundedRectangle(cornerRadius: 10).foregroundColor(Theme.Colors.background).shadow(radius: 4)) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .background( + RoundedRectangle(cornerRadius: 10) + .foregroundColor(Theme.Colors.background) + .shadow(radius: 4)) .frame(height: scrollViewHeight) } } diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitVerticalsDropdownView.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitVerticalsDropdownView.swift index cd9956aca..94c83ec7f 100644 --- a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitVerticalsDropdownView.swift +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitVerticalsDropdownView.swift @@ -42,3 +42,111 @@ struct CourseUnitVerticalsDropdownView: View { .transition(.opacity) } } + +#if DEBUG +struct CourseUnitVerticalsDropdownView_Previews: PreviewProvider { + static var previews: some View { + let blocks = [ + CourseBlock( + blockId: "1", + id: "1", + courseId: "123", + topicId: "1", + graded: false, + completion: 0, + type: .video, + displayName: "Lesson 1", + studentUrl: "", + videoUrl: nil, + youTubeUrl: nil + ), + CourseBlock( + blockId: "2", + id: "2", + courseId: "123", + topicId: "2", + graded: false, + completion: 0, + type: .video, + displayName: "Lesson 2", + studentUrl: "2", + videoUrl: nil, + youTubeUrl: nil + ), + CourseBlock( + blockId: "3", + id: "3", + courseId: "123", + topicId: "3", + graded: false, + completion: 0, + type: .unknown, + displayName: "Lesson 3", + studentUrl: "3", + videoUrl: nil, + youTubeUrl: nil + ), + CourseBlock( + blockId: "4", + id: "4", + courseId: "123", + topicId: "4", + graded: false, + completion: 0, + type: .unknown, + displayName: "4", + studentUrl: "4", + videoUrl: nil, + youTubeUrl: nil + ) + ] + + let verticals = [ + CourseVertical( + blockId: "1", + id: "1", + courseId: "123", + displayName: "First Unit", + type: .vertical, + completion: 0, + childs: blocks + ), + CourseVertical( + blockId: "2", + id: "2", + courseId: "123", + displayName: "Second Unit", + type: .vertical, + completion: 1, + childs: blocks + ), + CourseVertical( + blockId: "3", + id: "3", + courseId: "123", + displayName: "Third Unit", + type: .vertical, + completion: 0, + childs: blocks + ), + CourseVertical( + blockId: "4", + id: "4", + courseId: "123", + displayName: "Fourth Unit", + type: .vertical, + completion: 1, + childs: blocks + ) + ] + + CourseUnitVerticalsDropdownView( + verticals: verticals, + currentIndex: 0, + offsetY: 0, + showDropdown: .constant(true) + ) {_ in } + .padding(10) + } +} +#endif From 38bc833885583f473201021550dd2ef4dd7fd8ec Mon Sep 17 00:00:00 2001 From: forgotvas Date: Tue, 5 Dec 2023 13:14:18 +0300 Subject: [PATCH 07/11] fix: title for video type block --- Course/Course/Presentation/Unit/CourseUnitView.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Course/Course/Presentation/Unit/CourseUnitView.swift b/Course/Course/Presentation/Unit/CourseUnitView.swift index 22d11c249..07a56ea25 100644 --- a/Course/Course/Presentation/Unit/CourseUnitView.swift +++ b/Course/Course/Presentation/Unit/CourseUnitView.swift @@ -36,7 +36,7 @@ public struct CourseUnitView: View { private let portraitTopSpacing: CGFloat = 60 private let landscapeTopSpacing: CGFloat = 75 - let isDropdownActive: Bool = false + let isDropdownActive: Bool = true var sequenceTitle: String { let chapter = viewModel.chapters[viewModel.chapterIndex] @@ -77,8 +77,6 @@ public struct CourseUnitView: View { VStack(spacing: 0) { if isDropdownActive { HStack { - let block = viewModel.verticals[viewModel.verticalIndex] - .childs[viewModel.index] if block.type == .video { let title = block.displayName Text(title) From f8dc5b79a5bc43a4c31712e584e23d3c9b55253b Mon Sep 17 00:00:00 2001 From: forgotvas Date: Tue, 5 Dec 2023 13:17:28 +0300 Subject: [PATCH 08/11] chore: refactor --- .../Unit/Subviews/DropdownList/CourseUnitDropDownList.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift index 27ec20446..ef3964152 100644 --- a/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/CourseUnitDropDownList.swift @@ -33,12 +33,10 @@ struct CourseUnitDropDownList: View where Content: View { ) } } + .background(Theme.Colors.background) .clipShape(RoundedRectangle(cornerRadius: 10)) - .background( - RoundedRectangle(cornerRadius: 10) - .foregroundColor(Theme.Colors.background) - .shadow(radius: 4)) .frame(height: scrollViewHeight) + .shadow(radius: 4) } } From d95993649abfdb07688fdbd98443264d26cd0f22 Mon Sep 17 00:00:00 2001 From: forgotvas Date: Tue, 5 Dec 2023 14:37:09 +0300 Subject: [PATCH 09/11] feat: added feature flag --- Core/Core.xcodeproj/project.pbxproj | 4 +++ Core/Core/Configuration/Config/Config.swift | 1 + .../Config/UIComponentsConfig.swift | 28 +++++++++++++++++++ Course/Course.xcodeproj/project.pbxproj | 4 +++ .../Presentation/Unit/CourseUnitView.swift | 23 ++------------- .../DropdownAnimationModifier.swift | 27 ++++++++++++++++++ 6 files changed, 67 insertions(+), 20 deletions(-) create mode 100644 Core/Core/Configuration/Config/UIComponentsConfig.swift create mode 100644 Course/Course/Presentation/Unit/Subviews/DropdownList/DropdownAnimationModifier.swift diff --git a/Core/Core.xcodeproj/project.pbxproj b/Core/Core.xcodeproj/project.pbxproj index a54379167..204540777 100644 --- a/Core/Core.xcodeproj/project.pbxproj +++ b/Core/Core.xcodeproj/project.pbxproj @@ -85,6 +85,7 @@ 02F6EF3B28D9B8EC00835477 /* CourseCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02F6EF3A28D9B8EC00835477 /* CourseCellView.swift */; }; 02F6EF4A28D9F0A700835477 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02F6EF4928D9F0A700835477 /* DateExtension.swift */; }; 02F98A7F28F81EE900DE94C0 /* Container+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02F98A7E28F81EE900DE94C0 /* Container+App.swift */; }; + 06FD7EE12B1F3DBC008D632B /* UIComponentsConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FD7EE02B1F3DBC008D632B /* UIComponentsConfig.swift */; }; 070019A528F6F17900D5FC78 /* Data_Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = 070019A428F6F17900D5FC78 /* Data_Media.swift */; }; 070019AC28F6FD0100D5FC78 /* CourseDetailBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 070019AB28F6FD0100D5FC78 /* CourseDetailBlock.swift */; }; 070019AE28F701B200D5FC78 /* Certificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 070019AD28F701B200D5FC78 /* Certificate.swift */; }; @@ -230,6 +231,7 @@ 02F6EF3A28D9B8EC00835477 /* CourseCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseCellView.swift; sourceTree = ""; }; 02F6EF4928D9F0A700835477 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = ""; }; 02F98A7E28F81EE900DE94C0 /* Container+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Container+App.swift"; sourceTree = ""; }; + 06FD7EE02B1F3DBC008D632B /* UIComponentsConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIComponentsConfig.swift; sourceTree = ""; }; 070019A428F6F17900D5FC78 /* Data_Media.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data_Media.swift; sourceTree = ""; }; 070019AB28F6FD0100D5FC78 /* CourseDetailBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDetailBlock.swift; sourceTree = ""; }; 070019AD28F701B200D5FC78 /* Certificate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Certificate.swift; sourceTree = ""; }; @@ -629,6 +631,7 @@ DBF6F2402B014ADA0098414B /* FirebaseConfig.swift */, DBF6F2492B0380E00098414B /* FeaturesConfig.swift */, DBF6F2452B01DAFE0098414B /* AgreementConfig.swift */, + 06FD7EE02B1F3DBC008D632B /* UIComponentsConfig.swift */, ); path = Config; sourceTree = ""; @@ -864,6 +867,7 @@ 022C64E429AE0191000F532B /* TextWithUrls.swift in Sources */, 0283348028D4DCD200C828FC /* ViewExtension.swift in Sources */, 02A4833529B8A73400D33F33 /* CorePersistenceProtocol.swift in Sources */, + 06FD7EE12B1F3DBC008D632B /* UIComponentsConfig.swift in Sources */, 0233D5732AF13EEE00BAC8BD /* AppReviewButton.swift in Sources */, 02512FF0299533DF0024D438 /* CoreDataHandlerProtocol.swift in Sources */, 0260E58028FD792800BBBE18 /* WebUnitViewModel.swift in Sources */, diff --git a/Core/Core/Configuration/Config/Config.swift b/Core/Core/Configuration/Config/Config.swift index 49f1ffdc5..90117edc3 100644 --- a/Core/Core/Configuration/Config/Config.swift +++ b/Core/Core/Configuration/Config/Config.swift @@ -16,6 +16,7 @@ public protocol ConfigProtocol { var agreement: AgreementConfig { get } var firebase: FirebaseConfig { get } var features: FeaturesConfig { get } + var uiComponents: UIComponentsConfig { get } } public enum TokenType: String { diff --git a/Core/Core/Configuration/Config/UIComponentsConfig.swift b/Core/Core/Configuration/Config/UIComponentsConfig.swift new file mode 100644 index 000000000..1c2e48913 --- /dev/null +++ b/Core/Core/Configuration/Config/UIComponentsConfig.swift @@ -0,0 +1,28 @@ +// +// UIComponentsConfig.swift +// Core +// +// Created by Vadim Kuznetsov on 5.12.23. +// + +import Foundation + +private enum Keys: String { + case isVerticalsMenuEnabled = "VERTICALS_MENU_ENABLED" +} + +public class UIComponentsConfig: NSObject { + public var isVerticalsMenuEnabled: Bool = false + + init(dictionary: [String: Any]) { + isVerticalsMenuEnabled = dictionary[Keys.isVerticalsMenuEnabled.rawValue] as? Bool ?? false + super.init() + } +} + +private let key = "UI_COMPONENTS" +extension Config { + public var uiComponents: UIComponentsConfig { + return UIComponentsConfig(dictionary: properties[key] as? [String: AnyObject] ?? [:]) + } +} diff --git a/Course/Course.xcodeproj/project.pbxproj b/Course/Course.xcodeproj/project.pbxproj index df1a73520..05edc33a3 100644 --- a/Course/Course.xcodeproj/project.pbxproj +++ b/Course/Course.xcodeproj/project.pbxproj @@ -63,6 +63,7 @@ 068DDA612B1E198700FF8CCB /* CourseUnitDropDownCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068DDA5D2B1E198700FF8CCB /* CourseUnitDropDownCell.swift */; }; 068DDA622B1E198700FF8CCB /* CourseUnitDropDownTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068DDA5E2B1E198700FF8CCB /* CourseUnitDropDownTitle.swift */; }; 06FD7EDF2B1F29F3008D632B /* CourseVerticalImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FD7EDE2B1F29F3008D632B /* CourseVerticalImageView.swift */; }; + 06FD7EE32B1F3FF6008D632B /* DropdownAnimationModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FD7EE22B1F3FF6008D632B /* DropdownAnimationModifier.swift */; }; 073512E229C0E400005CFA41 /* BaseCourseViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 073512E129C0E400005CFA41 /* BaseCourseViewModel.swift */; }; 0766DFCC299AA7A600EBEF6A /* YouTubeVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0766DFCB299AA7A600EBEF6A /* YouTubeVideoPlayer.swift */; }; 0766DFCE299AB26D00EBEF6A /* EncodedVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0766DFCD299AB26D00EBEF6A /* EncodedVideoPlayer.swift */; }; @@ -145,6 +146,7 @@ 068DDA5D2B1E198700FF8CCB /* CourseUnitDropDownCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseUnitDropDownCell.swift; sourceTree = ""; }; 068DDA5E2B1E198700FF8CCB /* CourseUnitDropDownTitle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseUnitDropDownTitle.swift; sourceTree = ""; }; 06FD7EDE2B1F29F3008D632B /* CourseVerticalImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseVerticalImageView.swift; sourceTree = ""; }; + 06FD7EE22B1F3FF6008D632B /* DropdownAnimationModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownAnimationModifier.swift; sourceTree = ""; }; 073512E129C0E400005CFA41 /* BaseCourseViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCourseViewModel.swift; sourceTree = ""; }; 0766DFCB299AA7A600EBEF6A /* YouTubeVideoPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YouTubeVideoPlayer.swift; sourceTree = ""; }; 0766DFCD299AB26D00EBEF6A /* EncodedVideoPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodedVideoPlayer.swift; sourceTree = ""; }; @@ -361,6 +363,7 @@ 068DDA5C2B1E198700FF8CCB /* CourseUnitVerticalsDropdownView.swift */, 068DDA5D2B1E198700FF8CCB /* CourseUnitDropDownCell.swift */, 068DDA5E2B1E198700FF8CCB /* CourseUnitDropDownTitle.swift */, + 06FD7EE22B1F3FF6008D632B /* DropdownAnimationModifier.swift */, ); path = DropdownList; sourceTree = ""; @@ -720,6 +723,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 06FD7EE32B1F3FF6008D632B /* DropdownAnimationModifier.swift in Sources */, 02FFAD0D29E4347300140E46 /* VideoPlayerViewModel.swift in Sources */, 02454CA42A26193F0043052A /* WebView.swift in Sources */, 022C64DA29ACEC50000F532B /* HandoutsViewModel.swift in Sources */, diff --git a/Course/Course/Presentation/Unit/CourseUnitView.swift b/Course/Course/Presentation/Unit/CourseUnitView.swift index 07a56ea25..5c96ce420 100644 --- a/Course/Course/Presentation/Unit/CourseUnitView.swift +++ b/Course/Course/Presentation/Unit/CourseUnitView.swift @@ -36,7 +36,9 @@ public struct CourseUnitView: View { private let portraitTopSpacing: CGFloat = 60 private let landscapeTopSpacing: CGFloat = 75 - let isDropdownActive: Bool = true + let isDropdownActive: Bool = { + Container.shared.resolve(ConfigProtocol.self)?.uiComponents.isVerticalsMenuEnabled ?? false + }() var sequenceTitle: String { let chapter = viewModel.chapters[viewModel.chapterIndex] @@ -371,25 +373,6 @@ public struct CourseUnitView: View { } } -struct DropdownAnimationModifier: ViewModifier where V: Equatable { - var isActive: Bool - var value: V - func body(content: Content) -> some View { - if isActive { - content - .animation(.easeOut(duration: 0.2), value: value) - } else { - content - } - } -} - -extension View { - func dropdownAnimation(isActive: Bool, value: V) -> some View where V: Equatable { - modifier(DropdownAnimationModifier(isActive: isActive, value: value)) - } -} - #if DEBUG //swiftlint:disable all struct CourseUnitView_Previews: PreviewProvider { diff --git a/Course/Course/Presentation/Unit/Subviews/DropdownList/DropdownAnimationModifier.swift b/Course/Course/Presentation/Unit/Subviews/DropdownList/DropdownAnimationModifier.swift new file mode 100644 index 000000000..b1529edb9 --- /dev/null +++ b/Course/Course/Presentation/Unit/Subviews/DropdownList/DropdownAnimationModifier.swift @@ -0,0 +1,27 @@ +// +// DropdownAnimationModifier.swift +// Course +// +// Created by Vadim Kuznetsov on 5.12.23. +// + +import SwiftUI + +struct DropdownAnimationModifier: ViewModifier where V: Equatable { + var isActive: Bool + var value: V + func body(content: Content) -> some View { + if isActive { + content + .animation(.easeOut(duration: 0.2), value: value) + } else { + content + } + } +} + +extension View { + func dropdownAnimation(isActive: Bool, value: V) -> some View where V: Equatable { + modifier(DropdownAnimationModifier(isActive: isActive, value: value)) + } +} From fa1d0ee212186b4c2db309e51ff62b6b768dbb59 Mon Sep 17 00:00:00 2001 From: forgotvas Date: Thu, 7 Dec 2023 23:39:59 +0300 Subject: [PATCH 10/11] chore: PR review fixes --- .../Course/Presentation/Unit/CourseUnitView.swift | 13 +++++++------ .../Presentation/Unit/CourseUnitViewModel.swift | 2 +- OpenEdX/Router.swift | 12 ++++++++++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Course/Course/Presentation/Unit/CourseUnitView.swift b/Course/Course/Presentation/Unit/CourseUnitView.swift index 5c96ce420..6cbcb2aa3 100644 --- a/Course/Course/Presentation/Unit/CourseUnitView.swift +++ b/Course/Course/Presentation/Unit/CourseUnitView.swift @@ -9,7 +9,6 @@ import Foundation import SwiftUI import Core import Discussion -import Swinject import Combine import Theme @@ -36,9 +35,7 @@ public struct CourseUnitView: View { private let portraitTopSpacing: CGFloat = 60 private let landscapeTopSpacing: CGFloat = 75 - let isDropdownActive: Bool = { - Container.shared.resolve(ConfigProtocol.self)?.uiComponents.isVerticalsMenuEnabled ?? false - }() + let isDropdownActive: Bool var sequenceTitle: String { let chapter = viewModel.chapters[viewModel.chapterIndex] @@ -57,10 +54,14 @@ public struct CourseUnitView: View { viewModel.verticals.count > 1 } - public init(viewModel: CourseUnitViewModel, - sectionName: String) { + public init( + viewModel: CourseUnitViewModel, + sectionName: String, + isDropdownActive: Bool = false + ) { self.viewModel = viewModel self.sectionName = sectionName + self.isDropdownActive = isDropdownActive viewModel.loadIndex() viewModel.nextTitles() } diff --git a/Course/Course/Presentation/Unit/CourseUnitViewModel.swift b/Course/Course/Presentation/Unit/CourseUnitViewModel.swift index ca1f260db..6b2b10411 100644 --- a/Course/Course/Presentation/Unit/CourseUnitViewModel.swift +++ b/Course/Course/Presentation/Unit/CourseUnitViewModel.swift @@ -182,7 +182,7 @@ public class CourseUnitViewModel: ObservableObject { } func route(to vertical: CourseVertical) { - if let index = verticals.firstIndex(where: {$0.id == vertical.id}), + if let index = verticals.firstIndex(where: { $0.id == vertical.id }), let block = vertical.childs.first { router.replaceCourseUnit( courseName: courseName, diff --git a/OpenEdX/Router.swift b/OpenEdX/Router.swift index 7b0d391fc..264508884 100644 --- a/OpenEdX/Router.swift +++ b/OpenEdX/Router.swift @@ -302,7 +302,11 @@ public class Router: AuthorizationRouter, sequentialIndex, verticalIndex )! - let view = CourseUnitView(viewModel: viewModel, sectionName: sectionName) + + let config = Container.shared.resolve(ConfigProtocol.self) + let isDropdownActive = config?.uiComponents.isVerticalsMenuEnabled ?? false + + let view = CourseUnitView(viewModel: viewModel, sectionName: sectionName, isDropdownActive: isDropdownActive) let controller = UIHostingController(rootView: view) navigationController.pushViewController(controller, animated: true) } @@ -344,7 +348,11 @@ public class Router: AuthorizationRouter, sequentialIndex, verticalIndex )! - let view = CourseUnitView(viewModel: viewModel, sectionName: sectionName) + + let config = Container.shared.resolve(ConfigProtocol.self) + let isDropdownActive = config?.uiComponents.isVerticalsMenuEnabled ?? false + + let view = CourseUnitView(viewModel: viewModel, sectionName: sectionName, isDropdownActive: isDropdownActive) let controllerUnit = UIHostingController(rootView: view) var controllers = navigationController.viewControllers controllers.removeLast(2) From 4ef5de85a2a264006840b0542e0726e2b757e4fc Mon Sep 17 00:00:00 2001 From: forgotvas Date: Fri, 8 Dec 2023 10:22:58 +0300 Subject: [PATCH 11/11] fix: merge conflict --- Core/Core.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Core/Core.xcodeproj/project.pbxproj b/Core/Core.xcodeproj/project.pbxproj index 6d9e449a4..5bdd59a8e 100644 --- a/Core/Core.xcodeproj/project.pbxproj +++ b/Core/Core.xcodeproj/project.pbxproj @@ -85,6 +85,7 @@ 02F6EF3B28D9B8EC00835477 /* CourseCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02F6EF3A28D9B8EC00835477 /* CourseCellView.swift */; }; 02F6EF4A28D9F0A700835477 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02F6EF4928D9F0A700835477 /* DateExtension.swift */; }; 02F98A7F28F81EE900DE94C0 /* Container+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02F98A7E28F81EE900DE94C0 /* Container+App.swift */; }; + 0604C9AA2B22FACF00AD5DBF /* UIComponentsConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0604C9A92B22FACF00AD5DBF /* UIComponentsConfig.swift */; }; 070019A528F6F17900D5FC78 /* Data_Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = 070019A428F6F17900D5FC78 /* Data_Media.swift */; }; 070019AC28F6FD0100D5FC78 /* CourseDetailBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 070019AB28F6FD0100D5FC78 /* CourseDetailBlock.swift */; }; 070019AE28F701B200D5FC78 /* Certificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 070019AD28F701B200D5FC78 /* Certificate.swift */; }; @@ -245,6 +246,7 @@ 02F6EF3A28D9B8EC00835477 /* CourseCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseCellView.swift; sourceTree = ""; }; 02F6EF4928D9F0A700835477 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = ""; }; 02F98A7E28F81EE900DE94C0 /* Container+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Container+App.swift"; sourceTree = ""; }; + 0604C9A92B22FACF00AD5DBF /* UIComponentsConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIComponentsConfig.swift; sourceTree = ""; }; 070019A428F6F17900D5FC78 /* Data_Media.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data_Media.swift; sourceTree = ""; }; 070019AB28F6FD0100D5FC78 /* CourseDetailBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDetailBlock.swift; sourceTree = ""; }; 070019AD28F701B200D5FC78 /* Certificate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Certificate.swift; sourceTree = ""; }; @@ -688,6 +690,7 @@ DBF6F2422B014AF30098414B /* Config */ = { isa = PBXGroup; children = ( + 0604C9A92B22FACF00AD5DBF /* UIComponentsConfig.swift */, 0727876F28D23411002E9142 /* Config.swift */, DBF6F2402B014ADA0098414B /* FirebaseConfig.swift */, DBF6F2492B0380E00098414B /* FeaturesConfig.swift */, @@ -1020,6 +1023,7 @@ 025B36752A13B7D5001A640E /* UnitButtonView.swift in Sources */, 028F9F39293A452B00DE65D0 /* ResetPassword.swift in Sources */, 0233D56F2AF13EB200BAC8BD /* StarRatingView.swift in Sources */, + 0604C9AA2B22FACF00AD5DBF /* UIComponentsConfig.swift in Sources */, 027BD3B82909476200392132 /* DismissKeyboardTapViewModifier.swift in Sources */, 024BE3DF29B2615500BCDEE2 /* CGColorExtension.swift in Sources */, 0770DE6128D0B2CB006D8A5D /* Assets.swift in Sources */,