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

ALTAPPS-949: iOS make code editor an active input #677

Merged
merged 20 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ import org.hyperskill.app.android.step_quiz.view.model.StepQuizFeedbackState
import org.hyperskill.app.android.step_quiz_hints.fragment.StepQuizHintsFragment
import org.hyperskill.app.android.step_quiz_parsons.view.dialog.ParsonsStepQuizOnboardingBottomSheetDialogFragment
import org.hyperskill.app.android.view.base.ui.extension.snackbar
import org.hyperskill.app.problems_limit.domain.model.ProblemsLimitScreen
import org.hyperskill.app.problems_limit.presentation.ProblemsLimitFeature
import org.hyperskill.app.problems_limit.view.mapper.ProblemsLimitViewStateMapper
import org.hyperskill.app.step.domain.model.BlockName
import org.hyperskill.app.step.domain.model.Step
Expand Down Expand Up @@ -82,6 +80,7 @@ abstract class DefaultStepQuizFragment :

private var stepQuizStateDelegate: ViewStateDelegate<StepQuizFeature.StepQuizState>? = null

// TODO: ALTAPPS-950 delete problems limit UI
private var problemsLimitDelegate: ProblemsLimitDelegate? = null
private var problemsLimitViewStateMapper: ProblemsLimitViewStateMapper? = null

Expand Down Expand Up @@ -115,11 +114,9 @@ abstract class DefaultStepQuizFragment :
private fun injectComponent() {
val stepQuizComponent = HyperskillApp.graph().buildStepQuizComponent(stepRoute)
val platformStepQuizComponent = HyperskillApp.graph().buildPlatformStepQuizComponent(stepQuizComponent)
val problemsLimitComponent = HyperskillApp.graph().buildProblemsLimitComponent(ProblemsLimitScreen.STEP_QUIZ)
stepQuizStatsTextMapper = stepQuizComponent.stepQuizStatsTextMapper
stepQuizTitleMapper = stepQuizComponent.stepQuizTitleMapper
viewModelFactory = platformStepQuizComponent.reduxViewModelFactory
problemsLimitViewStateMapper = problemsLimitComponent.problemsLimitViewStateMapper
}

final override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Expand Down Expand Up @@ -158,14 +155,15 @@ abstract class DefaultStepQuizFragment :
stepQuizViewModel.onNewMessage(StepQuizFeature.Message.InitWithStep(step))
}

// TODO: ALTAPPS-950 delete problems limit UI
private fun initProblemsLimitDelegate() {
problemsLimitDelegate = ProblemsLimitDelegate(
viewBinding = viewBinding.stepQuizProblemsLimit,
onNewMessage = {
stepQuizViewModel.onNewMessage(StepQuizFeature.Message.ProblemsLimitMessage(it))
}
)
problemsLimitDelegate?.setup()
// problemsLimitDelegate = ProblemsLimitDelegate(
// viewBinding = viewBinding.stepQuizProblemsLimit,
// onNewMessage = {
// stepQuizViewModel.onNewMessage(StepQuizFeature.Message.ProblemsLimitMessage(it))
// }
// )
// problemsLimitDelegate?.setup()
}

private fun renderStatistics(textView: TextView, step: Step) {
Expand Down Expand Up @@ -294,12 +292,6 @@ abstract class DefaultStepQuizFragment :
ProblemsLimitReachedBottomSheet.newInstance(action.modalText)
.showIfNotExists(childFragmentManager, ProblemsLimitReachedBottomSheet.TAG)
}
is StepQuizFeature.Action.ViewAction.ProblemsLimitViewAction ->
when (action.viewAction) {
else -> {
// no op
}
}
StepQuizFeature.Action.ViewAction.ShowParsonsProblemOnboardingModal -> {
ParsonsStepQuizOnboardingBottomSheetDialogFragment.newInstance()
.showIfNotExists(childFragmentManager, ParsonsStepQuizOnboardingBottomSheetDialogFragment.TAG)
Expand Down Expand Up @@ -342,12 +334,13 @@ abstract class DefaultStepQuizFragment :
setStepHintsFragment(step)
renderAttemptLoaded(stepQuizState)

problemsLimitViewStateMapper?.let { mapper ->
val problemsLimitViewState = mapper.mapState(state.problemsLimitState)
viewBinding.problemsLimitDivider.root.isVisible =
problemsLimitViewState is ProblemsLimitFeature.ViewState.Content.Widget
problemsLimitDelegate?.render(problemsLimitViewState)
}
// TODO: ALTAPPS-950 delete problems limit UI
// problemsLimitViewStateMapper?.let { mapper ->
// val problemsLimitViewState = mapper.mapState(state.problemsLimitState)
// viewBinding.problemsLimitDivider.root.isVisible =
// problemsLimitViewState is ProblemsLimitFeature.ViewState.Content.Widget
// problemsLimitDelegate?.render(problemsLimitViewState)
// }
}
else -> {
// no op
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ class CodeStepQuizFullScreenDialogFragment : DialogFragment() {
get() = if (lang == ProgrammingLanguage.SQL.serverPrintableName) {
org.hyperskill.app.R.string.step_quiz_sql_title
} else {
org.hyperskill.app.R.string.step_quiz_code_write_program_text
org.hyperskill.app.R.string.step_quiz_code_title
}
}
}
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ plugin-kotlinSerialization = { module = "org.jetbrains.kotlin:kotlin-serializati
plugin-dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" }
plugin-dokkaBase = { module = "org.jetbrains.dokka:dokka-base", version.ref = "dokka" }
plugin-android = { module = "com.android.tools.build:gradle", version.ref = "androidGradlePlugin" }
plugin-ktlint = { module = "org.jlleitschuh.gradle:ktlint-gradle", version = "10.2.0" }
plugin-ktlint = { module = "org.jlleitschuh.gradle:ktlint-gradle", version = "11.1.0" }
plugin-gradleVersionUpdates = { module = "com.github.ben-manes:gradle-versions-plugin", version = "0.47.0" }
plugin-buildKonfig = { module = "com.codingfeline.buildkonfig:buildkonfig-gradle-plugin", version = "0.13.3" }
plugin-mokoResources = { module = "dev.icerock.moko:resources-generator", version.ref = "mokoResources" }
Expand Down
18 changes: 17 additions & 1 deletion iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@
2C5CBBE32948F4B600113007 /* StepQuizSQLViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C5CBBE22948F4B600113007 /* StepQuizSQLViewModel.swift */; };
2C5CBBE52948FA7400113007 /* StepQuizSQLAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C5CBBE42948FA7400113007 /* StepQuizSQLAssembly.swift */; };
2C5CBBE72948FC7A00113007 /* StepQuizSQLView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C5CBBE62948FC7A00113007 /* StepQuizSQLView.swift */; };
2C5EC2C82AC41CAF0098D126 /* StepQuizCodeEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C5EC2C72AC41CAF0098D126 /* StepQuizCodeEditorView.swift */; };
2C5F4A5A2971C71200677530 /* GamificationToolbarContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C5F4A592971C71200677530 /* GamificationToolbarContent.swift */; };
2C62AD582AB43A8F00F3DD5B /* BadgeRankView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C62AD572AB43A8F00F3DD5B /* BadgeRankView.swift */; };
2C6672062A527C0D0040EA2F /* ProgressScreenSectionTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6672052A527C0D0040EA2F /* ProgressScreenSectionTitleView.swift */; };
Expand Down Expand Up @@ -272,6 +273,7 @@
2C96743F288831BB0091B6C9 /* StepQuizCodeSamplesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C96743E288831BB0091B6C9 /* StepQuizCodeSamplesView.swift */; };
2C96744228883A180091B6C9 /* StepQuizCodeViewDataMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C96744128883A180091B6C9 /* StepQuizCodeViewDataMapper.swift */; };
2C96744428883E710091B6C9 /* BlockOptionsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C96744328883E710091B6C9 /* BlockOptionsExtensions.swift */; };
2C971B852AC2F5DC00868FCE /* ExpandableStepTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C971B842AC2F5DC00868FCE /* ExpandableStepTextView.swift */; };
2C975D662A1128670068FD4E /* FeedbackGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C975D652A1128670068FD4E /* FeedbackGenerator.swift */; };
2C97B0CD298124C1001DF1A0 /* StepQuizHintsFeatureViewStateKsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C97B0CC298124C1001DF1A0 /* StepQuizHintsFeatureViewStateKsExtensions.swift */; };
2C97E55A2859A2C500EA1A21 /* StepQuizNameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C97E5592859A2C500EA1A21 /* StepQuizNameView.swift */; };
Expand Down Expand Up @@ -794,6 +796,7 @@
2C5CBBE22948F4B600113007 /* StepQuizSQLViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepQuizSQLViewModel.swift; sourceTree = "<group>"; };
2C5CBBE42948FA7400113007 /* StepQuizSQLAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepQuizSQLAssembly.swift; sourceTree = "<group>"; };
2C5CBBE62948FC7A00113007 /* StepQuizSQLView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepQuizSQLView.swift; sourceTree = "<group>"; };
2C5EC2C72AC41CAF0098D126 /* StepQuizCodeEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepQuizCodeEditorView.swift; sourceTree = "<group>"; };
2C5F4A592971C71200677530 /* GamificationToolbarContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GamificationToolbarContent.swift; sourceTree = "<group>"; };
2C62AD572AB43A8F00F3DD5B /* BadgeRankView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeRankView.swift; sourceTree = "<group>"; };
2C6672052A527C0D0040EA2F /* ProgressScreenSectionTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressScreenSectionTitleView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -870,6 +873,7 @@
2C96743E288831BB0091B6C9 /* StepQuizCodeSamplesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepQuizCodeSamplesView.swift; sourceTree = "<group>"; };
2C96744128883A180091B6C9 /* StepQuizCodeViewDataMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepQuizCodeViewDataMapper.swift; sourceTree = "<group>"; };
2C96744328883E710091B6C9 /* BlockOptionsExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockOptionsExtensions.swift; sourceTree = "<group>"; };
2C971B842AC2F5DC00868FCE /* ExpandableStepTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandableStepTextView.swift; sourceTree = "<group>"; };
2C975D652A1128670068FD4E /* FeedbackGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackGenerator.swift; sourceTree = "<group>"; };
2C97B0CC298124C1001DF1A0 /* StepQuizHintsFeatureViewStateKsExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepQuizHintsFeatureViewStateKsExtensions.swift; sourceTree = "<group>"; };
2C97E5592859A2C500EA1A21 /* StepQuizNameView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepQuizNameView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2350,6 +2354,7 @@
2C967435288829E40091B6C9 /* Views */ = {
isa = PBXGroup;
children = (
2C5EC2C72AC41CAF0098D126 /* StepQuizCodeEditorView.swift */,
2CA3B0352888955F00EEF716 /* StepQuizCodeSkeletonView.swift */,
2C967433288824450091B6C9 /* StepQuizCodeView.swift */,
2C96743828882A130091B6C9 /* Details */,
Expand Down Expand Up @@ -2385,6 +2390,15 @@
path = DailyStudyReminders;
sourceTree = "<group>";
};
2C971B832AC2F5A600868FCE /* StepText */ = {
isa = PBXGroup;
children = (
2C971B842AC2F5DC00868FCE /* ExpandableStepTextView.swift */,
2C9F59A629267A530008ADC5 /* StepTextView.swift */,
);
path = StepText;
sourceTree = "<group>";
};
2C99B0FE2A14253B0018627B /* Model */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -2582,11 +2596,11 @@
isa = PBXGroup;
children = (
2CAE8CF628052F9600E6C83D /* StepHeaderView.swift */,
2C9F59A629267A530008ADC5 /* StepTextView.swift */,
2C8409522805BF3C009C6BE9 /* StepTheoryContentView.swift */,
2CAE8CF1280525C900E6C83D /* StepView.swift */,
2CAE8D0A280578A200E6C83D /* BottomControls */,
2CD0BA05298B985A0037479A /* Modals */,
2C971B832AC2F5A600868FCE /* StepText */,
);
path = Views;
sourceTree = "<group>";
Expand Down Expand Up @@ -3997,6 +4011,7 @@
2CCCA3A12862E62F00D98089 /* StepQuizStringViewData.swift in Sources */,
2C1061A2285C349400EBD614 /* StepQuizChildQuizAssembly.swift in Sources */,
2C11D5CA2A11311900C59238 /* FeedbackGeneratorPreviewView.swift in Sources */,
2C971B852AC2F5DC00868FCE /* ExpandableStepTextView.swift in Sources */,
2CB279AF28C72AA400EDDCC8 /* DeepLinkRouterProtocol.swift in Sources */,
2C023C86285D927A00D2D5A9 /* StepQuizTableAssembly.swift in Sources */,
2C20FBC4284F67F3006D879E /* ProcessedContentWebView.swift in Sources */,
Expand Down Expand Up @@ -4175,6 +4190,7 @@
2C7994B12A129D6100874C16 /* TrackSelectionListSkeletonView.swift in Sources */,
2C1F5875280D0E0000372A37 /* WKWebViewPanelManager.m in Sources */,
2CDF14D228EEF9690060D972 /* AppTabBarController.swift in Sources */,
2C5EC2C82AC41CAF0098D126 /* StepQuizCodeEditorView.swift in Sources */,
2C1F5870280D0CB700372A37 /* WebCacheCleaner.swift in Sources */,
E9D2D673284E0A97000757AC /* StepQuizMatchingItemView.swift in Sources */,
E94BB04E2A9E034700736B7C /* StepQuizParsonsAssembly.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "step-quiz-code-editor-expand.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ extension Block.Options {
isMultipleChoice: Bool? = nil,
language: String? = nil,
isCheckbox: Bool? = nil,
executionTimeLimit: Int? = nil,
executionMemoryLimit: Int? = nil,
limits: [String: Limit]? = nil,
codeTemplates: [String: String]? = nil,
samples: [[String]]? = nil,
Expand All @@ -27,26 +25,10 @@ extension Block.Options {
return nil
}()

let executionTimeLimit: KotlinInt? = {
if let executionTimeLimit = executionTimeLimit {
return KotlinInt(value: Int32(executionTimeLimit))
}
return nil
}()

let executionMemoryLimit: KotlinInt? = {
if let executionMemoryLimit = executionMemoryLimit {
return KotlinInt(value: Int32(executionMemoryLimit))
}
return nil
}()

self.init(
isMultipleChoice: isMultipleChoice,
language: language,
isCheckbox: isCheckbox,
executionTimeLimit: executionTimeLimit,
executionMemoryLimit: executionMemoryLimit,
limits: limits,
codeTemplates: codeTemplates,
samples: samples,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ struct CodeEditor: UIViewRepresentable {

var onDidEndEditing: (() -> Void)?

var onDidChangeHeight: ((CGFloat) -> Void)?

var onDidTapInputAccessoryButton: ((String) -> Void)?

// MARK: UIViewRepresentable

static func dismantleUIView(_ uiView: CodeEditorView, coordinator: Coordinator) {
Expand All @@ -36,6 +40,8 @@ struct CodeEditor: UIViewRepresentable {
coordinator.onCodeDidChange = nil
coordinator.onDidBeginEditing = nil
coordinator.onDidEndEditing = nil
coordinator.onDidChangeHeight = nil
coordinator.onDidTapInputAccessoryButton = nil
coordinator.suggestionsPresentationContextProvider = nil
}

Expand Down Expand Up @@ -89,6 +95,8 @@ struct CodeEditor: UIViewRepresentable {

onDidEndEditing?()
}
context.coordinator.onDidChangeHeight = onDidChangeHeight
context.coordinator.onDidTapInputAccessoryButton = onDidTapInputAccessoryButton
}
}

Expand All @@ -104,6 +112,10 @@ extension CodeEditor {

var onDidEndEditing: (() -> Void)?

var onDidChangeHeight: ((CGFloat) -> Void)?

var onDidTapInputAccessoryButton: ((String) -> Void)?

init(suggestionsPresentationContextProvider: CodeEditorSuggestionsPresentationContextProviding?) {
self.suggestionsPresentationContextProvider = suggestionsPresentationContextProvider
}
Expand All @@ -127,6 +139,22 @@ extension CodeEditor {
) -> UIViewController? {
suggestionsPresentationContextProvider?.presentationController(for: codeEditorView)
}

func codeEditorViewDidChangeHeight(_ codeEditorView: CodeEditorView, height: CGFloat) {
onDidChangeHeight?(height)
}

func codeEditorViewDidTapTabInputAccessoryButton(_ codeEditorView: CodeEditorView) {
onDidTapInputAccessoryButton?("tab")
}

func codeEditorViewDidTapHideKeyboardInputAccessoryButton(_ codeEditorView: CodeEditorView) {
onDidTapInputAccessoryButton?("hide")
}

func codeEditorView(_ codeEditorView: CodeEditorView, didTapInputAccessoryButton symbol: String) {
onDidTapInputAccessoryButton?(symbol)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ final class CodeEditorView: UIView {
return
}

strongSelf.delegate?.codeEditorViewDidTapTabInputAccessoryButton(strongSelf)

strongSelf.codePlaygroundManager.insertAtCurrentPosition(
symbols: String(repeating: " ", count: strongSelf.tabSize),
textView: strongSelf.codeTextView
Expand All @@ -149,14 +151,22 @@ final class CodeEditorView: UIView {
return
}

strongSelf.delegate?.codeEditorView(strongSelf, didTapInputAccessoryButton: symbols)

strongSelf.codePlaygroundManager.insertAtCurrentPosition(
symbols: symbols,
textView: strongSelf.codeTextView
)
strongSelf.analyzeCodeAndComplete()
},
hideKeyboardAction: { [weak self] in
self?.codeTextView.resignFirstResponder()
guard let strongSelf = self else {
return
}

strongSelf.delegate?.codeEditorViewDidTapHideKeyboardInputAccessoryButton(strongSelf)

strongSelf.codeTextView.resignFirstResponder()
},
pasteConfigurationSupporting: codeTextView
)
Expand Down Expand Up @@ -198,9 +208,13 @@ extension CodeEditorView: ProgrammaticallyInitializableViewProtocol {
}
}

// MARK: - CodeEditorView: UITextViewDelegate -
// MARK: - CodeEditorView: CodeTextViewDelegate -

extension CodeEditorView: CodeTextViewDelegate {
func codeTextViewDidChangeHeight(_ textView: CodeTextView, height: CGFloat) {
delegate?.codeEditorViewDidChangeHeight(self, height: height)
}

extension CodeEditorView: UITextViewDelegate {
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
delegate?.codeEditorView(self, beginEditing: isEditable)
return isEditable
Expand Down
Loading