()
- return filter { set.insert($0).inserted }
- }
-}
diff --git a/Core/Core/Extensions/StringExtension.swift b/Core/Core/Extensions/StringExtension.swift
index d9e3b57f6..3a5e6c141 100644
--- a/Core/Core/Extensions/StringExtension.swift
+++ b/Core/Core/Extensions/StringExtension.swift
@@ -21,10 +21,12 @@ public extension String {
guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else {
return self
}
- return detector.stringByReplacingMatches(in: self,
- options: [],
- range: NSRange(location: 0, length: self.utf16.count),
- withTemplate: "")
+ return detector.stringByReplacingMatches(
+ in: self,
+ options: [],
+ range: NSRange(location: 0, length: self.utf16.count),
+ withTemplate: ""
+ )
.replacingOccurrences(of: "<[^>]+>", with: "", options: String.CompareOptions.regularExpression, range: nil)
.replacingOccurrences(of: "", with: "")
.replacingOccurrences(of: "
", with: "")
@@ -39,12 +41,15 @@ public extension String {
var urls: [URL] = []
do {
let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
- detector.enumerateMatches(in: self, options: [],
- range: NSRange(location: 0, length: self.count), using: { (result, _, _) in
- if let match = result, let url = match.url {
- urls.append(url)
+ detector.enumerateMatches(
+ in: self, options: [],
+ range: NSRange(location: 0, length: self.count),
+ using: { (result, _, _) in
+ if let match = result, let url = match.url {
+ urls.append(url)
+ }
}
- })
+ )
} catch let error as NSError {
print(error.localizedDescription)
}
diff --git a/Core/Core/Extensions/UIApplication+.swift b/Core/Core/Extensions/UIApplication+.swift
deleted file mode 100644
index 94ead3d50..000000000
--- a/Core/Core/Extensions/UIApplication+.swift
+++ /dev/null
@@ -1,13 +0,0 @@
-//
-
-import UIKit
-
-extension UIApplication {
- var keyWindow: UIWindow? {
- UIApplication.shared.windows.first { $0.isKeyWindow }
- }
-
- func endEditing(force: Bool = true) {
- windows.forEach { $0.endEditing(force) }
- }
-}
diff --git a/Core/Core/Extensions/UIApplicationExtension.swift b/Core/Core/Extensions/UIApplicationExtension.swift
new file mode 100644
index 000000000..1c5dec36c
--- /dev/null
+++ b/Core/Core/Extensions/UIApplicationExtension.swift
@@ -0,0 +1,36 @@
+//
+// UIApplicationExtension.swift
+// Core
+//
+// Created by Stepanok Ivan on 15.06.2023.
+//
+
+import UIKit
+
+extension UIApplication {
+
+ public var keyWindow: UIWindow? {
+ UIApplication.shared.windows.first { $0.isKeyWindow }
+ }
+
+ public func endEditing(force: Bool = true) {
+ windows.forEach { $0.endEditing(force) }
+ }
+
+ public class func topViewController(
+ controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController
+ ) -> UIViewController? {
+ if let navigationController = controller as? UINavigationController {
+ return topViewController(controller: navigationController.visibleViewController)
+ }
+ if let tabController = controller as? UITabBarController {
+ if let selected = tabController.selectedViewController {
+ return topViewController(controller: selected)
+ }
+ }
+ if let presented = controller?.presentedViewController {
+ return topViewController(controller: presented)
+ }
+ return controller
+ }
+}
diff --git a/Core/Core/Extensions/ViewExtension.swift b/Core/Core/Extensions/ViewExtension.swift
index e5b1256cf..9dc0a8818 100644
--- a/Core/Core/Extensions/ViewExtension.swift
+++ b/Core/Core/Extensions/ViewExtension.swift
@@ -6,50 +6,19 @@
//
import Foundation
-import Introspect
+import SwiftUIIntrospect
import SwiftUI
public extension View {
- func introspectCollectionView(customize: @escaping (UICollectionView) -> Void) -> some View {
- return inject(UIKitIntrospectionView(
- selector: { introspectionView in
- guard let viewHost = Introspect.findViewHost(from: introspectionView) else {
- return nil
- }
- return Introspect.previousSibling(containing: UICollectionView.self, from: viewHost)
- },
- customize: customize
- ))
- }
- func introspectCollectionViewWithClipping(customize: @escaping (UICollectionView) -> Void) -> some View {
- return inject(UIKitIntrospectionView(
- selector: { introspectionView in
- guard let viewHost = Introspect.findViewHost(from: introspectionView) else {
- return nil
- }
- // first run Introspect as normal
- if let selectedView = Introspect.previousSibling(containing: UICollectionView.self,
- from: viewHost) {
- return selectedView
- } else if let superView = viewHost.superview {
- // if no view was found and a superview exists, search the superview as well
- return Introspect.previousSibling(containing: UICollectionView.self, from: superView)
- } else {
- // no view found at all
- return nil
- }
- },
- customize: customize
- ))
- }
-
- func cardStyle(top: CGFloat? = 0,
- bottom: CGFloat? = 0,
- leftLineEnabled: Bool = false,
- bgColor: Color = CoreAssets.background.swiftUIColor,
- strokeColor: Color = CoreAssets.cardViewStroke.swiftUIColor,
- textColor: Color = CoreAssets.textPrimary.swiftUIColor) -> some View {
+ func cardStyle(
+ top: CGFloat? = 0,
+ bottom: CGFloat? = 0,
+ leftLineEnabled: Bool = false,
+ bgColor: Color = CoreAssets.background.swiftUIColor,
+ strokeColor: Color = CoreAssets.cardViewStroke.swiftUIColor,
+ textColor: Color = CoreAssets.textPrimary.swiftUIColor
+ ) -> some View {
return self
.padding(.all, 20)
.padding(.vertical, leftLineEnabled ? 0 : 6)
@@ -81,10 +50,12 @@ public extension View {
.padding(.bottom, bottom)
}
- func shadowCardStyle(top: CGFloat? = 0,
- bottom: CGFloat? = 0,
- bgColor: Color = CoreAssets.cardViewBackground.swiftUIColor,
- textColor: Color = CoreAssets.textPrimary.swiftUIColor) -> some View {
+ func shadowCardStyle(
+ top: CGFloat? = 0,
+ bottom: CGFloat? = 0,
+ bgColor: Color = CoreAssets.cardViewBackground.swiftUIColor,
+ textColor: Color = CoreAssets.textPrimary.swiftUIColor
+ ) -> some View {
return self
.padding(.all, 16)
.padding(.vertical, 6)
@@ -103,8 +74,11 @@ public extension View {
}
- func titleSettings(top: CGFloat? = 10, bottom: CGFloat? = 20,
- color: Color = CoreAssets.textPrimary.swiftUIColor) -> some View {
+ func titleSettings(
+ top: CGFloat? = 10,
+ bottom: CGFloat? = 20,
+ color: Color = CoreAssets.textPrimary.swiftUIColor
+ ) -> some View {
return self
.lineLimit(1)
.truncationMode(.tail)
@@ -123,10 +97,12 @@ public extension View {
}
}
- func roundedBackground(_ color: Color = CoreAssets.background.swiftUIColor,
- strokeColor: Color = CoreAssets.backgroundStroke.swiftUIColor,
- ipadMaxHeight: CGFloat = .infinity,
- maxIpadWidth: CGFloat = 420) -> some View {
+ func roundedBackground(
+ _ color: Color = CoreAssets.background.swiftUIColor,
+ strokeColor: Color = CoreAssets.backgroundStroke.swiftUIColor,
+ ipadMaxHeight: CGFloat = .infinity,
+ maxIpadWidth: CGFloat = 420
+ ) -> some View {
var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
return ZStack {
RoundedCorners(tl: 24, tr: 24)
@@ -146,8 +122,12 @@ public extension View {
if #available(iOS 16.0, *) {
return self.navigationBarHidden(true)
} else {
- return self.introspectNavigationController { $0.isNavigationBarHidden = true }
- .navigationBarHidden(true)
+ return self.introspect(
+ .navigationView(style: .stack),
+ on: .iOS(.v14, .v15, .v16, .v17),
+ scope: .ancestor) {
+ $0.isNavigationBarHidden = true
+ }
}
}
diff --git a/Core/Core/Network/API.swift b/Core/Core/Network/API.swift
index 19bd2edc0..d891c7af5 100644
--- a/Core/Core/Network/API.swift
+++ b/Core/Core/Network/API.swift
@@ -7,6 +7,7 @@
import Foundation
import Alamofire
+import WebKit
public final class API {
@@ -127,6 +128,12 @@ public final class API {
let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields, for: url)
HTTPCookieStorage.shared.cookies?.forEach { HTTPCookieStorage.shared.deleteCookie($0) }
HTTPCookieStorage.shared.setCookies(cookies, for: url, mainDocumentURL: nil)
+ DispatchQueue.main.async {
+ let cookies = HTTPCookieStorage.shared.cookies ?? []
+ for c in cookies {
+ WKWebsiteDataStore.default().httpCookieStore.setCookie(c)
+ }
+ }
}
private func callResponse(
diff --git a/Core/Core/Network/DownloadManager.swift b/Core/Core/Network/DownloadManager.swift
index 0392c13f5..7166f5b90 100644
--- a/Core/Core/Network/DownloadManager.swift
+++ b/Core/Core/Network/DownloadManager.swift
@@ -188,12 +188,14 @@ public class DownloadManager: DownloadManagerProtocol {
let directoryURL = documentDirectoryURL.appendingPathComponent("Files", isDirectory: true)
if FileManager.default.fileExists(atPath: directoryURL.path) {
- print(directoryURL.path)
return URL(fileURLWithPath: directoryURL.path)
} else {
do {
- try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
- print(directoryURL.path)
+ try FileManager.default.createDirectory(
+ at: directoryURL,
+ withIntermediateDirectories: true,
+ attributes: nil
+ )
return URL(fileURLWithPath: directoryURL.path)
} catch {
print(error.localizedDescription)
diff --git a/Core/Core/Network/HeadersRedirectHandler.swift b/Core/Core/Network/HeadersRedirectHandler.swift
index ccb60e29e..3653392bd 100644
--- a/Core/Core/Network/HeadersRedirectHandler.swift
+++ b/Core/Core/Network/HeadersRedirectHandler.swift
@@ -17,16 +17,17 @@ public class HeadersRedirectHandler: RedirectHandler {
_ task: URLSessionTask,
willBeRedirectedTo request: URLRequest,
for response: HTTPURLResponse,
- completion: @escaping (URLRequest?) -> Void) {
- var redirectedRequest = request
-
- if let originalRequest = task.originalRequest,
- let headers = originalRequest.allHTTPHeaderFields {
- for (key, value) in headers {
- redirectedRequest.setValue(value, forHTTPHeaderField: key)
- }
+ completion: @escaping (URLRequest?) -> Void
+ ) {
+ var redirectedRequest = request
+
+ if let originalRequest = task.originalRequest,
+ let headers = originalRequest.allHTTPHeaderFields {
+ for (key, value) in headers {
+ redirectedRequest.setValue(value, forHTTPHeaderField: key)
}
-
- completion(redirectedRequest)
}
+
+ completion(redirectedRequest)
+ }
}
diff --git a/Core/Core/Network/RequestInterceptor.swift b/Core/Core/Network/RequestInterceptor.swift
index 0915d4f84..3f8e80b9a 100644
--- a/Core/Core/Network/RequestInterceptor.swift
+++ b/Core/Core/Network/RequestInterceptor.swift
@@ -27,10 +27,10 @@ final public class RequestInterceptor: Alamofire.RequestInterceptor {
_ urlRequest: URLRequest,
for session: Session,
completion: @escaping (Result) -> Void) {
- // guard urlRequest.url?.absoluteString.hasPrefix("https://api.authenticated.com") == true else {
- // /// If the request does not require authentication, we can directly return it as unmodified.
- // return completion(.success(urlRequest))
- // }
+// guard urlRequest.url?.absoluteString.hasPrefix("https://api.authenticated.com") == true else {
+// // If the request does not require authentication, we can directly return it as unmodified.
+// return completion(.success(urlRequest))
+// }
var urlRequest = urlRequest
// Set the Authorization header value using the access token.
diff --git a/Core/Core/SwiftGen/Assets.swift b/Core/Core/SwiftGen/Assets.swift
index 829d65a8f..887501306 100644
--- a/Core/Core/SwiftGen/Assets.swift
+++ b/Core/Core/SwiftGen/Assets.swift
@@ -38,6 +38,7 @@ public enum CoreAssets {
public static let shadowColor = ColorAsset(name: "ShadowColor")
public static let snackbarErrorColor = ColorAsset(name: "SnackbarErrorColor")
public static let snackbarErrorTextColor = ColorAsset(name: "SnackbarErrorTextColor")
+ public static let snackbarInfoAlert = ColorAsset(name: "SnackbarInfoAlert")
public static let styledButtonBackground = ColorAsset(name: "StyledButtonBackground")
public static let styledButtonText = ColorAsset(name: "StyledButtonText")
public static let textPrimary = ColorAsset(name: "TextPrimary")
@@ -56,8 +57,10 @@ public enum CoreAssets {
public static let allPosts = ImageAsset(name: "allPosts")
public static let chapter = ImageAsset(name: "chapter")
public static let discussion = ImageAsset(name: "discussion")
+ public static let discussionIcon = ImageAsset(name: "discussionIcon")
public static let extra = ImageAsset(name: "extra")
public static let filter = ImageAsset(name: "filter")
+ public static let finished = ImageAsset(name: "finished")
public static let followed = ImageAsset(name: "followed")
public static let pen = ImageAsset(name: "pen")
public static let question = ImageAsset(name: "question")
diff --git a/Core/Core/SwiftGen/Strings.swift b/Core/Core/SwiftGen/Strings.swift
index 6790017a0..0197b0494 100644
--- a/Core/Core/SwiftGen/Strings.swift
+++ b/Core/Core/SwiftGen/Strings.swift
@@ -24,6 +24,36 @@ public enum CoreLocalization {
/// Log out
public static let logout = CoreLocalization.tr("Localizable", "ALERT.LOGOUT", fallback: "Log out")
}
+ public enum Courseware {
+ /// Back to outline
+ public static let backToOutline = CoreLocalization.tr("Localizable", "COURSEWARE.BACK_TO_OUTLINE", fallback: "Back to outline")
+ /// Continue
+ public static let `continue` = CoreLocalization.tr("Localizable", "COURSEWARE.CONTINUE", fallback: "Continue")
+ /// Continue with:
+ public static let continueWith = CoreLocalization.tr("Localizable", "COURSEWARE.CONTINUE_WITH", fallback: "Continue with:")
+ /// Course content
+ public static let courseContent = CoreLocalization.tr("Localizable", "COURSEWARE.COURSE_CONTENT", fallback: "Course content")
+ /// Course units
+ public static let courseUnits = CoreLocalization.tr("Localizable", "COURSEWARE.COURSE_UNITS", fallback: "Course units")
+ /// Finish
+ public static let finish = CoreLocalization.tr("Localizable", "COURSEWARE.FINISH", fallback: "Finish")
+ /// Good Work!
+ public static let goodWork = CoreLocalization.tr("Localizable", "COURSEWARE.GOOD_WORK", fallback: "Good Work!")
+ /// “ is finished.
+ public static let isFinished = CoreLocalization.tr("Localizable", "COURSEWARE.IS_FINISHED", fallback: "“ is finished.")
+ /// Next
+ public static let next = CoreLocalization.tr("Localizable", "COURSEWARE.NEXT", fallback: "Next")
+ /// Next section
+ public static let nextSection = CoreLocalization.tr("Localizable", "COURSEWARE.NEXT_SECTION", fallback: "Next section")
+ /// To proceed with “
+ public static let nextSectionDescriptionFirst = CoreLocalization.tr("Localizable", "COURSEWARE.NEXT_SECTION_DESCRIPTION_FIRST", fallback: "To proceed with “")
+ /// ” press “Next section”.
+ public static let nextSectionDescriptionLast = CoreLocalization.tr("Localizable", "COURSEWARE.NEXT_SECTION_DESCRIPTION_LAST", fallback: "” press “Next section”.")
+ /// Prev
+ public static let previous = CoreLocalization.tr("Localizable", "COURSEWARE.PREVIOUS", fallback: "Prev")
+ /// Section “
+ public static let section = CoreLocalization.tr("Localizable", "COURSEWARE.SECTION", fallback: "Section “")
+ }
public enum Date {
/// Ended
public static let ended = CoreLocalization.tr("Localizable", "DATE.ENDED", fallback: "Ended")
@@ -53,6 +83,8 @@ public enum CoreLocalization {
public static let invalidCredentials = CoreLocalization.tr("Localizable", "ERROR.INVALID_CREDENTIALS", fallback: "Invalid credentials")
/// No cached data for offline mode
public static let noCachedData = CoreLocalization.tr("Localizable", "ERROR.NO_CACHED_DATA", fallback: "No cached data for offline mode")
+ /// Reload
+ public static let reload = CoreLocalization.tr("Localizable", "ERROR.RELOAD", fallback: "Reload")
/// Slow or no internet connection
public static let slowOrNoInternetConnection = CoreLocalization.tr("Localizable", "ERROR.SLOW_OR_NO_INTERNET_CONNECTION", fallback: "Slow or no internet connection")
/// Something went wrong
@@ -97,6 +129,14 @@ public enum CoreLocalization {
public static let tryAgainBtn = CoreLocalization.tr("Localizable", "VIEW.SNACKBAR.TRY_AGAIN_BTN", fallback: "Try Again")
}
}
+ public enum Webview {
+ public enum Alert {
+ /// Cancel
+ public static let cancel = CoreLocalization.tr("Localizable", "WEBVIEW.ALERT.CANCEL", fallback: "Cancel")
+ /// Ok
+ public static let ok = CoreLocalization.tr("Localizable", "WEBVIEW.ALERT.OK", fallback: "Ok")
+ }
+ }
}
// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
diff --git a/Core/Core/Theme.swift b/Core/Core/Theme.swift
index 57bc2bcd7..56db63501 100644
--- a/Core/Core/Theme.swift
+++ b/Core/Core/Theme.swift
@@ -38,6 +38,7 @@ public struct Theme {
public static let cardImageRadius = 10.0
public static let textInputShape = RoundedRectangle(cornerRadius: 8)
public static let buttonShape = RoundedCorners(tl: 8, tr: 8, bl: 8, br: 8)
+ public static let unitButtonShape = RoundedCorners(tl: 21, tr: 21, bl: 21, br: 21)
public static let roundedScreenBackgroundShape = RoundedCorners(
tl: Theme.Shapes.screenBackgroundRadius,
tr: Theme.Shapes.screenBackgroundRadius,
@@ -65,6 +66,7 @@ public extension Theme.Fonts {
guard let url = Bundle(for: __.self).url(forResource: "SF-Pro", withExtension: "ttf") else { return }
CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil)
}
+ // swiftlint:enable type_name
}
extension View {
diff --git a/Core/Core/View/Base/AlertView.swift b/Core/Core/View/Base/AlertView.swift
index 7a51d0bf8..e4dd061a2 100644
--- a/Core/Core/View/Base/AlertView.swift
+++ b/Core/Core/View/Base/AlertView.swift
@@ -27,8 +27,10 @@ public struct AlertView: View {
private var alertTitle: String
private var alertMessage: String
+ private var nextSectionName: String?
private var onCloseTapped: (() -> Void) = {}
private var okTapped: (() -> Void) = {}
+ private var nextSectionTapped: (() -> Void) = {}
private let type: AlertViewType
public init(
@@ -49,15 +51,19 @@ public struct AlertView: View {
public init(
alertTitle: String,
alertMessage: String,
+ nextSectionName: String? = nil,
mainAction: String,
image: SwiftUI.Image,
onCloseTapped: @escaping () -> Void,
- okTapped: @escaping () -> Void
+ okTapped: @escaping () -> Void,
+ nextSectionTapped: @escaping () -> Void
) {
self.alertTitle = alertTitle
self.alertMessage = alertMessage
self.onCloseTapped = onCloseTapped
+ self.nextSectionName = nextSectionName
self.okTapped = okTapped
+ self.nextSectionTapped = nextSectionTapped
type = .action(mainAction, image)
}
@@ -99,6 +105,7 @@ public struct AlertView: View {
.font(Theme.Fonts.bodyMedium)
.multilineTextAlignment(.center)
.padding(.horizontal, 40)
+ .frame(maxWidth: 250)
}
HStack {
switch type {
@@ -109,8 +116,29 @@ public struct AlertView: View {
.frame(maxWidth: 135)
.saturation(0)
case let .action(action, _):
- StyledButton(action, action: { okTapped() })
- .frame(maxWidth: 160)
+ VStack(spacing: 20) {
+ if nextSectionName != nil {
+ UnitButtonView(type: .nextSection, action: { nextSectionTapped() })
+ .frame(maxWidth: 215)
+ }
+ UnitButtonView(type: .custom(action),
+ bgColor: .clear,
+ action: { okTapped() })
+ .frame(maxWidth: 215)
+
+ if let nextSectionName {
+ Group {
+ Text(CoreLocalization.Courseware.nextSectionDescriptionFirst) +
+ Text(nextSectionName) +
+ Text(CoreLocalization.Courseware.nextSectionDescriptionLast)
+ }.frame(maxWidth: 215)
+ .padding(.horizontal, 40)
+ .multilineTextAlignment(.center)
+ .font(Theme.Fonts.labelSmall)
+ .foregroundColor(CoreAssets.textSecondary.swiftUIColor)
+ }
+
+ }
case .logOut:
Button(action: {
okTapped()
@@ -133,7 +161,12 @@ public struct AlertView: View {
)
.overlay(
RoundedRectangle(cornerRadius: 8)
- .stroke(style: .init(lineWidth: 1, lineCap: .round, lineJoin: .round, miterLimit: 1))
+ .stroke(style: .init(
+ lineWidth: 1,
+ lineCap: .round,
+ lineJoin: .round,
+ miterLimit: 1
+ ))
.foregroundColor(.clear)
)
.frame(maxWidth: 215)
@@ -157,7 +190,12 @@ public struct AlertView: View {
)
.overlay(
RoundedRectangle(cornerRadius: 8)
- .stroke(style: .init(lineWidth: 1, lineCap: .round, lineJoin: .round, miterLimit: 1))
+ .stroke(style: .init(
+ lineWidth: 1,
+ lineCap: .round,
+ lineJoin: .round,
+ miterLimit: 1
+ ))
.foregroundColor(.clear)
)
.frame(maxWidth: 215)
@@ -180,7 +218,12 @@ public struct AlertView: View {
)
.overlay(
RoundedRectangle(cornerRadius: 8)
- .stroke(style: .init(lineWidth: 1, lineCap: .round, lineJoin: .round, miterLimit: 1))
+ .stroke(style: .init(
+ lineWidth: 1,
+ lineCap: .round,
+ lineJoin: .round,
+ miterLimit: 1
+ ))
.foregroundColor(CoreAssets.textPrimary.swiftUIColor)
)
.frame(maxWidth: 215)
@@ -218,14 +261,17 @@ public struct AlertView: View {
struct AlertView_Previews: PreviewProvider {
static var previews: some View {
AlertView(
- alertTitle: "Warning!",
+ alertTitle: "Warning",
alertMessage: "Something goes wrong. Do you want to exterminate your phone, right now",
- positiveAction: "Accept",
+ nextSectionName: "Ahmad tea is a power",
+ mainAction: "Back to outline",
+ image: CoreAssets.goodWork.swiftUIImage,
onCloseTapped: {},
okTapped: {},
- type: .logOut
+ nextSectionTapped: {}
)
.previewLayout(.sizeThatFits)
.background(Color.gray)
}
}
+//swiftlint:enable all
diff --git a/Core/Core/View/Base/CourseButton.swift b/Core/Core/View/Base/CourseButton.swift
index d91b570d4..a7473e36e 100644
--- a/Core/Core/View/Base/CourseButton.swift
+++ b/Core/Core/View/Base/CourseButton.swift
@@ -26,7 +26,8 @@ public struct CourseButton: View {
public var body: some View {
HStack {
if isCompleted {
- Image(systemName: "checkmark.circle.fill")
+ CoreAssets.finished.swiftUIImage
+ .renderingMode(.template)
.foregroundColor(.accentColor)
} else {
image
@@ -58,6 +59,11 @@ public struct CourseButton: View {
struct CourseButton_Previews: PreviewProvider {
static var previews: some View {
- CourseButton(isCompleted: true, image: CoreAssets.pen.swiftUIImage, displayName: "Lets see whats happen", index: 0)
+ CourseButton(
+ isCompleted: true,
+ image: CoreAssets.pen.swiftUIImage,
+ displayName: "Lets see whats happen",
+ index: 0
+ )
}
}
diff --git a/Core/Core/View/Base/CourseCellView.swift b/Core/Core/View/Base/CourseCellView.swift
index 1388d1489..cc5e83022 100644
--- a/Core/Core/View/Base/CourseCellView.swift
+++ b/Core/Core/View/Base/CourseCellView.swift
@@ -33,7 +33,7 @@ public struct CourseCellView: View {
self.courseStart = model.courseStart?.dateToString(style: .startDDMonthYear) ?? ""
self.courseEnd = model.courseEnd?.dateToString(style: .endedMonthDay) ?? ""
self.courseOrg = model.org
- self.index = Double(index)+1
+ self.index = Double(index) + 1
self.cellsCount = cellsCount
}
@@ -144,3 +144,4 @@ struct CourseCellView_Previews: PreviewProvider {
}
}
+// swiftlint:enable all
diff --git a/Core/Core/View/Base/HTMLFormattedText.swift b/Core/Core/View/Base/HTMLFormattedText.swift
index d29f9e8b0..6276b9f2d 100644
--- a/Core/Core/View/Base/HTMLFormattedText.swift
+++ b/Core/Core/View/Base/HTMLFormattedText.swift
@@ -70,11 +70,14 @@ public struct HTMLFormattedText: UIViewRepresentable {
private func convertHTML(text: String) -> NSAttributedString? {
guard let data = text.data(using: .utf8) else { return nil }
- if let attributedString = try? NSAttributedString(data: data,
- options: [
- .documentType: NSAttributedString.DocumentType.html,
- .characterEncoding: String.Encoding.utf8.rawValue
- ], documentAttributes: nil) {
+ if let attributedString = try? NSAttributedString(
+ data: data,
+ options: [
+ .documentType: NSAttributedString.DocumentType.html,
+ .characterEncoding: String.Encoding.utf8.rawValue
+ ],
+ documentAttributes: nil
+ ) {
return attributedString
} else {
return nil
diff --git a/Core/Core/View/Base/PickerMenu.swift b/Core/Core/View/Base/PickerMenu.swift
index ab2377f89..ea0a78ce5 100644
--- a/Core/Core/View/Base/PickerMenu.swift
+++ b/Core/Core/View/Base/PickerMenu.swift
@@ -32,11 +32,13 @@ public struct PickerMenu: View {
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
private var selected: ((PickerItem) -> Void) = { _ in }
- public init(items: [PickerItem],
- titleText: String,
- router: BaseRouter,
- selectedItem: PickerItem? = nil,
- selected: @escaping (PickerItem) -> Void) {
+ public init(
+ items: [PickerItem],
+ titleText: String,
+ router: BaseRouter,
+ selectedItem: PickerItem? = nil,
+ selected: @escaping (PickerItem) -> Void
+ ) {
self.items = items
self.titleText = titleText
self.router = router
diff --git a/Core/Core/View/Base/StyledButton.swift b/Core/Core/View/Base/StyledButton.swift
index 4e1a0b872..65d223447 100644
--- a/Core/Core/View/Base/StyledButton.swift
+++ b/Core/Core/View/Base/StyledButton.swift
@@ -43,7 +43,7 @@ public struct StyledButton: View {
.frame(maxWidth: .infinity)
.padding(.horizontal, 16)
}
- .frame(maxWidth: idiom == .pad ? 260: .infinity, minHeight: isTransparent ? 36 : 48)
+ .frame(maxWidth: idiom == .pad ? 260: .infinity, minHeight: isTransparent ? 36 : 42)
.background(
Theme.Shapes.buttonShape
.fill(isTransparent ? .clear : buttonColor)
diff --git a/Core/Core/View/Base/TextWithUrls.swift b/Core/Core/View/Base/TextWithUrls.swift
index 70bc6934b..e516a22f8 100644
--- a/Core/Core/View/Base/TextWithUrls.swift
+++ b/Core/Core/View/Base/TextWithUrls.swift
@@ -62,12 +62,15 @@ public struct TextWithUrls: View {
var text = Text("")
attributedString.enumerateAttributes(in: stringRange, options: []) { attrs, range, _ in
let valueOfString: String = attributedString.attributedSubstring(from: range).string
- text = text + Text(.init((attrs[.underlineStyle] != nil ? getMarkupText(url: valueOfString): valueOfString)))
+ text = text + Text(.init((attrs[.underlineStyle] != nil
+ ? getMarkupText(url: valueOfString)
+ : valueOfString)))
}
return text
}
}
+// swiftlint:enable shorthand_operator
struct TextWithUrls_Previews: PreviewProvider {
static var previews: some View {
diff --git a/Core/Core/View/Base/UnitButtonView.swift b/Core/Core/View/Base/UnitButtonView.swift
new file mode 100644
index 000000000..52b3900c9
--- /dev/null
+++ b/Core/Core/View/Base/UnitButtonView.swift
@@ -0,0 +1,209 @@
+//
+// UnitButtonView.swift
+// Course
+//
+// Created by Stepanok Ivan on 14.02.2023.
+//
+
+import SwiftUI
+
+public enum UnitButtonType: Equatable {
+ case first
+ case next
+ case nextBig
+ case previous
+ case last
+ case finish
+ case reload
+ case continueLesson
+ case nextSection
+ case custom(String)
+
+ func stringValue() -> String {
+ switch self {
+ case .first:
+ return CoreLocalization.Courseware.next
+ case .next, .nextBig:
+ return CoreLocalization.Courseware.next
+ case .previous:
+ return CoreLocalization.Courseware.previous
+ case .last:
+ return CoreLocalization.Courseware.finish
+ case .finish:
+ return CoreLocalization.Courseware.finish
+ case .reload:
+ return CoreLocalization.Error.reload
+ case .continueLesson:
+ return CoreLocalization.Courseware.continue
+ case .nextSection:
+ return CoreLocalization.Courseware.nextSection
+ case let .custom(text):
+ return text
+ }
+ }
+}
+
+public struct UnitButtonView: View {
+
+ private let action: () -> Void
+ private let type: UnitButtonType
+ private let bgColor: Color?
+
+ public init(type: UnitButtonType, bgColor: Color? = nil, action: @escaping () -> Void) {
+ self.type = type
+ self.bgColor = bgColor
+ self.action = action
+ }
+
+ public var body: some View {
+ HStack {
+ Button(action: action) {
+ VStack {
+ switch type {
+ case .first:
+ HStack {
+ Text(type.stringValue())
+ .foregroundColor(CoreAssets.styledButtonText.swiftUIColor)
+ .font(Theme.Fonts.labelLarge)
+ CoreAssets.arrowLeft.swiftUIImage.renderingMode(.template)
+ .foregroundColor(CoreAssets.styledButtonText.swiftUIColor)
+ .rotationEffect(Angle.degrees(-90))
+ }.padding(.horizontal, 16)
+ case .next, .nextBig:
+ HStack {
+ Text(type.stringValue())
+ .foregroundColor(CoreAssets.styledButtonText.swiftUIColor)
+ .padding(.leading, 20)
+ .font(Theme.Fonts.labelLarge)
+ if type != .nextBig {
+ Spacer()
+ }
+ CoreAssets.arrowLeft.swiftUIImage.renderingMode(.template)
+ .foregroundColor(CoreAssets.styledButtonText.swiftUIColor)
+ .rotationEffect(Angle.degrees(-90))
+ .padding(.trailing, 20)
+ }
+ case .previous:
+ HStack {
+ Text(type.stringValue())
+ .foregroundColor(CoreAssets.accentColor.swiftUIColor)
+ .font(Theme.Fonts.labelLarge)
+ .padding(.leading, 20)
+ CoreAssets.arrowLeft.swiftUIImage.renderingMode(.template)
+ .rotationEffect(Angle.degrees(90))
+ .padding(.trailing, 20)
+ .foregroundColor(CoreAssets.accentColor.swiftUIColor)
+
+ }
+ case .last:
+ HStack {
+ Text(type.stringValue())
+ .foregroundColor(CoreAssets.styledButtonText.swiftUIColor)
+ .padding(.leading, 16)
+ .font(Theme.Fonts.labelLarge)
+ Spacer()
+ CoreAssets.check.swiftUIImage.renderingMode(.template)
+ .foregroundColor(CoreAssets.styledButtonText.swiftUIColor)
+ .padding(.trailing, 16)
+ }
+ case .finish:
+ HStack {
+ Text(type.stringValue())
+ .foregroundColor(CoreAssets.styledButtonText.swiftUIColor)
+ .font(Theme.Fonts.labelLarge)
+ CoreAssets.check.swiftUIImage.renderingMode(.template)
+ .foregroundColor(CoreAssets.styledButtonText.swiftUIColor)
+ }.padding(.horizontal, 16)
+ case .reload, .custom:
+ VStack(alignment: .center) {
+ Text(type.stringValue())
+ .foregroundColor(bgColor == nil ? .white : CoreAssets.accentColor.swiftUIColor)
+ .font(Theme.Fonts.labelLarge)
+ }.padding(.horizontal, 16)
+ case .continueLesson, .nextSection:
+ HStack {
+ Text(type.stringValue())
+ .foregroundColor(CoreAssets.styledButtonText.swiftUIColor)
+ .padding(.leading, 20)
+ .font(Theme.Fonts.labelLarge)
+ CoreAssets.arrowLeft.swiftUIImage.renderingMode(.template)
+ .foregroundColor(CoreAssets.styledButtonText.swiftUIColor)
+ .rotationEffect(Angle.degrees(180))
+ .padding(.trailing, 20)
+ }
+ }
+ }
+ .frame(maxWidth: .infinity, minHeight: 42)
+ .background(
+ VStack {
+ switch self.type {
+ case .first, .next, .nextBig, .previous, .last:
+ Theme.Shapes.buttonShape
+ .fill(type == .previous
+ ? CoreAssets.background.swiftUIColor
+ : CoreAssets.accentColor.swiftUIColor)
+ .shadow(color: Color.black.opacity(0.25), radius: 21, y: 4)
+ .overlay(
+ RoundedRectangle(cornerRadius: 8)
+ .stroke(style: .init(
+ lineWidth: 1,
+ lineCap: .round,
+ lineJoin: .round,
+ miterLimit: 1)
+ )
+ .foregroundColor(CoreAssets.accentColor.swiftUIColor)
+ )
+
+ case .continueLesson, .nextSection, .reload, .finish, .custom:
+ Theme.Shapes.buttonShape
+ .fill(bgColor ?? CoreAssets.accentColor.swiftUIColor)
+
+ .shadow(color: (type == .first
+ || type == .next
+ || type == .previous
+ || type == .last
+ || type == .finish
+ || type == .reload) ? Color.black.opacity(0.25) : .clear,
+ radius: 21, y: 4)
+ .overlay(
+ RoundedRectangle(cornerRadius: 8)
+ .stroke(style: .init(
+ lineWidth: 1,
+ lineCap: .round,
+ lineJoin: .round,
+ miterLimit: 1
+ ))
+ .foregroundColor(CoreAssets.accentColor.swiftUIColor)
+ )
+ }
+ }
+ )
+
+ }
+ .fixedSize(horizontal: (type == .first
+ || type == .next
+ || type == .previous
+ || type == .last
+ || type == .finish
+ || type == .reload)
+ , vertical: false)
+ }
+
+ }
+}
+
+struct UnitButtonView_Previews: PreviewProvider {
+ static var previews: some View {
+ VStack {
+ UnitButtonView(type: .first, action: {})
+ UnitButtonView(type: .previous, action: {})
+ UnitButtonView(type: .next, action: {})
+ UnitButtonView(type: .last, action: {})
+ UnitButtonView(type: .finish, action: {})
+ UnitButtonView(type: .reload, action: {})
+ UnitButtonView(type: .custom("Custom text"), action: {})
+ UnitButtonView(type: .continueLesson, action: {})
+ UnitButtonView(type: .nextSection, action: {})
+ }.padding()
+ }
+}
diff --git a/Core/Core/View/Base/WebBrowser.swift b/Core/Core/View/Base/WebBrowser.swift
index 04ae19b99..6d89e4528 100644
--- a/Core/Core/View/Base/WebBrowser.swift
+++ b/Core/Core/View/Base/WebBrowser.swift
@@ -6,6 +6,7 @@
//
import SwiftUI
+import WebKit
public struct WebBrowser: View {
@@ -24,17 +25,23 @@ public struct WebBrowser: View {
// MARK: - Page name
VStack(alignment: .center) {
- NavigationBar(title: pageTitle,
- leftButtonAction: { presentationMode.wrappedValue.dismiss() })
+ NavigationBar(
+ title: pageTitle,
+ leftButtonAction: { presentationMode.wrappedValue.dismiss() }
+ )
// MARK: - Page Body
VStack {
ZStack(alignment: .top) {
NavigationView {
- WebView(viewModel: .init(url: url), isLoading: $isShowProgress, refreshCookies: {})
- .navigationBarTitle(Text("")) // Needed for hide navBar on ios 14, 15
- .navigationBarHidden(true)
- .ignoresSafeArea()
+ WebView(
+ viewModel: .init(url: url, baseURL: ""),
+ isLoading: $isShowProgress,
+ refreshCookies: {}
+ )
+ .navigationBarTitle(Text("")) // Needed for hide navBar on ios 14, 15
+ .navigationBarHidden(true)
+ .ignoresSafeArea()
}
}
}
diff --git a/Core/Core/View/Base/WebUnitView.swift b/Core/Core/View/Base/WebUnitView.swift
index 12f6e162b..e1108b3dc 100644
--- a/Core/Core/View/Base/WebUnitView.swift
+++ b/Core/Core/View/Base/WebUnitView.swift
@@ -6,7 +6,7 @@
//
import SwiftUI
-import Introspect
+import SwiftUIIntrospect
public struct WebUnitView: View {
@@ -17,64 +17,64 @@ public struct WebUnitView: View {
public init(url: String, viewModel: WebUnitViewModel) {
self.viewModel = viewModel
self.url = url
- Task {
- await viewModel.updateCookies()
- }
}
@ViewBuilder
public var body: some View {
- ZStack(alignment: .center) {
- GeometryReader { reader in
- ScrollView {
- if viewModel.cookiesReady {
- WebView(viewModel: .init(url: url), isLoading: $isWebViewLoading, refreshCookies: {
- await viewModel.updateCookies(force: true)
- })
- .introspectScrollView(customize: { scrollView in
- scrollView.isScrollEnabled = false
- scrollView.alwaysBounceVertical = false
- scrollView.alwaysBounceHorizontal = false
- scrollView.bounces = false
- })
- .frame(width: reader.size.width, height: reader.size.height)
+ // MARK: - Error Alert
+ if viewModel.showError {
+ VStack(spacing: 28) {
+ Image(systemName: "nosign")
+ .resizable()
+ .scaledToFit()
+ .frame(width: 64)
+ .foregroundColor(CoreAssets.textPrimary.swiftUIColor)
+ Text(viewModel.errorMessage ?? "")
+ .foregroundColor(CoreAssets.textPrimary.swiftUIColor)
+ .multilineTextAlignment(.center)
+ .padding(.horizontal, 20)
+ Button(action: {
+ Task {
+ await viewModel.updateCookies(force: true)
}
- }
- if viewModel.updatingCookies || isWebViewLoading {
- VStack {
- ProgressBar(size: 40, lineWidth: 8)
+ }, label: {
+ Text(CoreLocalization.View.Snackbar.tryAgainBtn)
+ .frame(maxWidth: .infinity, minHeight: 48)
+ .background(Theme.Shapes.buttonShape.fill(.clear))
+ .overlay(RoundedRectangle(cornerRadius: 8)
+ .stroke(style: .init(lineWidth: 1, lineCap: .round, lineJoin: .round, miterLimit: 1))
+ .foregroundColor(CoreAssets.accentColor.swiftUIColor)
+ )
+ })
+ .frame(width: 100)
+ }.frame(maxWidth: .infinity, maxHeight: .infinity)
+ } else {
+ ZStack(alignment: .center) {
+ GeometryReader { reader in
+ ScrollView {
+ if viewModel.cookiesReady {
+ WebView(
+ viewModel: .init(url: url, baseURL: viewModel.config.baseURL.absoluteString),
+ isLoading: $isWebViewLoading, refreshCookies: {
+ await viewModel.updateCookies(force: true)
+ })
+ .introspect(.scrollView, on: .iOS(.v14, .v15, .v16, .v17), customize: { scrollView in
+ scrollView.isScrollEnabled = false
+ })
+ .frame(width: reader.size.width, height: reader.size.height)
+ }
}
- .frame(maxWidth: .infinity, maxHeight: .infinity)
- }
- }
-
- // MARK: - Error Alert
- if viewModel.showError {
- VStack(spacing: 28) {
- Image(systemName: "nosign")
- .resizable()
- .scaledToFit()
- .frame(width: 64)
- .foregroundColor(.black)
- Text(viewModel.errorMessage ?? "")
- .foregroundColor(.black)
- .multilineTextAlignment(.center)
- .padding(.horizontal, 20)
- Button(action: {
- Task {
- await viewModel.updateCookies(force: true)
+ if viewModel.updatingCookies || isWebViewLoading {
+ VStack {
+ ProgressBar(size: 40, lineWidth: 8)
}
- }, label: {
- Text(CoreLocalization.View.Snackbar.tryAgainBtn)
- .frame(maxWidth: .infinity, minHeight: 48)
- .background(Theme.Shapes.buttonShape.fill(.clear))
- .overlay(RoundedRectangle(cornerRadius: 8)
- .stroke(style: .init(lineWidth: 1, lineCap: .round, lineJoin: .round, miterLimit: 1))
- .foregroundColor(CoreAssets.accentColor.swiftUIColor)
- )
- })
- .frame(width: 100)
- }.frame(maxWidth: .infinity, maxHeight: .infinity)
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
+ }
+ }
+ }.onFirstAppear {
+ Task {
+ await viewModel.updateCookies()
+ }
}
}
}
diff --git a/Core/Core/View/Base/WebUnitViewModel.swift b/Core/Core/View/Base/WebUnitViewModel.swift
index 9b06a1daa..caa010a93 100644
--- a/Core/Core/View/Base/WebUnitViewModel.swift
+++ b/Core/Core/View/Base/WebUnitViewModel.swift
@@ -9,7 +9,10 @@ import Foundation
import SwiftUI
public class WebUnitViewModel: ObservableObject {
+
let authInteractor: AuthInteractorProtocol
+ let config: Config
+
@Published var updatingCookies: Bool = false
@Published var cookiesReady: Bool = false
@Published var showError: Bool = false
@@ -23,17 +26,20 @@ public class WebUnitViewModel: ObservableObject {
}
}
- public init(authInteractor: AuthInteractorProtocol) {
+ public init(authInteractor: AuthInteractorProtocol, config: Config) {
self.authInteractor = authInteractor
+ self.config = config
}
@MainActor
func updateCookies(force: Bool = false) async {
+ guard !updatingCookies else { return }
do {
updatingCookies = true
try await authInteractor.getCookies(force: force)
cookiesReady = true
updatingCookies = false
+ errorMessage = nil
} catch {
if error.isInternetError {
errorMessage = CoreLocalization.Error.slowOrNoInternetConnection
diff --git a/Core/Core/View/Base/WebView.swift b/Core/Core/View/Base/WebView.swift
index 14fb8da4e..463a9a315 100644
--- a/Core/Core/View/Base/WebView.swift
+++ b/Core/Core/View/Base/WebView.swift
@@ -12,10 +12,13 @@ import SwiftUI
public struct WebView: UIViewRepresentable {
public class ViewModel: ObservableObject {
+
@Published var url: String
+ let baseURL: String
- public init(url: String) {
+ public init(url: String, baseURL: String) {
self.url = url
+ self.baseURL = baseURL
}
}
@@ -29,7 +32,7 @@ public struct WebView: UIViewRepresentable {
self.refreshCookies = refreshCookies
}
- public class Coordinator: NSObject, WKNavigationDelegate {
+ public class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate {
var parent: WebView
init(_ parent: WebView) {
@@ -42,12 +45,62 @@ public struct WebView: UIViewRepresentable {
}
}
- public func webView(_ webView: WKWebView,
- decidePolicyFor navigationResponse: WKNavigationResponse) async -> WKNavigationResponsePolicy {
- guard let statusCode = (navigationResponse.response as? HTTPURLResponse)?.statusCode else {
+ public func webView(
+ _ webView: WKWebView,
+ runJavaScriptConfirmPanelWithMessage message: String,
+ initiatedByFrame frame: WKFrameInfo,
+ completionHandler: @escaping (Bool) -> Void
+ ) {
+
+ let alertController = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
+
+ alertController.addAction(UIAlertAction(
+ title: CoreLocalization.Webview.Alert.ok,
+ style: .default,
+ handler: { _ in
+ completionHandler(true)
+ }))
+
+ alertController.addAction(UIAlertAction(
+ title: CoreLocalization.Webview.Alert.cancel,
+ style: .cancel,
+ handler: { _ in
+ completionHandler(false)
+ }))
+
+ UIApplication.topViewController()?.present(alertController, animated: true, completion: nil)
+ }
+
+ public func webView(
+ _ webView: WKWebView,
+ decidePolicyFor navigationAction: WKNavigationAction
+ ) async -> WKNavigationActionPolicy {
+ guard let url = navigationAction.request.url else {
return .cancel
}
- if (401...404).contains(statusCode) {
+
+ let baseURL = await parent.viewModel.baseURL
+ if !baseURL.isEmpty, !url.absoluteString.starts(with: baseURL) {
+ await MainActor.run {
+ UIApplication.shared.open(url, options: [:])
+ }
+ return .cancel
+ }
+
+ return .allow
+ }
+
+ public func webView(
+ _ webView: WKWebView,
+ decidePolicyFor navigationResponse: WKNavigationResponse
+ ) async -> WKNavigationResponsePolicy {
+ guard let response = (navigationResponse.response as? HTTPURLResponse),
+ let url = response.url else {
+ return .cancel
+ }
+ let baseURL = await parent.viewModel.baseURL
+
+ if (401...404).contains(response.statusCode) || url.absoluteString.hasPrefix(baseURL + "/login") {
await parent.refreshCookies()
DispatchQueue.main.async {
if let url = webView.url {
@@ -65,34 +118,35 @@ public struct WebView: UIViewRepresentable {
}
public func makeUIView(context: UIViewRepresentableContext) -> WKWebView {
- let webview = WKWebView()
- webview.navigationDelegate = context.coordinator
+ let webViewConfig = WKWebViewConfiguration()
+
+ let webView = WKWebView(frame: .zero, configuration: webViewConfig)
+ webView.navigationDelegate = context.coordinator
+ webView.uiDelegate = context.coordinator
- webview.scrollView.bounces = false
- webview.scrollView.alwaysBounceHorizontal = false
- webview.scrollView.showsHorizontalScrollIndicator = false
- webview.scrollView.isScrollEnabled = true
- webview.configuration.suppressesIncrementalRendering = true
- webview.isOpaque = false
- webview.backgroundColor = .clear
- webview.scrollView.backgroundColor = UIColor.clear
- webview.scrollView.alwaysBounceVertical = false
+ webView.scrollView.bounces = false
+ webView.scrollView.alwaysBounceHorizontal = false
+ webView.scrollView.showsHorizontalScrollIndicator = false
+ webView.scrollView.isScrollEnabled = true
+ webView.configuration.suppressesIncrementalRendering = true
+ webView.isOpaque = false
+ webView.backgroundColor = .clear
+ webView.scrollView.backgroundColor = .white
+ webView.scrollView.alwaysBounceVertical = false
+ webView.scrollView.layer.cornerRadius = 24
+ webView.scrollView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
+ webView.scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 200, right: 0)
- return webview
+ return webView
}
public func updateUIView(_ webview: WKWebView, context: UIViewRepresentableContext) {
if let url = URL(string: viewModel.url) {
- let cookies = HTTPCookieStorage.shared.cookies ?? []
- for (cookie) in cookies {
- webview.configuration.websiteDataStore.httpCookieStore
- .setCookie(cookie)
- }
- let request = URLRequest(url: url)
if webview.url?.absoluteString != url.absoluteString {
DispatchQueue.main.async {
isLoading = true
}
+ let request = URLRequest(url: url)
webview.load(request)
}
}
diff --git a/Core/Core/en.lproj/Localizable.strings b/Core/Core/en.lproj/Localizable.strings
index 6fda66932..f16f781dc 100644
--- a/Core/Core/en.lproj/Localizable.strings
+++ b/Core/Core/en.lproj/Localizable.strings
@@ -21,6 +21,24 @@
"ERROR.UNKNOWN_ERROR" = "Something went wrong";
"ERROR.WIFI" = "You can only download files over Wi-Fi. You can change this in the settings.";
+"COURSEWARE.COURSE_CONTENT" = "Course content";
+"COURSEWARE.COURSE_UNITS" = "Course units";
+"COURSEWARE.NEXT" = "Next";
+"COURSEWARE.PREVIOUS" = "Prev";
+"COURSEWARE.FINISH" = "Finish";
+"COURSEWARE.GOOD_WORK" = "Good Work!";
+"COURSEWARE.BACK_TO_OUTLINE" = "Back to outline";
+"COURSEWARE.SECTION" = "Section “";
+"COURSEWARE.IS_FINISHED" = "“ is finished.";
+"COURSEWARE.CONTINUE" = "Continue";
+"COURSEWARE.CONTINUE_WITH" = "Continue with:";
+"COURSEWARE.NEXT_SECTION" = "Next section";
+
+"COURSEWARE.NEXT_SECTION_DESCRIPTION_FIRST" = "To proceed with “";
+"COURSEWARE.NEXT_SECTION_DESCRIPTION_LAST" = "” press “Next section”.";
+
+"ERROR.RELOAD" = "Reload";
+
"DATE.ENDED" = "Ended";
"DATE.START" = "Start";
"DATE.STARTED" = "Started";
@@ -47,3 +65,6 @@
"PICKER.SEARCH" = "Search";
"PICKER.ACCEPT" = "Accept";
+
+"WEBVIEW.ALERT.OK" = "Ok";
+"WEBVIEW.ALERT.CANCEL" = "Cancel";
diff --git a/Core/Core/uk.lproj/Localizable.strings b/Core/Core/uk.lproj/Localizable.strings
index 939524031..e06937311 100644
--- a/Core/Core/uk.lproj/Localizable.strings
+++ b/Core/Core/uk.lproj/Localizable.strings
@@ -21,6 +21,24 @@
"ERROR.UNKNOWN_ERROR" = "Щось пішло не так";
"ERROR.WIFI" = "Завантажувати файли можна лише через Wi-Fi. Ви можете змінити це в налаштуваннях.";
+"COURSEWARE.COURSE_CONTENT" = "Зміст курсу";
+"COURSEWARE.COURSE_UNITS" = "Модулі";
+"COURSEWARE.NEXT" = "Далі";
+"COURSEWARE.PREVIOUS" = "Назад";
+"COURSEWARE.FINISH" = "Завершити";
+"COURSEWARE.GOOD_WORK" = "Гарна робота!";
+"COURSEWARE.BACK_TO_OUTLINE" = "Повернутись до модуля";
+"COURSEWARE.SECTION" = "Секція “";
+"COURSEWARE.IS_FINISHED" = "“ завершена.";
+"COURSEWARE.CONTINUE" = "Продовжити";
+"COURSEWARE.CONTINUE_WITH" = "Продовжити далі:";
+"COURSEWARE.NEXT_SECTION" = "Наступний розділ";
+
+"COURSEWARE.NEXT_SECTION_DESCRIPTION_FIRST" = "Щоб перейти до “";
+"COURSEWARE.NEXT_SECTION_DESCRIPTION_LAST" = "” натисніть “Наступний розділ”.";
+
+"ERROR.RELOAD" = "Перезавантажити";
+
"DATE.ENDED" = "Кінець";
"DATE.START" = "Початок";
"DATE.STARTED" = "Почався";
@@ -30,6 +48,7 @@
"ALERT.CANCEL" = "СКАСУВАТИ";
"ALERT.LOGOUT" = "Вийти";
"ALERT.LEAVE" = "Покинути";
+"ALERT.KEEP_EDITING" = "Залишитись";
"NO_INTERNET.OFFLINE" = "Офлайн режим";
"NO_INTERNET.DISMISS" = "Сховати";
@@ -46,3 +65,6 @@
"PICKER.SEARCH" = "Знайти";
"PICKER.ACCEPT" = "Прийняти";
+
+"WEBVIEW.ALERT.OK" = "Так";
+"WEBVIEW.ALERT.CANCEL" = "Скасувати";
diff --git a/Course/Course.xcodeproj.xcworkspace/contents.xcworkspacedata b/Course/Course.xcodeproj.xcworkspace/contents.xcworkspacedata
index b74ad64b7..6e6981f01 100644
--- a/Course/Course.xcodeproj.xcworkspace/contents.xcworkspacedata
+++ b/Course/Course.xcodeproj.xcworkspace/contents.xcworkspacedata
@@ -17,7 +17,7 @@
location = "group:../Discovery/Discovery.xcodeproj">