Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 1.90 #514

Merged
merged 12 commits into from
Aug 23, 2019
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,4 @@ DEPENDENCIES
fastlane

BUNDLED WITH
1.16.3
2.0.2
100 changes: 98 additions & 2 deletions Stepic.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions Stepic/FullHeightTableView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@
// Copyright © 2017 Alex Karpov. All rights reserved.
//

import Foundation
import UIKit

class FullHeightTableView: UITableView {
final class FullHeightTableView: UITableView {
override var intrinsicContentSize: CGSize {
// self.layoutIfNeeded()
return CGSize(width: UIView.noIntrinsicMetric, height: self.contentSize.height)
}

Expand All @@ -29,5 +27,4 @@ class FullHeightTableView: UITableView {
super.endUpdates()
self.invalidateIntrinsicContentSize()
}

}
4 changes: 2 additions & 2 deletions Stepic/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.89</string>
<string>1.90</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
Expand Down Expand Up @@ -54,7 +54,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>143</string>
<string>146</string>
<key>Fabric</key>
<dict>
<key>APIKey</key>
Expand Down
12 changes: 6 additions & 6 deletions Stepic/MatchingDataset.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,22 @@
import UIKit
import SwiftyJSON

class MatchingDataset: Dataset {
final class MatchingDataset: Dataset {
typealias Pair = (first: String, second: String)

var pairs: [Pair]

var firstValues: [String] {
return pairs.map({return $0.first})
return self.pairs.map { $0.first }
}

var secondValues: [String] {
return pairs.map({return $0.second})
return self.pairs.map { $0.second }
}

required init(json: JSON) {
pairs = json["pairs"].arrayValue.map({
pairJSON in
self.pairs = json["pairs"].arrayValue.map { pairJSON in
(first: pairJSON["first"].stringValue, second: pairJSON["second"].stringValue)
})
}
}
}
3 changes: 2 additions & 1 deletion Stepic/NotificationReactionHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ final class NotificationReactionHandler {
}

if notification.action == .issuedCertificate {
return TabBarRouter(tab: .certificates).route()
// TODO: Route to certificates.
return TabBarRouter(tab: .profile).route()
}

switch notification.type {
Expand Down
2 changes: 1 addition & 1 deletion Stepic/SocialAuthViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ extension SocialAuthViewController: SocialAuthView {
SVProgressHUD.showError(withStatus: NSLocalizedString("FailedToSignIn", comment: ""))
}
case .noEmail:
SVProgressHUD.showError(withStatus: NSLocalizedString("FailedToSignIn", comment: ""))
SVProgressHUD.showError(withStatus: NSLocalizedString("FailedToSignInNoEmail", comment: ""))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ import WebKit

protocol ProcessedContentTextViewDelegate: class {
func processedContentTextViewDidLoadContent(_ view: ProcessedContentTextView)
func processedContentTextView(_ view: ProcessedContentTextView, didReportNewHeight height: Int)
func processedContentTextView(_ view: ProcessedContentTextView, didOpenImage url: URL)
func processedContentTextView(_ view: ProcessedContentTextView, didOpenLink url: URL)
}

extension ProcessedContentTextViewDelegate {
func processedContentTextView(_ view: ProcessedContentTextView, didReportNewHeight height: Int) { }
}

extension ProcessedContentTextView {
struct Appearance {
var insets = LayoutInsets(top: 10, left: 16, bottom: 4, right: 16)
Expand Down Expand Up @@ -78,8 +83,6 @@ final class ProcessedContentTextView: UIView {
return webView
}()

private var isLoadingHTMLText = false

override var intrinsicContentSize: CGSize {
return CGSize(
width: UIView.noIntrinsicMetric,
Expand All @@ -98,6 +101,23 @@ final class ProcessedContentTextView: UIView {
}
}

var insets: LayoutInsets? {
didSet {
let insets = self.insets ?? LayoutInsets(insets: .zero)
self.webView.snp.updateConstraints { make in
make.top.equalToSuperview().offset(insets.top)
make.leading.equalToSuperview().offset(insets.left)
make.trailing.equalToSuperview().offset(-insets.right)
make.bottom.equalToSuperview().offset(-insets.bottom)
}
self.layoutIfNeeded()
}
}

private var isLoadingHTMLText = false
/// Keeps track of current web view height.
private var currentWebViewHeight = Int(ProcessedContentTextView.defaultWebViewHeight)

init(frame: CGRect = .zero, appearance: Appearance = Appearance()) {
self.appearance = appearance
super.init(frame: frame)
Expand Down Expand Up @@ -143,8 +163,12 @@ final class ProcessedContentTextView: UIView {

private func getContentHeight() -> Guarantee<Int> {
return Guarantee { seal in
self.webView.evaluateJavaScript("document.body.scrollHeight;") { res, _ in
self.webView.evaluateJavaScript("document.body.scrollHeight;") { [weak self] res, _ in
if let height = res as? Int {
if let strongSelf = self, strongSelf.currentWebViewHeight != height {
strongSelf.currentWebViewHeight = height
strongSelf.delegate?.processedContentTextView(strongSelf, didReportNewHeight: height)
}
seal(height)
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ final class NewLessonViewController: TabmanViewController, ControllerWithStepikP
self.interactor.doLessonLoad(request: .init())
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.tooltipView?.dismiss()
}

override func pageboyViewController(
_ pageboyViewController: PageboyViewController,
didScrollToPageAt index: TabmanViewController.PageIndex,
Expand Down Expand Up @@ -313,6 +318,7 @@ final class NewLessonViewController: TabmanViewController, ControllerWithStepikP
if self.isTooltipVisible {
self.tooltipView?.dismiss()
} else {
// TODO: Refactor add support for custom content views to the `Tooltip`.
let contentView = LessonInfoTooltipView()
contentView.configure(viewModel: tooltipInfo.map { .init(icon: $0.iconImage, text: $0.text) })
contentView.sizeToFit()
Expand Down
2 changes: 1 addition & 1 deletion Stepic/Sources/Modules/NewStep/NewStepViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ final class NewStepViewController: UIViewController, ControllerWithStepikPlaceho

let quizController: UIViewController? = {
switch quizType {
case .string, .number, .math, .freeAnswer, .choice, .code:
case .string, .number, .math, .freeAnswer, .choice, .code, .sorting, .matching:
let assembly = BaseQuizAssembly(step: viewModel.step, output: self)
return assembly.makeModule()
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ final class BaseQuizPresenter: BaseQuizPresenterProtocol {
NewStep.QuizType.number,
NewStep.QuizType.math,
NewStep.QuizType.freeAnswer,
NewStep.QuizType.code
NewStep.QuizType.code,
NewStep.QuizType.sorting,
NewStep.QuizType.matching
].contains(NewStep.QuizType(blockName: step.block.name))

// 1. if quiz is not needed new attempt and status == wrong
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ final class QuizAssemblyFactory {
return NewChoiceQuizAssembly()
case .code:
return NewCodeQuizAssembly()
case .sorting:
return NewSortingQuizAssembly()
case .matching:
return NewMatchingQuizAssembly()
default:
fatalError("Unsupported quiz")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import UIKit

final class NewMatchingQuizAssembly: QuizAssembly {
var moduleInput: QuizInputProtocol?
weak var moduleOutput: QuizOutputProtocol?

func makeModule() -> UIViewController {
let presenter = NewMatchingQuizPresenter()
let interactor = NewMatchingQuizInteractor(presenter: presenter)
let viewController = NewMatchingQuizViewController(interactor: interactor)

presenter.viewController = viewController
self.moduleInput = interactor
interactor.moduleOutput = self.moduleOutput

return viewController
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Foundation

enum NewMatchingQuiz {
/// Show quiz state
enum ReplyLoad {
struct Response {
let items: [MatchItem]
let status: QuizStatus?
}

struct ViewModel {
let data: NewMatchingQuizViewModel
}
}

/// Convert options to reply
enum ReplyConvert {
struct Request {
let items: [MatchItem]
}
}

// MARK: - Common structs

struct MatchItem {
let title: Title
let option: Option

struct Title {
let id: Int
let text: String
}

struct Option {
let id: Int
let text: String
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import Foundation

protocol NewMatchingQuizInteractorProtocol {
func doReplyUpdate(request: NewMatchingQuiz.ReplyConvert.Request)
}

final class NewMatchingQuizInteractor: NewMatchingQuizInteractorProtocol {
weak var moduleOutput: QuizOutputProtocol?

private let presenter: NewMatchingQuizPresenterProtocol

private var currentStatus: QuizStatus?
private var currentDataset: MatchingDataset?
// swiftlint:disable:next discouraged_optional_collection
private var currentItems: [NewMatchingQuiz.MatchItem]?

init(presenter: NewMatchingQuizPresenterProtocol) {
self.presenter = presenter
}

func doReplyUpdate(request: NewMatchingQuiz.ReplyConvert.Request) {
self.currentItems = request.items
self.outputCurrentReply()
}

private func presentNewData() {
guard let items = self.currentItems else {
return
}

self.presenter.presentReply(
response: .init(
items: items,
status: self.currentStatus
)
)
}

private func outputCurrentReply() {
guard let items = self.currentItems else {
return
}

let reply = MatchingReply(ordering: items.map { $0.option.id })
self.moduleOutput?.update(reply: reply)
}
}

extension NewMatchingQuizInteractor: QuizInputProtocol {
func update(reply: Reply?) {
defer {
self.presentNewData()
}

guard let dataset = self.currentDataset else {
return
}

guard let reply = reply else {
self.currentItems = self.makeMatchItems(dataset: dataset)
self.outputCurrentReply()
return
}

self.moduleOutput?.update(reply: reply)

if let reply = reply as? MatchingReply {
self.currentItems = reply.ordering.enumerated().map { index, order in
.init(
title: .init(id: index, text: dataset.firstValues[index]),
option: .init(id: order, text: dataset.secondValues[order])
)
}
} else {
fatalError("Unsupported reply")
}
}

func update(status: QuizStatus?) {
self.currentStatus = status
self.presentNewData()
}

func update(dataset: Dataset?) {
guard let dataset = dataset as? MatchingDataset else {
return
}

self.currentDataset = dataset
self.currentItems = self.makeMatchItems(dataset: dataset)
}

private func makeMatchItems(dataset: MatchingDataset) -> [NewMatchingQuiz.MatchItem] {
return dataset.pairs.enumerated().map { index, pair in
.init(
title: .init(id: index, text: pair.first),
option: .init(id: index, text: pair.second)
)
}
}
}
Loading