From 932e179e39f669cce4f2c97aad6aad44bf08b8f8 Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Tue, 11 Aug 2020 17:19:25 +0900 Subject: [PATCH 1/7] Persist submission score --- Stepic.xcodeproj/project.pbxproj | 4 +- .../AttemptsSumbissions/Submission.swift | 7 + .../SubmissionEntity+CoreDataProperties.swift | 1 + .../Submission/SubmissionEntity.swift | 11 + .../Model.xcdatamodeld/.xccurrentversion | 2 +- .../contents | 406 ++++++++++++++++++ 6 files changed, 429 insertions(+), 2 deletions(-) create mode 100644 Stepic/Legacy/Model/Model.xcdatamodeld/Model_quiz_is_partially_correct_v61.xcdatamodel/contents diff --git a/Stepic.xcodeproj/project.pbxproj b/Stepic.xcodeproj/project.pbxproj index 4adeb09bfa..f51fc97469 100644 --- a/Stepic.xcodeproj/project.pbxproj +++ b/Stepic.xcodeproj/project.pbxproj @@ -1776,6 +1776,7 @@ 2C0B42BF234CD17C00B03EA1 /* CustomMenuBlockTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomMenuBlockTableViewCell.swift; sourceTree = ""; }; 2C0B42C0234CD17C00B03EA1 /* CustomMenuBlockTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CustomMenuBlockTableViewCell.xib; sourceTree = ""; }; 2C0C68FB247DBA6200B950F6 /* IAPReceiptValidationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IAPReceiptValidationService.swift; sourceTree = ""; }; + 2C0FBEB224E25A4D002CA67F /* Model_quiz_is_partially_correct_v61.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model_quiz_is_partially_correct_v61.xcdatamodel; sourceTree = ""; }; 2C101009239E7A1E00440651 /* Model_discount_policy.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model_discount_policy.xcdatamodel; sourceTree = ""; }; 2C10100A239EFAD700440651 /* DiscountingPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscountingPolicy.swift; sourceTree = ""; }; 2C104B672069064D0026FEB9 /* autocomplete_suggestions.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = autocomplete_suggestions.plist; sourceTree = ""; }; @@ -9881,6 +9882,7 @@ 08D1EF6E1BB5618700BE84E6 /* Model.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + 2C0FBEB224E25A4D002CA67F /* Model_quiz_is_partially_correct_v61.xcdatamodel */, 2C78573B24DE1EC40042AD2C /* Model_is_vote_notifications_enabled_v60.xcdatamodel */, 2C619A7824CFF9DC007D3529 /* Model_social_profiles_v59.xcdatamodel */, 2C46417A24CFD23400FB6696 /* Model_lessons_demo_access_v58.xcdatamodel */, @@ -9943,7 +9945,7 @@ 0802AC531C7222B200C4F3E6 /* Model_v2.xcdatamodel */, 08D1EF6F1BB5618700BE84E6 /* Model.xcdatamodel */, ); - currentVersion = 2C78573B24DE1EC40042AD2C /* Model_is_vote_notifications_enabled_v60.xcdatamodel */; + currentVersion = 2C0FBEB224E25A4D002CA67F /* Model_quiz_is_partially_correct_v61.xcdatamodel */; path = Model.xcdatamodeld; sourceTree = ""; versionGroupType = wrapper.xcdatamodel; diff --git a/Stepic/Legacy/Model/AttemptsSumbissions/Submission.swift b/Stepic/Legacy/Model/AttemptsSumbissions/Submission.swift index 2fcf4f456c..c3ff52313d 100644 --- a/Stepic/Legacy/Model/AttemptsSumbissions/Submission.swift +++ b/Stepic/Legacy/Model/AttemptsSumbissions/Submission.swift @@ -14,6 +14,7 @@ final class Submission: JSONSerializable { var id: IdType = 0 var statusString: String? + var score: Float = 0 var hint: String? var feedback: SubmissionFeedback? var time = Date() @@ -46,6 +47,7 @@ final class Submission: JSONSerializable { init( id: IdType, status: SubmissionStatus? = nil, + score: Float? = 0, hint: String? = nil, feedback: SubmissionFeedback? = nil, time: Date = Date(), @@ -56,6 +58,7 @@ final class Submission: JSONSerializable { ) { self.id = id self.statusString = status?.rawValue + self.score = score ?? 0 self.hint = hint self.feedback = feedback self.time = time @@ -85,6 +88,7 @@ final class Submission: JSONSerializable { self.init( id: submission?.id ?? 0, status: submission?.status, + score: submission?.score, hint: submission?.hint, feedback: submission?.feedback, time: submission?.time ?? Date(), @@ -97,6 +101,7 @@ final class Submission: JSONSerializable { func update(json: JSON) { self.id = json[JSONKey.id.rawValue].intValue self.statusString = json[JSONKey.status.rawValue].string + self.score = json[JSONKey.score.rawValue].floatValue self.hint = json[JSONKey.hint.rawValue].string self.feedback = self.getFeedbackFromJSON(json[JSONKey.feedback.rawValue]) self.attemptID = json[JSONKey.attempt.rawValue].intValue @@ -151,6 +156,7 @@ final class Submission: JSONSerializable { enum JSONKey: String { case id case status + case score case hint case attempt case reply @@ -169,6 +175,7 @@ extension Submission: CustomDebugStringConvertible { """ Submission(id: \(id), \ status: \(statusString ?? "nil"), \ + score: \(score), \ hint: \(hint ?? "nil"), \ feedback: \(feedback ??? "nil"), \ reply: \(reply ??? "nil"), \ diff --git a/Stepic/Legacy/Model/Entities/Submission/SubmissionEntity+CoreDataProperties.swift b/Stepic/Legacy/Model/Entities/Submission/SubmissionEntity+CoreDataProperties.swift index 421b6d6a45..081727994c 100644 --- a/Stepic/Legacy/Model/Entities/Submission/SubmissionEntity+CoreDataProperties.swift +++ b/Stepic/Legacy/Model/Entities/Submission/SubmissionEntity+CoreDataProperties.swift @@ -6,6 +6,7 @@ extension SubmissionEntity { @NSManaged var managedAttemptID: NSNumber @NSManaged var managedReply: Reply? @NSManaged var managedLocal: NSNumber + @NSManaged var managedScore: NSNumber @NSManaged var managedHint: String? @NSManaged var managedStatus: String? diff --git a/Stepic/Legacy/Model/Entities/Submission/SubmissionEntity.swift b/Stepic/Legacy/Model/Entities/Submission/SubmissionEntity.swift index a2e1d326f2..ee8be4e77a 100644 --- a/Stepic/Legacy/Model/Entities/Submission/SubmissionEntity.swift +++ b/Stepic/Legacy/Model/Entities/Submission/SubmissionEntity.swift @@ -44,6 +44,15 @@ final class SubmissionEntity: NSManagedObject { } } + var score: Float { + get { + self.managedScore.floatValue + } + set { + self.managedScore = NSNumber(value: newValue) + } + } + var hint: String? { get { self.managedHint @@ -111,6 +120,7 @@ extension SubmissionEntity { Submission( id: self.id, status: self.status, + score: self.score, hint: self.hint, feedback: self.feedback, time: self.time, @@ -134,6 +144,7 @@ extension SubmissionEntity { self.attemptID = submission.attemptID self.reply = submission.reply self.isLocal = submission.isLocal + self.score = submission.score self.hint = submission.hint self.statusString = submission.statusString self.feedback = submission.feedback diff --git a/Stepic/Legacy/Model/Model.xcdatamodeld/.xccurrentversion b/Stepic/Legacy/Model/Model.xcdatamodeld/.xccurrentversion index 164dae43d8..a5e1d136ec 100644 --- a/Stepic/Legacy/Model/Model.xcdatamodeld/.xccurrentversion +++ b/Stepic/Legacy/Model/Model.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - Model_is_vote_notifications_enabled_v60.xcdatamodel + Model_quiz_is_partially_correct_v61.xcdatamodel diff --git a/Stepic/Legacy/Model/Model.xcdatamodeld/Model_quiz_is_partially_correct_v61.xcdatamodel/contents b/Stepic/Legacy/Model/Model.xcdatamodeld/Model_quiz_is_partially_correct_v61.xcdatamodel/contents new file mode 100644 index 0000000000..9979ff7c17 --- /dev/null +++ b/Stepic/Legacy/Model/Model.xcdatamodeld/Model_quiz_is_partially_correct_v61.xcdatamodel/contents @@ -0,0 +1,406 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 3196f59230d1c8c98df500820805e2af8bd47bd7 Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Tue, 11 Aug 2020 17:21:05 +0900 Subject: [PATCH 2/7] Handle quiz partially correct state --- .../AttemptsSumbissions/Submission.swift | 2 + Stepic/Sources/Model/QuizStatus.swift | 5 ++ .../Quizzes/BaseQuiz/BaseQuizPresenter.swift | 29 ++++++---- .../BaseQuiz/BaseQuizViewController.swift | 12 +--- .../BaseQuiz/Views/QuizFeedbackView.swift | 20 ++++++- .../Quizzes/CodeQuiz/CodeQuizPresenter.swift | 2 +- .../NewChoiceQuizPresenter.swift | 2 +- .../NewMatchingQuizPresenter.swift | 2 +- .../NewSortingQuizPresenter.swift | 2 +- .../Modules/Solution/SolutionPresenter.swift | 2 + .../Solution/SolutionViewController.swift | 9 +-- Stepic/Theme/ColorPalette.swift | 55 +++++++++++++++++++ Stepic/en.lproj/Localizable.strings | 6 ++ Stepic/ru.lproj/Localizable.strings | 6 ++ 14 files changed, 122 insertions(+), 32 deletions(-) diff --git a/Stepic/Legacy/Model/AttemptsSumbissions/Submission.swift b/Stepic/Legacy/Model/AttemptsSumbissions/Submission.swift index c3ff52313d..6a2882e504 100644 --- a/Stepic/Legacy/Model/AttemptsSumbissions/Submission.swift +++ b/Stepic/Legacy/Model/AttemptsSumbissions/Submission.swift @@ -37,6 +37,8 @@ final class Submission: JSONSerializable { var isCorrect: Bool { self.status == .correct } + var isPartiallyCorrect: Bool { self.isCorrect && self.score < 1.0 } + var json: JSON { [ JSONKey.attempt.rawValue: attemptID, diff --git a/Stepic/Sources/Model/QuizStatus.swift b/Stepic/Sources/Model/QuizStatus.swift index 151f09b424..57467d416c 100644 --- a/Stepic/Sources/Model/QuizStatus.swift +++ b/Stepic/Sources/Model/QuizStatus.swift @@ -3,5 +3,10 @@ import Foundation enum QuizStatus { case wrong case correct + case partiallyCorrect case evaluation + + var isCorrect: Bool { + self == .correct || self == .partiallyCorrect + } } diff --git a/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizPresenter.swift b/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizPresenter.swift index 474cd6ca77..b1daca779b 100644 --- a/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizPresenter.swift +++ b/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizPresenter.swift @@ -46,7 +46,7 @@ final class BaseQuizPresenter: BaseQuizPresenterProtocol { case .wrong: return .wrong case .correct: - return .correct + return submission.isPartiallyCorrect ? .partiallyCorrect : .correct case .evaluation: return .evaluation case .none: @@ -65,10 +65,11 @@ final class BaseQuizPresenter: BaseQuizPresenterProtocol { StepDataFlow.QuizType.matching ].contains(StepDataFlow.QuizType(blockName: step.block.name)) + let isQuizCorrect = quizStatus?.isCorrect ?? false // 1. if quiz is not needed new attempt and status == wrong // => retry not needed (by quiz design or we've clean attempt) - // 2. if status == correct we always need to create new attempt - let retryWithNewAttempt = (!isQuizNotNeededNewAttempt && quizStatus == .wrong) || quizStatus == .correct + // 2. if status == correct or partiallyCorrect we always need to create new attempt + let retryWithNewAttempt = (!isQuizNotNeededNewAttempt && quizStatus == .wrong) || isQuizCorrect var submissionsLeft: Int? if step.hasSubmissionRestrictions, let maxSubmissionsCount = step.maxSubmissionsCount { @@ -88,9 +89,9 @@ final class BaseQuizPresenter: BaseQuizPresenterProtocol { ) let isSubmitButtonDisabled = quizStatus == .evaluation || submissionsLeft == 0 - let shouldPassPeerReview = quizStatus == .correct && step.hasReview - let canNavigateToNextStep = quizStatus == .correct && hasNextStep - let canRetry = quizStatus == .correct && !(submissionsLeft == 0) + let shouldPassPeerReview = isQuizCorrect && step.hasReview + let canNavigateToNextStep = isQuizCorrect && hasNextStep + let canRetry = isQuizCorrect && !(submissionsLeft == 0) let hintContent: String? = { if let text = submission.hint, !text.isEmpty { @@ -182,9 +183,7 @@ final class BaseQuizPresenter: BaseQuizPresenterProtocol { } private func makeFeedbackTitle(status: QuizStatus, step: Step, submissionsLeft: Int) -> String { - // swiftlint:disable:next nslocalizedstring_key - let correctTitles = (1...14).map { NSLocalizedString("CorrectFeedbackTitle\($0)", comment: "") } - + // swiftlint:disable nslocalizedstring_key switch status { case .correct: if step.hasReview { @@ -193,19 +192,27 @@ final class BaseQuizPresenter: BaseQuizPresenterProtocol { if case .freeAnswer = StepDataFlow.QuizType(blockName: step.block.name) { return NSLocalizedString("CorrectFeedbackTitleFreeAnswer", comment: "") } - return correctTitles.randomElement() ?? NSLocalizedString("Correct", comment: "") + return (1...14) + .map { NSLocalizedString("CorrectFeedbackTitle\($0)", comment: "") } + .randomElement() + .require() + case .partiallyCorrect: + return (1...6) + .map { NSLocalizedString("PartiallyCorrectFeedbackTitle\($0)", comment: "") } + .randomElement() + .require() case .wrong: if submissionsLeft == 0 { return NSLocalizedString("WrongFeedbackTitleLastTry", comment: "") } return (1...3) - // swiftlint:disable:next nslocalizedstring_key .map { NSLocalizedString("WrongFeedbackTitleNotLastTry\($0)", comment: "") } .randomElement() .require() case .evaluation: return NSLocalizedString("EvaluationFeedbackTitle", comment: "") } + // swiftlint:enable nslocalizedstring_key } private func makeURL(for step: Step) -> URL { diff --git a/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizViewController.swift b/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizViewController.swift index 98a10ae2d5..f1487b37ed 100644 --- a/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizViewController.swift +++ b/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizViewController.swift @@ -101,15 +101,9 @@ final class BaseQuizViewController: UIViewController, ControllerWithStepikPlaceh self.baseQuizView?.isDiscountPolicyAvailable = data.isDiscountingPolicyVisible self.baseQuizView?.discountPolicyTitle = data.discountingPolicyTitle - if let status = data.quizStatus { - switch status { - case .correct: - self.baseQuizView?.showFeedback(state: .correct, title: data.feedbackTitle, hint: data.hintContent) - case .wrong: - self.baseQuizView?.showFeedback(state: .wrong, title: data.feedbackTitle, hint: data.hintContent) - case .evaluation: - self.baseQuizView?.showFeedback(state: .evaluation, title: data.feedbackTitle, hint: data.hintContent) - } + if let quizStatus = data.quizStatus, + let feedbackState = QuizFeedbackView.State(quizStatus: quizStatus) { + self.baseQuizView?.showFeedback(state: feedbackState, title: data.feedbackTitle, hint: data.hintContent) } else { self.baseQuizView?.hideFeedback() } diff --git a/Stepic/Sources/Modules/Quizzes/BaseQuiz/Views/QuizFeedbackView.swift b/Stepic/Sources/Modules/Quizzes/BaseQuiz/Views/QuizFeedbackView.swift index 3e704aedef..2cad60e513 100644 --- a/Stepic/Sources/Modules/Quizzes/BaseQuiz/Views/QuizFeedbackView.swift +++ b/Stepic/Sources/Modules/Quizzes/BaseQuiz/Views/QuizFeedbackView.swift @@ -153,6 +153,7 @@ final class QuizFeedbackView: UIView { enum State { case correct + case partiallyCorrect case wrong case evaluation case validation @@ -161,6 +162,8 @@ final class QuizFeedbackView: UIView { switch self { case .correct: return .quizElementCorrectBackground + case .partiallyCorrect: + return .quizElementPartiallyCorrectBackground case .wrong: return .quizElementWrongBackground default: @@ -172,6 +175,8 @@ final class QuizFeedbackView: UIView { switch self { case .correct: return .stepikCallToActionText + case .partiallyCorrect: + return .stepikDarkYellow case .wrong: return .stepikLightRedFixed default: @@ -181,7 +186,7 @@ final class QuizFeedbackView: UIView { var leftView: UIView { switch self { - case .correct: + case .correct, .partiallyCorrect: let view = UIImageView( image: UIImage(named: "quiz-feedback-correct")?.withRenderingMode(.alwaysTemplate) ) @@ -209,6 +214,19 @@ final class QuizFeedbackView: UIView { return view } } + + init?(quizStatus: QuizStatus) { + switch quizStatus { + case .wrong: + self = .wrong + case .correct: + self = .correct + case .partiallyCorrect: + self = .partiallyCorrect + case .evaluation: + self = .evaluation + } + } } } diff --git a/Stepic/Sources/Modules/Quizzes/CodeQuiz/CodeQuizPresenter.swift b/Stepic/Sources/Modules/Quizzes/CodeQuiz/CodeQuizPresenter.swift index 51eced12e5..76826bfce5 100644 --- a/Stepic/Sources/Modules/Quizzes/CodeQuiz/CodeQuizPresenter.swift +++ b/Stepic/Sources/Modules/Quizzes/CodeQuiz/CodeQuizPresenter.swift @@ -24,7 +24,7 @@ final class CodeQuizPresenter: CodeQuizPresenterProtocol { } switch status { - case .correct: + case .correct, .partiallyCorrect: return .correct case .wrong: return .wrong diff --git a/Stepic/Sources/Modules/Quizzes/NewChoiceQuiz/NewChoiceQuizPresenter.swift b/Stepic/Sources/Modules/Quizzes/NewChoiceQuiz/NewChoiceQuizPresenter.swift index 605a003adf..3807f7188b 100644 --- a/Stepic/Sources/Modules/Quizzes/NewChoiceQuiz/NewChoiceQuizPresenter.swift +++ b/Stepic/Sources/Modules/Quizzes/NewChoiceQuiz/NewChoiceQuizPresenter.swift @@ -14,7 +14,7 @@ final class NewChoiceQuizPresenter: NewChoiceQuizPresenterProtocol { } switch status { - case .correct: + case .correct, .partiallyCorrect: return .correct case .wrong: return .wrong diff --git a/Stepic/Sources/Modules/Quizzes/NewMatchingQuiz/NewMatchingQuizPresenter.swift b/Stepic/Sources/Modules/Quizzes/NewMatchingQuiz/NewMatchingQuizPresenter.swift index 26dc00ba25..0cea00c526 100644 --- a/Stepic/Sources/Modules/Quizzes/NewMatchingQuiz/NewMatchingQuizPresenter.swift +++ b/Stepic/Sources/Modules/Quizzes/NewMatchingQuiz/NewMatchingQuizPresenter.swift @@ -14,7 +14,7 @@ final class NewMatchingQuizPresenter: NewMatchingQuizPresenterProtocol { } switch status { - case .correct: + case .correct, .partiallyCorrect: return .correct case .wrong: return .wrong diff --git a/Stepic/Sources/Modules/Quizzes/NewSortingQuiz/NewSortingQuizPresenter.swift b/Stepic/Sources/Modules/Quizzes/NewSortingQuiz/NewSortingQuizPresenter.swift index 12c2719b44..e5673b54c3 100644 --- a/Stepic/Sources/Modules/Quizzes/NewSortingQuiz/NewSortingQuizPresenter.swift +++ b/Stepic/Sources/Modules/Quizzes/NewSortingQuiz/NewSortingQuizPresenter.swift @@ -14,7 +14,7 @@ final class NewSortingQuizPresenter: NewSortingQuizPresenterProtocol { } switch status { - case .correct: + case .correct, .partiallyCorrect: return .correct case .wrong: return .wrong diff --git a/Stepic/Sources/Modules/Solution/SolutionPresenter.swift b/Stepic/Sources/Modules/Solution/SolutionPresenter.swift index f5c953d7f8..78778aad61 100644 --- a/Stepic/Sources/Modules/Solution/SolutionPresenter.swift +++ b/Stepic/Sources/Modules/Solution/SolutionPresenter.swift @@ -82,6 +82,8 @@ final class SolutionPresenter: SolutionPresenterProtocol { ] return correctTitles.randomElement() ?? NSLocalizedString("Correct", comment: "") + case .partiallyCorrect: + return NSLocalizedString("PartiallyCorrectFeedbackTitle1", comment: "") case .wrong: return NSLocalizedString("WrongFeedbackTitleLastTry", comment: "") case .evaluation: diff --git a/Stepic/Sources/Modules/Solution/SolutionViewController.swift b/Stepic/Sources/Modules/Solution/SolutionViewController.swift index 2ff82c3829..129124fc8f 100644 --- a/Stepic/Sources/Modules/Solution/SolutionViewController.swift +++ b/Stepic/Sources/Modules/Solution/SolutionViewController.swift @@ -115,13 +115,8 @@ final class SolutionViewController: UIViewController, ControllerWithStepikPlaceh self.addChild(quizController) self.solutionView?.addQuiz(view: quizController.view) - switch data.quizStatus { - case .correct: - self.solutionView?.showFeedback(state: .correct, title: data.feedbackTitle, hint: data.hintContent) - case .wrong: - self.solutionView?.showFeedback(state: .wrong, title: data.feedbackTitle, hint: data.hintContent) - case .evaluation: - self.solutionView?.showFeedback(state: .evaluation, title: data.feedbackTitle, hint: data.hintContent) + if let feedbackState = QuizFeedbackView.State(quizStatus: data.quizStatus) { + self.solutionView?.showFeedback(state: feedbackState, title: data.feedbackTitle, hint: data.hintContent) } self.solutionView?.actionIsHidden = true diff --git a/Stepic/Theme/ColorPalette.swift b/Stepic/Theme/ColorPalette.swift index 13859abfe8..d3cb404415 100644 --- a/Stepic/Theme/ColorPalette.swift +++ b/Stepic/Theme/ColorPalette.swift @@ -122,8 +122,32 @@ extension UIColor { ) } + /// Adaptable color with base hex value #FFF6E5 (yellow03). + static var stepikLightYellow: UIColor { + .dynamic( + light: ColorPalette.lightYellow50, + dark: ColorPalette.lightYellow300, + lightAccessibility: ColorPalette.lightYellow100, + darkAccessibility: ColorPalette.lightYellow200 + ) + } + + /// Adaptable color with base hex value #FEA832 (yellow01). + static var stepikDarkYellow: UIColor { + .dynamic( + light: ColorPalette.darkYellow400, + dark: ColorPalette.darkYellow300, + lightAccessibility: ColorPalette.darkYellow500, + darkAccessibility: ColorPalette.darkYellow200 + ) + } + /// A non adaptable color with hex value #FEDB41 (yellow02). static let stepikYellowFixed = ColorPalette.yellow600 + /// A non adaptable color with hex value #FEDB41 (yellow03). + static let stepikLightYellowFixed = ColorPalette.lightYellow50 + /// A non adaptable color with hex value #FEDB41 (yellow01). + static let stepikDarkYellowFixed = ColorPalette.darkYellow400 // MARK: Grey @@ -504,6 +528,13 @@ extension UIColor { .dynamic(light: .stepikLightGreen, dark: .stepikCallToActionBackground) } + static var quizElementPartiallyCorrectBackground: UIColor { + .dynamic( + light: .stepikLightYellow, + dark: UIColor.stepikLightYellow.withAlphaComponent(0.1) + ) + } + static var quizElementWrongBackground: UIColor { .dynamic( light: UIColor.stepikLightRed.withAlphaComponent(0.15), @@ -697,6 +728,8 @@ private enum ColorPalette { // MARK: - Yellow - + // MARK: Normal + /// Color to use in light/unspecified mode and with a high contrast level. static let yellow700 = UIColor(hex6: 0xFCC439) /// Color to use in light/unspecified mode and with a normal/unspecified contrast level. @@ -706,6 +739,28 @@ private enum ColorPalette { /// Color to use in dark mode and with a high contrast level. static let yellow200 = UIColor(hex6: 0xFEF5A0) + // MARK: Light + + /// Color to use in light/unspecified mode and with a high contrast level. + static let lightYellow100 = UIColor(hex6: 0xFFE7BB) + /// Color to use in light/unspecified mode and with a normal/unspecified contrast level. + static let lightYellow50 = UIColor(hex6: 0xFFF6E5) + /// Color to use in dark mode and with a normal/unspecified contrast level. + static let lightYellow300 = UIColor(hex6: 0xFFC75C) + /// Color to use in dark mode and with a high contrast level. + static let lightYellow200 = UIColor(hex6: 0xFFD88C) + + // MARK: Dark + + /// Color to use in light/unspecified mode and with a high contrast level. + static let darkYellow500 = UIColor(hex6: 0xFE9B1B) + /// Color to use in light/unspecified mode and with a normal/unspecified contrast level. + static let darkYellow400 = UIColor(hex6: 0xFEA832) + /// Color to use in dark mode and with a normal/unspecified contrast level. + static let darkYellow300 = UIColor(hex6: 0xFEB954) + /// Color to use in dark mode and with a high contrast level. + static let darkYellow200 = UIColor(hex6: 0xFECD84) + // MARK: - Grey - /// Color to use in light/unspecified mode and with a high contrast level. diff --git a/Stepic/en.lproj/Localizable.strings b/Stepic/en.lproj/Localizable.strings index c1fe3933d1..5385656b0c 100644 --- a/Stepic/en.lproj/Localizable.strings +++ b/Stepic/en.lproj/Localizable.strings @@ -870,6 +870,12 @@ CorrectFeedbackTitle11 = "Fabulous answer."; CorrectFeedbackTitle12 = "Yes!"; CorrectFeedbackTitle13 = "Great!"; CorrectFeedbackTitle14 = "Well done!"; +PartiallyCorrectFeedbackTitle1 = "Almost correct! Keep trying."; +PartiallyCorrectFeedbackTitle2 = "You're close to the correct answer. Let's try again!"; +PartiallyCorrectFeedbackTitle3 = "Your answer is almost correct. Try to improve your result!"; +PartiallyCorrectFeedbackTitle4 = "Partially correct, great! You can do better, try again!"; +PartiallyCorrectFeedbackTitle5 = "Partially correct. Try to improve your score!"; +PartiallyCorrectFeedbackTitle6 = "You're almost right, keep going!"; CorrectFeedbackTitleFreeAnswer = "Any text response will be graded as correct."; WrongFeedbackTitleNotLastTry1 = "Wrong. Let's try again."; WrongFeedbackTitleNotLastTry2 = "No. Time for the next try."; diff --git a/Stepic/ru.lproj/Localizable.strings b/Stepic/ru.lproj/Localizable.strings index 04e1bd8b46..22bf51c649 100644 --- a/Stepic/ru.lproj/Localizable.strings +++ b/Stepic/ru.lproj/Localizable.strings @@ -871,6 +871,12 @@ CorrectFeedbackTitle11 = "Прекрасный ответ."; CorrectFeedbackTitle12 = "Так точно!"; CorrectFeedbackTitle13 = "Отлично!"; CorrectFeedbackTitle14 = "Всё получилось!"; +PartiallyCorrectFeedbackTitle1 = "Почти верно. Так держать!"; +PartiallyCorrectFeedbackTitle2 = "Вы близки к верному ответу! Попробуйте улучшить результат."; +PartiallyCorrectFeedbackTitle3 = "Ваш ответ почти верен. Попробуете улучшить результат?"; +PartiallyCorrectFeedbackTitle4 = "Почти верно, отлично! Может быть, попробуете ещё?"; +PartiallyCorrectFeedbackTitle5 = "Почти верно. Попробуйте получить полный балл!"; +PartiallyCorrectFeedbackTitle6 = "Вы близки к верному ответу, так держать. Попробуете ещё?"; CorrectFeedbackTitleFreeAnswer = "Любой ответ будет оценен как правильный."; WrongFeedbackTitleNotLastTry1 = "Пока неправильно, попробуйте ещё раз."; WrongFeedbackTitleNotLastTry2 = "Пока неверно, но вы можете попробовать ещё."; From 79892a95f2152dc549e30c64d9ad75a3775b42cc Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Tue, 11 Aug 2020 18:32:14 +0900 Subject: [PATCH 3/7] Handle in discussions --- Stepic/Sources/Model/QuizStatus.swift | 15 ++++++++++++++ .../Discussions/DiscussionsPresenter.swift | 2 +- .../Discussions/DiscussionsViewModel.swift | 2 +- .../Views/Cell/DiscussionsCellView.swift | 5 +---- .../Views/DiscussionsSolutionControl.swift | 20 ++++++++++++++++--- 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/Stepic/Sources/Model/QuizStatus.swift b/Stepic/Sources/Model/QuizStatus.swift index 57467d416c..a457a74b86 100644 --- a/Stepic/Sources/Model/QuizStatus.swift +++ b/Stepic/Sources/Model/QuizStatus.swift @@ -9,4 +9,19 @@ enum QuizStatus { var isCorrect: Bool { self == .correct || self == .partiallyCorrect } + + init?(submission: Submission) { + guard let submissionStatus = submission.status else { + return nil + } + + switch submissionStatus { + case .correct: + self = submission.isPartiallyCorrect ? .partiallyCorrect : .correct + case .wrong: + self = .wrong + case .evaluation: + self = .evaluation + } + } } diff --git a/Stepic/Sources/Modules/Discussions/DiscussionsPresenter.swift b/Stepic/Sources/Modules/Discussions/DiscussionsPresenter.swift index ad3b1e9f0d..484d656abc 100644 --- a/Stepic/Sources/Modules/Discussions/DiscussionsPresenter.swift +++ b/Stepic/Sources/Modules/Discussions/DiscussionsPresenter.swift @@ -283,7 +283,7 @@ final class DiscussionsPresenter: DiscussionsPresenterProtocol { format: NSLocalizedString("DiscussionThreadCommentSolutionTitle", comment: ""), arguments: ["\(submission.id)"] ), - isCorrect: submission.isCorrect + status: QuizStatus(submission: submission) ?? .wrong ) }() diff --git a/Stepic/Sources/Modules/Discussions/DiscussionsViewModel.swift b/Stepic/Sources/Modules/Discussions/DiscussionsViewModel.swift index 5959e486ed..056afcf320 100644 --- a/Stepic/Sources/Modules/Discussions/DiscussionsViewModel.swift +++ b/Stepic/Sources/Modules/Discussions/DiscussionsViewModel.swift @@ -35,6 +35,6 @@ struct DiscussionsCommentViewModel { struct Solution { let id: Submission.IdType let title: String - let isCorrect: Bool + let status: QuizStatus } } diff --git a/Stepic/Sources/Modules/Discussions/Views/Cell/DiscussionsCellView.swift b/Stepic/Sources/Modules/Discussions/Views/Cell/DiscussionsCellView.swift index 907246ce50..496134fee9 100644 --- a/Stepic/Sources/Modules/Discussions/Views/Cell/DiscussionsCellView.swift +++ b/Stepic/Sources/Modules/Discussions/Views/Cell/DiscussionsCellView.swift @@ -323,10 +323,7 @@ final class DiscussionsCellView: UIView { } if let solution = viewModel.solution { - self.solutionControl.update( - state: solution.isCorrect ? .correct : .wrong, - title: solution.title - ) + self.solutionControl.update(state: .init(quizStatus: solution.status), title: solution.title) self.solutionContainerView.isHidden = false } else { self.solutionContainerView.isHidden = true diff --git a/Stepic/Sources/Modules/Discussions/Views/DiscussionsSolutionControl.swift b/Stepic/Sources/Modules/Discussions/Views/DiscussionsSolutionControl.swift index 7942f4b0a0..86bf58eb54 100644 --- a/Stepic/Sources/Modules/Discussions/Views/DiscussionsSolutionControl.swift +++ b/Stepic/Sources/Modules/Discussions/Views/DiscussionsSolutionControl.swift @@ -103,25 +103,39 @@ final class DiscussionsSolutionControl: UIControl { enum SolutionState { case correct + case partiallyCorrect case wrong var tintColor: UIColor { switch self { case .correct: - return UIColor.stepikGreen + return .stepikGreen + case .partiallyCorrect: + return .stepikDarkYellow case .wrong: - return UIColor.stepikLightRed + return .stepikLightRed } } var icon: UIImage? { switch self { - case .correct: + case .correct, .partiallyCorrect: return UIImage(named: "quiz-mark-correct") case .wrong: return UIImage(named: "quiz-mark-wrong") } } + + init(quizStatus: QuizStatus) { + switch quizStatus { + case .wrong, .evaluation: + self = .wrong + case .correct: + self = .correct + case .partiallyCorrect: + self = .partiallyCorrect + } + } } } From e8699fd1295d6fdafb40b66e3a29b7506a9400dc Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Tue, 11 Aug 2020 18:32:56 +0900 Subject: [PATCH 4/7] Handle in submissions --- .../Sources/Modules/Submissions/SubmissionsPresenter.swift | 2 +- .../Sources/Modules/Submissions/SubmissionsViewModel.swift | 2 +- .../Modules/Submissions/Views/Cell/SubmissionsCellView.swift | 5 +---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Stepic/Sources/Modules/Submissions/SubmissionsPresenter.swift b/Stepic/Sources/Modules/Submissions/SubmissionsPresenter.swift index 765646ce0e..3c2c90b638 100644 --- a/Stepic/Sources/Modules/Submissions/SubmissionsPresenter.swift +++ b/Stepic/Sources/Modules/Submissions/SubmissionsPresenter.swift @@ -73,7 +73,7 @@ final class SubmissionsPresenter: SubmissionsPresenterProtocol { formattedUsername: username, formattedDate: relativeDateString, submissionTitle: submissionTitle, - isSubmissionCorrect: submission.isCorrect + quizStatus: QuizStatus(submission: submission) ?? .wrong ) } } diff --git a/Stepic/Sources/Modules/Submissions/SubmissionsViewModel.swift b/Stepic/Sources/Modules/Submissions/SubmissionsViewModel.swift index 38ce9f78e2..9e2c280c76 100644 --- a/Stepic/Sources/Modules/Submissions/SubmissionsViewModel.swift +++ b/Stepic/Sources/Modules/Submissions/SubmissionsViewModel.swift @@ -7,5 +7,5 @@ struct SubmissionsViewModel: UniqueIdentifiable { let formattedUsername: String let formattedDate: String let submissionTitle: String - let isSubmissionCorrect: Bool + let quizStatus: QuizStatus } diff --git a/Stepic/Sources/Modules/Submissions/Views/Cell/SubmissionsCellView.swift b/Stepic/Sources/Modules/Submissions/Views/Cell/SubmissionsCellView.swift index 9f56cb915d..6ce2a5daed 100644 --- a/Stepic/Sources/Modules/Submissions/Views/Cell/SubmissionsCellView.swift +++ b/Stepic/Sources/Modules/Submissions/Views/Cell/SubmissionsCellView.swift @@ -87,10 +87,7 @@ final class SubmissionsCellView: UIView { self.nameLabel.text = viewModel.formattedUsername self.dateLabel.text = viewModel.formattedDate - self.solutionControl.update( - state: viewModel.isSubmissionCorrect ? .correct : .wrong, - title: viewModel.submissionTitle - ) + self.solutionControl.update(state: .init(quizStatus: viewModel.quizStatus), title: viewModel.submissionTitle) if let url = viewModel.avatarImageURL { self.avatarImageView.set(with: url) From 4c0e257dc88868ed8a0250b3fdad3b8c83943ebd Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Tue, 11 Aug 2020 18:41:58 +0900 Subject: [PATCH 5/7] Handle in write comment --- .../WriteComment/Views/WriteCommentSolutionControl.swift | 4 ++-- .../Modules/WriteComment/WriteCommentPresenter.swift | 9 ++++++++- .../Sources/Modules/WriteComment/WriteCommentView.swift | 2 +- .../Modules/WriteComment/WriteCommentViewModel.swift | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Stepic/Sources/Modules/WriteComment/Views/WriteCommentSolutionControl.swift b/Stepic/Sources/Modules/WriteComment/Views/WriteCommentSolutionControl.swift index 57f2292378..53ce7fa83f 100644 --- a/Stepic/Sources/Modules/WriteComment/Views/WriteCommentSolutionControl.swift +++ b/Stepic/Sources/Modules/WriteComment/Views/WriteCommentSolutionControl.swift @@ -69,7 +69,7 @@ final class WriteCommentSolutionControl: UIControl { self.solutionControl.isHidden = false self.titleLabel.isHidden = true - self.solutionControl.update(state: viewModel.isCorrect ? .correct : .wrong, title: viewModel.title) + self.solutionControl.update(state: .init(quizStatus: viewModel.status), title: viewModel.title) self.titleLabel.text = nil } else { self.solutionControl.isHidden = true @@ -82,7 +82,7 @@ final class WriteCommentSolutionControl: UIControl { struct ViewModel { let title: String? - let isCorrect: Bool + let status: QuizStatus let isSelected: Bool } } diff --git a/Stepic/Sources/Modules/WriteComment/WriteCommentPresenter.swift b/Stepic/Sources/Modules/WriteComment/WriteCommentPresenter.swift index 6c4b2fee9a..a192930c32 100644 --- a/Stepic/Sources/Modules/WriteComment/WriteCommentPresenter.swift +++ b/Stepic/Sources/Modules/WriteComment/WriteCommentPresenter.swift @@ -102,13 +102,20 @@ final class WriteCommentPresenter: WriteCommentPresenterProtocol { let isSolutionHidden = isReply || data.discussionThreadType != .solutions + let solutionStatus: QuizStatus = { + if let submission = data.submission, let quizStatus = QuizStatus(submission: submission) { + return quizStatus + } + return .wrong + }() + return .init( text: data.text, doneButtonTitle: doneButtonTitle, isFilled: isFilled, isSolutionHidden: isSolutionHidden, isSolutionSelected: data.submission != nil, - isSolutionCorrect: data.submission?.isCorrect ?? false, + solutionStatus: solutionStatus, solutionTitle: solutionTitle ) } diff --git a/Stepic/Sources/Modules/WriteComment/WriteCommentView.swift b/Stepic/Sources/Modules/WriteComment/WriteCommentView.swift index 720955805c..1f8023ebff 100644 --- a/Stepic/Sources/Modules/WriteComment/WriteCommentView.swift +++ b/Stepic/Sources/Modules/WriteComment/WriteCommentView.swift @@ -109,7 +109,7 @@ final class WriteCommentView: UIView { self.solutionControl.configure( viewModel: .init( title: viewModel.solutionTitle, - isCorrect: viewModel.isSolutionCorrect, + status: viewModel.solutionStatus, isSelected: viewModel.isSolutionSelected ) ) diff --git a/Stepic/Sources/Modules/WriteComment/WriteCommentViewModel.swift b/Stepic/Sources/Modules/WriteComment/WriteCommentViewModel.swift index f51e0abd95..9347f1dcdc 100644 --- a/Stepic/Sources/Modules/WriteComment/WriteCommentViewModel.swift +++ b/Stepic/Sources/Modules/WriteComment/WriteCommentViewModel.swift @@ -6,6 +6,6 @@ struct WriteCommentViewModel { let isFilled: Bool let isSolutionHidden: Bool let isSolutionSelected: Bool - let isSolutionCorrect: Bool + let solutionStatus: QuizStatus let solutionTitle: String? } From 89a48a9768911df6cf1fd1cd65bda8e4fb2825d0 Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Tue, 11 Aug 2020 18:45:42 +0900 Subject: [PATCH 6/7] Handle in solution --- .../Sources/Modules/Solution/SolutionPresenter.swift | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/Stepic/Sources/Modules/Solution/SolutionPresenter.swift b/Stepic/Sources/Modules/Solution/SolutionPresenter.swift index 78778aad61..0961c27f8d 100644 --- a/Stepic/Sources/Modules/Solution/SolutionPresenter.swift +++ b/Stepic/Sources/Modules/Solution/SolutionPresenter.swift @@ -26,16 +26,7 @@ final class SolutionPresenter: SolutionPresenterProtocol { submission: Submission, submissionURL: URL? ) -> SolutionViewModel { - let quizStatus: QuizStatus = { - switch submission.statusString { - case "wrong": - return .wrong - case "correct": - return .correct - default: - return .evaluation - } - }() + let quizStatus = QuizStatus(submission: submission) ?? .wrong let feedbackTitle = self.makeFeedbackTitle(status: quizStatus) From 02cdf22072f5100cd83429bebd58d0b6aa7b8f13 Mon Sep 17 00:00:00 2001 From: Ivan Magda Date: Tue, 11 Aug 2020 18:55:12 +0900 Subject: [PATCH 7/7] Clean up --- .../Quizzes/BaseQuiz/BaseQuizPresenter.swift | 13 +------------ .../Quizzes/BaseQuiz/BaseQuizViewController.swift | 9 ++++++--- .../Quizzes/BaseQuiz/Views/QuizFeedbackView.swift | 2 +- .../Modules/Solution/SolutionViewController.swift | 8 +++++--- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizPresenter.swift b/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizPresenter.swift index b1daca779b..e5fc00d18d 100644 --- a/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizPresenter.swift +++ b/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizPresenter.swift @@ -41,18 +41,7 @@ final class BaseQuizPresenter: BaseQuizPresenterProtocol { submissionsCount: Int, hasNextStep: Bool ) -> BaseQuizViewModel { - let quizStatus: QuizStatus? = { - switch submission.status { - case .wrong: - return .wrong - case .correct: - return submission.isPartiallyCorrect ? .partiallyCorrect : .correct - case .evaluation: - return .evaluation - case .none: - return nil - } - }() + let quizStatus = QuizStatus(submission: submission) // The following quizzes can be retried w/o new attempt let isQuizNotNeededNewAttempt = [ diff --git a/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizViewController.swift b/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizViewController.swift index f1487b37ed..62174e8cf9 100644 --- a/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizViewController.swift +++ b/Stepic/Sources/Modules/Quizzes/BaseQuiz/BaseQuizViewController.swift @@ -101,9 +101,12 @@ final class BaseQuizViewController: UIViewController, ControllerWithStepikPlaceh self.baseQuizView?.isDiscountPolicyAvailable = data.isDiscountingPolicyVisible self.baseQuizView?.discountPolicyTitle = data.discountingPolicyTitle - if let quizStatus = data.quizStatus, - let feedbackState = QuizFeedbackView.State(quizStatus: quizStatus) { - self.baseQuizView?.showFeedback(state: feedbackState, title: data.feedbackTitle, hint: data.hintContent) + if let quizStatus = data.quizStatus { + self.baseQuizView?.showFeedback( + state: .init(quizStatus: quizStatus), + title: data.feedbackTitle, + hint: data.hintContent + ) } else { self.baseQuizView?.hideFeedback() } diff --git a/Stepic/Sources/Modules/Quizzes/BaseQuiz/Views/QuizFeedbackView.swift b/Stepic/Sources/Modules/Quizzes/BaseQuiz/Views/QuizFeedbackView.swift index 2cad60e513..77c2a7ace2 100644 --- a/Stepic/Sources/Modules/Quizzes/BaseQuiz/Views/QuizFeedbackView.swift +++ b/Stepic/Sources/Modules/Quizzes/BaseQuiz/Views/QuizFeedbackView.swift @@ -215,7 +215,7 @@ final class QuizFeedbackView: UIView { } } - init?(quizStatus: QuizStatus) { + init(quizStatus: QuizStatus) { switch quizStatus { case .wrong: self = .wrong diff --git a/Stepic/Sources/Modules/Solution/SolutionViewController.swift b/Stepic/Sources/Modules/Solution/SolutionViewController.swift index 129124fc8f..1803fa47d6 100644 --- a/Stepic/Sources/Modules/Solution/SolutionViewController.swift +++ b/Stepic/Sources/Modules/Solution/SolutionViewController.swift @@ -115,9 +115,11 @@ final class SolutionViewController: UIViewController, ControllerWithStepikPlaceh self.addChild(quizController) self.solutionView?.addQuiz(view: quizController.view) - if let feedbackState = QuizFeedbackView.State(quizStatus: data.quizStatus) { - self.solutionView?.showFeedback(state: feedbackState, title: data.feedbackTitle, hint: data.hintContent) - } + self.solutionView?.showFeedback( + state: .init(quizStatus: data.quizStatus), + title: data.feedbackTitle, + hint: data.hintContent + ) self.solutionView?.actionIsHidden = true