Make manual level selection responsive
This commit is contained in:
@@ -20,8 +20,8 @@ android {
|
|||||||
applicationId = "solutions.tretter.githugandroid"
|
applicationId = "solutions.tretter.githugandroid"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 175
|
versionCode = 176
|
||||||
versionName = "0.1.174"
|
versionName = "0.1.175"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import androidx.compose.ui.test.junit4.createEmptyComposeRule
|
|||||||
import androidx.compose.ui.test.onNodeWithTag
|
import androidx.compose.ui.test.onNodeWithTag
|
||||||
import androidx.compose.ui.test.performClick
|
import androidx.compose.ui.test.performClick
|
||||||
import androidx.compose.ui.test.performImeAction
|
import androidx.compose.ui.test.performImeAction
|
||||||
|
import androidx.compose.ui.test.performScrollTo
|
||||||
import androidx.compose.ui.test.performTextClearance
|
import androidx.compose.ui.test.performTextClearance
|
||||||
import androidx.compose.ui.test.performTextInput
|
import androidx.compose.ui.test.performTextInput
|
||||||
import androidx.test.core.app.ActivityScenario
|
import androidx.test.core.app.ActivityScenario
|
||||||
@@ -62,6 +63,21 @@ class GitRepositoryRuntimeInstrumentedTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun selectingLevelFromLevelsPaneUpdatesExerciseImmediately() {
|
||||||
|
launchFreshApp().use {
|
||||||
|
composeRule.onNodeWithTag("level-submodule")
|
||||||
|
.performScrollTo()
|
||||||
|
.performClick()
|
||||||
|
|
||||||
|
composeRule.waitUntil(timeoutMillis = 1_500) {
|
||||||
|
composeRule.onAllNodes(hasText(submoduleLevel().title, substring = true))
|
||||||
|
.fetchSemanticsNodes()
|
||||||
|
.isNotEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun launchFreshApp(): ActivityScenario<MainActivity> {
|
private fun launchFreshApp(): ActivityScenario<MainActivity> {
|
||||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
File(context.filesDir, "githug-sandboxes").deleteRecursively()
|
File(context.filesDir, "githug-sandboxes").deleteRecursively()
|
||||||
|
|||||||
@@ -34,8 +34,10 @@ import androidx.compose.ui.platform.LocalDensity
|
|||||||
import androidx.compose.ui.text.TextRange
|
import androidx.compose.ui.text.TextRange
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GitHugApp() {
|
fun GitHugApp() {
|
||||||
@@ -73,6 +75,8 @@ fun GitHugApp() {
|
|||||||
var historyIndex by remember { mutableStateOf(-1) }
|
var historyIndex by remember { mutableStateOf(-1) }
|
||||||
var historyDraft by remember { mutableStateOf("") }
|
var historyDraft by remember { mutableStateOf("") }
|
||||||
var hasRestoredProgress by remember { mutableStateOf(false) }
|
var hasRestoredProgress by remember { mutableStateOf(false) }
|
||||||
|
var isPreparingLevel by remember { mutableStateOf(false) }
|
||||||
|
var levelLoadRequestId by remember { mutableStateOf(0) }
|
||||||
var editorState by remember { mutableStateOf<TextEditorState?>(null) }
|
var editorState by remember { mutableStateOf<TextEditorState?>(null) }
|
||||||
var gitMessageEditorState by remember { mutableStateOf<GitMessageEditorState?>(null) }
|
var gitMessageEditorState by remember { mutableStateOf<GitMessageEditorState?>(null) }
|
||||||
var manPageState by remember { mutableStateOf<ManPageState?>(null) }
|
var manPageState by remember { mutableStateOf<ManPageState?>(null) }
|
||||||
@@ -233,11 +237,14 @@ fun GitHugApp() {
|
|||||||
index: Int,
|
index: Int,
|
||||||
message: List<String> = listOf("Loaded level: ${levels[index].title}"),
|
message: List<String> = listOf("Loaded level: ${levels[index].title}"),
|
||||||
keepSuppressedImeEcho: Boolean = false,
|
keepSuppressedImeEcho: Boolean = false,
|
||||||
|
prepareInBackground: Boolean = false,
|
||||||
) {
|
) {
|
||||||
AppLog.d("GitHugApp", "Loading level index=$index id=${levels[index].id} title=${levels[index].title}")
|
val level = levels[index]
|
||||||
|
val requestId = levelLoadRequestId + 1
|
||||||
|
levelLoadRequestId = requestId
|
||||||
|
AppLog.d("GitHugApp", "Loading level index=$index id=${level.id} title=${level.title} background=$prepareInBackground")
|
||||||
currentLevelIndex = index
|
currentLevelIndex = index
|
||||||
repo = runtime.prepareLevel(levels[index])
|
output = if (prepareInBackground) listOf("Preparing level: ${level.title}") else message
|
||||||
output = message
|
|
||||||
clearCommandInput(recreateField = true)
|
clearCommandInput(recreateField = true)
|
||||||
if (!keepSuppressedImeEcho) {
|
if (!keepSuppressedImeEcho) {
|
||||||
suppressedImeEcho = null
|
suppressedImeEcho = null
|
||||||
@@ -250,10 +257,29 @@ fun GitHugApp() {
|
|||||||
editorState = null
|
editorState = null
|
||||||
gitMessageEditorState = null
|
gitMessageEditorState = null
|
||||||
manPageState = null
|
manPageState = null
|
||||||
|
if (prepareInBackground) {
|
||||||
|
isPreparingLevel = true
|
||||||
|
repo = RepoState()
|
||||||
|
scope.launch {
|
||||||
|
val preparedRepo = withContext(Dispatchers.Default) {
|
||||||
|
runtime.prepareLevel(level)
|
||||||
|
}
|
||||||
|
if (levelLoadRequestId == requestId) {
|
||||||
|
AppLog.d("GitHugApp", "Prepared level index=$index id=${level.id}")
|
||||||
|
repo = preparedRepo
|
||||||
|
output = message
|
||||||
|
isPreparingLevel = false
|
||||||
|
clearCommandInput(recreateField = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isPreparingLevel = false
|
||||||
|
repo = runtime.prepareLevel(level)
|
||||||
|
}
|
||||||
paneLayout = paneLayout.copy(
|
paneLayout = paneLayout.copy(
|
||||||
weights = paneLayout.weights + recommendedPaneWeights(
|
weights = paneLayout.weights + recommendedPaneWeights(
|
||||||
heights = recommendedPaneHeights(
|
heights = recommendedPaneHeights(
|
||||||
level = levels[index],
|
level = level,
|
||||||
levelCount = levels.size,
|
levelCount = levels.size,
|
||||||
outputLineCount = 1,
|
outputLineCount = 1,
|
||||||
screenHeightDp = screenHeightDp,
|
screenHeightDp = screenHeightDp,
|
||||||
@@ -457,6 +483,12 @@ fun GitHugApp() {
|
|||||||
val submittedText = commandInput.text
|
val submittedText = commandInput.text
|
||||||
val raw = submittedText.trim()
|
val raw = submittedText.trim()
|
||||||
if (raw.isBlank()) return
|
if (raw.isBlank()) return
|
||||||
|
if (isPreparingLevel) {
|
||||||
|
output = output + "Still preparing ${currentLevel.title}. Try again in a moment."
|
||||||
|
clearCommandInput(recreateField = true)
|
||||||
|
applyRecommendedPaneWeights(persist = false, outputLineCount = terminalOutputLineCount(output))
|
||||||
|
return
|
||||||
|
}
|
||||||
val isInteractiveInput = repo.interactiveAddSession != null
|
val isInteractiveInput = repo.interactiveAddSession != null
|
||||||
|
|
||||||
showHelpOverlay = false
|
showHelpOverlay = false
|
||||||
@@ -560,7 +592,9 @@ fun GitHugApp() {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
levelsContent = {
|
levelsContent = {
|
||||||
LevelsPane(levels, currentLevelIndex, completedLevels) { index -> loadLevel(index) }
|
LevelsPane(levels, currentLevelIndex, completedLevels) { index ->
|
||||||
|
loadLevel(index, prepareInBackground = true)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
visualContent = { VisualPane(repo) },
|
visualContent = { VisualPane(repo) },
|
||||||
exerciseContent = {
|
exerciseContent = {
|
||||||
|
|||||||
Reference in New Issue
Block a user