Lee Morgan пре 2 недеља
родитељ
комит
86b1e8ac61

+ 3 - 0
AGENTS.md

@@ -1,6 +1,9 @@
 # App Description
 # App Description
 This is meant to be an exercise application for all sorts of exercise. It will primarily store data locally. Storing to a database will be an optional upgrade. One of the big purposes of this is to avoid the garbage apps that have tons of ads and other stuff. Nice and simple and easy to use for any type of exercise.
 This is meant to be an exercise application for all sorts of exercise. It will primarily store data locally. Storing to a database will be an optional upgrade. One of the big purposes of this is to avoid the garbage apps that have tons of ads and other stuff. Nice and simple and easy to use for any type of exercise.
 
 
+# Code
+Always use Kotlin over Java unless there is no other option
+
 # Style
 # Style
 
 
 Dark only. Surfaces stay quiet so effort reads as heat. Prefer borders over shadows.
 Dark only. Surfaces stay quiet so effort reads as heat. Prefer borders over shadows.

+ 40 - 38
app/src/main/java/com/example/torus/ui/TorusApp.kt

@@ -15,67 +15,69 @@ import androidx.compose.ui.Modifier
 import com.example.torus.data.WorkoutRepository
 import com.example.torus.data.WorkoutRepository
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.launch
 
 
-private enum class Screen {
-    Home,
-    CreateWorkout,
-    WorkoutDetail,
+private object Routes {
+    const val HOME = "home"
+    const val CREATE = "create"
+
+    fun workout(id: String) = "workout/$id"
+
+    fun isWorkout(route: String) = route.startsWith("workout/")
+
+    fun workoutId(route: String) = route.removePrefix("workout/")
 }
 }
 
 
 @Composable
 @Composable
 fun TorusApp(workoutRepository: WorkoutRepository) {
 fun TorusApp(workoutRepository: WorkoutRepository) {
-    var screen by rememberSaveable { mutableStateOf(Screen.Home) }
-    var selectedWorkoutId by rememberSaveable { mutableStateOf<String?>(null) }
+    var navigationStack by rememberSaveable { mutableStateOf(listOf(Routes.HOME)) }
+    val currentRoute = navigationStack.last()
     val coroutineScope = rememberCoroutineScope()
     val coroutineScope = rememberCoroutineScope()
     val workouts by workoutRepository.observeWorkouts().collectAsState(initial = emptyList())
     val workouts by workoutRepository.observeWorkouts().collectAsState(initial = emptyList())
 
 
-    BackHandler(enabled = screen != Screen.Home) {
-        screen = Screen.Home
-        selectedWorkoutId = null
+    fun navigateTo(route: String) {
+        navigationStack = navigationStack + route
+    }
+
+    fun popBack() {
+        if (navigationStack.size > 1) {
+            navigationStack = navigationStack.dropLast(1)
+        }
+    }
+
+    fun goHome() {
+        navigationStack = listOf(Routes.HOME)
+    }
+
+    BackHandler(enabled = navigationStack.size > 1) {
+        popBack()
     }
     }
 
 
     Scaffold(
     Scaffold(
         modifier = Modifier.fillMaxSize(),
         modifier = Modifier.fillMaxSize(),
-        topBar = {
-            AppHeader(onClick = {
-                screen = Screen.Home
-                selectedWorkoutId = null
-            })
-        }
+        topBar = { AppHeader(onClick = { goHome() }) }
     ) { innerPadding ->
     ) { innerPadding ->
         val contentModifier = Modifier.padding(innerPadding)
         val contentModifier = Modifier.padding(innerPadding)
-        when (screen) {
-            Screen.Home -> HomeScreen(
+        when {
+            currentRoute == Routes.HOME -> HomeScreen(
                 workouts = workouts,
                 workouts = workouts,
                 modifier = contentModifier,
                 modifier = contentModifier,
-                onAddExercise = { screen = Screen.CreateWorkout },
-                onWorkoutClick = { workoutId ->
-                    selectedWorkoutId = workoutId
-                    screen = Screen.WorkoutDetail
-                }
+                onAddExercise = { navigateTo(Routes.CREATE) },
+                onWorkoutClick = { workoutId -> navigateTo(Routes.workout(workoutId)) }
             )
             )
-            Screen.CreateWorkout -> CreateWorkoutScreen(
+            currentRoute == Routes.CREATE -> CreateWorkoutScreen(
                 modifier = contentModifier,
                 modifier = contentModifier,
                 onSaveWorkout = { name, exercises ->
                 onSaveWorkout = { name, exercises ->
                     coroutineScope.launch {
                     coroutineScope.launch {
                         workoutRepository.saveWorkout(name, exercises)
                         workoutRepository.saveWorkout(name, exercises)
-                        screen = Screen.Home
+                        goHome()
                     }
                     }
                 }
                 }
             )
             )
-            Screen.WorkoutDetail -> {
-                val workoutId = selectedWorkoutId
-                if (workoutId != null) {
-                    WorkoutDetailScreen(
-                        workoutId = workoutId,
-                        workoutRepository = workoutRepository,
-                        modifier = contentModifier,
-                        onBackToHome = {
-                            screen = Screen.Home
-                            selectedWorkoutId = null
-                        }
-                    )
-                }
-            }
+            Routes.isWorkout(currentRoute) -> WorkoutDetailScreen(
+                workoutId = Routes.workoutId(currentRoute),
+                workoutRepository = workoutRepository,
+                modifier = contentModifier,
+                onHome = { goHome() }
+            )
         }
         }
     }
     }
 }
 }

+ 62 - 86
app/src/main/java/com/example/torus/ui/WorkoutDetailScreen.kt

@@ -1,15 +1,12 @@
 package com.example.torus.ui
 package com.example.torus.ui
 
 
-import androidx.compose.foundation.border
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.Button
 import androidx.compose.material3.ButtonDefaults
 import androidx.compose.material3.ButtonDefaults
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.OutlinedButton
 import androidx.compose.material3.OutlinedButton
@@ -20,15 +17,12 @@ import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.dp
 import com.example.torus.R
 import com.example.torus.R
 import com.example.torus.data.WorkoutRepository
 import com.example.torus.data.WorkoutRepository
-import com.example.torus.model.ExerciseType
-import com.example.torus.model.SavedExercise
 import com.example.torus.model.WorkoutDetail
 import com.example.torus.model.WorkoutDetail
 import com.example.torus.ui.theme.Sand
 import com.example.torus.ui.theme.Sand
 import com.example.torus.ui.theme.TorusTheme
 import com.example.torus.ui.theme.TorusTheme
@@ -38,7 +32,9 @@ fun WorkoutDetailScreen(
     workoutId: String,
     workoutId: String,
     workoutRepository: WorkoutRepository,
     workoutRepository: WorkoutRepository,
     modifier: Modifier = Modifier,
     modifier: Modifier = Modifier,
-    onBackToHome: () -> Unit = {}
+    onStartWorkout: () -> Unit = {},
+    onEditWorkout: () -> Unit = {},
+    onHome: () -> Unit = {}
 ) {
 ) {
     var detail by remember(workoutId) { mutableStateOf<WorkoutDetail?>(null) }
     var detail by remember(workoutId) { mutableStateOf<WorkoutDetail?>(null) }
 
 
@@ -50,7 +46,9 @@ fun WorkoutDetailScreen(
         WorkoutDetailContent(
         WorkoutDetailContent(
             workout = workout,
             workout = workout,
             modifier = modifier,
             modifier = modifier,
-            onBackToHome = onBackToHome
+            onStartWorkout = onStartWorkout,
+            onEditWorkout = onEditWorkout,
+            onHome = onHome
         )
         )
     }
     }
 }
 }
@@ -59,75 +57,68 @@ fun WorkoutDetailScreen(
 private fun WorkoutDetailContent(
 private fun WorkoutDetailContent(
     workout: WorkoutDetail,
     workout: WorkoutDetail,
     modifier: Modifier = Modifier,
     modifier: Modifier = Modifier,
-    onBackToHome: () -> Unit = {}
+    onStartWorkout: () -> Unit = {},
+    onEditWorkout: () -> Unit = {},
+    onHome: () -> Unit = {}
 ) {
 ) {
-    Column(modifier = modifier.fillMaxSize()) {
-        Column(
-            modifier = Modifier
-                .weight(1f)
-                .fillMaxWidth()
-                .verticalScroll(rememberScrollState())
-                .padding(horizontal = 16.dp, vertical = 8.dp),
-            verticalArrangement = Arrangement.spacedBy(16.dp)
-        ) {
-            Text(
-                text = workout.name.ifBlank { stringResource(R.string.untitled_workout) },
-                color = Sand,
-                style = MaterialTheme.typography.headlineMedium
-            )
-            workout.exercises.forEachIndexed { index, exercise ->
-                ExerciseRow(number = index + 1, exercise = exercise)
-            }
-        }
-        OutlinedButton(
-            onClick = onBackToHome,
-            modifier = Modifier
-                .fillMaxWidth()
-                .padding(horizontal = 16.dp, vertical = 12.dp),
-            shape = RoundedCornerShape(50),
-            colors = ButtonDefaults.outlinedButtonColors(
-                contentColor = MaterialTheme.colorScheme.onBackground
-            )
-        ) {
-            Text(text = stringResource(R.string.back_to_home))
-        }
+    Column(
+        modifier = modifier
+            .fillMaxSize()
+            .padding(horizontal = 16.dp, vertical = 8.dp),
+        verticalArrangement = Arrangement.spacedBy(12.dp)
+    ) {
+        Text(
+            text = workout.name.ifBlank { stringResource(R.string.untitled_workout) },
+            color = Sand,
+            style = MaterialTheme.typography.headlineMedium
+        )
+        EmberButton(
+            text = stringResource(R.string.start_workout),
+            onClick = onStartWorkout
+        )
+        GhostButton(
+            text = stringResource(R.string.edit_workout),
+            onClick = onEditWorkout
+        )
+        GhostButton(
+            text = stringResource(R.string.home),
+            onClick = onHome
+        )
     }
     }
 }
 }
 
 
 @Composable
 @Composable
-private fun ExerciseRow(
-    number: Int,
-    exercise: SavedExercise
+private fun EmberButton(
+    text: String,
+    onClick: () -> Unit
 ) {
 ) {
-    val shape = RoundedCornerShape(14.dp)
-    Row(
-        modifier = Modifier
-            .fillMaxWidth()
-            .border(1.dp, MaterialTheme.colorScheme.outline, shape)
-            .padding(16.dp),
-        horizontalArrangement = Arrangement.SpaceBetween,
-        verticalAlignment = Alignment.CenterVertically
+    Button(
+        onClick = onClick,
+        modifier = Modifier.fillMaxWidth(),
+        shape = RoundedCornerShape(50),
+        colors = ButtonDefaults.buttonColors(
+            containerColor = MaterialTheme.colorScheme.primary,
+            contentColor = MaterialTheme.colorScheme.onPrimary
+        )
     ) {
     ) {
-        Column(
-            modifier = Modifier.weight(1f),
-            verticalArrangement = Arrangement.spacedBy(4.dp)
-        ) {
-            Text(
-                text = stringResource(exercise.type.labelRes),
-                color = MaterialTheme.colorScheme.onSurfaceVariant,
-                style = MaterialTheme.typography.labelLarge
-            )
-            Text(
-                text = exercise.name.ifBlank { stringResource(R.string.untitled_exercise) },
-                color = MaterialTheme.colorScheme.onBackground,
-                style = MaterialTheme.typography.bodyLarge
-            )
-        }
-        Text(
-            text = number.toString(),
-            color = MaterialTheme.colorScheme.onSurfaceVariant,
-            style = MaterialTheme.typography.labelSmall
+        Text(text = text)
+    }
+}
+
+@Composable
+private fun GhostButton(
+    text: String,
+    onClick: () -> Unit
+) {
+    OutlinedButton(
+        onClick = onClick,
+        modifier = Modifier.fillMaxWidth(),
+        shape = RoundedCornerShape(50),
+        colors = ButtonDefaults.outlinedButtonColors(
+            contentColor = MaterialTheme.colorScheme.onBackground
         )
         )
+    ) {
+        Text(text = text)
     }
     }
 }
 }
 
 
@@ -141,22 +132,7 @@ private fun WorkoutDetailContentPreview() {
                 workout = WorkoutDetail(
                 workout = WorkoutDetail(
                     id = "1",
                     id = "1",
                     name = "Push day",
                     name = "Push day",
-                    exercises = listOf(
-                        SavedExercise(
-                            id = "a",
-                            name = "Bench",
-                            type = ExerciseType.Weights,
-                            isActive = true,
-                            notes = ""
-                        ),
-                        SavedExercise(
-                            id = "b",
-                            name = "Easy run",
-                            type = ExerciseType.Run,
-                            isActive = true,
-                            notes = ""
-                        )
-                    )
+                    exercises = emptyList()
                 )
                 )
             )
             )
         }
         }

+ 3 - 2
app/src/main/res/values/strings.xml

@@ -10,6 +10,7 @@
     <string name="exercise_name">Exercise name</string>
     <string name="exercise_name">Exercise name</string>
     <string name="save_workout">Save Workout</string>
     <string name="save_workout">Save Workout</string>
     <string name="untitled_workout">Untitled workout</string>
     <string name="untitled_workout">Untitled workout</string>
-    <string name="untitled_exercise">Untitled exercise</string>
-    <string name="back_to_home">Back to home</string>
+    <string name="start_workout">Start Workout</string>
+    <string name="edit_workout">Edit Workout</string>
+    <string name="home">Home</string>
 </resources>
 </resources>