From 291a399fc78f313bda9d6af5f88392e06e7f277d Mon Sep 17 00:00:00 2001
From: Gene <76485998+eyatsenkoperpetio@users.noreply.github.com>
Date: Mon, 12 Feb 2024 14:04:54 +0100
Subject: [PATCH] Improvements for Download videos (#279)
* chore: click loader and open download detail
* chore: new delete video alert
* chore: remove question mark
* chore: back config directory
* chore: resolve PR comments
---
.../warning.imageset/Contents.json | 22 +
.../warning.imageset/exclamation-mark 1.svg | 3 +
.../warning.imageset/exclamation-mark 2.svg | 3 +
Core/Core/Extensions/ViewExtension.swift | 13 +
Core/Core/SwiftGen/Assets.swift | 1 +
Core/Core/View/Base/AlertView.swift | 545 ++++++++++--------
Core/Core/View/Base/DownloadView.swift | 29 +-
.../Container/CourseContainerView.swift | 2 +-
.../Container/CourseContainerViewModel.swift | 8 +
Course/Course/Presentation/CourseRouter.swift | 11 +-
.../Downloads/DownloadsView.swift | 63 +-
.../Downloads/DownloadsViewModel.swift | 5 +
.../CourseStructureNestedListView.swift | 23 +-
.../CourseVideoDownloadBarViewModel.swift | 6 +-
OpenEdX/Router.swift | 11 +-
.../Subviews/ProfileSupportInfoView.swift | 1 -
16 files changed, 462 insertions(+), 284 deletions(-)
create mode 100644 Core/Core/Assets.xcassets/warning.imageset/Contents.json
create mode 100644 Core/Core/Assets.xcassets/warning.imageset/exclamation-mark 1.svg
create mode 100644 Core/Core/Assets.xcassets/warning.imageset/exclamation-mark 2.svg
diff --git a/Core/Core/Assets.xcassets/warning.imageset/Contents.json b/Core/Core/Assets.xcassets/warning.imageset/Contents.json
new file mode 100644
index 000000000..417f9d554
--- /dev/null
+++ b/Core/Core/Assets.xcassets/warning.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "filename" : "exclamation-mark 1.svg",
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "exclamation-mark 2.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Core/Core/Assets.xcassets/warning.imageset/exclamation-mark 1.svg b/Core/Core/Assets.xcassets/warning.imageset/exclamation-mark 1.svg
new file mode 100644
index 000000000..e79501009
--- /dev/null
+++ b/Core/Core/Assets.xcassets/warning.imageset/exclamation-mark 1.svg
@@ -0,0 +1,3 @@
+
diff --git a/Core/Core/Assets.xcassets/warning.imageset/exclamation-mark 2.svg b/Core/Core/Assets.xcassets/warning.imageset/exclamation-mark 2.svg
new file mode 100644
index 000000000..86b215583
--- /dev/null
+++ b/Core/Core/Assets.xcassets/warning.imageset/exclamation-mark 2.svg
@@ -0,0 +1,3 @@
+
diff --git a/Core/Core/Extensions/ViewExtension.swift b/Core/Core/Extensions/ViewExtension.swift
index 1c8833612..ef2e493a2 100644
--- a/Core/Core/Extensions/ViewExtension.swift
+++ b/Core/Core/Extensions/ViewExtension.swift
@@ -256,6 +256,19 @@ public extension View {
}
}
+public extension View {
+ @ViewBuilder
+ func sheetNavigation(isSheet: Bool) -> some View {
+ if isSheet {
+ NavigationView {
+ self
+ }
+ } else {
+ self
+ }
+ }
+}
+
private struct FirstAppear: ViewModifier {
let action: () -> Void
diff --git a/Core/Core/SwiftGen/Assets.swift b/Core/Core/SwiftGen/Assets.swift
index 1d1c84625..50f49634f 100644
--- a/Core/Core/SwiftGen/Assets.swift
+++ b/Core/Core/SwiftGen/Assets.swift
@@ -109,6 +109,7 @@ public enum CoreAssets {
public static let playVideo = ImageAsset(name: "playVideo")
public static let star = ImageAsset(name: "star")
public static let starOutline = ImageAsset(name: "star_outline")
+ public static let warning = ImageAsset(name: "warning")
public static let warningFilled = ImageAsset(name: "warning_filled")
}
// swiftlint:enable identifier_name line_length nesting type_body_length type_name
diff --git a/Core/Core/View/Base/AlertView.swift b/Core/Core/View/Base/AlertView.swift
index ad501bdb9..70e99bd2b 100644
--- a/Core/Core/View/Base/AlertView.swift
+++ b/Core/Core/View/Base/AlertView.swift
@@ -13,12 +13,13 @@ public enum AlertViewType: Equatable {
case action(String, SwiftUI.Image)
case logOut
case leaveProfile
-
+ case deleteVideo
+
var contentPadding: CGFloat {
switch self {
case .`default`:
return 16
- case .action, .logOut, .leaveProfile:
+ case .action, .logOut, .leaveProfile, .deleteVideo:
return 36
}
}
@@ -69,248 +70,342 @@ public struct AlertView: View {
self.nextSectionTapped = nextSectionTapped
type = .action(mainAction, image)
}
-
+
public var body: some View {
ZStack(alignment: .center) {
Color.black.opacity(0.5)
.onTapGesture {
onCloseTapped()
}
- ZStack(alignment: .topTrailing) {
- adaptiveStack(spacing: isHorizontal ? 10 : 20, isHorizontal: (type == .leaveProfile && isHorizontal)) {
- if type == .logOut {
- HStack {
- Spacer(minLength: 100)
- CoreAssets.logOut.swiftUIImage
- .padding(.top, isHorizontal ? 20 : 54)
- Spacer(minLength: 100)
- }
- Text(alertMessage)
- .font(Theme.Fonts.titleLarge)
- .padding(.vertical, isHorizontal ? 6 : 40)
- .multilineTextAlignment(.center)
- .padding(.horizontal, 40)
- .frame(maxWidth: 250)
- } else if type == .leaveProfile {
+ content
+ }
+ .ignoresSafeArea()
+ }
+
+ private var content: some View {
+ ZStack(alignment: .topTrailing) {
+ adaptiveStack(
+ spacing: isHorizontal ? 10 : 20,
+ isHorizontal: (type == .leaveProfile && isHorizontal)
+ ) {
+ titles
+ buttons
+ }
+ close
+ }
+ .frame(maxWidth: type == .logOut ? 390 : nil)
+ .background(
+ Theme.Shapes.cardShape
+ .fill(Theme.Colors.cardViewBackground)
+ .shadow(radius: 24)
+ .fixedSize(horizontal: false, vertical: false)
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 12)
+ .stroke(
+ style: .init(
+ lineWidth: 1,
+ lineCap: .round,
+ lineJoin: .round,
+ miterLimit: 1
+ )
+ )
+ .foregroundColor(Theme.Colors.backgroundStroke)
+ .fixedSize(horizontal: false, vertical: false)
+ )
+ .frame(maxWidth: isHorizontal ? nil : 390)
+ .padding(40)
+ }
+
+ private var close: some View {
+ Button {
+ onCloseTapped()
+ } label: {
+ Image(systemName: "xmark")
+ .padding(.trailing, 40)
+ .padding(.top, 24)
+ }
+ }
+
+ @ViewBuilder
+ private var titles: some View {
+ switch type {
+ case .logOut:
+ HStack {
+ Spacer(minLength: 100)
+ CoreAssets.logOut.swiftUIImage
+ .padding(.top, isHorizontal ? 20 : 54)
+ Spacer(minLength: 100)
+ }
+ Text(alertMessage)
+ .font(Theme.Fonts.titleLarge)
+ .padding(.vertical, isHorizontal ? 6 : 40)
+ .multilineTextAlignment(.center)
+ .padding(.horizontal, 40)
+ .frame(maxWidth: 250)
+ case .leaveProfile, .deleteVideo:
+ VStack(spacing: 20) {
+ if type == .deleteVideo {
+ CoreAssets.warning.swiftUIImage
+ .padding(.top, isHorizontal ? 20 : 54)
+ } else {
+ CoreAssets.leaveProfile.swiftUIImage
+ .padding(.top, isHorizontal ? 20 : 54)
+ }
+ Text(alertTitle)
+ .font(Theme.Fonts.titleLarge)
+ .padding(.horizontal, 40)
+ Text(alertMessage)
+ .font(Theme.Fonts.bodyMedium)
+ .multilineTextAlignment(.center)
+ .padding(.horizontal, 40)
+
+ }
+ .padding(.bottom, 20)
+ default:
+ HStack {
+ VStack(alignment: .center, spacing: 10) {
+ if case let .action(_, image) = type {
+ image.padding(.top, 48)
+ }
+ if case let .default(_, image) = type {
+ image.flatMap { $0.padding(.top, 48) }
+ }
+ Text(alertTitle)
+ .font(Theme.Fonts.titleLarge)
+ .padding(.horizontal, 40)
+ .padding(.top, 10)
+ Text(alertMessage)
+ .font(Theme.Fonts.bodyMedium)
+ .multilineTextAlignment(.center)
+ .padding(.horizontal, 40)
+ .frame(maxWidth: 250)
+ }
+ if isHorizontal {
+ if case let .action(action, _) = type {
VStack(spacing: 20) {
- CoreAssets.leaveProfile.swiftUIImage
- .padding(.top, isHorizontal ? 20 : 54)
- Text(alertTitle)
- .font(Theme.Fonts.titleLarge)
- .padding(.horizontal, 40)
- Text(alertMessage)
- .font(Theme.Fonts.bodyMedium)
- .multilineTextAlignment(.center)
- .padding(.horizontal, 40)
-
- }.padding(.bottom, 20)
- } else {
- HStack {
- VStack(alignment: .center, spacing: 10) {
- if case let .action(_, image) = type {
- image.padding(.top, 48)
- }
- if case let .default(_, image) = type {
- image.flatMap { $0.padding(.top, 48) }
- }
- Text(alertTitle)
- .font(Theme.Fonts.titleLarge)
+ if nextSectionName != nil {
+ UnitButtonView(type: .nextSection, action: { nextSectionTapped() })
+ .frame(maxWidth: 215)
+ }
+ UnitButtonView(type: .custom(action),
+ bgColor: .clear,
+ action: { okTapped() })
+ .frame(maxWidth: 215)
+
+ if let nextSectionName {
+ Group {
+ Text(CoreLocalization.Courseware.nextSectionDescriptionFirst) +
+ Text(nextSectionName) +
+ Text(CoreLocalization.Courseware.nextSectionDescriptionLast)
+ }.frame(maxWidth: 215)
.padding(.horizontal, 40)
- .padding(.top, 10)
- Text(alertMessage)
- .font(Theme.Fonts.bodyMedium)
.multilineTextAlignment(.center)
- .padding(.horizontal, 40)
- .frame(maxWidth: 250)
+ .font(Theme.Fonts.labelSmall)
+ .foregroundColor(Theme.Colors.textSecondary)
}
- if isHorizontal {
- if case let .action(action, _) = type {
- VStack(spacing: 20) {
- if nextSectionName != nil {
- UnitButtonView(type: .nextSection, action: { nextSectionTapped() })
- .frame(maxWidth: 215)
- }
- UnitButtonView(type: .custom(action),
- bgColor: .clear,
- action: { okTapped() })
- .frame(maxWidth: 215)
-
- if let nextSectionName {
- Group {
- Text(CoreLocalization.Courseware.nextSectionDescriptionFirst) +
- Text(nextSectionName) +
- Text(CoreLocalization.Courseware.nextSectionDescriptionLast)
- }.frame(maxWidth: 215)
- .padding(.horizontal, 40)
- .multilineTextAlignment(.center)
- .font(Theme.Fonts.labelSmall)
- .foregroundColor(Theme.Colors.textSecondary)
- }
- }.padding(.top, 70)
- .padding(.trailing, 20)
- }
- }
- }
+ }.padding(.top, 70)
+ .padding(.trailing, 20)
}
- HStack {
- switch type {
- case let .`default`(positiveAction, _):
- HStack {
- StyledButton(positiveAction, action: { okTapped() })
- .frame(maxWidth: 135)
- StyledButton(CoreLocalization.Alert.cancel, action: { onCloseTapped() })
- .frame(maxWidth: 135)
- .saturation(0)
- }
- .padding(.leading, 10)
- .padding(.trailing, 10)
- .padding(.bottom, 10)
- case let .action(action, _):
- if !isHorizontal {
- VStack(spacing: 20) {
- if nextSectionName != nil {
- UnitButtonView(type: .nextSection, action: { nextSectionTapped() })
- .frame(maxWidth: 215)
- }
- UnitButtonView(type: .custom(action),
- bgColor: .clear,
- action: { okTapped() })
- .frame(maxWidth: 215)
-
- if let nextSectionName {
- Group {
- Text(CoreLocalization.Courseware.nextSectionDescriptionFirst) +
- Text(nextSectionName) +
- Text(CoreLocalization.Courseware.nextSectionDescriptionLast)
- }.frame(maxWidth: 215)
- .padding(.horizontal, 40)
- .multilineTextAlignment(.center)
- .font(Theme.Fonts.labelSmall)
- .foregroundColor(Theme.Colors.textSecondary)
- }
- }
- } else {
- EmptyView()
- }
- case .logOut:
- Button(action: {
- okTapped()
- }, label: {
- ZStack {
- Text(CoreLocalization.Alert.logout)
- .foregroundColor(.black)
- .font(Theme.Fonts.labelLarge)
- .frame(maxWidth: .infinity)
- .padding(.horizontal, 16)
- Image(systemName: "rectangle.portrait.and.arrow.right")
- .foregroundColor(.black)
- .frame(minWidth: 190, minHeight: 48, alignment: .trailing)
- }
- .frame(maxWidth: 215, minHeight: 48)
- })
- .background(
- Theme.Shapes.buttonShape
- .fill(Theme.Colors.warning)
- )
- .overlay(
- RoundedRectangle(cornerRadius: 8)
- .stroke(style: .init(
- lineWidth: 1,
- lineCap: .round,
- lineJoin: .round,
- miterLimit: 1
- ))
- .foregroundColor(.clear)
- )
- .frame(maxWidth: 215)
- case .leaveProfile:
- VStack(spacing: 0) {
- Button(action: {
- okTapped()
- }, label: {
- ZStack {
- Text(CoreLocalization.Alert.leave)
- .foregroundColor(.black)
- .font(Theme.Fonts.labelLarge)
- .frame(maxWidth: .infinity)
- .padding(.horizontal, 16)
- }
- .frame(maxWidth: 215, minHeight: 48)
- })
- .background(
- Theme.Shapes.buttonShape
- .fill(Theme.Colors.warning)
- )
- .overlay(
- RoundedRectangle(cornerRadius: 8)
- .stroke(style: .init(
- lineWidth: 1,
- lineCap: .round,
- lineJoin: .round,
- miterLimit: 1
- ))
- .foregroundColor(.clear)
- )
- .frame(maxWidth: 215)
- .padding(.bottom, isHorizontal ? 10 : 24)
- Button(action: {
- onCloseTapped()
- }, label: {
- ZStack {
- Text(CoreLocalization.Alert.keepEditing)
- .foregroundColor(Theme.Colors.textPrimary)
- .font(Theme.Fonts.labelLarge)
- .frame(maxWidth: .infinity)
- .padding(.horizontal, 16)
- }
- .frame(maxWidth: 215, minHeight: 48)
- })
- .background(
- Theme.Shapes.buttonShape
- .fill(.clear)
- )
- .overlay(
- RoundedRectangle(cornerRadius: 8)
- .stroke(style: .init(
- lineWidth: 1,
- lineCap: .round,
- lineJoin: .round,
- miterLimit: 1
- ))
- .foregroundColor(Theme.Colors.textPrimary)
- )
+ }
+ }
+ }
+ }
+
+ private var buttons: some View {
+ HStack {
+ switch type {
+ case let .`default`(positiveAction, _):
+ HStack {
+ StyledButton(positiveAction, action: { okTapped() })
+ .frame(maxWidth: 135)
+ StyledButton(CoreLocalization.Alert.cancel, action: { onCloseTapped() })
+ .frame(maxWidth: 135)
+ .saturation(0)
+ }
+ .padding(.leading, 10)
+ .padding(.trailing, 10)
+ .padding(.bottom, 10)
+ case let .action(action, _):
+ if !isHorizontal {
+ VStack(spacing: 20) {
+ if nextSectionName != nil {
+ UnitButtonView(type: .nextSection, action: { nextSectionTapped() })
.frame(maxWidth: 215)
- }.padding(.trailing, isHorizontal ? 20 : 0)
+ }
+ UnitButtonView(type: .custom(action),
+ bgColor: .clear,
+ action: { okTapped() })
+ .frame(maxWidth: 215)
+
+ if let nextSectionName {
+ Group {
+ Text(CoreLocalization.Courseware.nextSectionDescriptionFirst) +
+ Text(nextSectionName) +
+ Text(CoreLocalization.Courseware.nextSectionDescriptionLast)
+ }.frame(maxWidth: 215)
+ .padding(.horizontal, 40)
+ .multilineTextAlignment(.center)
+ .font(Theme.Fonts.labelSmall)
+ .foregroundColor(Theme.Colors.textSecondary)
}
}
- .padding(.top, 16)
- .padding(.bottom, isHorizontal ? 16 : type.contentPadding)
+ } else {
+ EmptyView()
}
+ case .logOut:
Button(action: {
- onCloseTapped()
+ okTapped()
}, label: {
- Image(systemName: "xmark")
- .padding(.trailing, 40)
- .padding(.top, 24)
+ ZStack {
+ Text(CoreLocalization.Alert.logout)
+ .foregroundColor(.black)
+ .font(Theme.Fonts.labelLarge)
+ .frame(maxWidth: .infinity)
+ .padding(.horizontal, 16)
+ Image(systemName: "rectangle.portrait.and.arrow.right")
+ .foregroundColor(.black)
+ .frame(minWidth: 190, minHeight: 48, alignment: .trailing)
+ }
+ .frame(maxWidth: 215, minHeight: 48)
})
-
- }.frame(maxWidth: type == .logOut ? 390 : nil)
- .background(
- Theme.Shapes.cardShape
- .fill(Theme.Colors.cardViewBackground)
- .shadow(radius: 24)
- .fixedSize(horizontal: false, vertical: false)
- )
- .overlay(
- RoundedRectangle(cornerRadius: 12)
- .stroke(style: .init(lineWidth: 1, lineCap: .round, lineJoin: .round, miterLimit: 1))
- .foregroundColor(Theme.Colors.backgroundStroke)
- .fixedSize(horizontal: false, vertical: false)
- )
- .frame(maxWidth: isHorizontal ? nil : 390)
- .padding(40)
+ .background(
+ Theme.Shapes.buttonShape
+ .fill(Theme.Colors.warning)
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 8)
+ .stroke(style: .init(
+ lineWidth: 1,
+ lineCap: .round,
+ lineJoin: .round,
+ miterLimit: 1
+ ))
+ .foregroundColor(.clear)
+ )
+ .frame(maxWidth: 215)
+ case .leaveProfile:
+ VStack(spacing: 0) {
+ Button(action: {
+ okTapped()
+ }, label: {
+ ZStack {
+ Text(CoreLocalization.Alert.leave)
+ .foregroundColor(.black)
+ .font(Theme.Fonts.labelLarge)
+ .frame(maxWidth: .infinity)
+ .padding(.horizontal, 16)
+ }
+ .frame(maxWidth: 215, minHeight: 48)
+ })
+ .background(
+ Theme.Shapes.buttonShape
+ .fill(Theme.Colors.warning)
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 8)
+ .stroke(style: .init(
+ lineWidth: 1,
+ lineCap: .round,
+ lineJoin: .round,
+ miterLimit: 1
+ ))
+ .foregroundColor(.clear)
+ )
+ .frame(maxWidth: 215)
+ .padding(.bottom, isHorizontal ? 10 : 24)
+ Button(action: {
+ onCloseTapped()
+ }, label: {
+ ZStack {
+ Text(CoreLocalization.Alert.keepEditing)
+ .foregroundColor(Theme.Colors.textPrimary)
+ .font(Theme.Fonts.labelLarge)
+ .frame(maxWidth: .infinity)
+ .padding(.horizontal, 16)
+ }
+ .frame(maxWidth: 215, minHeight: 48)
+ })
+ .background(
+ Theme.Shapes.buttonShape
+ .fill(.clear)
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 8)
+ .stroke(style: .init(
+ lineWidth: 1,
+ lineCap: .round,
+ lineJoin: .round,
+ miterLimit: 1
+ ))
+ .foregroundColor(Theme.Colors.textPrimary)
+ )
+ .frame(maxWidth: 215)
+ }
+ .padding(.trailing, isHorizontal ? 20 : 0)
+ case .deleteVideo:
+ VStack(spacing: 0) {
+ Button {
+ okTapped()
+ } label: {
+ ZStack {
+ Text(CoreLocalization.Alert.delete)
+ .foregroundColor(.black)
+ .font(Theme.Fonts.labelLarge)
+ .frame(maxWidth: .infinity)
+ .padding(.horizontal, 16)
+ }
+ .frame(maxWidth: 215, minHeight: 48)
+ }
+ .background(
+ Theme.Shapes.buttonShape
+ .fill(Theme.Colors.warning)
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 8)
+ .stroke(style: .init(
+ lineWidth: 1,
+ lineCap: .round,
+ lineJoin: .round,
+ miterLimit: 1
+ ))
+ .foregroundColor(.clear)
+ )
+ .frame(maxWidth: 215)
+ .padding(.bottom, isHorizontal ? 10 : 24)
+ Button(action: {
+ onCloseTapped()
+ }, label: {
+ ZStack {
+ Text(CoreLocalization.Alert.cancel)
+ .foregroundColor(Theme.Colors.textPrimary)
+ .font(Theme.Fonts.labelLarge)
+ .frame(maxWidth: .infinity)
+ .padding(.horizontal, 16)
+ }
+ .frame(maxWidth: 215, minHeight: 48)
+ })
+ .background(
+ Theme.Shapes.buttonShape
+ .fill(.clear)
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 8)
+ .stroke(style: .init(
+ lineWidth: 1,
+ lineCap: .round,
+ lineJoin: .round,
+ miterLimit: 1
+ ))
+ .foregroundColor(Theme.Colors.textPrimary)
+ )
+ .frame(maxWidth: 215)
+ }
+ .padding(.trailing, isHorizontal ? 20 : 0)
+ }
}
- .ignoresSafeArea()
+ .padding(.top, 16)
+ .padding(.bottom, isHorizontal ? 16 : type.contentPadding)
}
}
diff --git a/Core/Core/View/Base/DownloadView.swift b/Core/Core/View/Base/DownloadView.swift
index 806aee51b..37f63e41d 100644
--- a/Core/Core/View/Base/DownloadView.swift
+++ b/Core/Core/View/Base/DownloadView.swift
@@ -19,11 +19,14 @@ public struct DownloadAvailableView: View {
}
public var body: some View {
- CoreAssets.startDownloading.swiftUIImage.renderingMode(.template)
- .resizable()
- .scaledToFit()
- .frame(width: 24, height: 24)
- .foregroundColor(Theme.Colors.textPrimary)
+ VStack(spacing: 0) {
+ CoreAssets.startDownloading.swiftUIImage.renderingMode(.template)
+ .resizable()
+ .scaledToFit()
+ .frame(width: 24, height: 24)
+ .foregroundColor(Theme.Colors.textPrimary)
+ }
+ .frame(width: 30, height: 30)
}
}
@@ -33,13 +36,12 @@ public struct DownloadProgressView: View {
public var body: some View {
ZStack {
- ProgressBar(size: 36, lineWidth: 1.75)
+ ProgressBar(size: 30, lineWidth: 1.75)
CoreAssets.stopDownloading.swiftUIImage.renderingMode(.template)
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundColor(Theme.Colors.textPrimary)
- .padding(6)
}
}
}
@@ -49,10 +51,13 @@ public struct DownloadFinishedView: View {
}
public var body: some View {
- CoreAssets.deleteDownloading.swiftUIImage.renderingMode(.template)
- .resizable()
- .scaledToFit()
- .frame(width: 24, height: 24)
- .foregroundColor(Theme.Colors.textPrimary)
+ VStack(spacing: 0) {
+ CoreAssets.deleteDownloading.swiftUIImage.renderingMode(.template)
+ .resizable()
+ .scaledToFit()
+ .frame(width: 24, height: 24)
+ .foregroundColor(Theme.Colors.textPrimary)
+ }
+ .frame(width: 30, height: 30)
}
}
diff --git a/Course/Course/Presentation/Container/CourseContainerView.swift b/Course/Course/Presentation/Container/CourseContainerView.swift
index 2e154ba69..79a797ddd 100644
--- a/Course/Course/Presentation/Container/CourseContainerView.swift
+++ b/Course/Course/Presentation/Container/CourseContainerView.swift
@@ -20,8 +20,8 @@ public struct CourseContainerView: View {
case course
case videos
- case dates
case discussion
+ case dates
case handounds
var title: String {
diff --git a/Course/Course/Presentation/Container/CourseContainerViewModel.swift b/Course/Course/Presentation/Container/CourseContainerViewModel.swift
index ce5463cd9..9a7a34c4c 100644
--- a/Course/Course/Presentation/Container/CourseContainerViewModel.swift
+++ b/Course/Course/Presentation/Container/CourseContainerViewModel.swift
@@ -165,6 +165,14 @@ public class CourseContainerViewModel: BaseCourseViewModel {
return verticals.flatMap { $0.vertical.childs.filter { $0.isDownloadable } }
}
+ func getTasks(sequential: CourseSequential) -> [DownloadDataTask] {
+ let blocks = verticalsBlocksDownloadable(by: sequential)
+ let tasks = blocks.compactMap { block in
+ courseDownloadTasks.first(where: { $0.id == block.id})
+ }
+ return tasks
+ }
+
func continueDownload() {
guard let blocks = waitingDownloads else {
return
diff --git a/Course/Course/Presentation/CourseRouter.swift b/Course/Course/Presentation/CourseRouter.swift
index 35619bc2d..d4cd7c68a 100644
--- a/Course/Course/Presentation/CourseRouter.swift
+++ b/Course/Course/Presentation/CourseRouter.swift
@@ -55,6 +55,11 @@ public protocol CourseRouter: BaseRouter {
componentID: String,
courseStructure: CourseStructure
)
+
+ func showDownloads(
+ downloads: [DownloadDataTask],
+ manager: DownloadManagerProtocol
+ )
}
// Mark - For testing and SwiftUI preview
@@ -108,6 +113,10 @@ public class CourseRouterMock: BaseRouterMock, CourseRouter {
componentID: String,
courseStructure: CourseStructure
) {}
-
+
+ public func showDownloads(
+ downloads: [Core.DownloadDataTask],
+ manager: Core.DownloadManagerProtocol
+ ) {}
}
#endif
diff --git a/Course/Course/Presentation/Downloads/DownloadsView.swift b/Course/Course/Presentation/Downloads/DownloadsView.swift
index c6b0262d1..958380a6d 100644
--- a/Course/Course/Presentation/Downloads/DownloadsView.swift
+++ b/Course/Course/Presentation/Downloads/DownloadsView.swift
@@ -10,49 +10,64 @@ import Core
import Theme
import Combine
-struct DownloadsView: View {
+public struct DownloadsView: View {
// MARK: - Properties
@Environment(\.dismiss) private var dismiss
@StateObject private var viewModel: DownloadsViewModel
- init(
+ var isSheet: Bool = true
+
+ public init(
+ isSheet: Bool = true,
courseId: String? = nil,
+ downloads: [DownloadDataTask] = [],
manager: DownloadManagerProtocol
) {
+ self.isSheet = isSheet
self._viewModel = .init(
- wrappedValue: .init(courseId: courseId, manager: manager)
+ wrappedValue: .init(
+ courseId: courseId,
+ downloads: downloads,
+ manager: manager
+ )
)
}
// MARK: - Body
- var body: some View {
- NavigationView {
- ScrollView {
- LazyVStack {
- ForEach(
- viewModel.downloads,
- content: cell
- )
- }
+ public var body: some View {
+ content
+ .sheetNavigation(isSheet: isSheet)
+ }
+
+ private var content: some View {
+ ScrollView {
+ LazyVStack {
+ ForEach(
+ viewModel.downloads,
+ content: cell
+ )
}
- .navigationBarTitleDisplayMode(.inline)
- .navigationTitle(CourseLocalization.Download.downloads)
- .toolbar {
- ToolbarItem(placement: .navigationBarTrailing) {
- Button {
- dismiss()
- } label: {
- Image(systemName: "xmark")
- .foregroundColor(Theme.Colors.accentColor)
+ }
+ .navigationBarTitleDisplayMode(.inline)
+ .navigationTitle(CourseLocalization.Download.downloads)
+ .if(isSheet) { view in
+ view
+ .toolbar {
+ ToolbarItem(placement: .navigationBarTrailing) {
+ Button {
+ dismiss()
+ } label: {
+ Image(systemName: "xmark")
+ .foregroundColor(Theme.Colors.accentColor)
+ }
+ .accessibilityIdentifier("close_button")
}
- .accessibilityIdentifier("close_button")
}
- }
- .padding(.top, 1)
}
+ .padding(.top, 1)
}
// MARK: - Views
diff --git a/Course/Course/Presentation/Downloads/DownloadsViewModel.swift b/Course/Course/Presentation/Downloads/DownloadsViewModel.swift
index b71566f58..78c063778 100644
--- a/Course/Course/Presentation/Downloads/DownloadsViewModel.swift
+++ b/Course/Course/Presentation/Downloads/DownloadsViewModel.swift
@@ -21,10 +21,12 @@ final class DownloadsViewModel: ObservableObject {
init(
courseId: String? = nil,
+ downloads: [DownloadDataTask] = [],
manager: DownloadManagerProtocol
) {
self.courseId = courseId
self.manager = manager
+ self.downloads = downloads
Task { await configure() }
observers()
}
@@ -52,6 +54,9 @@ final class DownloadsViewModel: ObservableObject {
defer {
filter()
}
+ if !downloads.isEmpty {
+ return
+ }
if let courseId = courseId {
downloads = await manager.getDownloadTasksForCourse(courseId)
return
diff --git a/Course/Course/Presentation/Outline/CourseStructure/CourseStructureNestedListView.swift b/Course/Course/Presentation/Outline/CourseStructure/CourseStructureNestedListView.swift
index 8cada0ce0..176594c23 100644
--- a/Course/Course/Presentation/Outline/CourseStructure/CourseStructureNestedListView.swift
+++ b/Course/Course/Presentation/Outline/CourseStructure/CourseStructureNestedListView.swift
@@ -131,22 +131,16 @@ struct CourseStructureNestedListView: View {
.accessibilityElement(children: .ignore)
.accessibilityLabel(CourseLocalization.Accessibility.download)
}
- downloadCount(sequential: sequential)
}
case .downloading:
if viewModel.isInternetAvaliable {
Button {
- Task {
- await viewModel.onDownloadViewTap(
- chapter: chapter,
- blockId: sequential.id,
- state: state
- )
- }
+ viewModel.router.showDownloads(
+ downloads: viewModel.getTasks(sequential: sequential),
+ manager: viewModel.manager
+ )
} label: {
- DownloadProgressView()
- .accessibilityElement(children: .ignore)
- .accessibilityLabel(CourseLocalization.Accessibility.cancelDownload)
+ ProgressBar(size: 30, lineWidth: 1.75)
}
}
case .finished:
@@ -168,18 +162,15 @@ struct CourseStructureNestedListView: View {
}
viewModel.router.dismiss(animated: true)
},
- type: .default(
- positiveAction: CoreLocalization.Alert.delete,
- image: CoreAssets.bgDelete.swiftUIImage
- )
+ type: .deleteVideo
)
} label: {
DownloadFinishedView()
.accessibilityElement(children: .ignore)
.accessibilityLabel(CourseLocalization.Accessibility.deleteDownload)
}
- downloadCount(sequential: sequential)
}
+ downloadCount(sequential: sequential)
}
}
diff --git a/Course/Course/Presentation/Subviews/CourseVideoDownloadBarView/CourseVideoDownloadBarViewModel.swift b/Course/Course/Presentation/Subviews/CourseVideoDownloadBarView/CourseVideoDownloadBarViewModel.swift
index 4a64dbf4f..91b0cd281 100644
--- a/Course/Course/Presentation/Subviews/CourseVideoDownloadBarView/CourseVideoDownloadBarViewModel.swift
+++ b/Course/Course/Presentation/Subviews/CourseVideoDownloadBarView/CourseVideoDownloadBarViewModel.swift
@@ -134,7 +134,7 @@ final class CourseVideoDownloadBarViewModel: ObservableObject {
}
self.courseViewModel.router.dismiss(animated: true)
},
- type: .default(positiveAction: CoreLocalization.Alert.delete, image: CoreAssets.bgDelete.swiftUIImage)
+ type: .deleteVideo
)
return
}
@@ -142,7 +142,7 @@ final class CourseVideoDownloadBarViewModel: ObservableObject {
if isOn {
courseViewModel.router.presentAlert(
alertTitle: "Warning",
- alertMessage: "\(CourseLocalization.Alert.stopDownloading) \"\(courseStructure.displayName)\"?",
+ alertMessage: "\(CourseLocalization.Alert.stopDownloading) \"\(courseStructure.displayName)\"",
positiveAction: CoreLocalization.Alert.accept,
onCloseTapped: { [weak self] in
self?.courseViewModel.router.dismiss(animated: true)
@@ -154,7 +154,7 @@ final class CourseVideoDownloadBarViewModel: ObservableObject {
}
self.courseViewModel.router.dismiss(animated: true)
},
- type: .default(positiveAction: CoreLocalization.Alert.accept, image: nil)
+ type: .deleteVideo
)
return
}
diff --git a/OpenEdX/Router.swift b/OpenEdX/Router.swift
index adc46d1f6..10a267a53 100644
--- a/OpenEdX/Router.swift
+++ b/OpenEdX/Router.swift
@@ -410,7 +410,16 @@ public class Router: AuthorizationRouter,
}
}
}
-
+
+ public func showDownloads(
+ downloads: [DownloadDataTask],
+ manager: DownloadManagerProtocol
+ ) {
+ let downloadsView = DownloadsView(isSheet: false, downloads: downloads, manager: manager)
+ let controller = UIHostingController(rootView: downloadsView)
+ navigationController.pushViewController(controller, animated: true)
+ }
+
public func replaceCourseUnit(
courseName: String,
blockId: String,
diff --git a/Profile/Profile/Presentation/Profile/Subviews/ProfileSupportInfoView.swift b/Profile/Profile/Presentation/Profile/Subviews/ProfileSupportInfoView.swift
index 81b669355..da2695845 100644
--- a/Profile/Profile/Presentation/Profile/Subviews/ProfileSupportInfoView.swift
+++ b/Profile/Profile/Presentation/Profile/Subviews/ProfileSupportInfoView.swift
@@ -45,7 +45,6 @@ struct ProfileSupportInfoView: View {
),
isEmailSupport: true
)
-
}
private func terms(url: URL) -> some View {