Auto-commit after successful build: update app gameplay/UI, improve build setup
Changed files:\napp/build.gradle.kts app/src/main/java/solutions/tretter/githugandroid/GameProgressStore.kt app/src/main/java/solutions/tretter/githugandroid/GitHugApp.kt
This commit is contained in:
@@ -11,8 +11,8 @@ android {
|
|||||||
applicationId = "solutions.tretter.githugandroid"
|
applicationId = "solutions.tretter.githugandroid"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 64
|
versionCode = 67
|
||||||
versionName = "0.1.63"
|
versionName = "0.1.66"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package solutions.tretter.githugandroid
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
|
||||||
|
import androidx.datastore.preferences.core.edit
|
||||||
|
import androidx.datastore.preferences.core.emptyPreferences
|
||||||
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
|
import androidx.datastore.preferences.preferencesDataStoreFile
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class GameProgressStore(private val context: Context) {
|
||||||
|
private val dataStore = PreferenceDataStoreFactory.create(
|
||||||
|
produceFile = { context.preferencesDataStoreFile("game_progress_preferences") }
|
||||||
|
)
|
||||||
|
|
||||||
|
private val completedLevelsKey = stringPreferencesKey("completed_levels")
|
||||||
|
|
||||||
|
val completedLevelsFlow: Flow<Set<String>> = dataStore.data
|
||||||
|
.catch { error ->
|
||||||
|
if (error is IOException) emit(emptyPreferences()) else throw error
|
||||||
|
}
|
||||||
|
.map { preferences ->
|
||||||
|
preferences[completedLevelsKey]
|
||||||
|
?.split(',')
|
||||||
|
?.map { it.trim() }
|
||||||
|
?.filter { it.isNotEmpty() }
|
||||||
|
?.toSet()
|
||||||
|
?: emptySet()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun saveCompletedLevels(completedLevels: Set<String>) {
|
||||||
|
dataStore.edit { preferences ->
|
||||||
|
preferences[completedLevelsKey] = completedLevels.joinToString(",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,7 +38,9 @@ fun GitHugApp() {
|
|||||||
val levels = remember { sampleLevels() }
|
val levels = remember { sampleLevels() }
|
||||||
val runtime = remember(context) { GitRepositoryRuntime(context.applicationContext) }
|
val runtime = remember(context) { GitRepositoryRuntime(context.applicationContext) }
|
||||||
val paneLayoutStore = remember(context) { PaneLayoutStore(context.applicationContext) }
|
val paneLayoutStore = remember(context) { PaneLayoutStore(context.applicationContext) }
|
||||||
|
val gameProgressStore = remember(context) { GameProgressStore(context.applicationContext) }
|
||||||
val persistedPaneLayout by paneLayoutStore.layoutFlow.collectAsState(initial = defaultPaneLayout())
|
val persistedPaneLayout by paneLayoutStore.layoutFlow.collectAsState(initial = defaultPaneLayout())
|
||||||
|
val persistedCompletedLevels by gameProgressStore.completedLevelsFlow.collectAsState(initial = emptySet())
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val screenScrollState = rememberScrollState()
|
val screenScrollState = rememberScrollState()
|
||||||
val screenHeightDp = configuration.screenHeightDp
|
val screenHeightDp = configuration.screenHeightDp
|
||||||
@@ -57,6 +59,7 @@ fun GitHugApp() {
|
|||||||
var commandHistory by remember { mutableStateOf(listOf<String>()) }
|
var commandHistory by remember { mutableStateOf(listOf<String>()) }
|
||||||
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) }
|
||||||
val currentLevel = levels[currentLevelIndex]
|
val currentLevel = levels[currentLevelIndex]
|
||||||
|
|
||||||
LaunchedEffect(persistedPaneLayout) {
|
LaunchedEffect(persistedPaneLayout) {
|
||||||
@@ -78,6 +81,45 @@ fun GitHugApp() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(persistedCompletedLevels) {
|
||||||
|
completedLevels = persistedCompletedLevels
|
||||||
|
if (!hasRestoredProgress) {
|
||||||
|
hasRestoredProgress = true
|
||||||
|
val firstIncompleteIndex = levels.indexOfFirst { it.id !in persistedCompletedLevels }
|
||||||
|
val resumeIndex = firstIncompleteIndex.takeIf { it >= 0 } ?: levels.lastIndex
|
||||||
|
currentLevelIndex = resumeIndex
|
||||||
|
repo = runtime.prepareLevel(levels[resumeIndex])
|
||||||
|
output = listOf(
|
||||||
|
if (persistedCompletedLevels.size == levels.size) {
|
||||||
|
"🏁 All Githug levels completed."
|
||||||
|
} else if (persistedCompletedLevels.isEmpty()) {
|
||||||
|
runtime.startupBanner()
|
||||||
|
} else {
|
||||||
|
"Resumed at level: ${levels[resumeIndex].title}"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
commandInput = TextFieldValue(text = "", selection = TextRange.Zero)
|
||||||
|
suppressedImeEcho = null
|
||||||
|
hintIndex = 0
|
||||||
|
activeExerciseDetail = null
|
||||||
|
visibleHint = null
|
||||||
|
historyIndex = -1
|
||||||
|
historyDraft = ""
|
||||||
|
paneLayout = paneLayout.copy(
|
||||||
|
weights = paneLayout.weights + recommendedPaneWeights(
|
||||||
|
heights = recommendedPaneHeights(
|
||||||
|
level = levels[resumeIndex],
|
||||||
|
levelCount = levels.size,
|
||||||
|
outputLineCount = 1,
|
||||||
|
screenHeightDp = screenHeightDp,
|
||||||
|
suggestionsVisible = false,
|
||||||
|
visibleHint = null,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun applyRecommendedPaneWeights(persist: Boolean = true) {
|
fun applyRecommendedPaneWeights(persist: Boolean = true) {
|
||||||
updatePaneLayout(
|
updatePaneLayout(
|
||||||
transform = { layout ->
|
transform = { layout ->
|
||||||
@@ -230,10 +272,12 @@ fun GitHugApp() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (solvedAfterCommand && !wasAlreadyCompleted) {
|
if (solvedAfterCommand && !wasAlreadyCompleted) {
|
||||||
completedLevels = completedLevels + currentLevel.id
|
val updatedCompletedLevels = completedLevels + currentLevel.id
|
||||||
val hasNextLevel = currentLevelIndex < levels.lastIndex
|
completedLevels = updatedCompletedLevels
|
||||||
if (hasNextLevel) {
|
scope.launch { gameProgressStore.saveCompletedLevels(updatedCompletedLevels) }
|
||||||
loadLevel(currentLevelIndex + 1)
|
val nextLevelIndex = levels.indexOfFirst { it.id !in updatedCompletedLevels }
|
||||||
|
if (nextLevelIndex >= 0) {
|
||||||
|
loadLevel(nextLevelIndex)
|
||||||
} else {
|
} else {
|
||||||
repo = newRepo
|
repo = newRepo
|
||||||
output = listOf("🏁 All Githug levels completed.")
|
output = listOf("🏁 All Githug levels completed.")
|
||||||
|
|||||||
Reference in New Issue
Block a user