Skip to content

Commit

Permalink
Merge pull request #218 from touchapp/feat/surveyAndDD
Browse files Browse the repository at this point in the history
Feat/survey and dd
  • Loading branch information
forgotvas authored Jan 16, 2024
2 parents b0b829b + 11aec71 commit 8bac9c7
Show file tree
Hide file tree
Showing 14 changed files with 345 additions and 47 deletions.
88 changes: 67 additions & 21 deletions Core/Core.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Core/Core/Domain/Model/CourseDetailBlock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
14 changes: 10 additions & 4 deletions Core/Core/View/Base/WebUnitView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import Theme
public struct WebUnitView: View {

private var url: String
@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) {
self.viewModel = viewModel
public init(url: String, viewModel: WebUnitViewModel, injections: [WebviewInjection]?) {
self._viewModel = .init(wrappedValue: viewModel)
self.url = url
self.injections = injections
}

@ViewBuilder
Expand Down Expand Up @@ -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)
})
Expand Down
34 changes: 34 additions & 0 deletions Core/Core/View/Base/Webview/Models/DragAndDropCssInjection.swift
Original file line number Diff line number Diff line change
@@ -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 = "DragAndDropCSSInjection"
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
}
}
36 changes: 36 additions & 0 deletions Core/Core/View/Base/Webview/Models/SurveyCssInjection.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
46 changes: 46 additions & 0 deletions Core/Core/View/Base/Webview/Models/WebviewInjection.swift
Original file line number Diff line number Diff line change
@@ -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 dragAndDropCss: WebviewInjection {
DragAndDropCssInjection()
.webviewInjection()
}

}
16 changes: 16 additions & 0 deletions Core/Core/View/Base/Webview/Models/WebviewMessage.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
31 changes: 31 additions & 0 deletions Core/Core/View/Base/Webview/Protocols/CSSInjectionProtocol.swift
Original file line number Diff line number Diff line change
@@ -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));
}
})
"""
}
}
Original file line number Diff line number Diff line change
@@ -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
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ public struct WebView: UIViewRepresentable {

@Published var url: String
let baseURL: String
let injections: [WebviewInjection]?

public init(url: String, baseURL: String) {
public init(url: String, baseURL: String, injections: [WebviewInjection]? = nil) {
self.url = url
self.baseURL = baseURL
self.injections = injections
}
}

Expand All @@ -33,7 +35,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) {
Expand Down Expand Up @@ -117,6 +119,16 @@ 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 {
Expand All @@ -127,6 +139,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

Expand All @@ -143,6 +160,19 @@ 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: 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
}

Expand All @@ -157,4 +187,9 @@ public struct WebView: UIViewRepresentable {
}
}
}

public static func dismantleUIView(_ uiView: WKWebView, coordinator: Coordinator) {
uiView.configuration.userContentController.removeAllUserScripts()
uiView.configuration.userContentController.removeAllScriptMessageHandlers()
}
}
File renamed without changes.
17 changes: 10 additions & 7 deletions Course/Course/Presentation/Unit/CourseUnitView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +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)
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 {
Expand Down
Loading

0 comments on commit 8bac9c7

Please sign in to comment.