From 07e4dcbae72fbb51fcabd32c7b9d0b24f3004346 Mon Sep 17 00:00:00 2001 From: forgotvas Date: Wed, 27 Dec 2023 12:27:45 +0300 Subject: [PATCH 1/9] feat: added survey css injection --- Core/Core/Domain/Model/CourseBlockModel.swift | 10 +++ .../Core/Domain/Model/CourseDetailBlock.swift | 2 + Core/Core/View/Base/WebUnitView.swift | 10 ++- Core/Core/View/Base/WebView.swift | 62 ++++++++++++++++++- .../Presentation/Unit/CourseUnitView.swift | 6 +- .../Unit/CourseUnitViewModel.swift | 2 +- .../Presentation/Unit/Subviews/WebView.swift | 3 +- 7 files changed, 89 insertions(+), 6 deletions(-) diff --git a/Core/Core/Domain/Model/CourseBlockModel.swift b/Core/Core/Domain/Model/CourseBlockModel.swift index aeba9df44..6b7d641d1 100644 --- a/Core/Core/Domain/Model/CourseBlockModel.swift +++ b/Core/Core/Domain/Model/CourseBlockModel.swift @@ -189,4 +189,14 @@ public struct CourseBlock: Equatable { self.videoUrl = videoUrl self.youTubeUrl = youTubeUrl } + + public func webInjections() -> [WebViewScriptInjectionProtocol] { + switch type { + case .survey: + return [SurveyCssInjection()] + default: + break + } + return [] + } } diff --git a/Core/Core/Domain/Model/CourseDetailBlock.swift b/Core/Core/Domain/Model/CourseDetailBlock.swift index 8230d44df..8f2f9db73 100644 --- a/Core/Core/Domain/Model/CourseDetailBlock.swift +++ b/Core/Core/Domain/Model/CourseDetailBlock.swift @@ -39,7 +39,9 @@ public enum BlockType: String { case chapter case video case problem + case survey case unknown + case dragAndDropV2 = "drag-and-drop-v2" public var image: Image { switch self { diff --git a/Core/Core/View/Base/WebUnitView.swift b/Core/Core/View/Base/WebUnitView.swift index f85f0914f..926d1d6aa 100644 --- a/Core/Core/View/Base/WebUnitView.swift +++ b/Core/Core/View/Base/WebUnitView.swift @@ -12,12 +12,14 @@ import Theme public struct WebUnitView: View { private var url: String + private var injections: [WebViewScriptInjectionProtocol]? @ObservedObject private var viewModel: WebUnitViewModel @State private var isWebViewLoading = false - public init(url: String, viewModel: WebUnitViewModel) { + public init(url: String, viewModel: WebUnitViewModel, injections: [WebViewScriptInjectionProtocol]?) { self.viewModel = viewModel self.url = url + self.injections = injections } @ViewBuilder @@ -55,7 +57,11 @@ public struct WebUnitView: View { ScrollView { if viewModel.cookiesReady { WebView( - viewModel: .init(url: url, baseURL: viewModel.config.baseURL.absoluteString), + viewModel: .init( + url: url, + baseURL: viewModel.config.baseURL.absoluteString, + injections: injections + ), isLoading: $isWebViewLoading, refreshCookies: { await viewModel.updateCookies(force: true) }) diff --git a/Core/Core/View/Base/WebView.swift b/Core/Core/View/Base/WebView.swift index 917fa9c7b..bf8ea2024 100644 --- a/Core/Core/View/Base/WebView.swift +++ b/Core/Core/View/Base/WebView.swift @@ -10,16 +10,66 @@ import WebKit import SwiftUI import Theme +public struct WebviewMessage { + var name: String + var handler: (String, WKWebView) -> Void +} + +public protocol WebViewScriptInjectionProtocol { + var script: String { get } + var message: WebviewMessage? { get } +} + +public struct SurveyCssInjection: WebViewScriptInjectionProtocol { + public var message: WebviewMessage? + + public var script: String { + """ + window.addEventListener("load", () => { + var css = `\(css)`, + head = document.head || document.getElementsByTagName('head')[0], + style = document.createElement('style'); + head.appendChild(style); + style.type = 'text/css'; + if (style.styleSheet) { + style.styleSheet.cssText = css; + } else { + style.appendChild(document.createTextNode(css)); + } + }) + """ + } + + var css: String { + """ + .survey-table .survey-option .visible-mobile-only { + width: calc(100% - 20px) !important; + } + .survey-percentage .percentage { + width: 54px !important; + } + """ + } + + public init() {} + + public static func == (lhs: SurveyCssInjection, rhs: SurveyCssInjection) -> Bool { + lhs.script == rhs.script + } +} + public struct WebView: UIViewRepresentable { public class ViewModel: ObservableObject { @Published var url: String let baseURL: String + let injections: [WebViewScriptInjectionProtocol]? - public init(url: String, baseURL: String) { + public init(url: String, baseURL: String, injections: [WebViewScriptInjectionProtocol]? = nil) { self.url = url self.baseURL = baseURL + self.injections = injections } } @@ -127,6 +177,11 @@ public struct WebView: UIViewRepresentable { let webViewConfig = WKWebViewConfiguration() let webView = WKWebView(frame: .zero, configuration: webViewConfig) +#if DEBUG + if #available(iOS 16.4, *) { + webView.isInspectable = true + } +#endif webView.navigationDelegate = context.coordinator webView.uiDelegate = context.coordinator @@ -143,6 +198,11 @@ public struct WebView: UIViewRepresentable { webView.scrollView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] webView.scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 200, right: 0) + for injection in viewModel.injections ?? [] { + let script = WKUserScript(source: injection.script, injectionTime: .atDocumentStart, forMainFrameOnly: true) + webView.configuration.userContentController.addUserScript(script) + } + return webView } diff --git a/Course/Course/Presentation/Unit/CourseUnitView.swift b/Course/Course/Presentation/Unit/CourseUnitView.swift index 311643472..86c1d732a 100644 --- a/Course/Course/Presentation/Unit/CourseUnitView.swift +++ b/Course/Course/Presentation/Unit/CourseUnitView.swift @@ -145,7 +145,11 @@ public struct CourseUnitView: View { case .web(let url): if index >= viewModel.index - 1 && index <= viewModel.index + 1 { if viewModel.connectivity.isInternetAvaliable { - WebView(url: url, viewModel: viewModel) + WebView( + url: url, + viewModel: viewModel, + injections: block.webInjections() + ) } else { NoInternetView(playerStateSubject: playerStateSubject) } diff --git a/Course/Course/Presentation/Unit/CourseUnitViewModel.swift b/Course/Course/Presentation/Unit/CourseUnitViewModel.swift index bf4f22807..2967da43c 100644 --- a/Course/Course/Presentation/Unit/CourseUnitViewModel.swift +++ b/Course/Course/Presentation/Unit/CourseUnitViewModel.swift @@ -34,7 +34,7 @@ public enum LessonType: Equatable { return .unknown(block.studentUrl) } - case .problem: + case .problem, .dragAndDropV2, .survey: return .web(block.studentUrl) } } diff --git a/Course/Course/Presentation/Unit/Subviews/WebView.swift b/Course/Course/Presentation/Unit/Subviews/WebView.swift index a97ff7d14..968aa3e75 100644 --- a/Course/Course/Presentation/Unit/Subviews/WebView.swift +++ b/Course/Course/Presentation/Unit/Subviews/WebView.swift @@ -13,10 +13,11 @@ import Theme struct WebView: View { let url: String let viewModel: CourseUnitViewModel + let injections: [WebViewScriptInjectionProtocol]? var body: some View { VStack(spacing: 0) { - WebUnitView(url: url, viewModel: Container.shared.resolve(WebUnitViewModel.self)!) + WebUnitView(url: url, viewModel: Container.shared.resolve(WebUnitViewModel.self)!, injections: injections) Spacer(minLength: 5) } .roundedBackgroundWeb(strokeColor: Theme.Colors.textInputUnfocusedStroke, maxIpadWidth: .infinity) From e9f02b788b2c2ba1c9807c9a5479f8e90a63d005 Mon Sep 17 00:00:00 2001 From: forgotvas Date: Wed, 27 Dec 2023 13:36:34 +0300 Subject: [PATCH 2/9] fix: survey css --- Core/Core/Domain/Model/CourseBlockModel.swift | 2 +- Core/Core/View/Base/WebUnitView.swift | 4 ++-- Core/Core/View/Base/WebView.swift | 11 ++++++----- .../Course/Presentation/Unit/Subviews/WebView.swift | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Core/Core/Domain/Model/CourseBlockModel.swift b/Core/Core/Domain/Model/CourseBlockModel.swift index 6b7d641d1..25cc6fb72 100644 --- a/Core/Core/Domain/Model/CourseBlockModel.swift +++ b/Core/Core/Domain/Model/CourseBlockModel.swift @@ -190,7 +190,7 @@ public struct CourseBlock: Equatable { self.youTubeUrl = youTubeUrl } - public func webInjections() -> [WebViewScriptInjectionProtocol] { + public func webInjections() -> [any WebViewScriptInjectionProtocol] { switch type { case .survey: return [SurveyCssInjection()] diff --git a/Core/Core/View/Base/WebUnitView.swift b/Core/Core/View/Base/WebUnitView.swift index 926d1d6aa..d5e3be070 100644 --- a/Core/Core/View/Base/WebUnitView.swift +++ b/Core/Core/View/Base/WebUnitView.swift @@ -12,11 +12,11 @@ import Theme public struct WebUnitView: View { private var url: String - private var injections: [WebViewScriptInjectionProtocol]? + private var injections: [any WebViewScriptInjectionProtocol]? @ObservedObject private var viewModel: WebUnitViewModel @State private var isWebViewLoading = false - public init(url: String, viewModel: WebUnitViewModel, injections: [WebViewScriptInjectionProtocol]?) { + public init(url: String, viewModel: WebUnitViewModel, injections: [any WebViewScriptInjectionProtocol]?) { self.viewModel = viewModel self.url = url self.injections = injections diff --git a/Core/Core/View/Base/WebView.swift b/Core/Core/View/Base/WebView.swift index bf8ea2024..e3cad5981 100644 --- a/Core/Core/View/Base/WebView.swift +++ b/Core/Core/View/Base/WebView.swift @@ -15,7 +15,7 @@ public struct WebviewMessage { var handler: (String, WKWebView) -> Void } -public protocol WebViewScriptInjectionProtocol { +public protocol WebViewScriptInjectionProtocol: Equatable { var script: String { get } var message: WebviewMessage? { get } } @@ -42,9 +42,10 @@ public struct SurveyCssInjection: WebViewScriptInjectionProtocol { var css: String { """ - .survey-table .survey-option .visible-mobile-only { - width: calc(100% - 20px) !important; + .survey-table:not(.poll-results) .survey-option .visible-mobile-only { + width: calc(100% - 21px) !important; } + .survey-percentage .percentage { width: 54px !important; } @@ -64,9 +65,9 @@ public struct WebView: UIViewRepresentable { @Published var url: String let baseURL: String - let injections: [WebViewScriptInjectionProtocol]? + let injections: [any WebViewScriptInjectionProtocol]? - public init(url: String, baseURL: String, injections: [WebViewScriptInjectionProtocol]? = nil) { + public init(url: String, baseURL: String, injections: [any WebViewScriptInjectionProtocol]? = nil) { self.url = url self.baseURL = baseURL self.injections = injections diff --git a/Course/Course/Presentation/Unit/Subviews/WebView.swift b/Course/Course/Presentation/Unit/Subviews/WebView.swift index 968aa3e75..8c427dc2f 100644 --- a/Course/Course/Presentation/Unit/Subviews/WebView.swift +++ b/Course/Course/Presentation/Unit/Subviews/WebView.swift @@ -13,7 +13,7 @@ import Theme struct WebView: View { let url: String let viewModel: CourseUnitViewModel - let injections: [WebViewScriptInjectionProtocol]? + let injections: [any WebViewScriptInjectionProtocol]? var body: some View { VStack(spacing: 0) { From 24d1973bfeba084e5428864fe797f2ebba91d3c8 Mon Sep 17 00:00:00 2001 From: forgotvas Date: Wed, 27 Dec 2023 18:12:19 +0300 Subject: [PATCH 3/9] fix: added css injection --- Core/Core/Domain/Model/CourseBlockModel.swift | 10 ----- Core/Core/View/Base/WebUnitView.swift | 8 ++-- Core/Core/View/Base/WebView.swift | 38 +++++++++++++++++-- .../Presentation/Unit/CourseUnitView.swift | 21 +++++----- .../Unit/CourseUnitViewModel.swift | 10 +++-- .../Presentation/Unit/Subviews/WebView.swift | 3 +- 6 files changed, 56 insertions(+), 34 deletions(-) diff --git a/Core/Core/Domain/Model/CourseBlockModel.swift b/Core/Core/Domain/Model/CourseBlockModel.swift index 25cc6fb72..aeba9df44 100644 --- a/Core/Core/Domain/Model/CourseBlockModel.swift +++ b/Core/Core/Domain/Model/CourseBlockModel.swift @@ -189,14 +189,4 @@ public struct CourseBlock: Equatable { self.videoUrl = videoUrl self.youTubeUrl = youTubeUrl } - - public func webInjections() -> [any WebViewScriptInjectionProtocol] { - switch type { - case .survey: - return [SurveyCssInjection()] - default: - break - } - return [] - } } diff --git a/Core/Core/View/Base/WebUnitView.swift b/Core/Core/View/Base/WebUnitView.swift index d5e3be070..797074e92 100644 --- a/Core/Core/View/Base/WebUnitView.swift +++ b/Core/Core/View/Base/WebUnitView.swift @@ -12,12 +12,12 @@ import Theme public struct WebUnitView: View { private var url: String - private var injections: [any WebViewScriptInjectionProtocol]? - @ObservedObject private var viewModel: WebUnitViewModel + private var injections: [WebviewInjection]? + @StateObject private var viewModel: WebUnitViewModel @State private var isWebViewLoading = false - public init(url: String, viewModel: WebUnitViewModel, injections: [any WebViewScriptInjectionProtocol]?) { - self.viewModel = viewModel + public init(url: String, viewModel: WebUnitViewModel, injections: [WebviewInjection]?) { + self._viewModel = .init(wrappedValue: viewModel) self.url = url self.injections = injections } diff --git a/Core/Core/View/Base/WebView.swift b/Core/Core/View/Base/WebView.swift index e3cad5981..2e8eb4d7f 100644 --- a/Core/Core/View/Base/WebView.swift +++ b/Core/Core/View/Base/WebView.swift @@ -15,12 +15,44 @@ public struct WebviewMessage { var handler: (String, WKWebView) -> Void } -public protocol WebViewScriptInjectionProtocol: Equatable { +public protocol WebViewScriptInjectionProtocol: Equatable, Identifiable { + var id: String { get } var script: String { get } var message: WebviewMessage? { get } } +extension WebViewScriptInjectionProtocol { + public func webviewInjection() -> WebviewInjection { + WebviewInjection(id: self.id, script: self.script) + } +} + +public struct WebviewInjection: WebViewScriptInjectionProtocol { + public var id: String + public var script: String + public var message: WebviewMessage? + init(id: String, script: String, message: WebviewMessage? = nil) { + self.id = id + self.script = script + self.message = message + } + + public static func == (lhs: WebviewInjection, rhs: WebviewInjection) -> Bool { + return lhs.id == rhs.id + } +} + +public extension WebviewInjection { + + static var surveyCSS: WebviewInjection { + SurveyCssInjection() + .webviewInjection() + } + +} + public struct SurveyCssInjection: WebViewScriptInjectionProtocol { + public var id: String = "SurveyCSSInjection" public var message: WebviewMessage? public var script: String { @@ -65,9 +97,9 @@ public struct WebView: UIViewRepresentable { @Published var url: String let baseURL: String - let injections: [any WebViewScriptInjectionProtocol]? + let injections: [WebviewInjection]? - public init(url: String, baseURL: String, injections: [any WebViewScriptInjectionProtocol]? = nil) { + public init(url: String, baseURL: String, injections: [WebviewInjection]? = nil) { self.url = url self.baseURL = baseURL self.injections = injections diff --git a/Course/Course/Presentation/Unit/CourseUnitView.swift b/Course/Course/Presentation/Unit/CourseUnitView.swift index 86c1d732a..dfc420f8f 100644 --- a/Course/Course/Presentation/Unit/CourseUnitView.swift +++ b/Course/Course/Presentation/Unit/CourseUnitView.swift @@ -142,20 +142,19 @@ public struct CourseUnitView: View { } } // MARK: Web - case .web(let url): + case let .web(url, injections): if index >= viewModel.index - 1 && index <= viewModel.index + 1 { - if viewModel.connectivity.isInternetAvaliable { - WebView( - url: url, - viewModel: viewModel, - injections: block.webInjections() - ) + if viewModel.connectivity.isInternetAvaliable { + WebView( + url: url, + injections: injections + ) + } else { + NoInternetView(playerStateSubject: playerStateSubject) + } } else { - NoInternetView(playerStateSubject: playerStateSubject) + EmptyView() } - } else { - EmptyView() - } // MARK: Unknown case .unknown(let url): if index >= viewModel.index - 1 && index <= viewModel.index + 1 { diff --git a/Course/Course/Presentation/Unit/CourseUnitViewModel.swift b/Course/Course/Presentation/Unit/CourseUnitViewModel.swift index 2967da43c..3d7876818 100644 --- a/Course/Course/Presentation/Unit/CourseUnitViewModel.swift +++ b/Course/Course/Presentation/Unit/CourseUnitViewModel.swift @@ -9,7 +9,7 @@ import SwiftUI import Core public enum LessonType: Equatable { - case web(String) + case web(url: String, injections: [WebviewInjection]) case youtube(viewYouTubeUrl: String, blockID: String) case video(videoUrl: String, blockID: String) case unknown(String) @@ -20,7 +20,7 @@ public enum LessonType: Equatable { case .course, .chapter, .vertical, .sequential, .unknown: return .unknown(block.studentUrl) case .html: - return .web(block.studentUrl) + return .web(url: block.studentUrl, injections: []) case .discussion: return .discussion(block.topicId ?? "", block.id, block.displayName) case .video: @@ -34,8 +34,10 @@ public enum LessonType: Equatable { return .unknown(block.studentUrl) } - case .problem, .dragAndDropV2, .survey: - return .web(block.studentUrl) + case .problem, .dragAndDropV2: + return .web(url: block.studentUrl, injections: []) + case .survey: + return .web(url: block.studentUrl, injections: [.surveyCSS]) } } } diff --git a/Course/Course/Presentation/Unit/Subviews/WebView.swift b/Course/Course/Presentation/Unit/Subviews/WebView.swift index 8c427dc2f..bb3964b4b 100644 --- a/Course/Course/Presentation/Unit/Subviews/WebView.swift +++ b/Course/Course/Presentation/Unit/Subviews/WebView.swift @@ -12,8 +12,7 @@ import Theme struct WebView: View { let url: String - let viewModel: CourseUnitViewModel - let injections: [any WebViewScriptInjectionProtocol]? + let injections: [WebviewInjection] var body: some View { VStack(spacing: 0) { From eddecc8abe8ea76086fc53ec70c2a7630fa83488 Mon Sep 17 00:00:00 2001 From: forgotvas Date: Wed, 27 Dec 2023 22:34:27 +0300 Subject: [PATCH 4/9] fix: added webview messages --- Core/Core/View/Base/WebView.swift | 59 +++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/Core/Core/View/Base/WebView.swift b/Core/Core/View/Base/WebView.swift index 2e8eb4d7f..119761d0d 100644 --- a/Core/Core/View/Base/WebView.swift +++ b/Core/Core/View/Base/WebView.swift @@ -10,35 +10,55 @@ import WebKit import SwiftUI import Theme -public struct WebviewMessage { +public struct WebviewMessage: Equatable { var name: String - var handler: (String, WKWebView) -> Void + var handler: (Any, WKWebView?) -> Void + + public static func == (lhs: WebviewMessage, rhs: WebviewMessage) -> Bool { + lhs.name == rhs.name + } } public protocol WebViewScriptInjectionProtocol: Equatable, Identifiable { var id: String { get } var script: String { get } - var message: WebviewMessage? { get } + var messages: [WebviewMessage]? { get } + var injectionTime: WKUserScriptInjectionTime { get } } extension WebViewScriptInjectionProtocol { public func webviewInjection() -> WebviewInjection { - WebviewInjection(id: self.id, script: self.script) + WebviewInjection( + id: self.id, + script: self.script, + messages: self.messages, + injectionTime: self.injectionTime + ) } } public struct WebviewInjection: WebViewScriptInjectionProtocol { public var id: String public var script: String - public var message: WebviewMessage? - init(id: String, script: String, message: WebviewMessage? = nil) { + public var messages: [WebviewMessage]? + public var injectionTime: WKUserScriptInjectionTime + init( + id: String, + script: String, + messages: [WebviewMessage]? = nil, + injectionTime: WKUserScriptInjectionTime = .atDocumentEnd + ) { self.id = id self.script = script - self.message = message + self.messages = messages + self.injectionTime = injectionTime } public static func == (lhs: WebviewInjection, rhs: WebviewInjection) -> Bool { - return lhs.id == rhs.id + lhs.id == rhs.id && + lhs.script == rhs.script && + lhs.injectionTime == rhs.injectionTime && + lhs.messages == rhs.messages } } @@ -53,7 +73,8 @@ public extension WebviewInjection { public struct SurveyCssInjection: WebViewScriptInjectionProtocol { public var id: String = "SurveyCSSInjection" - public var message: WebviewMessage? + public var messages: [WebviewMessage]? + public var injectionTime: WKUserScriptInjectionTime = .atDocumentStart public var script: String { """ @@ -116,7 +137,7 @@ public struct WebView: UIViewRepresentable { self.refreshCookies = refreshCookies } - public class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate { + public class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler { var parent: WebView init(_ parent: WebView) { @@ -200,6 +221,13 @@ public struct WebView: UIViewRepresentable { } return .allow } + + public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { + let messages = parent.viewModel.injections?.compactMap({$0.messages}).flatMap({$0}) ?? [] + if let currentMessage = messages.first(where: { $0.name == message.name }) { + currentMessage.handler(message.body, message.webView) + } + } } public func makeCoordinator() -> Coordinator { @@ -232,8 +260,12 @@ public struct WebView: UIViewRepresentable { webView.scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 200, right: 0) for injection in viewModel.injections ?? [] { - let script = WKUserScript(source: injection.script, injectionTime: .atDocumentStart, forMainFrameOnly: true) + let script = WKUserScript(source: injection.script, injectionTime: injection.injectionTime, forMainFrameOnly: true) webView.configuration.userContentController.addUserScript(script) + + for message in injection.messages ?? [] { + webView.configuration.userContentController.add(context.coordinator, name: message.name) + } } return webView @@ -250,4 +282,9 @@ public struct WebView: UIViewRepresentable { } } } + + public static func dismantleUIView(_ uiView: WKWebView, coordinator: Coordinator) { + uiView.configuration.userContentController.removeAllUserScripts() + uiView.configuration.userContentController.removeAllScriptMessageHandlers() + } } From 6cdf66a3644fbc7922e308f3d3fe130af9fe0c1d Mon Sep 17 00:00:00 2001 From: forgotvas Date: Thu, 4 Jan 2024 14:53:25 +0300 Subject: [PATCH 5/9] feat: added drag and drop css --- Core/Core.xcodeproj/project.pbxproj | 52 +++++++- .../Models/DragAndDropCssInjection.swift | 34 ++++++ .../Webview/Models/SurveyCssInjection.swift | 36 ++++++ .../Webview/Models/WebviewInjection.swift | 46 +++++++ .../Base/Webview/Models/WebviewMessage.swift | 16 +++ .../Protocols/CSSInjectionProtocol.swift | 31 +++++ .../WebViewScriptInjectionProtocol.swift | 25 ++++ .../View/Base/{ => Webview}/WebView.swift | 113 ++---------------- .../View/Base/{ => Webview}/WebViewHTML.swift | 0 .../Unit/CourseUnitViewModel.swift | 4 +- 10 files changed, 250 insertions(+), 107 deletions(-) create mode 100644 Core/Core/View/Base/Webview/Models/DragAndDropCssInjection.swift create mode 100644 Core/Core/View/Base/Webview/Models/SurveyCssInjection.swift create mode 100644 Core/Core/View/Base/Webview/Models/WebviewInjection.swift create mode 100644 Core/Core/View/Base/Webview/Models/WebviewMessage.swift create mode 100644 Core/Core/View/Base/Webview/Protocols/CSSInjectionProtocol.swift create mode 100644 Core/Core/View/Base/Webview/Protocols/WebViewScriptInjectionProtocol.swift rename Core/Core/View/Base/{ => Webview}/WebView.swift (68%) rename Core/Core/View/Base/{ => Webview}/WebViewHTML.swift (100%) diff --git a/Core/Core.xcodeproj/project.pbxproj b/Core/Core.xcodeproj/project.pbxproj index c3289cc3d..f1795f4d2 100644 --- a/Core/Core.xcodeproj/project.pbxproj +++ b/Core/Core.xcodeproj/project.pbxproj @@ -86,6 +86,12 @@ 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 */; }; + 064987562B46D1A60071642A /* WebviewMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 064987552B46D1A60071642A /* WebviewMessage.swift */; }; + 0649875A2B46D1E10071642A /* WebViewScriptInjectionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 064987592B46D1E00071642A /* WebViewScriptInjectionProtocol.swift */; }; + 0649875C2B46D1FB0071642A /* WebviewInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0649875B2B46D1FB0071642A /* WebviewInjection.swift */; }; + 0649875E2B46D23B0071642A /* CSSInjectionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0649875D2B46D23B0071642A /* CSSInjectionProtocol.swift */; }; + 064987602B46D25A0071642A /* SurveyCssInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0649875F2B46D25A0071642A /* SurveyCssInjection.swift */; }; + 064987622B46D2760071642A /* DragAndDropCssInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 064987612B46D2760071642A /* DragAndDropCssInjection.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 */; }; @@ -250,6 +256,12 @@ 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 = ""; }; + 064987552B46D1A60071642A /* WebviewMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebviewMessage.swift; sourceTree = ""; }; + 064987592B46D1E00071642A /* WebViewScriptInjectionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewScriptInjectionProtocol.swift; sourceTree = ""; }; + 0649875B2B46D1FB0071642A /* WebviewInjection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebviewInjection.swift; sourceTree = ""; }; + 0649875D2B46D23B0071642A /* CSSInjectionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CSSInjectionProtocol.swift; sourceTree = ""; }; + 0649875F2B46D25A0071642A /* SurveyCssInjection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurveyCssInjection.swift; sourceTree = ""; }; + 064987612B46D2760071642A /* DragAndDropCssInjection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DragAndDropCssInjection.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 = ""; }; @@ -462,6 +474,37 @@ path = AppReview; sourceTree = ""; }; + 064987542B46D1550071642A /* Webview */ = { + isa = PBXGroup; + children = ( + 064987582B46D1D00071642A /* Protocols */, + 064987572B46D1BE0071642A /* Models */, + 024D865D28F02C6B0077E0A0 /* WebView.swift */, + 02C2DC0729B63D6200F4445D /* WebViewHTML.swift */, + ); + path = Webview; + sourceTree = ""; + }; + 064987572B46D1BE0071642A /* Models */ = { + isa = PBXGroup; + children = ( + 064987552B46D1A60071642A /* WebviewMessage.swift */, + 0649875B2B46D1FB0071642A /* WebviewInjection.swift */, + 0649875F2B46D25A0071642A /* SurveyCssInjection.swift */, + 064987612B46D2760071642A /* DragAndDropCssInjection.swift */, + ); + path = Models; + sourceTree = ""; + }; + 064987582B46D1D00071642A /* Protocols */ = { + isa = PBXGroup; + children = ( + 064987592B46D1E00071642A /* WebViewScriptInjectionProtocol.swift */, + 0649875D2B46D23B0071642A /* CSSInjectionProtocol.swift */, + ); + path = Protocols; + sourceTree = ""; + }; 0727876E28D233EC002E9142 /* Configuration */ = { isa = PBXGroup; children = ( @@ -608,6 +651,7 @@ 0770DE7728D0C49E006D8A5D /* Base */ = { isa = PBXGroup; children = ( + 064987542B46D1550071642A /* Webview */, E0D586352B314CD3009B4BA7 /* LogistrationBottomView.swift */, 02A4833B29B8C57800D33F33 /* DownloadView.swift */, 02D800CB29348F460099CF16 /* ImagePicker.swift */, @@ -621,8 +665,6 @@ 024FCCFF28EF1CD300232339 /* WebBrowser.swift */, 028CE96829858ECC00B6B1C3 /* FlexibleKeyboardInputView.swift */, 023A4DD3299E66BD006C0E48 /* OfflineSnackBarView.swift */, - 024D865D28F02C6B0077E0A0 /* WebView.swift */, - 02C2DC0729B63D6200F4445D /* WebViewHTML.swift */, 021D925628DCF12900ACC565 /* AlertView.swift */, 0295C884299B99DD00ABE571 /* RefreshableScrollView.swift */, 02B3F16D2AB489A400DDDD4E /* RefreshableScrollViewCompat.swift */, @@ -943,6 +985,7 @@ 0727878128D25EFD002E9142 /* SnackBarView.swift in Sources */, 021D924828DC860C00ACC565 /* Data_UserProfile.swift in Sources */, 070019AC28F6FD0100D5FC78 /* CourseDetailBlock.swift in Sources */, + 064987622B46D2760071642A /* DragAndDropCssInjection.swift in Sources */, 0727877028D23411002E9142 /* Config.swift in Sources */, CFC84952299F8B890055E497 /* Debounce.swift in Sources */, 0236F3B728F4351E0050F09B /* CourseButton.swift in Sources */, @@ -956,8 +999,10 @@ 02512FF0299533DF0024D438 /* CoreDataHandlerProtocol.swift in Sources */, 0260E58028FD792800BBBE18 /* WebUnitViewModel.swift in Sources */, 02A4833A29B8A9AB00D33F33 /* DownloadManager.swift in Sources */, + 064987602B46D25A0071642A /* SurveyCssInjection.swift in Sources */, 027BD3AE2909475000392132 /* KeyboardScrollerOptions.swift in Sources */, BAFB99922B14E23D007D09F9 /* AppleSignInConfig.swift in Sources */, + 0649875E2B46D23B0071642A /* CSSInjectionProtocol.swift in Sources */, 027BD3BE2909478B00392132 /* UIResponder+CurrentResponder.swift in Sources */, 070019AE28F701B200D5FC78 /* Certificate.swift in Sources */, 076F297F2A1F80C800967E7D /* Pagination.swift in Sources */, @@ -976,6 +1021,7 @@ BA8FA6612AD5974300EA029A /* AppleAuthProvider.swift in Sources */, BA8FA6702AD59EA300EA029A /* MicrosoftAuthProvider.swift in Sources */, BA76135C2B21BC7300B599B7 /* SocialAuthResponse.swift in Sources */, + 064987562B46D1A60071642A /* WebviewMessage.swift in Sources */, 020306CC2932C0C4000949EA /* PickerView.swift in Sources */, 027BD3C52909707700392132 /* Shake.swift in Sources */, 027BD39C2908810C00392132 /* RegisterUser.swift in Sources */, @@ -1025,12 +1071,14 @@ 024D865E28F02C6B0077E0A0 /* WebView.swift in Sources */, DBF6F24A2B0380E00098414B /* FeaturesConfig.swift in Sources */, 02F164372902A9EB0090DDEF /* StringExtension.swift in Sources */, + 0649875C2B46D1FB0071642A /* WebviewInjection.swift in Sources */, 0231CDBE2922422D00032416 /* CSSInjector.swift in Sources */, BAAD62C62AFCF00B000E6103 /* CustomDisclosureGroup.swift in Sources */, BADB3F5B2AD6EC56004D5CFA /* ResultExtension.swift in Sources */, 0236961928F9A26900EEF206 /* AuthRepository.swift in Sources */, 023A1136291432B200D0D354 /* RegistrationTextField.swift in Sources */, 02C2DC0829B63D6200F4445D /* WebViewHTML.swift in Sources */, + 0649875A2B46D1E10071642A /* WebViewScriptInjectionProtocol.swift in Sources */, 02A463112AEA966C00331037 /* AppReviewView.swift in Sources */, 025B36752A13B7D5001A640E /* UnitButtonView.swift in Sources */, 028F9F39293A452B00DE65D0 /* ResetPassword.swift in Sources */, diff --git a/Core/Core/View/Base/Webview/Models/DragAndDropCssInjection.swift b/Core/Core/View/Base/Webview/Models/DragAndDropCssInjection.swift new file mode 100644 index 000000000..db4f1cb23 --- /dev/null +++ b/Core/Core/View/Base/Webview/Models/DragAndDropCssInjection.swift @@ -0,0 +1,34 @@ +// +// DragAndDropCssInjection.swift +// Core +// +// Created by Vadim Kuznetsov on 4.01.24. +// + +import WebKit + +public struct DragAndDropCssInjection: WebViewScriptInjectionProtocol, CSSInjectionProtocol { + public var id: String = "SurveyCSSInjection" + public var messages: [WebviewMessage]? + public var injectionTime: WKUserScriptInjectionTime = .atDocumentStart + + public var script: String { + cssScript(with: css) + } + + var css: String { + """ + .xblock--drag-and-drop .drag-container { + -webkit-user-select: none !important; + -ms-user-select: none !important; + user-select: none !important; + } + """ + } + + public init() {} + + public static func == (lhs: DragAndDropCssInjection, rhs: DragAndDropCssInjection) -> Bool { + lhs.script == rhs.script + } +} diff --git a/Core/Core/View/Base/Webview/Models/SurveyCssInjection.swift b/Core/Core/View/Base/Webview/Models/SurveyCssInjection.swift new file mode 100644 index 000000000..ebcd4cc54 --- /dev/null +++ b/Core/Core/View/Base/Webview/Models/SurveyCssInjection.swift @@ -0,0 +1,36 @@ +// +// SurveyCssInjection.swift +// Core +// +// Created by Vadim Kuznetsov on 4.01.24. +// + +import WebKit + +public struct SurveyCssInjection: WebViewScriptInjectionProtocol, CSSInjectionProtocol { + public var id: String = "SurveyCSSInjection" + public var messages: [WebviewMessage]? + public var injectionTime: WKUserScriptInjectionTime = .atDocumentStart + + public var script: String { + cssScript(with: css) + } + + var css: String { + """ + .survey-table:not(.poll-results) .survey-option .visible-mobile-only { + width: calc(100% - 21px) !important; + } + + .survey-percentage .percentage { + width: 54px !important; + } + """ + } + + public init() {} + + public static func == (lhs: SurveyCssInjection, rhs: SurveyCssInjection) -> Bool { + lhs.script == rhs.script + } +} diff --git a/Core/Core/View/Base/Webview/Models/WebviewInjection.swift b/Core/Core/View/Base/Webview/Models/WebviewInjection.swift new file mode 100644 index 000000000..05adc4027 --- /dev/null +++ b/Core/Core/View/Base/Webview/Models/WebviewInjection.swift @@ -0,0 +1,46 @@ +// +// WebviewInjection.swift +// Core +// +// Created by Vadim Kuznetsov on 4.01.24. +// + +import WebKit +public struct WebviewInjection: WebViewScriptInjectionProtocol { + public var id: String + public var script: String + public var messages: [WebviewMessage]? + public var injectionTime: WKUserScriptInjectionTime + init( + id: String, + script: String, + messages: [WebviewMessage]? = nil, + injectionTime: WKUserScriptInjectionTime = .atDocumentEnd + ) { + self.id = id + self.script = script + self.messages = messages + self.injectionTime = injectionTime + } + + public static func == (lhs: WebviewInjection, rhs: WebviewInjection) -> Bool { + lhs.id == rhs.id && + lhs.script == rhs.script && + lhs.injectionTime == rhs.injectionTime && + lhs.messages == rhs.messages + } +} + +public extension WebviewInjection { + + static var surveyCSS: WebviewInjection { + SurveyCssInjection() + .webviewInjection() + } + + static var dragAndDropXss: WebviewInjection { + DragAndDropCssInjection() + .webviewInjection() + } + +} diff --git a/Core/Core/View/Base/Webview/Models/WebviewMessage.swift b/Core/Core/View/Base/Webview/Models/WebviewMessage.swift new file mode 100644 index 000000000..ba5467398 --- /dev/null +++ b/Core/Core/View/Base/Webview/Models/WebviewMessage.swift @@ -0,0 +1,16 @@ +// +// WebviewMessage.swift +// Core +// +// Created by Vadim Kuznetsov on 4.01.24. +// + +import WebKit +public struct WebviewMessage: Equatable { + var name: String + var handler: (Any, WKWebView?) -> Void + + public static func == (lhs: WebviewMessage, rhs: WebviewMessage) -> Bool { + lhs.name == rhs.name + } +} diff --git a/Core/Core/View/Base/Webview/Protocols/CSSInjectionProtocol.swift b/Core/Core/View/Base/Webview/Protocols/CSSInjectionProtocol.swift new file mode 100644 index 000000000..0bbc621d3 --- /dev/null +++ b/Core/Core/View/Base/Webview/Protocols/CSSInjectionProtocol.swift @@ -0,0 +1,31 @@ +// +// CSSInjectionProtocol.swift +// Core +// +// Created by Vadim Kuznetsov on 4.01.24. +// + +import Foundation + +public protocol CSSInjectionProtocol { + func cssScript(with css: String) -> String +} + +extension CSSInjectionProtocol { + public func cssScript(with css: String) -> String { + """ + window.addEventListener("load", () => { + var css = `\(css)`, + head = document.head || document.getElementsByTagName('head')[0], + style = document.createElement('style'); + head.appendChild(style); + style.type = 'text/css'; + if (style.styleSheet) { + style.styleSheet.cssText = css; + } else { + style.appendChild(document.createTextNode(css)); + } + }) + """ + } +} diff --git a/Core/Core/View/Base/Webview/Protocols/WebViewScriptInjectionProtocol.swift b/Core/Core/View/Base/Webview/Protocols/WebViewScriptInjectionProtocol.swift new file mode 100644 index 000000000..8cc0cc1f1 --- /dev/null +++ b/Core/Core/View/Base/Webview/Protocols/WebViewScriptInjectionProtocol.swift @@ -0,0 +1,25 @@ +// +// WebViewScriptInjectionProtocol.swift +// Core +// +// Created by Vadim Kuznetsov on 4.01.24. +// + +import WebKit +public protocol WebViewScriptInjectionProtocol: Equatable, Identifiable { + var id: String { get } + var script: String { get } + var messages: [WebviewMessage]? { get } + var injectionTime: WKUserScriptInjectionTime { get } +} + +extension WebViewScriptInjectionProtocol { + public func webviewInjection() -> WebviewInjection { + WebviewInjection( + id: self.id, + script: self.script, + messages: self.messages, + injectionTime: self.injectionTime + ) + } +} diff --git a/Core/Core/View/Base/WebView.swift b/Core/Core/View/Base/Webview/WebView.swift similarity index 68% rename from Core/Core/View/Base/WebView.swift rename to Core/Core/View/Base/Webview/WebView.swift index 119761d0d..3dffc3976 100644 --- a/Core/Core/View/Base/WebView.swift +++ b/Core/Core/View/Base/Webview/WebView.swift @@ -10,108 +10,6 @@ import WebKit import SwiftUI import Theme -public struct WebviewMessage: Equatable { - var name: String - var handler: (Any, WKWebView?) -> Void - - public static func == (lhs: WebviewMessage, rhs: WebviewMessage) -> Bool { - lhs.name == rhs.name - } -} - -public protocol WebViewScriptInjectionProtocol: Equatable, Identifiable { - var id: String { get } - var script: String { get } - var messages: [WebviewMessage]? { get } - var injectionTime: WKUserScriptInjectionTime { get } -} - -extension WebViewScriptInjectionProtocol { - public func webviewInjection() -> WebviewInjection { - WebviewInjection( - id: self.id, - script: self.script, - messages: self.messages, - injectionTime: self.injectionTime - ) - } -} - -public struct WebviewInjection: WebViewScriptInjectionProtocol { - public var id: String - public var script: String - public var messages: [WebviewMessage]? - public var injectionTime: WKUserScriptInjectionTime - init( - id: String, - script: String, - messages: [WebviewMessage]? = nil, - injectionTime: WKUserScriptInjectionTime = .atDocumentEnd - ) { - self.id = id - self.script = script - self.messages = messages - self.injectionTime = injectionTime - } - - public static func == (lhs: WebviewInjection, rhs: WebviewInjection) -> Bool { - lhs.id == rhs.id && - lhs.script == rhs.script && - lhs.injectionTime == rhs.injectionTime && - lhs.messages == rhs.messages - } -} - -public extension WebviewInjection { - - static var surveyCSS: WebviewInjection { - SurveyCssInjection() - .webviewInjection() - } - -} - -public struct SurveyCssInjection: WebViewScriptInjectionProtocol { - public var id: String = "SurveyCSSInjection" - public var messages: [WebviewMessage]? - public var injectionTime: WKUserScriptInjectionTime = .atDocumentStart - - public var script: String { - """ - window.addEventListener("load", () => { - var css = `\(css)`, - head = document.head || document.getElementsByTagName('head')[0], - style = document.createElement('style'); - head.appendChild(style); - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.appendChild(document.createTextNode(css)); - } - }) - """ - } - - var css: String { - """ - .survey-table:not(.poll-results) .survey-option .visible-mobile-only { - width: calc(100% - 21px) !important; - } - - .survey-percentage .percentage { - width: 54px !important; - } - """ - } - - public init() {} - - public static func == (lhs: SurveyCssInjection, rhs: SurveyCssInjection) -> Bool { - lhs.script == rhs.script - } -} - public struct WebView: UIViewRepresentable { public class ViewModel: ObservableObject { @@ -222,7 +120,10 @@ public struct WebView: UIViewRepresentable { return .allow } - public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { + public func userContentController( + _ userContentController: WKUserContentController, + didReceive message: WKScriptMessage + ) { let messages = parent.viewModel.injections?.compactMap({$0.messages}).flatMap({$0}) ?? [] if let currentMessage = messages.first(where: { $0.name == message.name }) { currentMessage.handler(message.body, message.webView) @@ -260,7 +161,11 @@ public struct WebView: UIViewRepresentable { webView.scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 200, right: 0) for injection in viewModel.injections ?? [] { - let script = WKUserScript(source: injection.script, injectionTime: injection.injectionTime, forMainFrameOnly: true) + let script = WKUserScript( + source: injection.script, + injectionTime: injection.injectionTime, + forMainFrameOnly: true + ) webView.configuration.userContentController.addUserScript(script) for message in injection.messages ?? [] { diff --git a/Core/Core/View/Base/WebViewHTML.swift b/Core/Core/View/Base/Webview/WebViewHTML.swift similarity index 100% rename from Core/Core/View/Base/WebViewHTML.swift rename to Core/Core/View/Base/Webview/WebViewHTML.swift diff --git a/Course/Course/Presentation/Unit/CourseUnitViewModel.swift b/Course/Course/Presentation/Unit/CourseUnitViewModel.swift index 3d7876818..0efd2eefe 100644 --- a/Course/Course/Presentation/Unit/CourseUnitViewModel.swift +++ b/Course/Course/Presentation/Unit/CourseUnitViewModel.swift @@ -34,8 +34,10 @@ public enum LessonType: Equatable { return .unknown(block.studentUrl) } - case .problem, .dragAndDropV2: + case .problem: return .web(url: block.studentUrl, injections: []) + case .dragAndDropV2: + return .web(url: block.studentUrl, injections: [.dragAndDropXss]) case .survey: return .web(url: block.studentUrl, injections: [.surveyCSS]) } From b9e83cb4f7bb74213f826943f0a9bb4f6bd6629b Mon Sep 17 00:00:00 2001 From: forgotvas Date: Thu, 4 Jan 2024 15:03:59 +0300 Subject: [PATCH 6/9] chore: fixed warning --- .../Unit/CourseUnitViewModel.swift | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/Course/Course/Presentation/Unit/CourseUnitViewModel.swift b/Course/Course/Presentation/Unit/CourseUnitViewModel.swift index 0efd2eefe..cab19fe7c 100644 --- a/Course/Course/Presentation/Unit/CourseUnitViewModel.swift +++ b/Course/Course/Presentation/Unit/CourseUnitViewModel.swift @@ -50,7 +50,13 @@ public class CourseUnitViewModel: ObservableObject { case next case previous } - + + struct VerticalData { + var chapterIndex: Int + var sequentialIndex: Int + var verticalIndex: Int + } + var verticals: [CourseVertical] var verticalIndex: Int var courseName: String @@ -185,10 +191,15 @@ public class CourseUnitViewModel: ObservableObject { analytics.finishVerticalBackToOutlineClicked(courseId: courseID, courseName: courseName) } - //MARK: Navigation to next vertical - typealias VerticalData = (chapterIndex: Int, sequentialIndex: Int, verticalIndex: Int) + // MARK: Navigation to next vertical var nextData: VerticalData? { - nextData(from: (chapterIndex, sequentialIndex, verticalIndex)) + nextData( + from: VerticalData( + chapterIndex: chapterIndex, + sequentialIndex: sequentialIndex, + verticalIndex: verticalIndex + ) + ) } private func chapter(for data: VerticalData) -> CourseChapter? { @@ -223,15 +234,18 @@ public class CourseUnitViewModel: ObservableObject { private func nextData(from data: VerticalData) -> VerticalData? { var resultData: VerticalData = data if let verticals = verticals(for: data), verticals.count > data.verticalIndex + 1 { - resultData = (data.chapterIndex, data.sequentialIndex, data.verticalIndex + 1) + resultData.verticalIndex = data.verticalIndex + 1 } else if let sequentials = sequentials(for: data), sequentials.count > data.sequentialIndex + 1 { - resultData = (data.chapterIndex, data.sequentialIndex + 1, 0) + resultData.sequentialIndex = data.sequentialIndex + 1 + resultData.verticalIndex = 0 } else if chapters.count > data.chapterIndex + 1 { - resultData = (data.chapterIndex + 1, 0, 0) + resultData.chapterIndex = data.chapterIndex + 1 + resultData.sequentialIndex = 0 + resultData.verticalIndex = 0 } else { return nil } - + if let vertical = vertical(for: resultData), vertical.childs.count > 0 { return resultData } else { From 9defff011feff93bf865138ea9c4175c5e368a45 Mon Sep 17 00:00:00 2001 From: forgotvas Date: Fri, 5 Jan 2024 15:55:28 +0300 Subject: [PATCH 7/9] chore: review notes --- .../View/Base/Webview/Models/DragAndDropCssInjection.swift | 2 +- Core/Core/View/Base/Webview/WebView.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/Core/View/Base/Webview/Models/DragAndDropCssInjection.swift b/Core/Core/View/Base/Webview/Models/DragAndDropCssInjection.swift index db4f1cb23..6a1228cee 100644 --- a/Core/Core/View/Base/Webview/Models/DragAndDropCssInjection.swift +++ b/Core/Core/View/Base/Webview/Models/DragAndDropCssInjection.swift @@ -8,7 +8,7 @@ import WebKit public struct DragAndDropCssInjection: WebViewScriptInjectionProtocol, CSSInjectionProtocol { - public var id: String = "SurveyCSSInjection" + public var id: String = "DragAndDropCSSInjection" public var messages: [WebviewMessage]? public var injectionTime: WKUserScriptInjectionTime = .atDocumentStart diff --git a/Core/Core/View/Base/Webview/WebView.swift b/Core/Core/View/Base/Webview/WebView.swift index 3dffc3976..9a0a70c26 100644 --- a/Core/Core/View/Base/Webview/WebView.swift +++ b/Core/Core/View/Base/Webview/WebView.swift @@ -139,11 +139,11 @@ public struct WebView: UIViewRepresentable { let webViewConfig = WKWebViewConfiguration() let webView = WKWebView(frame: .zero, configuration: webViewConfig) -#if DEBUG + #if DEBUG if #available(iOS 16.4, *) { webView.isInspectable = true } -#endif + #endif webView.navigationDelegate = context.coordinator webView.uiDelegate = context.coordinator From 115d62624fb58f4d61bb2673700d6ad882ead513 Mon Sep 17 00:00:00 2001 From: forgotvas Date: Mon, 8 Jan 2024 11:05:44 +0300 Subject: [PATCH 8/9] chore: changed property name --- Core/Core/View/Base/Webview/Models/WebviewInjection.swift | 2 +- Course/Course/Presentation/Unit/CourseUnitViewModel.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/Core/View/Base/Webview/Models/WebviewInjection.swift b/Core/Core/View/Base/Webview/Models/WebviewInjection.swift index 05adc4027..403f81a00 100644 --- a/Core/Core/View/Base/Webview/Models/WebviewInjection.swift +++ b/Core/Core/View/Base/Webview/Models/WebviewInjection.swift @@ -38,7 +38,7 @@ public extension WebviewInjection { .webviewInjection() } - static var dragAndDropXss: WebviewInjection { + static var dragAndDropCss: WebviewInjection { DragAndDropCssInjection() .webviewInjection() } diff --git a/Course/Course/Presentation/Unit/CourseUnitViewModel.swift b/Course/Course/Presentation/Unit/CourseUnitViewModel.swift index cab19fe7c..cd65b58c6 100644 --- a/Course/Course/Presentation/Unit/CourseUnitViewModel.swift +++ b/Course/Course/Presentation/Unit/CourseUnitViewModel.swift @@ -37,7 +37,7 @@ public enum LessonType: Equatable { case .problem: return .web(url: block.studentUrl, injections: []) case .dragAndDropV2: - return .web(url: block.studentUrl, injections: [.dragAndDropXss]) + return .web(url: block.studentUrl, injections: [.dragAndDropCss]) case .survey: return .web(url: block.studentUrl, injections: [.surveyCSS]) } From 11aec716d9c94533498d290a5e51ee5e09ea47ba Mon Sep 17 00:00:00 2001 From: forgotvas Date: Tue, 9 Jan 2024 14:49:41 +0300 Subject: [PATCH 9/9] fix: merge conflict --- Core/Core.xcodeproj/project.pbxproj | 88 ++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 21 deletions(-) diff --git a/Core/Core.xcodeproj/project.pbxproj b/Core/Core.xcodeproj/project.pbxproj index 4d617e42e..3bb3b36c5 100644 --- a/Core/Core.xcodeproj/project.pbxproj +++ b/Core/Core.xcodeproj/project.pbxproj @@ -32,7 +32,6 @@ 0248C92329C075EF00DC8402 /* CourseBlockModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0248C92229C075EF00DC8402 /* CourseBlockModel.swift */; }; 024BE3DF29B2615500BCDEE2 /* CGColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 024BE3DE29B2615500BCDEE2 /* CGColorExtension.swift */; }; 024D723529C8BB1A006D36ED /* NavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 024D723429C8BB1A006D36ED /* NavigationBar.swift */; }; - 024D865E28F02C6B0077E0A0 /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 024D865D28F02C6B0077E0A0 /* WebView.swift */; }; 024FCD0028EF1CD300232339 /* WebBrowser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 024FCCFF28EF1CD300232339 /* WebBrowser.swift */; }; 02512FF0299533DF0024D438 /* CoreDataHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02512FEF299533DE0024D438 /* CoreDataHandlerProtocol.swift */; }; 0255D5582936283A004DBC1A /* UploadBodyEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0255D55729362839004DBC1A /* UploadBodyEncoding.swift */; }; @@ -73,7 +72,6 @@ 02B2B594295C5C7A00914876 /* Thread.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B2B593295C5C7A00914876 /* Thread.swift */; }; 02B3E3B32930198600A50475 /* AVPlayerViewControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B3E3B22930198600A50475 /* AVPlayerViewControllerExtension.swift */; }; 02B3F16E2AB489A400DDDD4E /* RefreshableScrollViewCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B3F16D2AB489A400DDDD4E /* RefreshableScrollViewCompat.swift */; }; - 02C2DC0829B63D6200F4445D /* WebViewHTML.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02C2DC0729B63D6200F4445D /* WebViewHTML.swift */; }; 02C917F029CDA99E00DBB8BD /* Data_Dashboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02C917EF29CDA99E00DBB8BD /* Data_Dashboard.swift */; }; 02CF46C829546AA200A698EE /* NoCachedDataError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02CF46C729546AA200A698EE /* NoCachedDataError.swift */; }; 02D400612B0678190029D168 /* SKStoreReviewControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D400602B0678190029D168 /* SKStoreReviewControllerExtension.swift */; }; @@ -85,6 +83,14 @@ 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 */; }; + 064987932B4D69FF0071642A /* DragAndDropCssInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0649878A2B4D69FE0071642A /* DragAndDropCssInjection.swift */; }; + 064987942B4D69FF0071642A /* WebviewInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0649878B2B4D69FE0071642A /* WebviewInjection.swift */; }; + 064987952B4D69FF0071642A /* SurveyCssInjection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0649878C2B4D69FE0071642A /* SurveyCssInjection.swift */; }; + 064987962B4D69FF0071642A /* WebviewMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0649878D2B4D69FE0071642A /* WebviewMessage.swift */; }; + 064987972B4D69FF0071642A /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0649878E2B4D69FE0071642A /* WebView.swift */; }; + 064987982B4D69FF0071642A /* CSSInjectionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 064987902B4D69FE0071642A /* CSSInjectionProtocol.swift */; }; + 064987992B4D69FF0071642A /* WebViewScriptInjectionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 064987912B4D69FE0071642A /* WebViewScriptInjectionProtocol.swift */; }; + 0649879A2B4D69FF0071642A /* WebViewHTML.swift in Sources */ = {isa = PBXBuildFile; fileRef = 064987922B4D69FE0071642A /* WebViewHTML.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 */; }; @@ -117,12 +123,11 @@ 0770DE5F28D0B22C006D8A5D /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0770DE5E28D0B22C006D8A5D /* Strings.swift */; }; 0770DE6128D0B2CB006D8A5D /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0770DE6028D0B2CB006D8A5D /* Assets.swift */; }; 07DDFCBD29A780BB00572595 /* UINavigationController+Animation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07DDFCBC29A780BB00572595 /* UINavigationController+Animation.swift */; }; - BA593F1C2AF8E498009ADB51 /* ScrollSlidingTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA593F1B2AF8E498009ADB51 /* ScrollSlidingTabBar.swift */; }; - BA593F1E2AF8E4A0009ADB51 /* FrameReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA593F1D2AF8E4A0009ADB51 /* FrameReader.swift */; }; - BAAD62C62AFCF00B000E6103 /* CustomDisclosureGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAAD62C52AFCF00B000E6103 /* CustomDisclosureGroup.swift */; }; 07E0939F2B308D2800F1E4B2 /* Data_Certificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E0939E2B308D2800F1E4B2 /* Data_Certificate.swift */; }; A53A32352B233DEC005FE38A /* ThemeConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53A32342B233DEC005FE38A /* ThemeConfig.swift */; }; BA30427F2B20B320009B64B7 /* SocialAuthError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA30427D2B20B299009B64B7 /* SocialAuthError.swift */; }; + BA593F1C2AF8E498009ADB51 /* ScrollSlidingTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA593F1B2AF8E498009ADB51 /* ScrollSlidingTabBar.swift */; }; + BA593F1E2AF8E4A0009ADB51 /* FrameReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA593F1D2AF8E4A0009ADB51 /* FrameReader.swift */; }; BA76135C2B21BC7300B599B7 /* SocialAuthResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA76135B2B21BC7300B599B7 /* SocialAuthResponse.swift */; }; BA8B3A2F2AD546A700D25EF5 /* DebugLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA8B3A2E2AD546A700D25EF5 /* DebugLog.swift */; }; BA8FA6612AD5974300EA029A /* AppleAuthProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA8FA6602AD5974300EA029A /* AppleAuthProvider.swift */; }; @@ -200,7 +205,6 @@ 0248C92229C075EF00DC8402 /* CourseBlockModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseBlockModel.swift; sourceTree = ""; }; 024BE3DE29B2615500BCDEE2 /* CGColorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGColorExtension.swift; sourceTree = ""; }; 024D723429C8BB1A006D36ED /* NavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBar.swift; sourceTree = ""; }; - 024D865D28F02C6B0077E0A0 /* WebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = ""; }; 024FCCFF28EF1CD300232339 /* WebBrowser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebBrowser.swift; sourceTree = ""; }; 02512FEF299533DE0024D438 /* CoreDataHandlerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataHandlerProtocol.swift; sourceTree = ""; }; 0255D55729362839004DBC1A /* UploadBodyEncoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadBodyEncoding.swift; sourceTree = ""; }; @@ -240,7 +244,6 @@ 02B2B593295C5C7A00914876 /* Thread.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thread.swift; sourceTree = ""; }; 02B3E3B22930198600A50475 /* AVPlayerViewControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVPlayerViewControllerExtension.swift; sourceTree = ""; }; 02B3F16D2AB489A400DDDD4E /* RefreshableScrollViewCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshableScrollViewCompat.swift; sourceTree = ""; }; - 02C2DC0729B63D6200F4445D /* WebViewHTML.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewHTML.swift; sourceTree = ""; }; 02C917EF29CDA99E00DBB8BD /* Data_Dashboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data_Dashboard.swift; sourceTree = ""; }; 02CF46C729546AA200A698EE /* NoCachedDataError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoCachedDataError.swift; sourceTree = ""; }; 02D400602B0678190029D168 /* SKStoreReviewControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SKStoreReviewControllerExtension.swift; sourceTree = ""; }; @@ -253,6 +256,14 @@ 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 = ""; }; + 0649878A2B4D69FE0071642A /* DragAndDropCssInjection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragAndDropCssInjection.swift; sourceTree = ""; }; + 0649878B2B4D69FE0071642A /* WebviewInjection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebviewInjection.swift; sourceTree = ""; }; + 0649878C2B4D69FE0071642A /* SurveyCssInjection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SurveyCssInjection.swift; sourceTree = ""; }; + 0649878D2B4D69FE0071642A /* WebviewMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebviewMessage.swift; sourceTree = ""; }; + 0649878E2B4D69FE0071642A /* WebView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = ""; }; + 064987902B4D69FE0071642A /* CSSInjectionProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSInjectionProtocol.swift; sourceTree = ""; }; + 064987912B4D69FE0071642A /* WebViewScriptInjectionProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebViewScriptInjectionProtocol.swift; sourceTree = ""; }; + 064987922B4D69FE0071642A /* WebViewHTML.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebViewHTML.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 = ""; }; @@ -295,11 +306,10 @@ 3B74C6685E416657F3C5F5A8 /* Pods-App-Core.releaseprod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.releaseprod.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.releaseprod.xcconfig"; sourceTree = ""; }; 60153262DBC2F9E660D7E11B /* Pods-App-Core.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.release.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.release.xcconfig"; sourceTree = ""; }; 9D5B06CAA99EA5CD49CBE2BB /* Pods-App-Core.debugdev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Core.debugdev.xcconfig"; path = "Target Support Files/Pods-App-Core/Pods-App-Core.debugdev.xcconfig"; sourceTree = ""; }; - BA593F1B2AF8E498009ADB51 /* ScrollSlidingTabBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollSlidingTabBar.swift; sourceTree = ""; }; - BA593F1D2AF8E4A0009ADB51 /* FrameReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FrameReader.swift; sourceTree = ""; }; - BAAD62C52AFCF00B000E6103 /* CustomDisclosureGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDisclosureGroup.swift; sourceTree = ""; }; A53A32342B233DEC005FE38A /* ThemeConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeConfig.swift; sourceTree = ""; }; BA30427D2B20B299009B64B7 /* SocialAuthError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocialAuthError.swift; sourceTree = ""; }; + BA593F1B2AF8E498009ADB51 /* ScrollSlidingTabBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollSlidingTabBar.swift; sourceTree = ""; }; + BA593F1D2AF8E4A0009ADB51 /* FrameReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FrameReader.swift; sourceTree = ""; }; BA76135B2B21BC7300B599B7 /* SocialAuthResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialAuthResponse.swift; sourceTree = ""; }; BA8B3A2E2AD546A700D25EF5 /* DebugLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugLog.swift; sourceTree = ""; }; BA8FA6602AD5974300EA029A /* AppleAuthProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleAuthProvider.swift; sourceTree = ""; }; @@ -470,6 +480,37 @@ path = AppReview; sourceTree = ""; }; + 064987882B4D69FE0071642A /* Webview */ = { + isa = PBXGroup; + children = ( + 064987892B4D69FE0071642A /* Models */, + 0649878E2B4D69FE0071642A /* WebView.swift */, + 0649878F2B4D69FE0071642A /* Protocols */, + 064987922B4D69FE0071642A /* WebViewHTML.swift */, + ); + path = Webview; + sourceTree = ""; + }; + 064987892B4D69FE0071642A /* Models */ = { + isa = PBXGroup; + children = ( + 0649878A2B4D69FE0071642A /* DragAndDropCssInjection.swift */, + 0649878B2B4D69FE0071642A /* WebviewInjection.swift */, + 0649878C2B4D69FE0071642A /* SurveyCssInjection.swift */, + 0649878D2B4D69FE0071642A /* WebviewMessage.swift */, + ); + path = Models; + sourceTree = ""; + }; + 0649878F2B4D69FE0071642A /* Protocols */ = { + isa = PBXGroup; + children = ( + 064987902B4D69FE0071642A /* CSSInjectionProtocol.swift */, + 064987912B4D69FE0071642A /* WebViewScriptInjectionProtocol.swift */, + ); + path = Protocols; + sourceTree = ""; + }; 0727876E28D233EC002E9142 /* Configuration */ = { isa = PBXGroup; children = ( @@ -616,6 +657,7 @@ 0770DE7728D0C49E006D8A5D /* Base */ = { isa = PBXGroup; children = ( + 064987882B4D69FE0071642A /* Webview */, E0D586352B314CD3009B4BA7 /* LogistrationBottomView.swift */, 02A4833B29B8C57800D33F33 /* DownloadView.swift */, 02D800CB29348F460099CF16 /* ImagePicker.swift */, @@ -629,8 +671,6 @@ 024FCCFF28EF1CD300232339 /* WebBrowser.swift */, 028CE96829858ECC00B6B1C3 /* FlexibleKeyboardInputView.swift */, 023A4DD3299E66BD006C0E48 /* OfflineSnackBarView.swift */, - 024D865D28F02C6B0077E0A0 /* WebView.swift */, - 02C2DC0729B63D6200F4445D /* WebViewHTML.swift */, 021D925628DCF12900ACC565 /* AlertView.swift */, 0295C884299B99DD00ABE571 /* RefreshableScrollView.swift */, 02B3F16D2AB489A400DDDD4E /* RefreshableScrollViewCompat.swift */, @@ -672,21 +712,21 @@ path = Error; sourceTree = ""; }; - BA8FA65F2AD5973500EA029A /* Providers */ = { + BA593F1A2AF8E487009ADB51 /* ScrollSlidingTabBar */ = { isa = PBXGroup; children = ( - BA30427C2B20B235009B64B7 /* SocialAuth */, + BA593F1D2AF8E4A0009ADB51 /* FrameReader.swift */, + BA593F1B2AF8E498009ADB51 /* ScrollSlidingTabBar.swift */, ); - path = Providers; + path = ScrollSlidingTabBar; sourceTree = ""; }; - BA593F1A2AF8E487009ADB51 /* ScrollSlidingTabBar */ = { + BA8FA65F2AD5973500EA029A /* Providers */ = { isa = PBXGroup; children = ( - BA593F1D2AF8E4A0009ADB51 /* FrameReader.swift */, - BA593F1B2AF8E498009ADB51 /* ScrollSlidingTabBar.swift */, + BA30427C2B20B235009B64B7 /* SocialAuth */, ); - path = ScrollSlidingTabBar; + path = Providers; sourceTree = ""; }; C9DFE47E699CFFA85A77AF2C /* Pods */ = { @@ -937,6 +977,7 @@ buildActionMask = 2147483647; files = ( 0727878528D31657002E9142 /* Data_User.swift in Sources */, + 064987942B4D69FF0071642A /* WebviewInjection.swift in Sources */, 02F6EF4A28D9F0A700835477 /* DateExtension.swift in Sources */, DBF6F2462B01DAFE0098414B /* AgreementConfig.swift in Sources */, 027BD3AF2909475000392132 /* DismissKeyboardTapHandler.swift in Sources */, @@ -948,6 +989,7 @@ 02D800CC29348F460099CF16 /* ImagePicker.swift in Sources */, 027BD3B92909476200392132 /* KeyboardAvoidingModifier.swift in Sources */, 0770DE2C28D092B3006D8A5D /* NetworkLogger.swift in Sources */, + 064987972B4D69FF0071642A /* WebView.swift in Sources */, 0770DE2A28D0929E006D8A5D /* HTTPTask.swift in Sources */, 02284C182A3B1AE00007117F /* UIApplicationExtension.swift in Sources */, 0255D5582936283A004DBC1A /* UploadBodyEncoding.swift in Sources */, @@ -979,6 +1021,7 @@ 02A4833A29B8A9AB00D33F33 /* DownloadManager.swift in Sources */, 027BD3AE2909475000392132 /* KeyboardScrollerOptions.swift in Sources */, BAFB99922B14E23D007D09F9 /* AppleSignInConfig.swift in Sources */, + 064987992B4D69FF0071642A /* WebViewScriptInjectionProtocol.swift in Sources */, 027BD3BE2909478B00392132 /* UIResponder+CurrentResponder.swift in Sources */, 070019AE28F701B200D5FC78 /* Certificate.swift in Sources */, 076F297F2A1F80C800967E7D /* Pagination.swift in Sources */, @@ -988,6 +1031,7 @@ 024FCD0028EF1CD300232339 /* WebBrowser.swift in Sources */, 027BD3B52909475900392132 /* KeyboardStateObserver.swift in Sources */, 0283347D28D4D3DE00C828FC /* Data_Discovery.swift in Sources */, + 064987962B4D69FF0071642A /* WebviewMessage.swift in Sources */, 07DDFCBD29A780BB00572595 /* UINavigationController+Animation.swift in Sources */, 023A4DD4299E66BD006C0E48 /* OfflineSnackBarView.swift in Sources */, 021D925728DCF12900ACC565 /* AlertView.swift in Sources */, @@ -1002,11 +1046,13 @@ 027BD39C2908810C00392132 /* RegisterUser.swift in Sources */, 071009C428D1C9D000344290 /* StyledButton.swift in Sources */, 07460FE1294B706200F70538 /* CollectionExtension.swift in Sources */, + 064987932B4D69FF0071642A /* DragAndDropCssInjection.swift in Sources */, 027BD3B42909475900392132 /* KeyboardState.swift in Sources */, 027BD3922907D88F00392132 /* Data_RegistrationFields.swift in Sources */, 07460FE3294B72D700F70538 /* Notification.swift in Sources */, 0727877F28D25B24002E9142 /* Alamofire+Error.swift in Sources */, 02A4833829B8A8F900D33F33 /* CoreDataModel.xcdatamodeld in Sources */, + 064987952B4D69FF0071642A /* SurveyCssInjection.swift in Sources */, 0259104A29C4A5B6004B5A55 /* UserSettings.swift in Sources */, 021D925028DC89D100ACC565 /* UserProfile.swift in Sources */, 071009D028D1E3A600344290 /* Constants.swift in Sources */, @@ -1036,21 +1082,21 @@ 027BD3A92909474200392132 /* KeyboardAvoidingViewControllerRepr.swift in Sources */, 02F98A7F28F81EE900DE94C0 /* Container+App.swift in Sources */, 02B3F16E2AB489A400DDDD4E /* RefreshableScrollViewCompat.swift in Sources */, + 0649879A2B4D69FF0071642A /* WebViewHTML.swift in Sources */, 0727877B28D24A1D002E9142 /* HeadersRedirectHandler.swift in Sources */, 0236961B28F9A28B00EEF206 /* AuthInteractor.swift in Sources */, 0770DE3028D09793006D8A5D /* EndPointType.swift in Sources */, 020C31C9290AC3F700D6DEA2 /* PickerFields.swift in Sources */, 02F6EF3B28D9B8EC00835477 /* CourseCellView.swift in Sources */, 023A1138291432FD00D0D354 /* FieldConfiguration.swift in Sources */, - 024D865E28F02C6B0077E0A0 /* WebView.swift in Sources */, DBF6F24A2B0380E00098414B /* FeaturesConfig.swift in Sources */, 02F164372902A9EB0090DDEF /* StringExtension.swift in Sources */, 0231CDBE2922422D00032416 /* CSSInjector.swift in Sources */, + 064987982B4D69FF0071642A /* CSSInjectionProtocol.swift in Sources */, BAAD62C62AFCF00B000E6103 /* CustomDisclosureGroup.swift in Sources */, BADB3F5B2AD6EC56004D5CFA /* ResultExtension.swift in Sources */, 0236961928F9A26900EEF206 /* AuthRepository.swift in Sources */, 023A1136291432B200D0D354 /* RegistrationTextField.swift in Sources */, - 02C2DC0829B63D6200F4445D /* WebViewHTML.swift in Sources */, 02A463112AEA966C00331037 /* AppReviewView.swift in Sources */, 025B36752A13B7D5001A640E /* UnitButtonView.swift in Sources */, 028F9F39293A452B00DE65D0 /* ResetPassword.swift in Sources */,