diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index af8d140..c2bbdee 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -1,25 +1,31 @@ import org.jetbrains.compose.desktop.application.dsl.TargetFormat -import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi -import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig plugins { alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.androidApplication) alias(libs.plugins.composeMultiplatform) alias(libs.plugins.composeCompiler) + alias(libs.plugins.sqlDelight) } kotlin { androidTarget { - @OptIn(ExperimentalKotlinGradlePluginApi::class) - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) + compilations.all { + kotlinOptions { + jvmTarget = "21" + } } } jvm("desktop") + + sqldelight{ + databases { + create("PolyculeDatabase") { + packageName = "com.menagerie.ophelia" + } + } + } sourceSets { val desktopMain by getting @@ -28,6 +34,7 @@ kotlin { implementation(compose.preview) implementation(libs.androidx.activity.compose) implementation(libs.androidx.material3.android) + implementation(libs.sqldelight.android) } commonMain.dependencies { implementation(compose.runtime) @@ -43,11 +50,13 @@ kotlin { implementation("org.jetbrains.androidx.navigation:navigation-compose:2.7.0-alpha07") api(libs.datastore.preferences) api(libs.datastore) + implementation(libs.sqldelight.coroutines) } desktopMain.dependencies { implementation(compose.desktop.currentOs) implementation(libs.kotlinx.coroutines.swing) implementation(libs.androidx.material3.desktop) + implementation(libs.sqldelight.jvm) } } } @@ -74,8 +83,8 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } } dependencies { diff --git a/composeApp/src/androidMain/kotlin/AndroidAppModule.kt b/composeApp/src/androidMain/kotlin/AndroidAppModule.kt new file mode 100644 index 0000000..f87ff77 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/AndroidAppModule.kt @@ -0,0 +1,17 @@ +import android.content.Context +import com.menagerie.ophelia.AppModule +import com.menagerie.ophelia.DatabaseDriverFactory +import com.menagerie.ophelia.PolyculeDatabase +import com.menagerie.ophelia.data.user.UserDataSource + +class AndroidAppModule( + private val context: Context, +) : AppModule { + private val db by lazy { + PolyculeDatabase( + driver = DatabaseDriverFactory(context).create() + ) + } + + override fun provideUserDataSource() = UserDataSource(db) +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/menagerie/ophelia/DatabaseDriverFactory.android.kt b/composeApp/src/androidMain/kotlin/com/menagerie/ophelia/DatabaseDriverFactory.android.kt new file mode 100644 index 0000000..c6faba3 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/menagerie/ophelia/DatabaseDriverFactory.android.kt @@ -0,0 +1,10 @@ +package com.menagerie.ophelia + +import android.content.Context +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.android.AndroidSqliteDriver + +actual class DatabaseDriverFactory(private val context: Context) { + actual fun create(): SqlDriver = + AndroidSqliteDriver(PolyculeDatabase.Schema, context, "POLYCULE_DATABASE") +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/menagerie/ophelia/MainActivity.kt b/composeApp/src/androidMain/kotlin/com/menagerie/ophelia/MainActivity.kt index a2f00f9..24fe342 100644 --- a/composeApp/src/androidMain/kotlin/com/menagerie/ophelia/MainActivity.kt +++ b/composeApp/src/androidMain/kotlin/com/menagerie/ophelia/MainActivity.kt @@ -12,13 +12,12 @@ class MainActivity : ComponentActivity() { WindowCompat.setDecorFitsSystemWindows(window, false) - setContent { - MainView( + MainView( prefs = remember { createDataStore(application) } - ) + ) } } } diff --git a/composeApp/src/androidMain/res/values/strings.xml b/composeApp/src/androidMain/res/values/strings.xml index 69d8175..afc421d 100644 --- a/composeApp/src/androidMain/res/values/strings.xml +++ b/composeApp/src/androidMain/res/values/strings.xml @@ -1,3 +1,4 @@ Ophelia + \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/font/header_font.xml b/composeApp/src/commonMain/composeResources/font/header_font.xml new file mode 100644 index 0000000..348b911 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/font/header_font.xml @@ -0,0 +1,8 @@ + + + + diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml new file mode 100644 index 0000000..afb796a --- /dev/null +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -0,0 +1,71 @@ + + + Back + A Polycule Pocket-dex + Get Started + Welcome Back + + ##Routes + polyculeHome + home + + ##Bio + Add Bio + Edit Bio + Finish + Bio Loaded + Open context menu + Name + New Bio Added! + Bio Edited! + Welcome Aboard! + + + ##Tags + Monogamous + Plural + Furry + Therian + BDSM + Alcohol + Weed + Asexual + Transgender + + ##Ophelia + Ophelia Says + What do you think; looks like you? + + Hi! + I'm Ophelia, a Polycule companion system designed to help you keep track of your metas, reference a charter, and find things in common with your polycule. + I'm meant to work dynamically with how polyamory works for you, so what all those words mean will be something we work out together later. + First, I need to get to know "you". + Click Go to get started. + + + I'm Ophelia, what's your name? + Your Name is how you'll be addressed in the app, and is the primary way you'll be referred to in polycules. + But don't worry; your name, like everything about you, can be changed at any time! + + "Nice to meet you,%1$s! + + Personal Pronouns are words used to refer to a person without always using their name. + Since Pronouns are a personal choice, I want to leave them as open as possible for you! + We're dealing with four Personal Pronouns, and using mine as an example: She (subjective) / Her (objective) / Hers (possessive) / Herself (reflexive) + Your Pronouns will be used when referring to you in specific contexts, and are displayed alongside your name in most contexts. + + + Tags are a way to highlight important things about yourself. + They also lead to more detailed parts of you bio, where you can flesh out specific parts of your bio. + + + + Great! + Now, there's just one more thing! We need to create a polycule for you. A "polycule" for our purposes is defined as a group of people with a shared relationship process. + A person may be in a polycule with any number of people, including just themselves, and in any number of polycules. + All we need to get started is a name: + + + ##Helpers + Go + \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/App.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/App.kt index 5b1950e..9eed838 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/App.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/App.kt @@ -2,7 +2,9 @@ package com.menagerie.ophelia import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.material3.Scaffold @@ -18,14 +20,19 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -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.view.HomeScreen.HomeScreen +import com.menagerie.ophelia.view.HomeScreen.MainMenu +import com.menagerie.ophelia.view.HomeScreen.Settings +import com.menagerie.ophelia.view.newUser.AddNameScreen +import com.menagerie.ophelia.view.recipe.model.Recipe +import com.menagerie.ophelia.view.recipe.model.recipesList +import com.menagerie.ophelia.view.recipe.recipesdetails.RecipeDetails +import com.menagerie.ophelia.view.recipe.recipeslist.RecipesListScreen +import com.menagerie.ophelia.view.tags.model.Tag +import com.menagerie.ophelia.view.tags.tagsdetails.TagsDetails +import com.menagerie.ophelia.view.tags.tagslist.TagsListScreen +import com.menagerie.ophelia.view.newUser.NewUserStartScreen enum class DetailListAppScreen { List, @@ -47,8 +54,6 @@ fun App( sensorManager = sensorManager, prefs = prefs ) - - } @Composable @@ -59,38 +64,58 @@ fun OpheliaNavHost( prefs: DataStore ){ val items by remember { mutableStateOf(recipesList) } - var show by remember { mutableStateOf(false) } + var showBar by remember { mutableStateOf(false) } Scaffold { Column { //TODO : replace this with a composable task bar - if (show) { - Row { - Button( - onClick = { - navController.popBackStack() + //TODO : replace row with a lazyGrid for better mobile support + if (showBar) { + + val listState = rememberLazyGridState() + LazyVerticalGrid( + state = listState, columns = GridCells.Fixed(if (isLarge) 3 else 1) + ) { + item { + Button( + onClick = { + navController.popBackStack() + } + ) { + Text(text = "Back - TEMP") } - ) { - Text(text = "Back - TEMP") } - Button( - onClick = { - navController.navigate("mainMenu") + item { + Button( + onClick = { + navController.navigate("mainMenu") + } + ) { + Text(text = "Home - TEMP") } - ) { - Text(text = "Home - TEMP") } - Button( - onClick = { - navController.navigate("settings") + item { + Button( + onClick = { + navController.navigate("settings") + } + ) { + Text(text = "Settings - TEMP") } - ) { - Text(text = "Settings - TEMP") } - Button(onClick = { - //TODO : This should take the user to their bio page - }){ - Text(text = "You - TEMP") + item { + Button(onClick = { + //TODO : This should take the user to their bio page + }) { + Text(text = "You - TEMP") + } + } + item { + Button(onClick = { + navController.navigate("dex") + }) { + Text(text = "Encyclopedia - TEMP") + } } } } @@ -106,15 +131,17 @@ fun OpheliaNavHost( isLarge = isLarge, sensorManager = sensorManager ) + welcomeGraph(navController) composable("home") { + showBar = false HomeScreen { - show = true - navController.navigate("mainMenu") + navController.navigate("newUserStart") } } composable("mainMenu") { + showBar = true MainMenu( onPolyClick = { //TODO : This should take the user to their polycule's main page @@ -137,13 +164,14 @@ fun OpheliaNavHost( } } +//Graph for new user startup fun NavGraphBuilder.welcomeGraph(navController: NavHostController) { -// composable("newUserStart") { -// NewUserStartScreen { navController.navigate("newUserName") } -// } -// composable("newUserName") { -// AddNameScreen { navController.navigate("newUserPronouns") } -// } + composable("newUserStart") { + NewUserStartScreen { navController.navigate("mainMenu") } + } + composable("newUserName") { + AddNameScreen { navController.navigate("newUserPronouns") } + } // composable("newUserPronouns") { // AddPronounsScreen { navController.navigate("newUserTags") } // } @@ -161,6 +189,7 @@ fun NavGraphBuilder.welcomeGraph(navController: NavHostController) { // } } +//Graph for Polycule navigation fun NavGraphBuilder.polyculeGraph(navController: NavHostController) { // composable("polyculeHome") { // PolyculeHomeView( @@ -197,6 +226,7 @@ fun NavGraphBuilder.polyculeGraph(navController: NavHostController) { // } } +//Graph for your Cookbook fun NavGraphBuilder.recipeGraph( navController: NavHostController, items: List, @@ -220,7 +250,37 @@ fun NavGraphBuilder.recipeGraph( isLarge = isLarge, sensorManager = sensorManager, recipe = currentRecipe, - goBack = { navController.popBackStack() } ) } +} + +//Graph for the Tags +fun NavGraphBuilder.tagGraph( + navController: NavHostController, + items: List, + isLarge: Boolean, + sensorManager: SensorManager?, + ) { + + var currentTag = items.first() + + composable(route = DetailListAppScreen.List.name){ + TagsListScreen( + isLarge = isLarge, + items = items, + onClick = { tag -> + currentTag = tag + navController.navigate(DetailListAppScreen.Details.name) + } + ) + } + + composable(route = DetailListAppScreen.Details.name) { + TagsDetails( + isLarge = isLarge, + sensorManager = sensorManager, + tag = currentTag, + ) + } + } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/AppModule.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/AppModule.kt new file mode 100644 index 0000000..75ff34e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/AppModule.kt @@ -0,0 +1,7 @@ +package com.menagerie.ophelia + +import com.menagerie.ophelia.data.user.UserDataSource + +interface AppModule { + fun provideUserDataSource(): UserDataSource +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/DatabaseDriverFactory.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/DatabaseDriverFactory.kt new file mode 100644 index 0000000..46e7153 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/DatabaseDriverFactory.kt @@ -0,0 +1,7 @@ +package com.menagerie.ophelia + +import app.cash.sqldelight.db.SqlDriver + +expect class DatabaseDriverFactory { + fun create(): SqlDriver +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/data/user/DataSource.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/data/user/DataSource.kt new file mode 100644 index 0000000..b9b4efb --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/data/user/DataSource.kt @@ -0,0 +1,24 @@ +package com.menagerie.ophelia.data.user + +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import com.menagerie.ophelia.PolyculeDatabase +import kotlinx.coroutines.Dispatchers + +class UserDataSource(db: PolyculeDatabase) { + private val queries = db.userQueries + + fun insert(id: Long?, name: String) { + queries.insert(id = id, name = name) + } + + fun getAll() = queries.getAll().asFlow().mapToList(Dispatchers.IO) + + fun update(id: Long, name: String) { + queries.updateName(id = id, name = name) + } + + fun delete(id: Long) { + queries.delete(id = id) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/RecipeDetailsLarge.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/RecipeDetailsLarge.kt deleted file mode 100644 index 414b6de..0000000 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/RecipeDetailsLarge.kt +++ /dev/null @@ -1,270 +0,0 @@ -package com.menagerie.ophelia.recipesdetails - -import androidx.compose.animation.core.animateIntOffsetAsState -import androidx.compose.animation.core.tween -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material3.Card -import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.blur -import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.rotate -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.input.nestedscroll.NestedScrollSource -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.input.pointer.PointerEventType -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.IntSize -import androidx.compose.ui.unit.Velocity -import androidx.compose.ui.unit.dp -import com.menagerie.ophelia.model.Recipe -import com.menagerie.ophelia.sensor.Listener -import com.menagerie.ophelia.sensor.SensorData -import com.menagerie.ophelia.sensor.SensorManager -import org.jetbrains.compose.resources.painterResource -import kotlin.math.PI -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import com.menagerie.ophelia.ui.theme.orangeDark -import com.menagerie.ophelia.ui.theme.sugar -import com.menagerie.ophelia.ui.theme.yellow - -@Composable -fun RecipeDetailsLarge( - recipe: Recipe, - goBack: () -> Unit, - sensorManager: SensorManager?, -) { - val imageRotation = remember { mutableStateOf(0) } - val sensorDataLive = remember { mutableStateOf(SensorData(0.0f, 0.0f)) } - val roll by derivedStateOf { (sensorDataLive.value.roll * 20).coerceIn(-4f, 4f) } - val pitch by derivedStateOf { (sensorDataLive.value.pitch * 20).coerceIn(-4f, 4f) } - - val tweenDuration = 300 - - sensorManager?.registerListener(object : Listener { - override fun onUpdate(sensorData: SensorData) { - sensorDataLive.value = sensorData - } - }) - - val backgroundShadowOffset = animateIntOffsetAsState( - targetValue = IntOffset((roll * 6f).toInt(), (pitch * 6f).toInt()), - animationSpec = tween(tweenDuration) - ) - val backgroundImageOffset = animateIntOffsetAsState( - targetValue = IntOffset(-roll.toInt(), pitch.toInt()), animationSpec = tween(tweenDuration) - ) - - val nestedScrollConnection = remember { - object : NestedScrollConnection { - override fun onPreScroll( - available: Offset, source: NestedScrollSource - ): Offset { - imageRotation.value += (available.y * 0.5).toInt() - return Offset.Zero - } - - override fun onPostScroll( - consumed: Offset, available: Offset, source: NestedScrollSource - ): Offset { - val delta = available.y - imageRotation.value += ((delta * PI / 180) * 10).toInt() - return super.onPostScroll(consumed, available, source) - } - - override suspend fun onPreFling(available: Velocity): Velocity { - imageRotation.value += available.y.toInt() - return super.onPreFling(available) - } - } - } - - Box( - modifier = Modifier.fillMaxSize() - .background(if (recipe.bgColor == sugar) yellow else sugar) - ) { - val size = mutableStateOf(IntSize(0, 0)) - Row { - Box(modifier = Modifier.fillMaxSize().onGloballyPositioned { - size.value = it.size - }.weight(1f).pointerInput(Unit) { - awaitPointerEventScope { - while (true) { - val event = awaitPointerEvent() - // on every relayout Compose will send synthetic Move event, - // so we skip it to avoid event spam - if (event.type == PointerEventType.Move) { - val position = event.changes.first().position - sensorDataLive.value = SensorData( - roll = position.x - size.value.height / 4, - pitch = (position.y - size.value.width / 4) - ) - } - } - - } - }) { - Card( - modifier = Modifier - .clip(RoundedCornerShape(topEnd = 35.dp, bottomEnd = 35.dp)), - shape = RoundedCornerShape( - topEnd = 35.dp, - bottomEnd = 35.dp, - ), - ) { - // background image + its shadow - Box(modifier = Modifier.fillMaxSize().background(recipe.bgColor)) { - if (recipe.bgImageLarge != null) { - val painter = painterResource(recipe.bgImageLarge) - Image(painter = painter, - contentDescription = null, - contentScale = ContentScale.Crop, - modifier = Modifier.offset { - backgroundShadowOffset.value - }.graphicsLayer { - scaleX = 1.050f - scaleY = 1.050f - }.blur(radius = 8.dp), - colorFilter = ColorFilter.tint( - orangeDark.copy(alpha = 0.3f) - ) - ) - - Image( - painter = painter, - contentDescription = null, - contentScale = ContentScale.Crop, - modifier = Modifier.background( - Color.Transparent, - RoundedCornerShape( - bottomEnd = 35.dp, bottomStart = 35.dp - ), - ).offset { - backgroundImageOffset.value - }.graphicsLayer { - shadowElevation = 8f - scaleX = 1.050f - scaleY = 1.050f - }, - ) - } - - // image shadows and image - Box( - modifier = Modifier.aspectRatio(1f).padding(32.dp) - .align(Alignment.Center) - ) { - Box(modifier = Modifier.padding(32.dp)) { - Image( - painter = painterResource(recipe.image), - contentDescription = null, - modifier = Modifier.aspectRatio(1f).align(Alignment.Center) - .padding(16.dp).rotate(imageRotation.value.toFloat()) - ) - } - } - } - } - } - - Box( - modifier = Modifier.fillMaxSize() - .background(if (recipe.bgColor == sugar) yellow else sugar) - .pointerInput(Unit) { - awaitPointerEventScope { - while (true) { - val event = awaitPointerEvent() - if (event.type == PointerEventType.Scroll) { - val position = event.changes.first().position - // on every relayout Compose will send synthetic Move event, - // so we skip it to avoid event spam - imageRotation.value = - (imageRotation.value + position.getDistance() - .toInt() * 0.010).toInt() - } - } - } - }.weight(1f) - ) { - val listState = rememberLazyListState() - - Box( - modifier = Modifier.fillMaxSize() - ) { - LazyColumn( - contentPadding = PaddingValues(64.dp), - userScrollEnabled = true, - verticalArrangement = Arrangement.Absolute.spacedBy(16.dp), - modifier = Modifier.fillMaxSize().nestedScroll(nestedScrollConnection), - state = listState - ) { - StepsAndDetails( - recipe = recipe - ) - } - } - } - } - - BackButton(goBack) - - } -} - -@Composable -fun BackButton(goBack: () -> Unit) { - Box( - modifier = Modifier.padding(start = 32.dp, top = 16.dp).clip( - RoundedCornerShape(50) - ).clickable { - goBack() - }.background( - color = Color.Black, shape = RoundedCornerShape(50) - ).padding(top = 8.dp, bottom = 8.dp, start = 16.dp, end = 16.dp) - ) { - Row(verticalAlignment = Alignment.CenterVertically) { - Icon( - imageVector = Icons.AutoMirrored.Default.ArrowBack, - contentDescription = null, - tint = Color.White, - modifier = Modifier.size(20.dp) - ) - Spacer(Modifier.padding(start = 8.dp)) - Text( - text = "Back to Recipes", - color = Color.White - ) - } - } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/RecipeDetailsSmall.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/RecipeDetailsSmall.kt deleted file mode 100644 index 780fd5b..0000000 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/RecipeDetailsSmall.kt +++ /dev/null @@ -1,252 +0,0 @@ -package com.menagerie.ophelia.recipesdetails - -import androidx.compose.animation.core.animateIntOffsetAsState -import androidx.compose.animation.core.tween -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha -import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.shadow -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.input.nestedscroll.NestedScrollSource -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.Velocity -import androidx.compose.ui.unit.dp -import com.menagerie.ophelia.model.Recipe -import com.menagerie.ophelia.ui.theme.sugar -import com.menagerie.ophelia.ui.theme.yellow -import com.menagerie.ophelia.ui.theme.orangeDark -import com.menagerie.ophelia.sensor.Listener -import com.menagerie.ophelia.sensor.SensorData -import com.menagerie.ophelia.sensor.SensorManager -import kotlin.math.PI -import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.systemBars -import androidx.compose.foundation.layout.windowInsetsPadding -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.ui.draw.blur -import androidx.compose.ui.graphics.graphicsLayer -import org.jetbrains.compose.resources.painterResource -import androidx.compose.ui.draw.* -import androidx.compose.material3.Icon -import androidx.compose.ui.layout.ContentScale - -@OptIn(ExperimentalFoundationApi::class) -@Composable -fun RecipeDetailsSmall( - recipe: Recipe, - goBack: () -> Unit, - sensorManager: SensorManager?, -) { - val imageRotation = remember { mutableStateOf(0) } - val sensorDataLive = remember { mutableStateOf(SensorData(0.0f, 0.0f)) } - val roll by derivedStateOf { (sensorDataLive.value.roll).coerceIn(-3f, 3f) } - val pitch by derivedStateOf { (sensorDataLive.value.pitch).coerceIn(-2f, 2f) } - - val tweenDuration = 300 - - sensorManager?.registerListener(object : Listener { - override fun onUpdate(sensorData: SensorData) { - sensorDataLive.value = sensorData - } - }) - - val backgroundShadowOffset = animateIntOffsetAsState( - targetValue = IntOffset((roll * 6f).toInt(), (pitch * 6f).toInt()), - animationSpec = tween(tweenDuration) - ) - val backgroundImageOffset = animateIntOffsetAsState( - targetValue = IntOffset(-roll.toInt(), pitch.toInt()), animationSpec = tween(tweenDuration) - ) - - val toolbarOffsetHeightPx = remember { mutableStateOf(340f) } - val nestedScrollConnection = remember { - object : NestedScrollConnection { - override fun onPreScroll( - available: Offset, source: NestedScrollSource - ): Offset { - val delta = available.y - val newOffset = toolbarOffsetHeightPx.value + delta - toolbarOffsetHeightPx.value = newOffset.coerceIn(0f, 340f) - imageRotation.value += (available.y * 0.5).toInt() - return Offset.Zero - } - - override fun onPostScroll( - consumed: Offset, available: Offset, source: NestedScrollSource - ): Offset { - val delta = available.y - imageRotation.value += ((delta * PI / 180) * 10).toInt() - return super.onPostScroll(consumed, available, source) - } - - override suspend fun onPreFling(available: Velocity): Velocity { - imageRotation.value += available.y.toInt() - return super.onPreFling(available) - } - } - } - - val candidateHeight = maxOf(toolbarOffsetHeightPx.value, 300f) - val listState = rememberLazyListState() - val (fraction, setFraction) = remember { mutableStateOf(1f) } - - - Box( - modifier = Modifier.fillMaxSize() - .background(color = if (recipe.bgColor == sugar) yellow else sugar) - ) { - LazyColumn( - modifier = Modifier.fillMaxSize().nestedScroll(nestedScrollConnection), - state = listState - ) { - - stickyHeader { - Box( - modifier = Modifier.shadow( - elevation = if (fraction < 0.05) { - ((1 - fraction) * 16).dp - } else 0.dp, - clip = false, - ambientColor = Color(0xffCE5A01).copy(if (fraction < 0.1) 1f - fraction else 0f), - spotColor = Color(0xffCE5A01).copy(if (fraction < 0.1) 1f - fraction else 0f) - ).alpha(if (fraction < 0.2) 1f - fraction else 0f).fillMaxWidth() - .background( - recipe.bgColor, - RoundedCornerShape( - bottomEnd = 35.dp, bottomStart = 35.dp - ), - ).clip(RoundedCornerShape(bottomEnd = 35.dp, bottomStart = 35.dp)) - .height(candidateHeight.dp) - ) { - Box( - modifier = Modifier.fillMaxSize() - ) { - - //bg image and shadow - recipe.bgImage?.let { - Image(painter = painterResource(it), - contentDescription = null, - contentScale = ContentScale.FillWidth, - modifier = Modifier.offset { - backgroundShadowOffset.value - }.graphicsLayer { - scaleX = 1.050f - scaleY = 1.050f - }.blur(radius = 8.dp), - colorFilter = androidx.compose.ui.graphics.ColorFilter.tint( - orangeDark.copy(alpha = 0.3f) - ) - ) - Image(painter = painterResource(it), - contentDescription = null, - contentScale = ContentScale.FillWidth, - modifier = Modifier.background( - Color.Transparent, - RoundedCornerShape( - bottomEnd = 35.dp, bottomStart = 35.dp - ), - ).offset { - backgroundImageOffset.value - }.graphicsLayer { - shadowElevation = 8f - scaleX = 1.050f - scaleY = 1.050f - }, - alpha = 1 - fraction - ) - } - - Box( - modifier = Modifier.aspectRatio(1f).align(Alignment.Center) - ) { - Box { - //image rounded shadow -// Box(modifier = Modifier.offset { -// IntOffset( -// x = (roll * 2).dp.roundToPx(), -// y = -(pitch * 2).dp.roundToPx() -// ) -// }) { -// -// Image( -// painter = painterResource(recipe.image), -// contentDescription = null, -// modifier = Modifier.aspectRatio(1f) -// .align(Alignment.Center).padding(16.dp).shadow( -// elevation = 16.dp, -// shape = CircleShape, -// clip = false, -// ambientColor = Color.Red, -// spotColor = Color.Red, -// ), -// colorFilter = androidx.compose.ui.graphics.ColorFilter.tint( -// orangeDark.copy(alpha = 0.0f) -// ) -// ) -// } - - Image( - painter = painterResource(recipe.image), - contentDescription = null, - modifier = Modifier.aspectRatio(1f).align(Alignment.Center) - .windowInsetsPadding(WindowInsets.systemBars) - .padding(16.dp).rotate(imageRotation.value.toFloat()) - .background( - Color.Transparent, - CircleShape, - ) - ) - } - } - } - } - } - - StepsAndDetails( - recipe = recipe - ) - } - - Box(modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars).size(50.dp) - .padding(10.dp).alpha( - alpha = if (fraction <= 0) 1f else 0f, - ).background( - color = Color.Black, shape = RoundedCornerShape(50) - ).shadow(elevation = 16.dp).padding(5.dp).clickable { - goBack() - }) { - Icon( - imageVector = Icons.AutoMirrored.Default.ArrowBack, - contentDescription = null, - tint = recipe.bgColor, - modifier = Modifier.size(30.dp) - ) - } - } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/HomeScreen/Home.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/HomeScreen/Home.kt similarity index 94% rename from composeApp/src/commonMain/kotlin/com/menagerie/ophelia/HomeScreen/Home.kt rename to composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/HomeScreen/Home.kt index 8eed7f0..f84eb7e 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/HomeScreen/Home.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/HomeScreen/Home.kt @@ -1,4 +1,4 @@ -package com.menagerie.ophelia.HomeScreen +package com.menagerie.ophelia.view.HomeScreen import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -14,7 +14,6 @@ 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( @@ -56,6 +55,7 @@ fun HeaderText( ) { Text(text = text) } + Spacer(modifier = Modifier.weight(1f)) } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/HomeScreen/MainMenu.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/HomeScreen/MainMenu.kt similarity index 94% rename from composeApp/src/commonMain/kotlin/com/menagerie/ophelia/HomeScreen/MainMenu.kt rename to composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/HomeScreen/MainMenu.kt index 536bd12..611b8c9 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/HomeScreen/MainMenu.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/HomeScreen/MainMenu.kt @@ -1,4 +1,4 @@ -package com.menagerie.ophelia.HomeScreen +package com.menagerie.ophelia.view.HomeScreen import androidx.compose.foundation.layout.Column import androidx.compose.material.Button diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/HomeScreen/Settings.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/HomeScreen/Settings.kt similarity index 97% rename from composeApp/src/commonMain/kotlin/com/menagerie/ophelia/HomeScreen/Settings.kt rename to composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/HomeScreen/Settings.kt index 7bc369a..76aa387 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/HomeScreen/Settings.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/HomeScreen/Settings.kt @@ -1,4 +1,4 @@ -package com.menagerie.ophelia.HomeScreen +package com.menagerie.ophelia.view.HomeScreen import androidx.compose.foundation.layout.Column import androidx.compose.material.Button diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/Ophelia.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/Ophelia.kt new file mode 100644 index 0000000..7bfe541 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/Ophelia.kt @@ -0,0 +1,73 @@ +package com.menagerie.ophelia.view + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.unit.dp +import ophelia.composeapp.generated.resources.Res +import ophelia.composeapp.generated.resources.header_font +import ophelia.composeapp.generated.resources.ophelia +import ophelia.composeapp.generated.resources.welcome_blurb +import org.jetbrains.compose.resources.StringArrayResource +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.resources.stringArrayResource + + +//@Composable +//fun OpheliaSpanStyle(): SpanStyle { +// val fontFamily = (Res.font.header_font) +// +// return SpanStyle( +// letterSpacing = MaterialTheme.typography.bodyLarge.letterSpacing, +// fontFamily = fontFamily, +// fontSize = MaterialTheme.typography.bodyLarge.fontSize) +//} + + +@Composable +fun OpheliaFace( + modifier: Modifier +) { + Image(painter = painterResource(Res.drawable.ophelia), + contentDescription = null, + modifier = modifier + ) +} + +@Composable +fun OpheliaWelcome( + modifier: Modifier +) { + Box( + modifier = modifier + .fillMaxSize() + .padding(24.dp) + ) { + Text( + text = OpheliaSaysArray(Res.array.welcome_blurb) + ) + } +} + +@Composable +fun OpheliaSaysArray( + res: StringArrayResource, + separator: String = "\n\n" +): AnnotatedString { + return buildAnnotatedString { + //pushStyle(OpheliaSpanStyle()) + append(stringArrayResource(res).joinToString(separator = separator)) + toAnnotatedString() + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipeImage.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/common/list/ListItemImage.kt similarity index 91% rename from composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipeImage.kt rename to composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/common/list/ListItemImage.kt index a50d040..111b556 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipeImage.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/common/list/ListItemImage.kt @@ -1,4 +1,4 @@ -package com.menagerie.ophelia.recipeslist +package com.menagerie.ophelia.view.common.list import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -14,7 +14,7 @@ import org.jetbrains.compose.resources.DrawableResource import org.jetbrains.compose.resources.painterResource @Composable -fun RecipeImage(imageBitmap: DrawableResource, modifier: Modifier){ +fun ListImage(imageBitmap: DrawableResource, modifier: Modifier){ Box(modifier = modifier) { Box( modifier = modifier diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipeListItemImageWrapper.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/common/list/ListItemImageWrapper.kt similarity index 83% rename from composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipeListItemImageWrapper.kt rename to composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/common/list/ListItemImageWrapper.kt index 5a45038..003a21f 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipeListItemImageWrapper.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/common/list/ListItemImageWrapper.kt @@ -1,4 +1,4 @@ -package com.menagerie.ophelia.recipeslist +package com.menagerie.ophelia.view.common.list import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.FastOutSlowInEasing @@ -18,14 +18,14 @@ import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.unit.dp @Composable -fun RecipeListItemImageWrapper( +fun ListItemImageWrapper( modifier: Modifier, child: @Composable () -> Unit, ) { val animationDuration = 700 val scale = remember { Animatable(0.3f) } - val rotation = remember {Animatable(20f)} - val offset = remember {Animatable(0f)} + val rotation = remember { Animatable(20f) } + val offset = remember { Animatable(0f) } LaunchedEffect(Unit) { scale.animateTo( @@ -47,13 +47,13 @@ fun RecipeListItemImageWrapper( } LaunchedEffect(Unit) { - offset.animateTo( - 60f, - animationSpec = tween( - durationMillis = animationDuration / 2, - easing = FastOutSlowInEasing + offset.animateTo( + 60f, + animationSpec = tween( + durationMillis = animationDuration / 2, + easing = FastOutSlowInEasing + ) ) - ) offset.animateTo( targetValue = 0f, animationSpec = spring( diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipeListItemWrapper.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/common/list/ListItemWrapper.kt similarity index 93% rename from composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipeListItemWrapper.kt rename to composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/common/list/ListItemWrapper.kt index 3091b95..8f2739d 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipeListItemWrapper.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/common/list/ListItemWrapper.kt @@ -1,4 +1,4 @@ -package com.menagerie.ophelia.recipeslist +package com.menagerie.ophelia.view.common.list import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.CubicBezierEasing @@ -11,13 +11,13 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer -const val perspectiveValue = 0.004 const val rotateX = 9f +//Wrapper for animating cards @Composable -fun RecipeListItemWrapper( +fun ListItemWrapper( child: @Composable () -> Unit, - scrollDirection: Boolean + scrollDirection: Boolean, ) { val scaleAnimatable = remember { Animatable(initialValue = 0.75f) } val rotateXAnimatable = @@ -66,4 +66,5 @@ fun RecipeListItemWrapper( { child() } + } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/newUser/AddNameScreen.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/newUser/AddNameScreen.kt new file mode 100644 index 0000000..6918c32 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/newUser/AddNameScreen.kt @@ -0,0 +1,11 @@ +package com.menagerie.ophelia.view.newUser + +import androidx.compose.runtime.Composable + +@Composable +fun AddNameScreen( + onGo: () -> Unit, +) { + //val inputViewModel: BioListViewModel.InputBioViewModel() + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/newUser/NewUserStartScreen.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/newUser/NewUserStartScreen.kt new file mode 100644 index 0000000..16935fc --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/newUser/NewUserStartScreen.kt @@ -0,0 +1,46 @@ +package com.menagerie.ophelia.view.newUser + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +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.view.OpheliaFace +import com.menagerie.ophelia.view.OpheliaWelcome +import ophelia.composeapp.generated.resources.Res +import ophelia.composeapp.generated.resources.go +import org.jetbrains.compose.resources.stringResource + +@Composable +fun NewUserStartScreen( + onGo: () -> Unit = {}, +) { + Scaffold { + Column { + OpheliaFace( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(top = 12.dp) + ) + OpheliaWelcome( + modifier = Modifier.weight(1f) + ) + Button( + onClick = onGo, + modifier = Modifier + .padding(12.dp) + .align(Alignment.End) + ) { + Text(stringResource(Res.string.go)) + } + Spacer( + modifier = Modifier.padding(12.dp) + ) + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/model/Recipe.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/model/Recipe.kt similarity index 92% rename from composeApp/src/commonMain/kotlin/com/menagerie/ophelia/model/Recipe.kt rename to composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/model/Recipe.kt index d8245dd..61888eb 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/model/Recipe.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/model/Recipe.kt @@ -1,4 +1,4 @@ -package com.menagerie.ophelia.model +package com.menagerie.ophelia.view.recipe.model import androidx.compose.ui.graphics.Color import org.jetbrains.compose.resources.DrawableResource diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/model/exampleRecipes.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/model/exampleRecipes.kt similarity index 99% rename from composeApp/src/commonMain/kotlin/com/menagerie/ophelia/model/exampleRecipes.kt rename to composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/model/exampleRecipes.kt index f2755cb..62ad743 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/model/exampleRecipes.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/model/exampleRecipes.kt @@ -1,4 +1,4 @@ -package com.menagerie.ophelia.model +package com.menagerie.ophelia.view.recipe.model import androidx.compose.ui.graphics.Color import com.menagerie.ophelia.ui.theme.green diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/IngredientItem.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/IngredientItem.kt similarity index 96% rename from composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/IngredientItem.kt rename to composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/IngredientItem.kt index 9d96849..5258b28 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/IngredientItem.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/IngredientItem.kt @@ -1,4 +1,4 @@ -package com.menagerie.ophelia.recipesdetails +package com.menagerie.ophelia.view.recipe.recipesdetails import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -20,7 +20,7 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.luminance import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import com.menagerie.ophelia.model.Recipe +import com.menagerie.ophelia.view.recipe.model.Recipe import ophelia.composeapp.generated.resources.Res import ophelia.composeapp.generated.resources.ophelia import org.jetbrains.compose.resources.ExperimentalResourceApi diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/InstructionItem.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/InstructionItem.kt similarity index 96% rename from composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/InstructionItem.kt rename to composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/InstructionItem.kt index fa0aaad..c1af6a9 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/InstructionItem.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/InstructionItem.kt @@ -1,4 +1,4 @@ -package com.menagerie.ophelia.recipesdetails +package com.menagerie.ophelia.view.recipe.recipesdetails import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -22,7 +22,7 @@ import androidx.compose.ui.text.style.LineHeightStyle import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.menagerie.ophelia.model.Recipe +import com.menagerie.ophelia.view.recipe.model.Recipe @Composable fun InstructionItem(recipe: Recipe, index: Int) { diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/RecipeDetails.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/RecipeDetails.kt similarity index 51% rename from composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/RecipeDetails.kt rename to composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/RecipeDetails.kt index cc8173c..0a9ffbe 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/RecipeDetails.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/RecipeDetails.kt @@ -1,28 +1,21 @@ -package com.menagerie.ophelia.recipesdetails +package com.menagerie.ophelia.view.recipe.recipesdetails -import androidx.compose.animation.AnimatedVisibilityScope -import androidx.compose.animation.ExperimentalSharedTransitionApi -import androidx.compose.animation.SharedTransitionScope import androidx.compose.runtime.Composable -import com.menagerie.ophelia.model.Recipe +import com.menagerie.ophelia.view.recipe.model.Recipe import com.menagerie.ophelia.sensor.SensorManager -@OptIn(ExperimentalSharedTransitionApi::class) @Composable fun RecipeDetails( recipe: Recipe, - goBack: () -> Unit, sensorManager: SensorManager?, isLarge: Boolean, ) { if (isLarge) RecipeDetailsLarge( recipe = recipe, - goBack = goBack, sensorManager = sensorManager ) else RecipeDetailsSmall( recipe = recipe, - goBack = goBack, sensorManager = sensorManager ) } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/RecipeDetailsLarge.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/RecipeDetailsLarge.kt new file mode 100644 index 0000000..9eebdf0 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/RecipeDetailsLarge.kt @@ -0,0 +1,238 @@ +package com.menagerie.ophelia.view.recipe.recipesdetails + +import androidx.compose.animation.core.animateIntOffsetAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.Card +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.blur +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.Velocity +import androidx.compose.ui.unit.dp +import com.menagerie.ophelia.view.recipe.model.Recipe +import com.menagerie.ophelia.sensor.Listener +import com.menagerie.ophelia.sensor.SensorData +import com.menagerie.ophelia.sensor.SensorManager +import com.menagerie.ophelia.ui.theme.orangeDark +import com.menagerie.ophelia.ui.theme.sugar +import com.menagerie.ophelia.ui.theme.yellow +import org.jetbrains.compose.resources.painterResource +import kotlin.math.PI + +@Composable +fun RecipeDetailsLarge( + recipe: Recipe, + sensorManager: SensorManager?, +) { + val imageRotation = remember { mutableStateOf(0) } + val sensorDataLive = remember { mutableStateOf(SensorData(0.0f, 0.0f)) } + val roll by derivedStateOf { (sensorDataLive.value.roll * 20).coerceIn(-4f, 4f) } + val pitch by derivedStateOf { (sensorDataLive.value.pitch * 20).coerceIn(-4f, 4f) } + + val tweenDuration = 300 + + sensorManager?.registerListener(object : Listener { + override fun onUpdate(sensorData: SensorData) { + sensorDataLive.value = sensorData + } + }) + + val backgroundShadowOffset = animateIntOffsetAsState( + targetValue = IntOffset((roll * 6f).toInt(), (pitch * 6f).toInt()), + animationSpec = tween(tweenDuration) + ) + val backgroundImageOffset = animateIntOffsetAsState( + targetValue = IntOffset(-roll.toInt(), pitch.toInt()), animationSpec = tween(tweenDuration) + ) + + val nestedScrollConnection = remember { + object : NestedScrollConnection { + override fun onPreScroll( + available: Offset, source: NestedScrollSource + ): Offset { + imageRotation.value += (available.y * 0.5).toInt() + return Offset.Zero + } + + override fun onPostScroll( + consumed: Offset, available: Offset, source: NestedScrollSource + ): Offset { + val delta = available.y + imageRotation.value += ((delta * PI / 180) * 10).toInt() + return super.onPostScroll(consumed, available, source) + } + + override suspend fun onPreFling(available: Velocity): Velocity { + imageRotation.value += available.y.toInt() + return super.onPreFling(available) + } + } + } + + Box( + modifier = Modifier.fillMaxSize() + .background(if (recipe.bgColor == sugar) yellow else sugar) + ) { + val size = mutableStateOf(IntSize(0, 0)) + Row { + Box(modifier = Modifier.fillMaxSize().onGloballyPositioned { + size.value = it.size + }.weight(1f).pointerInput(Unit) { + awaitPointerEventScope { + while (true) { + val event = awaitPointerEvent() + // on every relayout Compose will send synthetic Move event, + // so we skip it to avoid event spam + if (event.type == PointerEventType.Move) { + val position = event.changes.first().position + sensorDataLive.value = SensorData( + roll = position.x - size.value.height / 4, + pitch = (position.y - size.value.width / 4) + ) + } + } + + } + }) { + Card( + modifier = Modifier + .clip(RoundedCornerShape(topEnd = 35.dp, bottomEnd = 35.dp)), + shape = RoundedCornerShape( + topEnd = 35.dp, + bottomEnd = 35.dp, + ), + ) { + // background image + its shadow + Box(modifier = Modifier.fillMaxSize().background(recipe.bgColor)) { + if (recipe.bgImageLarge != null) { + val painter = painterResource(recipe.bgImageLarge) + Image(painter = painter, + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier.offset { + backgroundShadowOffset.value + }.graphicsLayer { + scaleX = 1.050f + scaleY = 1.050f + }.blur(radius = 8.dp), + colorFilter = ColorFilter.tint( + orangeDark.copy(alpha = 0.3f) + ) + ) + + Image( + painter = painter, + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier.background( + Color.Transparent, + RoundedCornerShape( + bottomEnd = 35.dp, bottomStart = 35.dp + ), + ).offset { + backgroundImageOffset.value + }.graphicsLayer { + shadowElevation = 8f + scaleX = 1.050f + scaleY = 1.050f + }, + ) + } + + // image shadows and image + Box( + modifier = Modifier.aspectRatio(1f).padding(32.dp) + .align(Alignment.Center) + ) { + Box(modifier = Modifier.padding(32.dp)) { + Image( + painter = painterResource(recipe.image), + contentDescription = null, + modifier = Modifier.aspectRatio(1f).align(Alignment.Center) + .padding(16.dp).rotate(imageRotation.value.toFloat()) + ) + } + } + } + } + } + + Box( + modifier = Modifier.fillMaxSize() + .background(if (recipe.bgColor == sugar) yellow else sugar) + .pointerInput(Unit) { + awaitPointerEventScope { + while (true) { + val event = awaitPointerEvent() + if (event.type == PointerEventType.Scroll) { + val position = event.changes.first().position + // on every relayout Compose will send synthetic Move event, + // so we skip it to avoid event spam + imageRotation.value = + (imageRotation.value + position.getDistance() + .toInt() * 0.010).toInt() + } + } + } + }.weight(1f) + ) { + val listState = rememberLazyListState() + + Box( + modifier = Modifier.fillMaxSize() + ) { + LazyColumn( + contentPadding = PaddingValues(64.dp), + userScrollEnabled = true, + verticalArrangement = Arrangement.Absolute.spacedBy(16.dp), + modifier = Modifier.fillMaxSize().nestedScroll(nestedScrollConnection), + state = listState + ) { + StepsAndDetails( + recipe = recipe + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/RecipeDetailsSmall.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/RecipeDetailsSmall.kt new file mode 100644 index 0000000..2558028 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/RecipeDetailsSmall.kt @@ -0,0 +1,210 @@ +package com.menagerie.ophelia.view.recipe.recipesdetails + +import androidx.compose.animation.core.animateIntOffsetAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.Velocity +import androidx.compose.ui.unit.dp +import com.menagerie.ophelia.view.recipe.model.Recipe +import com.menagerie.ophelia.ui.theme.sugar +import com.menagerie.ophelia.ui.theme.yellow +import com.menagerie.ophelia.ui.theme.orangeDark +import com.menagerie.ophelia.sensor.Listener +import com.menagerie.ophelia.sensor.SensorData +import com.menagerie.ophelia.sensor.SensorManager +import kotlin.math.PI +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.ui.draw.blur +import androidx.compose.ui.graphics.graphicsLayer +import org.jetbrains.compose.resources.painterResource +import androidx.compose.ui.draw.* +import androidx.compose.material3.Icon +import androidx.compose.ui.layout.ContentScale + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun RecipeDetailsSmall( + recipe: Recipe, + sensorManager: SensorManager?, +) { + val imageRotation = remember { mutableStateOf(0) } + val sensorDataLive = remember { mutableStateOf(SensorData(0.0f, 0.0f)) } + val roll by derivedStateOf { (sensorDataLive.value.roll).coerceIn(-3f, 3f) } + val pitch by derivedStateOf { (sensorDataLive.value.pitch).coerceIn(-2f, 2f) } + + val tweenDuration = 300 + + sensorManager?.registerListener(object : Listener { + override fun onUpdate(sensorData: SensorData) { + sensorDataLive.value = sensorData + } + }) + + val backgroundShadowOffset = animateIntOffsetAsState( + targetValue = IntOffset((roll * 6f).toInt(), (pitch * 6f).toInt()), + animationSpec = tween(tweenDuration) + ) + val backgroundImageOffset = animateIntOffsetAsState( + targetValue = IntOffset(-roll.toInt(), pitch.toInt()), animationSpec = tween(tweenDuration) + ) + + val toolbarOffsetHeightPx = remember { mutableStateOf(340f) } + val nestedScrollConnection = remember { + object : NestedScrollConnection { + override fun onPreScroll( + available: Offset, source: NestedScrollSource + ): Offset { + val delta = available.y + val newOffset = toolbarOffsetHeightPx.value + delta + toolbarOffsetHeightPx.value = newOffset.coerceIn(0f, 340f) + imageRotation.value += (available.y * 0.5).toInt() + return Offset.Zero + } + + override fun onPostScroll( + consumed: Offset, available: Offset, source: NestedScrollSource + ): Offset { + val delta = available.y + imageRotation.value += ((delta * PI / 180) * 10).toInt() + return super.onPostScroll(consumed, available, source) + } + + override suspend fun onPreFling(available: Velocity): Velocity { + imageRotation.value += available.y.toInt() + return super.onPreFling(available) + } + } + } + + val candidateHeight = maxOf(toolbarOffsetHeightPx.value, 300f) + val listState = rememberLazyListState() + val (fraction, setFraction) = remember { mutableStateOf(1f) } + + + Box( + modifier = Modifier.fillMaxSize() + .background(color = if (recipe.bgColor == sugar) yellow else sugar) + ) { + LazyColumn( + modifier = Modifier.fillMaxSize().nestedScroll(nestedScrollConnection), + state = listState + ) { + + stickyHeader { + Box( + modifier = Modifier.shadow( + elevation = if (fraction < 0.05) { + ((1 - fraction) * 16).dp + } else 0.dp, + clip = false, + ambientColor = Color(0xffCE5A01).copy(if (fraction < 0.1) 1f - fraction else 0f), + spotColor = Color(0xffCE5A01).copy(if (fraction < 0.1) 1f - fraction else 0f) + ).alpha(if (fraction < 0.2) 1f - fraction else 0f).fillMaxWidth() + .background( + recipe.bgColor, + RoundedCornerShape( + bottomEnd = 35.dp, bottomStart = 35.dp + ), + ).clip(RoundedCornerShape(bottomEnd = 35.dp, bottomStart = 35.dp)) + .height(candidateHeight.dp) + ) { + Box( + modifier = Modifier.fillMaxSize() + ) { + + //bg image and shadow + recipe.bgImage?.let { + Image(painter = painterResource(it), + contentDescription = null, + contentScale = ContentScale.FillWidth, + modifier = Modifier.offset { + backgroundShadowOffset.value + }.graphicsLayer { + scaleX = 1.050f + scaleY = 1.050f + }.blur(radius = 8.dp), + colorFilter = androidx.compose.ui.graphics.ColorFilter.tint( + orangeDark.copy(alpha = 0.3f) + ) + ) + Image(painter = painterResource(it), + contentDescription = null, + contentScale = ContentScale.FillWidth, + modifier = Modifier.background( + Color.Transparent, + RoundedCornerShape( + bottomEnd = 35.dp, bottomStart = 35.dp + ), + ).offset { + backgroundImageOffset.value + }.graphicsLayer { + shadowElevation = 8f + scaleX = 1.050f + scaleY = 1.050f + }, + alpha = 1 - fraction + ) + } + + Box( + modifier = Modifier.aspectRatio(1f).align(Alignment.Center) + ) { + Box { + Image( + painter = painterResource(recipe.image), + contentDescription = null, + modifier = Modifier.aspectRatio(1f).align(Alignment.Center) + .windowInsetsPadding(WindowInsets.systemBars) + .padding(16.dp).rotate(imageRotation.value.toFloat()) + .background( + Color.Transparent, + CircleShape, + ) + ) + } + } + } + } + } + + StepsAndDetails( + recipe = recipe + ) + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/StepsAndDetails.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/StepsAndDetails.kt similarity index 90% rename from composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/StepsAndDetails.kt rename to composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/StepsAndDetails.kt index 4e81c88..2fb0092 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipesdetails/StepsAndDetails.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipesdetails/StepsAndDetails.kt @@ -1,4 +1,4 @@ -package com.menagerie.ophelia.recipesdetails +package com.menagerie.ophelia.view.recipe.recipesdetails import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyListScope @@ -8,7 +8,7 @@ import androidx.compose.material3.Text import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.menagerie.ophelia.model.Recipe +import com.menagerie.ophelia.view.recipe.model.Recipe internal fun LazyListScope.StepsAndDetails( @@ -39,7 +39,7 @@ internal fun LazyListScope.StepsAndDetails( } - itemsIndexed(recipe.ingredients) { index, value -> + itemsIndexed(recipe.ingredients) { _, value -> IngredientItem(recipe, value) } diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipeListItem.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipeslist/RecipeListItem.kt similarity index 90% rename from composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipeListItem.kt rename to composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipeslist/RecipeListItem.kt index ee9df74..f35957f 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipeListItem.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipeslist/RecipeListItem.kt @@ -1,4 +1,4 @@ -package com.menagerie.ophelia.recipeslist +package com.menagerie.ophelia.view.recipe.recipeslist import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -22,7 +22,9 @@ import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import com.menagerie.ophelia.model.Recipe +import com.menagerie.ophelia.view.common.list.ListImage +import com.menagerie.ophelia.view.common.list.ListItemImageWrapper +import com.menagerie.ophelia.view.recipe.model.Recipe @Composable @@ -73,9 +75,9 @@ fun RecipeListItem( Spacer(modifier = Modifier.weight(1f)) } } - RecipeListItemImageWrapper(modifier = Modifier.align(Alignment.BottomEnd) + ListItemImageWrapper(modifier = Modifier.align(Alignment.BottomEnd) .fillMaxWidth(0.45f).aspectRatio(1f), child = { - RecipeImage( + ListImage( imageBitmap = recipe.image, modifier = Modifier ) }) diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipesList.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipeslist/RecipesList.kt similarity index 92% rename from composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipesList.kt rename to composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipeslist/RecipesList.kt index 6c7e1df..101e82d 100644 --- a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/recipeslist/RecipesList.kt +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/recipe/recipeslist/RecipesList.kt @@ -1,4 +1,4 @@ -package com.menagerie.ophelia.recipeslist +package com.menagerie.ophelia.view.recipe.recipeslist import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.ExperimentalSharedTransitionApi @@ -17,7 +17,8 @@ import androidx.compose.foundation.lazy.grid.LazyGridState import androidx.compose.runtime.Composable import androidx.compose.runtime.* import androidx.compose.ui.Modifier -import com.menagerie.ophelia.model.Recipe +import com.menagerie.ophelia.view.common.list.ListItemWrapper +import com.menagerie.ophelia.view.recipe.model.Recipe import com.menagerie.ophelia.ui.theme.sugar @Composable @@ -39,7 +40,7 @@ fun RecipesListScreen( } items(items.size) { item -> val recipe = items[item] - RecipeListItemWrapper( + ListItemWrapper( scrollDirection = listState.isScrollingUp(), child = { RecipeListItem( diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/model/Tag.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/model/Tag.kt new file mode 100644 index 0000000..9d5227e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/model/Tag.kt @@ -0,0 +1,15 @@ +package com.menagerie.ophelia.view.tags.model + +import androidx.compose.ui.graphics.Color +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.ExperimentalResourceApi + +data class Tag @OptIn(ExperimentalResourceApi::class) constructor( + val id: Int, + val title: String, + val description: String, + val image: DrawableResource, + val bgImage: DrawableResource? = null, + val bgImageLarge: DrawableResource? = null, + val bgColor: Color +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/model/defaultTags.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/model/defaultTags.kt new file mode 100644 index 0000000..c38d4f9 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/model/defaultTags.kt @@ -0,0 +1,71 @@ +package com.menagerie.ophelia.view.tags.model + +import com.menagerie.ophelia.ui.theme.sugar +import ophelia.composeapp.generated.resources.Res +import ophelia.composeapp.generated.resources.ophelia + +val tagList = listOf( + Tag( + id = 1, + title = "Alcohol", + description = "", + image = Res.drawable.ophelia, + bgColor = sugar + ), + Tag( + id = 2, + title = "Asexual", + description = "", + image = Res.drawable.ophelia, + bgColor = sugar + ), + Tag( + id = 3, + title = "BDSM", + description = "", + image = Res.drawable.ophelia, + bgColor = sugar + ), + Tag( + id = 4, + title = "Furry", + description = "", + image = Res.drawable.ophelia, + bgColor = sugar + ), + Tag( + id = 5, + title = "Monogamous", + description = "", + image = Res.drawable.ophelia, + bgColor = sugar + ), + Tag( + id = 6, + title = "Plural", + description = "", + image = Res.drawable.ophelia, + bgColor = sugar + ), + Tag( + id = 7, + title = "Therian", + description = "", + image = Res.drawable.ophelia, + bgColor = sugar + ), + Tag( + id = 8, + title = "Transgender", + description = "", + image = Res.drawable.ophelia, + bgColor = sugar + ), + Tag( + id = 9, + title = "Weed", + description = "", + image = Res.drawable.ophelia, + bgColor = sugar + ), +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/tagsdetails/TagsDetails.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/tagsdetails/TagsDetails.kt new file mode 100644 index 0000000..4a4a0c4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/tagsdetails/TagsDetails.kt @@ -0,0 +1,14 @@ +package com.menagerie.ophelia.view.tags.tagsdetails + +import androidx.compose.runtime.Composable +import com.menagerie.ophelia.sensor.SensorManager +import com.menagerie.ophelia.view.tags.model.Tag + +@Composable +fun TagsDetails( + sensorManager: SensorManager?, + tag: Tag, + isLarge: Boolean +) { + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/tagslist/TagListItem.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/tagslist/TagListItem.kt new file mode 100644 index 0000000..7148e9a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/tagslist/TagListItem.kt @@ -0,0 +1,89 @@ +package com.menagerie.ophelia.view.tags.tagslist + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.menagerie.ophelia.view.common.list.ListImage +import com.menagerie.ophelia.view.common.list.ListItemImageWrapper +import com.menagerie.ophelia.view.tags.model.Tag + +//The Actual Content of the Card +@Composable +fun TagListItem( + tag: Tag, + onClick: (tag: Tag) -> Unit, +) { + Box(modifier = Modifier) { + Box(modifier = Modifier.padding(top = 8.dp, start = 16.dp, end = 16.dp, bottom = 16.dp) + .fillMaxWidth().aspectRatio(1.5f) + .shadow( + elevation = 16.dp, + shape = RoundedCornerShape(35.dp), + clip = true, + ambientColor = Color(0xffCE5A01), + spotColor = Color(0xffCE5A01) + ) + .background(color = tag.bgColor, shape = RoundedCornerShape(35.dp)).fillMaxHeight().clickable { + onClick(tag) + }) { + Card( + backgroundColor = tag.bgColor, + shape = RoundedCornerShape(35.dp), + modifier = Modifier.clip(RoundedCornerShape(35.dp)) + ) { + Box( + modifier = Modifier.fillMaxWidth().aspectRatio(1.5f) + ) { + Row( + modifier = Modifier.fillMaxHeight().padding(16.dp).fillMaxWidth(0.55f), + verticalAlignment = Alignment.Bottom + ) { + Column(modifier = Modifier.align(Alignment.Bottom)) { + Text( + text = tag.title, + style = MaterialTheme.typography.headlineSmall, + modifier = Modifier + ) + + Text( + tag.description, + style = MaterialTheme.typography.displayMedium, + maxLines = 3, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.padding(top = 8.dp), + ) + } + Spacer(modifier = Modifier.weight(1f)) + } + } + ListItemImageWrapper(modifier = Modifier.align(Alignment.BottomEnd) + .fillMaxWidth(0.45f).aspectRatio(1f), child = { + ListImage( + imageBitmap = tag.image, modifier = Modifier + ) + }) + } + + } + } + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/tagslist/TagsList.kt b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/tagslist/TagsList.kt new file mode 100644 index 0000000..548196e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/menagerie/ophelia/view/tags/tagslist/TagsList.kt @@ -0,0 +1,81 @@ +package com.menagerie.ophelia.view.tags.tagslist + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyGridState +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.rememberLazyGridState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import com.menagerie.ophelia.view.common.list.ListItemWrapper +import com.menagerie.ophelia.view.tags.model.Tag +import com.menagerie.ophelia.ui.theme.sugar + +@Composable +fun TagsListScreen( + items: List, + onClick: (tag: Tag) -> Unit, + isLarge: Boolean, +) { + //wrap the whole screen in a box (or scaffold) + Box( + modifier = Modifier + .fillMaxSize() + .background(sugar) + ){ + val listState = rememberLazyGridState() //keep track of how the cells are arranged + LazyVerticalGrid( + state = listState, + columns = GridCells.Fixed(if (isLarge) 3 else 1) //TODO : replace check with enum values? + + ) { + if(isLarge.not()) + item { + Spacer(modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars)) + } //add a top spacer on mobile for the system bars + items(items.size) { item -> + val tag = items[item] + ListItemWrapper( + scrollDirection = listState.isScrollingUp(), + child = { + TagListItem( + tag = tag, + onClick = onClick + ) + } + ) + } //grid items list of tags + } // grid + } // Box +} // screen + + +//TODO : Move this to its own helper file +@Composable +private fun LazyGridState.isScrollingUp(): Boolean { + var previousIndex by remember(this) {mutableStateOf(firstVisibleItemIndex)} + var previousScrollOffset by remember(this) {mutableStateOf(firstVisibleItemScrollOffset)} + return remember(this) { + derivedStateOf { + if(previousIndex != firstVisibleItemIndex) { + previousIndex > firstVisibleItemIndex + } else { + previousScrollOffset >= firstVisibleItemScrollOffset + }.also { + previousIndex = firstVisibleItemIndex + previousScrollOffset = firstVisibleItemScrollOffset + } + } + }.value +} \ No newline at end of file diff --git a/composeApp/src/commonMain/sqldelight/com/menagerie/ophelia/User.sq b/composeApp/src/commonMain/sqldelight/com/menagerie/ophelia/User.sq new file mode 100644 index 0000000..059184e --- /dev/null +++ b/composeApp/src/commonMain/sqldelight/com/menagerie/ophelia/User.sq @@ -0,0 +1,20 @@ +CREATE TABLE User ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL +); + +insert: +INSERT OR REPLACE INTO User(id, name) +VALUES(?,?); + +getAll: +SELECT * FROM User; + +updateName: +UPDATE User +SET name = :name +WHERE id IS :id; + +delete: +DELETE FROM User +WHERE id IS :id; diff --git a/composeApp/src/desktopMain/kotlin/com/menagerie/ophelia/DatabaseDriverFactory.desktop.kt b/composeApp/src/desktopMain/kotlin/com/menagerie/ophelia/DatabaseDriverFactory.desktop.kt new file mode 100644 index 0000000..56cbb5c --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/com/menagerie/ophelia/DatabaseDriverFactory.desktop.kt @@ -0,0 +1,12 @@ +package com.menagerie.ophelia + +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver + +actual class DatabaseDriverFactory { + actual fun create(): SqlDriver { + val driver: SqlDriver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) + PolyculeDatabase.Schema.create(driver) + return driver + } +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/menagerie/ophelia/DesktopAppModule.kt b/composeApp/src/desktopMain/kotlin/com/menagerie/ophelia/DesktopAppModule.kt new file mode 100644 index 0000000..98e9a4c --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/com/menagerie/ophelia/DesktopAppModule.kt @@ -0,0 +1,13 @@ +package com.menagerie.ophelia + +import com.menagerie.ophelia.data.user.UserDataSource + +class DesktopAppModule : AppModule { + private val db by lazy { + PolyculeDatabase( + driver = DatabaseDriverFactory().create() + ) + } + + override fun provideUserDataSource() = UserDataSource(db) +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/com/menagerie/ophelia/main.kt b/composeApp/src/desktopMain/kotlin/com/menagerie/ophelia/main.kt index e64b158..caeeb43 100644 --- a/composeApp/src/desktopMain/kotlin/com/menagerie/ophelia/main.kt +++ b/composeApp/src/desktopMain/kotlin/com/menagerie/ophelia/main.kt @@ -3,8 +3,10 @@ package com.menagerie.ophelia import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application +import androidx.compose.ui.window.rememberWindowState import androidx.datastore.preferences.core.stringPreferencesKey import com.menagerie.ophelia.ui.theme.OpheliaTheme import kotlinx.coroutines.flow.map @@ -28,6 +30,7 @@ fun main() { Window( onCloseRequest = ::exitApplication, title = "Ophelia", + state = rememberWindowState(height = 900.dp, width = 1200.dp) ) { val currentThemeString by prefs diff --git a/composeApp/src/main/res/values/arrays.xml b/composeApp/src/main/res/values/arrays.xml new file mode 100644 index 0000000..141bfc0 --- /dev/null +++ b/composeApp/src/main/res/values/arrays.xml @@ -0,0 +1,17 @@ + + + + @array/com_google_android_gms_fonts_certs_dev + @array/com_google_android_gms_fonts_certs_prod + + + + MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs= + + + + + MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK + + + \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3752de5..048c0b4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,6 +19,7 @@ material3Android = "1.3.1" material3Desktop = "1.3.1" uiTextGoogleFonts = "1.7.5" core = "1.15.0" +sqldelight = "2.0.2" navigation-compose = "2.7.0-alpha07" datastore = "1.1.1" @@ -39,6 +40,9 @@ kotlinx-coroutines-swing = { group = "org.jetbrains.kotlinx", name = "kotlinx-co androidx-material3-android = { group = "androidx.compose.material3", name = "material3-android", version.ref = "material3Android" } 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" } +sqldelight-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } +sqldelight-jvm = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqldelight" } +sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" } 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" } @@ -48,4 +52,5 @@ androidApplication = { id = "com.android.application", version.ref = "agp" } androidLibrary = { id = "com.android.library", version.ref = "agp" } composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" } composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } -kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } \ No newline at end of file +kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +sqlDelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } \ No newline at end of file