Skip to content

Commit

Permalink
Merge branch 'develop' into upstream-develop-crashfix-6
Browse files Browse the repository at this point in the history
  • Loading branch information
rnr committed Sep 30, 2024
2 parents 07eb634 + e85e970 commit 6e2f030
Show file tree
Hide file tree
Showing 101 changed files with 9,230 additions and 408 deletions.
840 changes: 840 additions & 0 deletions Authorization/AuthorizationTests/AuthorizationMock.generated.swift

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions Core/Core.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Core/Core/Analytics/CoreAnalytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ public enum AnalyticsEvent: String {
case finishVerticalBackToOutlineClicked = "Course:Unit Finish Back To Outline Clicked"
case courseOutlineCourseTabClicked = "Course:Home Tab"
case courseOutlineVideosTabClicked = "Course:Videos Tab"
case courseOutlineOfflineTabClicked = "Course:Offline Tab"
case courseOutlineDatesTabClicked = "Course:Dates Tab"
case courseOutlineDiscussionTabClicked = "Course:Discussion Tab"
case courseOutlineHandoutsTabClicked = "Course:Handouts Tab"
Expand Down Expand Up @@ -189,6 +190,7 @@ public enum EventBIValue: String {
case bulkDeleteVideosSubsection = "edx.bi.app.video.delete.subsection"
case dashboardCourseClicked = "edx.bi.app.course.dashboard"
case courseOutlineVideosTabClicked = "edx.bi.app.course.video_tab"
case courseOutlineOfflineTabClicked = "edx.bi.app.course.offline_tab"
case courseOutlineDatesTabClicked = "edx.bi.app.course.dates_tab"
case courseOutlineDiscussionTabClicked = "edx.bi.app.course.discussion_tab"
case courseOutlineHandoutsTabClicked = "edx.bi.app.course.handouts_tab"
Expand Down
15 changes: 15 additions & 0 deletions Core/Core/Assets.xcassets/check_circle.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "check_circle.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions Core/Core/Assets.xcassets/download.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "download.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
12 changes: 12 additions & 0 deletions Core/Core/Assets.xcassets/download.imageset/download.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions Core/Core/Assets.xcassets/remove.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "remove.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
3 changes: 3 additions & 0 deletions Core/Core/Assets.xcassets/remove.imageset/remove.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions Core/Core/Assets.xcassets/report_octagon.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "report.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
3 changes: 3 additions & 0 deletions Core/Core/Assets.xcassets/report_octagon.imageset/report.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions Core/Core/Assets.xcassets/visibility.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "visibility.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
3 changes: 3 additions & 0 deletions Core/Core/Assets.xcassets/visibility.imageset/visibility.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions Core/Core/Data/Model/Data_PrimaryEnrollment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public extension DataLayer {

// MARK: - Primary
struct ActiveEnrollment: Codable {
public let auditAccessExpires: Date?
public let auditAccessExpires: String?
public let created: String?
public let mode: String?
public let isActive: Bool?
Expand All @@ -53,7 +53,7 @@ public extension DataLayer {
}

public init(
auditAccessExpires: Date?,
auditAccessExpires: String?,
created: String?,
mode: String?,
isActive: Bool?,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22522" systemVersion="22G91" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22758" systemVersion="23F79" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="CDDownloadData" representedClassName="CDDownloadData" syncable="YES" codeGenerationType="class">
<attribute name="blockId" optional="YES" attributeType="String"/>
<attribute name="courseId" optional="YES" attributeType="String"/>
<attribute name="displayName" optional="YES" attributeType="String"/>
<attribute name="fileName" optional="YES" attributeType="String"/>
<attribute name="fileSize" optional="YES" attributeType="Integer 32" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="id" optional="YES" attributeType="String"/>
<attribute name="lastModified" optional="YES" attributeType="String"/>
<attribute name="progress" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="resumeData" optional="YES" attributeType="Binary"/>
<attribute name="state" optional="YES" attributeType="String"/>
Expand All @@ -19,4 +20,13 @@
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="CDOfflineProgress" representedClassName="CDOfflineProgress" syncable="YES" codeGenerationType="class">
<attribute name="blockID" optional="YES" attributeType="String"/>
<attribute name="progressJson" optional="YES" attributeType="String"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="blockID"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
</model>
33 changes: 33 additions & 0 deletions Core/Core/Data/Persistence/CorePersistenceProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,18 @@
import CoreData
import Combine

//sourcery: AutoMockable
public protocol CorePersistenceProtocol {
func set(userId: Int)
func getUserID() -> Int?
func publisher() -> AnyPublisher<Int, Never>
func addToDownloadQueue(tasks: [DownloadDataTask])
func saveOfflineProgress(progress: OfflineProgress)
func loadProgress(for blockID: String) -> OfflineProgress?
func loadAllOfflineProgress() -> [OfflineProgress]
func deleteProgress(for blockID: String)
func deleteAllProgress()

func addToDownloadQueue(blocks: [CourseBlock], downloadQuality: DownloadQuality) async
func nextBlockForDownloading() async -> DownloadDataTask?
func updateDownloadState(id: String, state: DownloadState, resumeData: Data?)
Expand All @@ -22,6 +30,31 @@ public protocol CorePersistenceProtocol {
func getDownloadDataTasksForCourse(_ courseId: String) async -> [DownloadDataTask]
}

#if DEBUG
public class CorePersistenceMock: CorePersistenceProtocol {

public init() {}

public func set(userId: Int) {}
public func getUserID() -> Int? {1}
public func publisher() -> AnyPublisher<Int, Never> { Just(0).eraseToAnyPublisher() }
public func addToDownloadQueue(blocks: [CourseBlock], downloadQuality: DownloadQuality) {}
public func addToDownloadQueue(tasks: [DownloadDataTask]) {}
public func nextBlockForDownloading() -> DownloadDataTask? { nil }
public func updateDownloadState(id: String, state: DownloadState, resumeData: Data?) {}
public func deleteDownloadDataTask(id: String) throws {}
public func downloadDataTask(for blockId: String) -> DownloadDataTask? { nil }
public func saveOfflineProgress(progress: OfflineProgress) {}
public func loadProgress(for blockID: String) -> OfflineProgress? { nil }
public func loadAllOfflineProgress() -> [OfflineProgress] { [] }
public func deleteProgress(for blockID: String) {}
public func deleteAllProgress() {}
public func saveDownloadDataTask(_ task: DownloadDataTask) {}
public func getDownloadDataTasks() async -> [DownloadDataTask] {[]}
public func getDownloadDataTasksForCourse(_ courseId: String) async -> [DownloadDataTask] {[]}
}
#endif

public final class CoreBundle {
private init() {}
}
42 changes: 42 additions & 0 deletions Core/Core/Data/Repository/OfflineSyncRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// OfflineSyncRepository.swift
// Core
//
// Created by  Stepanok Ivan on 20.06.2024.
//

import Foundation

public protocol OfflineSyncRepositoryProtocol {
func submitOfflineProgress(courseID: String, blockID: String, data: String) async throws -> Bool
}

public class OfflineSyncRepository: OfflineSyncRepositoryProtocol {

private let api: API

public init(api: API) {
self.api = api
}

public func submitOfflineProgress(courseID: String, blockID: String, data: String) async throws -> Bool {
let request = try await api.request(
OfflineSyncEndpoint.submitOfflineProgress(
courseID: courseID,
blockID: blockID,
data: data
)
)

return request.statusCode == 200
}
}

// Mark - For testing and SwiftUI preview
#if DEBUG
class OfflineSyncRepositoryMock: OfflineSyncRepositoryProtocol {
public func submitOfflineProgress(courseID: String, blockID: String, data: String) async throws -> Bool {
true
}
}
#endif
48 changes: 46 additions & 2 deletions Core/Core/Domain/Model/CourseBlockModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ public struct CourseSequential: Identifiable {
return childs.first(where: { $0.isDownloadable }) != nil
}

public var totalSize: Int {
childs.flatMap { $0.childs.filter({ $0.isDownloadable }) }.reduce(0) { $0 + ($1.fileSize ?? 0) }
}

public init(
blockId: String,
id: String,
Expand Down Expand Up @@ -233,9 +237,31 @@ public struct CourseBlock: Hashable, Identifiable {
public let subtitles: [SubtitleUrl]?
public let encodedVideo: CourseBlockEncodedVideo?
public let multiDevice: Bool?
public var offlineDownload: OfflineDownload?
public var actualFileSize: Int?

public var isDownloadable: Bool {
encodedVideo?.isDownloadable ?? false
encodedVideo?.isDownloadable ?? false || offlineDownload?.isDownloadable ?? false
}

public var fileSize: Int? {
if let actualFileSize {
return actualFileSize
} else if let fileSize = encodedVideo?.desktopMP4?.fileSize {
return fileSize
} else if let fileSize = encodedVideo?.fallback?.fileSize {
return fileSize
} else if let fileSize = encodedVideo?.hls?.fileSize {
return fileSize
} else if let fileSize = encodedVideo?.mobileHigh?.fileSize {
return fileSize
} else if let fileSize = encodedVideo?.mobileLow?.fileSize {
return fileSize
} else if let fileSize = offlineDownload?.fileSize {
return fileSize
} else {
return nil
}
}

public init(
Expand All @@ -252,7 +278,8 @@ public struct CourseBlock: Hashable, Identifiable {
webUrl: String,
subtitles: [SubtitleUrl]? = nil,
encodedVideo: CourseBlockEncodedVideo?,
multiDevice: Bool?
multiDevice: Bool?,
offlineDownload: OfflineDownload?
) {
self.blockId = blockId
self.id = id
Expand All @@ -268,6 +295,23 @@ public struct CourseBlock: Hashable, Identifiable {
self.subtitles = subtitles
self.encodedVideo = encodedVideo
self.multiDevice = multiDevice
self.offlineDownload = offlineDownload
}
}

public struct OfflineDownload {
public let fileUrl: String
public var lastModified: String
public let fileSize: Int

public init(fileUrl: String, lastModified: String, fileSize: Int) {
self.fileUrl = fileUrl
self.lastModified = lastModified
self.fileSize = fileSize
}

public var isDownloadable: Bool {
[".zip"].contains(where: { fileUrl.contains($0) == true })
}
}

Expand Down
50 changes: 50 additions & 0 deletions Core/Core/Domain/Model/OfflineProgress.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// OfflineProgress.swift
// Core
//
// Created by  Stepanok Ivan on 20.06.2024.
//

import Foundation

public struct OfflineProgress {
public let blockID: String
public let data: String
public let courseID: String
public let progressJson: String

public init(progressJson: String) {
self.progressJson = progressJson
if let jsonData = progressJson.data(using: .utf8) {
if let jsonObject = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
if let url = jsonObject["url"] as? String,
let data = jsonObject["data"] as? String {
self.blockID = extractBlockID(from: url)
self.data = data
self.courseID = extractCourseID(from: url)
return
}
}
}
// Default values if parsing fails
self.blockID = ""
self.data = ""
self.courseID = ""

func extractBlockID(from url: String) -> String {
if let range = url.range(of: "xblock/")?.upperBound,
let endRange = url.range(of: "/handler", range: range..<url.endIndex)?.lowerBound {
return String(url[range..<endRange])
}
return ""
}

func extractCourseID(from url: String) -> String {
if let range = url.range(of: "courses/")?.upperBound,
let endRange = url.range(of: "/xblock", range: range..<url.endIndex)?.lowerBound {
return String(url[range..<endRange])
}
return ""
}
}
}
Loading

0 comments on commit 6e2f030

Please sign in to comment.