Skip to content

Commit

Permalink
Unit progress navigation style (#258)
Browse files Browse the repository at this point in the history
* chore: add lesson line progress view

* chore: add colors and  config to course unit view model

* chore: add ajax provider and refactor code, fix crash on Config resolve

* chore: update when back if some changed

* chore: hide progress dots when line show

* chore:  padding and separate views

* chore: added one more state and change inset what fixes dark mode

* fix: arrow buttons in unit content

set next and prev buttons in unit content to be horizontal if COURSE_UNIT_PROGRESS_ENABLED key is true

* fix: animation

set next and prev animation in unit content to be horizontal if COURSE_UNIT_PROGRESS_ENABLED key is true

* fix: different navigation stacks

* fix: offset in landscape mode

* fix: offset on orientation change

* fix: unit tests

* chore: fixed warnings

* chore: fixed warnings

* chore: fixed rounded corners and padding for progress line

* fix: paddings

* chore: moved rounded corners to view extension

* chore: delete unused code

* fix: use full width if we are using horizontal navigation

* chore: refactor of ajax completion handler

* chore: warning fix

* chore: added forMainFrameOnly parameter

* fix: previous button for horizontal navigation

* chore:  resolve PR comments

* chore: update course struct after complete unit

* chore:  down up dropdown button

* chore:  remove refresh course

---------

Co-authored-by: Eugene Yatsenko <[email protected]>
Co-authored-by: Vadim Kuznetsov <[email protected]>
  • Loading branch information
3 people authored Feb 2, 2024
1 parent a050bf0 commit 02fcec9
Show file tree
Hide file tree
Showing 41 changed files with 1,131 additions and 486 deletions.
16 changes: 16 additions & 0 deletions Core/Core.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@
BA8FA66C2AD59BBC00EA029A /* GoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = BA8FA66B2AD59BBC00EA029A /* GoogleSignIn */; };
BA8FA66E2AD59E7D00EA029A /* FacebookAuthProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA8FA66D2AD59E7D00EA029A /* FacebookAuthProvider.swift */; };
BA8FA6702AD59EA300EA029A /* MicrosoftAuthProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA8FA66F2AD59EA300EA029A /* MicrosoftAuthProvider.swift */; };
BAD9CA2F2B289B3500DE790A /* ajaxHandler.js in Resources */ = {isa = PBXBuildFile; fileRef = BAD9CA2E2B289B3500DE790A /* ajaxHandler.js */; };
BAD9CA332B28A8F300DE790A /* AjaxProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAD9CA322B28A8F300DE790A /* AjaxProvider.swift */; };
BAAD62C62AFCF00B000E6103 /* CustomDisclosureGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAAD62C52AFCF00B000E6103 /* CustomDisclosureGroup.swift */; };
BAD9CA422B2B140100DE790A /* AgreementConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAD9CA412B2B140100DE790A /* AgreementConfigTests.swift */; };
BADB3F5B2AD6EC56004D5CFA /* ResultExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BADB3F5A2AD6EC56004D5CFA /* ResultExtension.swift */; };
Expand Down Expand Up @@ -306,6 +308,8 @@
BA8FA6692AD59B5500EA029A /* GoogleAuthProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleAuthProvider.swift; sourceTree = "<group>"; };
BA8FA66D2AD59E7D00EA029A /* FacebookAuthProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FacebookAuthProvider.swift; sourceTree = "<group>"; };
BA8FA66F2AD59EA300EA029A /* MicrosoftAuthProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MicrosoftAuthProvider.swift; sourceTree = "<group>"; };
BAD9CA2E2B289B3500DE790A /* ajaxHandler.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = ajaxHandler.js; sourceTree = "<group>"; };
BAD9CA322B28A8F300DE790A /* AjaxProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AjaxProvider.swift; sourceTree = "<group>"; };
BAAD62C52AFCF00B000E6103 /* CustomDisclosureGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDisclosureGroup.swift; sourceTree = "<group>"; };
BAD9CA412B2B140100DE790A /* AgreementConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgreementConfigTests.swift; sourceTree = "<group>"; };
BADB3F5A2AD6EC56004D5CFA /* ResultExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultExtension.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -718,10 +722,20 @@
isa = PBXGroup;
children = (
BA30427C2B20B235009B64B7 /* SocialAuth */,
BAD9CA3D2B29BB1A00DE790A /* Ajax */,
);
path = Providers;
sourceTree = "<group>";
};
BAD9CA3D2B29BB1A00DE790A /* Ajax */ = {
isa = PBXGroup;
children = (
BAD9CA322B28A8F300DE790A /* AjaxProvider.swift */,
BAD9CA2E2B289B3500DE790A /* ajaxHandler.js */,
);
path = Ajax;
sourceTree = "<group>";
};
C9DFE47E699CFFA85A77AF2C /* Pods */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -906,6 +920,7 @@
0770DE5228D0ADFF006D8A5D /* Assets.xcassets in Resources */,
0770DE5B28D0B209006D8A5D /* Localizable.strings in Resources */,
0770DE5428D0B00C006D8A5D /* swiftgen.yml in Resources */,
BAD9CA2F2B289B3500DE790A /* ajaxHandler.js in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -992,6 +1007,7 @@
0727877D28D25212002E9142 /* ProgressBar.swift in Sources */,
0236961F28F9A2F600EEF206 /* AuthEndpoint.swift in Sources */,
02B3E3B32930198600A50475 /* AVPlayerViewControllerExtension.swift in Sources */,
BAD9CA332B28A8F300DE790A /* AjaxProvider.swift in Sources */,
0295C885299B99DD00ABE571 /* RefreshableScrollView.swift in Sources */,
0282DA7328F98CC9003C3F07 /* WebUnitView.swift in Sources */,
E0D586362B314CD3009B4BA7 /* LogistrationBottomView.swift in Sources */,
Expand Down
13 changes: 8 additions & 5 deletions Core/Core/Configuration/Config/UIComponentsConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,24 @@

import Foundation

private enum Keys: String {
private enum Keys: String, RawStringExtractable {
case courseNestedListEnabled = "COURSE_NESTED_LIST_ENABLED"
case courseTopTabBarEnabled = "COURSE_TOP_TAB_BAR_ENABLED"
case courseBannerEnabled = "COURSE_BANNER_ENABLED"
case courseUnitProgressEnabled = "COURSE_UNIT_PROGRESS_ENABLED"
}

public class UIComponentsConfig: NSObject {
public var courseNestedListEnabled: Bool = false
public var courseNestedListEnabled: Bool
public var courseBannerEnabled: Bool
public var courseUnitProgressEnabled: Bool
public var courseTopTabBarEnabled: Bool

init(dictionary: [String: Any]) {
courseNestedListEnabled = dictionary[Keys.courseNestedListEnabled.rawValue] as? Bool ?? false
courseBannerEnabled = dictionary[Keys.courseBannerEnabled.rawValue] as? Bool ?? false
courseTopTabBarEnabled = dictionary[Keys.courseTopTabBarEnabled.rawValue] as? Bool ?? false
courseNestedListEnabled = dictionary[Keys.courseNestedListEnabled] as? Bool ?? false
courseBannerEnabled = dictionary[Keys.courseBannerEnabled] as? Bool ?? false
courseUnitProgressEnabled = dictionary[Keys.courseUnitProgressEnabled] as? Bool ?? false
courseTopTabBarEnabled = dictionary[Keys.courseTopTabBarEnabled] as? Bool ?? false
super.init()
}
}
Expand Down
16 changes: 8 additions & 8 deletions Core/Core/Domain/Model/CourseBlockModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public struct CourseStructure: Equatable {
public let encodedVideo: String
public let displayName: String
public let topicID: String?
public let childs: [CourseChapter]
public var childs: [CourseChapter]
public let media: DataLayer.CourseMedia //FIXME Domain model
public let certificate: Certificate?

Expand Down Expand Up @@ -72,7 +72,7 @@ public struct CourseChapter: Identifiable {
public let id: String
public let displayName: String
public let type: BlockType
public let childs: [CourseSequential]
public var childs: [CourseSequential]

public init(
blockId: String,
Expand All @@ -96,8 +96,8 @@ public struct CourseSequential: Identifiable {
public let displayName: String
public let type: BlockType
public let completion: Double
public let childs: [CourseVertical]
public var childs: [CourseVertical]

public var isDownloadable: Bool {
return childs.first(where: { $0.isDownloadable }) != nil
}
Expand Down Expand Up @@ -130,7 +130,7 @@ public struct CourseVertical: Identifiable, Hashable {
public let displayName: String
public let type: BlockType
public let completion: Double
public let childs: [CourseBlock]
public var childs: [CourseBlock]

public var isDownloadable: Bool {
return childs.first(where: { $0.isDownloadable }) != nil
Expand Down Expand Up @@ -165,7 +165,7 @@ public struct SubtitleUrl: Equatable {
}
}

public struct CourseBlock: Hashable {
public struct CourseBlock: Hashable, Identifiable {
public static func == (lhs: CourseBlock, rhs: CourseBlock) -> Bool {
lhs.id == rhs.id &&
lhs.blockId == rhs.blockId &&
Expand All @@ -175,13 +175,13 @@ public struct CourseBlock: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(id)
}

public let blockId: String
public let id: String
public let courseId: String
public let topicId: String?
public let graded: Bool
public let completion: Double
public var completion: Double
public let type: BlockType
public let displayName: String
public let studentUrl: String
Expand Down
1 change: 1 addition & 0 deletions Core/Core/Extensions/Notification.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ public extension Notification.Name {
static let onAppUpgradeAccountSettingsTapped = Notification.Name("onAppUpgradeAccountSettingsTapped")
static let onNewVersionAvaliable = Notification.Name("onNewVersionAvaliable")
static let webviewReloadNotification = Notification.Name("webviewReloadNotification")
static let onBlockCompletion = Notification.Name.init("onBlockCompletion")
}
1 change: 1 addition & 0 deletions Core/Core/Extensions/ViewExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ public extension View {
ZStack {
self
.frame(maxWidth: maxIpadWidth, maxHeight: idiom == .pad ? ipadMaxHeight : .infinity)
.clipShape(RoundedCorners(tl: 24, tr: 24))
RoundedCorners(tl: 24, tr: 24)
.stroke(style: StrokeStyle(lineWidth: 1))
.foregroundColor(strokeColor)
Expand Down
87 changes: 87 additions & 0 deletions Core/Core/Providers/Ajax/AjaxProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// AjaxProvider.swift
// Core
//
// Created by Eugene Yatsenko on 12.12.2023.
//

import Foundation
import WebKit
import Swinject

struct AjaxInjection: WebViewScriptInjectionProtocol {
private struct AJAXCallbackData {
private enum Keys: String {
case url = "url"
case statusCode = "status"
case responseText = "response_text"
}

let url: String
let statusCode: Int
let responseText: String

init(data: [AnyHashable: Any]) {
url = data[Keys.url.rawValue] as? String ?? ""
statusCode = data[Keys.statusCode.rawValue] as? Int ?? 0
responseText = data[Keys.responseText.rawValue] as? String ?? ""
}
}

private enum XBlockCompletionCallbackType: String {
case html = "publish_completion"
case problem = "problem_check"
case dragAndDrop = "do_attempt"
case ora = "render_grade"
}

private let AJAXCallBackHandler = "ajaxCallbackHandler"
private let ajaxScriptFile = "ajaxHandler"

var id: String = "AjaxInjection"
var script: String {
guard let url = Bundle(for: CoreBundle.self).url(forResource: ajaxScriptFile, withExtension: "js"),
let script = try? String(contentsOf: url, encoding: .utf8) else { return "" }
return script
}

var messages: [WebviewMessage]? {
[
WebviewMessage(name: AJAXCallBackHandler) { result, _ in
guard let data = result as? [AnyHashable: Any] else { return }
let callback = AJAXCallbackData(data: data)
let requestURL = callback.url

if callback.statusCode != 200 {
return
}

var complete = false
if isBlockOf(type: .ora, with: requestURL) {
complete = callback.responseText.contains("is--complete")
} else {
complete = isBlockOf(type: .html, with: requestURL)
|| isBlockOf(type: .problem, with: requestURL)
|| isBlockOf(type: .dragAndDrop, with: requestURL)
}
if complete {
NotificationCenter.default.post(
name: NSNotification.blockCompletion,
object: nil
)
}
}
]
}
var forMainFrameOnly: Bool = false

var injectionTime: WKUserScriptInjectionTime = .atDocumentEnd

private func isBlockOf(type: XBlockCompletionCallbackType, with requestURL: String) -> Bool {
return requestURL.contains(type.rawValue)
}
}

public extension NSNotification {
static let blockCompletion = Notification.Name.init("block_completion")
}
18 changes: 18 additions & 0 deletions Core/Core/Providers/Ajax/ajaxHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//Every time an Ajax call is being invoked the listener will recognize it and will call the native app with the request details

$(document).ajaxSuccess(function(event, request, settings) {
callNativeApp({
"status": request.status,
"url":settings.url,
"response_text": request.responseText
});
});

function callNativeApp(data) {
try {
webkit.messageHandlers.ajaxCallbackHandler.postMessage(data);
}
catch(err) {
console.log('The native context does not exist yet');
}
}
4 changes: 2 additions & 2 deletions Core/Core/View/Base/AppReview/AppReviewViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ public class AppReviewViewModel: ObservableObject {
@Published var clients: [ThirdPartyMailClient] = []
let allClients = ThirdPartyMailClient.clients

private let config: Config
private let config: ConfigProtocol
var storage: CoreStorage

public init(config: Config, storage: CoreStorage) {
public init(config: ConfigProtocol, storage: CoreStorage) {
self.config = config
self.storage = storage
}
Expand Down
44 changes: 32 additions & 12 deletions Core/Core/View/Base/UnitButtonView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,22 @@ public struct UnitButtonView: View {
private let action: () -> Void
private let type: UnitButtonType
private let bgColor: Color?
private let isVerticalNavigation: Bool

public init(type: UnitButtonType, bgColor: Color? = nil, action: @escaping () -> Void) {
private var nextButtonDegrees: Double {
isVerticalNavigation ? -90 : 180
}

public init(
type: UnitButtonType,
isVerticalNavigation: Bool = true,
bgColor: Color? = nil,
action: @escaping () -> Void
) {
self.type = type
self.bgColor = bgColor
self.action = action
self.isVerticalNavigation = isVerticalNavigation
}

public var body: some View {
Expand All @@ -68,7 +79,7 @@ public struct UnitButtonView: View {
.font(Theme.Fonts.labelLarge)
CoreAssets.arrowLeft.swiftUIImage.renderingMode(.template)
.foregroundColor(Theme.Colors.styledButtonText)
.rotationEffect(Angle.degrees(-90))
.rotationEffect(Angle.degrees(nextButtonDegrees))
}.padding(.horizontal, 16)
case .next, .nextBig:
HStack {
Expand All @@ -81,20 +92,29 @@ public struct UnitButtonView: View {
}
CoreAssets.arrowLeft.swiftUIImage.renderingMode(.template)
.foregroundColor(Theme.Colors.styledButtonText)
.rotationEffect(Angle.degrees(-90))
.rotationEffect(Angle.degrees(nextButtonDegrees))
.padding(.trailing, 20)
}
case .previous:
HStack {
Text(type.stringValue())
.foregroundColor(Theme.Colors.accentColor)
.font(Theme.Fonts.labelLarge)
.padding(.leading, 20)
CoreAssets.arrowLeft.swiftUIImage.renderingMode(.template)
.rotationEffect(Angle.degrees(90))
.padding(.trailing, 20)
.foregroundColor(Theme.Colors.accentColor)

if isVerticalNavigation {
Text(type.stringValue())
.foregroundColor(Theme.Colors.accentColor)
.font(Theme.Fonts.labelLarge)
.padding(.leading, 20)
CoreAssets.arrowLeft.swiftUIImage.renderingMode(.template)
.rotationEffect(Angle.degrees(90))
.padding(.trailing, 20)
.foregroundColor(Theme.Colors.accentColor)
} else {
CoreAssets.arrowLeft.swiftUIImage.renderingMode(.template)
.padding(.leading, 20)
.foregroundColor(Theme.Colors.accentColor)
Text(type.stringValue())
.foregroundColor(Theme.Colors.accentColor)
.font(Theme.Fonts.labelLarge)
.padding(.trailing, 20)
}
}
case .last:
HStack {
Expand Down
Loading

0 comments on commit 02fcec9

Please sign in to comment.