Compare commits

..

3 commits

15 changed files with 457 additions and 159 deletions

View file

@ -21,26 +21,6 @@ kotlin {
jvm("desktop")
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = "composeApp"
browser {
val rootDirPath = project.rootDir.path
val projectDirPath = project.projectDir.path
commonWebpackConfig {
outputFileName = "composeApp.js"
devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply {
static = (static ?: mutableListOf()).apply {
// Serve sources to debug inside browser
add(rootDirPath)
add(projectDirPath)
}
}
}
}
binaries.executable()
}
sourceSets {
val desktopMain by getting
@ -61,6 +41,8 @@ kotlin {
implementation("org.jetbrains.compose.material3:material3:1.7.0") // Or latest version
implementation("org.jetbrains.compose.material3:material3-window-size-class:1.7.0") // For window size classes
implementation("org.jetbrains.androidx.navigation:navigation-compose:2.7.0-alpha07")
api(libs.datastore.preferences)
api(libs.datastore)
}
desktopMain.dependencies {
implementation(compose.desktop.currentOs)

View file

@ -1,17 +1,10 @@
package com.menagerie.ophelia
import android.app.Activity
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.runtime.remember
import androidx.core.view.WindowCompat
import com.menagerie.ophelia.ui.theme.OpheliaTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@ -19,8 +12,13 @@ class MainActivity : ComponentActivity() {
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
MainView()
MainView(
prefs = remember {
createDataStore(application)
}
)
}
}
}

View file

@ -0,0 +1,11 @@
package com.menagerie.ophelia
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
fun createDataStore(context: Context): DataStore<Preferences> {
return createDataStore {
context.filesDir.resolve(DATA_STORE_FILE_NAME).absolutePath
}
}

View file

@ -1,18 +1,37 @@
package com.menagerie.ophelia
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringPreferencesKey
import com.menagerie.ophelia.sensor.SensorDataManager
import com.menagerie.ophelia.sensor.SensorManagerImpl
import com.menagerie.ophelia.ui.theme.OpheliaTheme
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
enum class Theme {
Auto,
Light,
Dark
}
val themeKey = stringPreferencesKey("theme")
@Composable
fun MainView(isLargeScreen: Boolean = false) {
fun MainView(
isLargeScreen: Boolean = false,
prefs: DataStore<Preferences>,
) {
val sensorManager = SensorManagerImpl()
@ -26,7 +45,7 @@ fun MainView(isLargeScreen: Boolean = false) {
val job = scope.launch {
dataManager.data
.receiveAsFlow()
.onEach {sensorManager.listener?.onUpdate(it)}
.onEach { sensorManager.listener?.onUpdate(it) }
.collect()
}
@ -36,5 +55,25 @@ fun MainView(isLargeScreen: Boolean = false) {
}
}
App(sensorManager, isLargeScreen)
val currentThemeString by prefs
.data
.map {
it[themeKey] ?: Theme.Dark.name
}
.collectAsState(Theme.Dark.name)
val currentTheme = Theme.valueOf(currentThemeString)
val isDarkTheme: Boolean? = when (currentTheme) {
Theme.Dark -> true
Theme.Light -> false
Theme.Auto -> null
}
OpheliaTheme(isDarkTheme ?: isSystemInDarkTheme()) {
App(
sensorManager = sensorManager,
isLarge = isLargeScreen,
prefs = prefs,
)
}
}

View file

@ -1,34 +1,31 @@
package com.menagerie.ophelia
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.menagerie.ophelia.ui.theme.OpheliaTheme
import com.menagerie.ophelia.sensor.SensorManager
import com.menagerie.ophelia.HomeScreen.HomeScreen
import com.menagerie.ophelia.HomeScreen.MainMenu
import com.menagerie.ophelia.HomeScreen.Settings
import com.menagerie.ophelia.model.Recipe
import com.menagerie.ophelia.model.recipesList
import com.menagerie.ophelia.recipesdetails.RecipeDetails
import com.menagerie.ophelia.recipeslist.RecipesListScreen
import com.menagerie.ophelia.sensor.SensorManager
import com.menagerie.ophelia.ui.theme.OpheliaTheme
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
enum class Theme {
Auto,
Light,
Dark
}
enum class DetailListAppScreen {
List,
@ -37,81 +34,193 @@ enum class DetailListAppScreen {
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun App(sensorManager: SensorManager?, isLarge: Boolean = false) {
fun App(
sensorManager: SensorManager?,
isLarge: Boolean = false,
prefs: DataStore<Preferences>,
) {
val navController = rememberNavController()
OpheliaNavHost(
navController = navController,
isLarge = isLarge,
sensorManager = sensorManager,
prefs = prefs
)
var currentTheme by rememberSaveable { mutableStateOf(Theme.Light)}
val isDarkTheme: Boolean? = when (currentTheme) {
Theme.Dark -> true
Theme.Light -> false
Theme.Auto -> null
}
val onThemeToggle = {
currentTheme = when (currentTheme) {
Theme.Auto -> Theme.Light
Theme.Light -> Theme.Dark
Theme.Dark -> Theme.Auto
}
}
}
OpheliaTheme(isDarkTheme ?: isSystemInDarkTheme()) {
@Composable
fun OpheliaNavHost(
navController: NavHostController,
isLarge: Boolean,
sensorManager: SensorManager?,
prefs: DataStore<Preferences>
){
val items by remember { mutableStateOf(recipesList) }
var show by remember { mutableStateOf(false) }
//TODO : Replace/Duplicate This and use it to show Polycule Members instead.
//TODO : Move this to its own page
val items by remember { mutableStateOf(recipesList) }
var currentRecipe = items.first()
Scaffold {
Column {
//TODO : replace this with a composable task bar
if (show) {
Row {
Button(
onClick = {
navController.popBackStack()
}
) {
Text(text = "Back - TEMP")
}
Button(
onClick = {
navController.navigate("mainMenu")
}
) {
Text(text = "Home - TEMP")
}
Button(
onClick = {
navController.navigate("settings")
}
) {
Text(text = "Settings - TEMP")
}
Button(onClick = {
//TODO : This should take the user to their bio page
}){
Text(text = "You - TEMP")
}
}
}
NavHost(
navController = navController,
startDestination = DetailListAppScreen.List.name,
modifier = Modifier.fillMaxSize()
) {
composable(route = DetailListAppScreen.List.name) {
RecipesListScreen(
isLarge = isLarge,
NavHost(
navController = navController,
startDestination = "home"
) {
polyculeGraph(navController)
recipeGraph(
navController = navController,
items = items,
onClick = { recipe ->
currentRecipe = recipe
navController.navigate(DetailListAppScreen.Details.name)
})
}
composable(route = DetailListAppScreen.Details.name) {
RecipeDetails(
isLarge = isLarge,
sensorManager = sensorManager,
recipe = currentRecipe,
goBack = { navController.popBackStack() }
sensorManager = sensorManager
)
welcomeGraph(navController)
composable("home") {
HomeScreen {
show = true
navController.navigate("mainMenu")
}
}
composable("mainMenu") {
MainMenu(
onPolyClick = {
//TODO : This should take the user to their polycule's main page
},
onUserBioClick = {
//TODO : This should take the user to their bio page
},
onCookBookClick = {
navController.navigate(DetailListAppScreen.List.name)
},
)
}
composable("settings") {
Settings(prefs)
}
}
}
}
// Scaffold {
// var showContent by remember { mutableStateOf(false) }
// Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
// Row {
// Button(onClick = { showContent = !showContent }) {
// Text("Click Me!")
// }
// Button(onClick = onThemeToggle){
// val text : String = currentTheme.name
// Text(text)
// }
// }
// AnimatedVisibility(showContent) {
// val greeting = remember { Greeting().greet() }
// Column(
// Modifier.fillMaxWidth(),
// horizontalAlignment = Alignment.CenterHorizontally
// ) {
// Image(painterResource(Res.drawable.ophelia), null)
// Text("Compose: $greeting")
// }
// }
// }
// }
}
}
fun NavGraphBuilder.welcomeGraph(navController: NavHostController) {
// composable("newUserStart") {
// NewUserStartScreen { navController.navigate("newUserName") }
// }
// composable("newUserName") {
// AddNameScreen { navController.navigate("newUserPronouns") }
// }
// composable("newUserPronouns") {
// AddPronounsScreen { navController.navigate("newUserTags") }
// }
// composable("newUserTags") {
// AddTagsScreen { navController.navigate("confirmBio") }
// }
// composable("confirmBio") {
// ConfirmBio {
// navController.popBackStack()
// navController.navigate("polyculeHome")
// }
// }
// composable("makePolycule") {
// AddPolyculeScreen()
// }
}
fun NavGraphBuilder.polyculeGraph(navController: NavHostController) {
// composable("polyculeHome") {
// PolyculeHomeView(
// onBioClick = {
// navController.navigate("bioDetail/${it.id}")
// },
// onAddClick = {
// navController.navigate("addBio") {}
// }
// )
// }
// composable(
// "bioDetail/{bioId}",
// arguments = listOf(navArgument("bioId") {
// type = NavType.LongType
// })
// ) {
// BioDetailsScreen(
// id = it.arguments?.getLong("bioId") ?: 0,
// onBackClick = { navController.navigateUp() },
// onEditClick = { id -> navController.navigate("editBio/${id}")}
// )
// }
// composable("addBio") {
// AddBiography()
// }
// composable(
// "editBio/{bioId}",
// arguments = listOf(navArgument("bioId") {
// type = NavType.LongType
// })
// ){
// EditBiography(bioId = it.arguments?.getLong("bioId") ?: 0) { navController.navigateUp() }
// }
}
fun NavGraphBuilder.recipeGraph(
navController: NavHostController,
items: List<Recipe>,
isLarge: Boolean,
sensorManager: SensorManager?,
) {
var currentRecipe = items.first()
composable(route = DetailListAppScreen.List.name) {
RecipesListScreen(
isLarge = isLarge,
items = items,
onClick = { recipe ->
currentRecipe = recipe
navController.navigate(DetailListAppScreen.Details.name)
})
}
composable(route = DetailListAppScreen.Details.name) {
RecipeDetails(
isLarge = isLarge,
sensorManager = sensorManager,
recipe = currentRecipe,
goBack = { navController.popBackStack() }
)
}
}

View file

@ -1,15 +1,61 @@
package com.menagerie.ophelia.HomeScreen
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.menagerie.ophelia.ui.theme.OpheliaTheme
@Composable
fun HomeScreen() {
Scaffold {
Column {
fun HomeScreen(
onClick: () -> Unit,
) {
val text = if(true) "Get Started" else "Welcome Back"
HeaderText(text, onClick)
}
@Composable
fun HeaderText(
text: String,
onClick: () -> Unit
) {
Box(
modifier = Modifier
.fillMaxSize()
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp)
.align(Alignment.Center),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Spacer(modifier = Modifier.weight(1f))
Text (
text = "Ophelia",
style = MaterialTheme.typography.h1
)
Text (
text = "A Polycule Pocket-dex!"
)
Spacer(modifier = Modifier.weight(1f))
Button(
modifier = Modifier.padding(12.dp),
onClick = onClick
) {
Text(text = text)
}
}
}
}

View file

@ -0,0 +1,32 @@
package com.menagerie.ophelia.HomeScreen
import androidx.compose.foundation.layout.Column
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@Composable
fun MainMenu(
modifier: Modifier = Modifier,
onPolyClick: () -> Unit,
onUserBioClick: () -> Unit,
onCookBookClick: () -> Unit,
) {
Scaffold {
Column {
Button(onClick = onPolyClick){
Text(text = "Your Polycule - TEMP")
}
Button(onClick = onCookBookClick){
Text(text = "Your Recipes - TEMP")
}
Button(onClick = onUserBioClick){
Text(text = "You - TEMP")
}
}
}
}

View file

@ -0,0 +1,60 @@
package com.menagerie.ophelia.HomeScreen
import androidx.compose.foundation.layout.Column
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
val themeKey = stringPreferencesKey("theme")
enum class Theme {
Auto,
Light,
Dark
}
@Composable
fun Settings(prefs: DataStore<Preferences>) {
val currentThemeString by prefs
.data
.map {
it[themeKey] ?: Theme.Dark.name
}
.collectAsState(Theme.Dark.name)
var currentTheme = Theme.valueOf(currentThemeString)
val onThemeToggle = {
currentTheme = when (currentTheme) {
Theme.Auto -> Theme.Light
Theme.Light -> Theme.Dark
Theme.Dark -> Theme.Auto
}
}
val scope = rememberCoroutineScope()
Scaffold {
Column {
Button(onClick = {
scope.launch {
onThemeToggle.invoke()
prefs.edit {
it[themeKey] = currentTheme.name
}
}
}) {
Text(currentThemeString)
}
}
}
}

View file

@ -0,0 +1,14 @@
package com.menagerie.ophelia
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import okio.Path.Companion.toPath
fun createDataStore(producePath: () -> String): DataStore<Preferences> {
return PreferenceDataStoreFactory.createWithPath(
produceFile = { producePath().toPath() }
)
}
internal const val DATA_STORE_FILE_NAME = "prefs.preferences_pb"

View file

@ -1,13 +1,55 @@
package com.menagerie.ophelia
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import androidx.datastore.preferences.core.stringPreferencesKey
import com.menagerie.ophelia.ui.theme.OpheliaTheme
import kotlinx.coroutines.flow.map
import org.jetbrains.compose.resources.ExperimentalResourceApi
fun main() = application {
Window(
onCloseRequest = ::exitApplication,
title = "Ophelia",
) {
App(null, true)
enum class Theme {
Auto,
Light,
Dark
}
val themeKey = stringPreferencesKey("theme")
@OptIn(ExperimentalResourceApi::class)
fun main() {
val prefs = createDataStore{
"settings/files/$DATA_STORE_FILE_NAME"
}
application {
Window(
onCloseRequest = ::exitApplication,
title = "Ophelia",
) {
val currentThemeString by prefs
.data
.map {
it[themeKey] ?: Theme.Dark.name
}
.collectAsState(Theme.Dark.name)
val currentTheme = Theme.valueOf(currentThemeString)
val isDarkTheme: Boolean? = when (currentTheme) {
Theme.Dark -> true
Theme.Light -> false
Theme.Auto -> null
}
OpheliaTheme(isDarkTheme ?: isSystemInDarkTheme()) {
App(
sensorManager = null,
isLarge = true,
prefs = prefs
)
}
}
}
}

View file

@ -1,7 +0,0 @@
package com.menagerie.ophelia
class WasmPlatform: Platform {
override val name: String = "Web with Kotlin/Wasm"
}
actual fun getPlatform(): Platform = WasmPlatform()

View file

@ -1,12 +0,0 @@
package com.menagerie.ophelia
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.window.ComposeViewport
import kotlinx.browser.document
@OptIn(ExperimentalComposeUiApi::class)
fun main() {
ComposeViewport(document.body!!) {
App(null, true)
}
}

View file

@ -1,12 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ophelia</title>
<link type="text/css" rel="stylesheet" href="styles.css">
<script type="application/javascript" src="composeApp.js"></script>
</head>
<body>
</body>
</html>

View file

@ -1,7 +0,0 @@
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}

View file

@ -20,6 +20,7 @@ material3Desktop = "1.3.1"
uiTextGoogleFonts = "1.7.5"
core = "1.15.0"
navigation-compose = "2.7.0-alpha07"
datastore = "1.1.1"
[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
@ -39,6 +40,8 @@ androidx-material3-android = { group = "androidx.compose.material3", name = "mat
androidx-material3-desktop = { group = "androidx.compose.material3", name = "material3-desktop", version.ref = "material3Desktop" }
androidx-ui-text-google-fonts = { group = "androidx.compose.ui", name = "ui-text-google-fonts", version.ref = "uiTextGoogleFonts" }
androidx-core = { group = "androidx.core", name = "core", version.ref = "core" }
datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" }
datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" }
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }