Compare commits

...

2 Commits

4 changed files with 109 additions and 39 deletions

View File

@ -11,6 +11,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
@ -20,6 +21,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.vectorResource import androidx.compose.ui.res.vectorResource
@ -41,6 +43,9 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
val navController = rememberNavController() val navController = rememberNavController()
val openTagFilterDialog = remember { mutableStateOf(false) }
val selectedEntries = remember { mutableStateOf(listOf<RecipeWithTags>()) }
val isInSearch = isInSearch@{ entry: RecipeWithTags -> val isInSearch = isInSearch@{ entry: RecipeWithTags ->
val isSearchEmpty = search.value == null || search.value!!.isEmpty() val isSearchEmpty = search.value == null || search.value!!.isEmpty()
val hasTitle = entry.recipe.title.contains(search.value.orEmpty(), ignoreCase = true) val hasTitle = entry.recipe.title.contains(search.value.orEmpty(), ignoreCase = true)
@ -52,7 +57,7 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
return@isInSearch false return@isInSearch false
} }
val totalFilters = filters.value.stream().filter{ f -> f.checked }.count() val totalFilters = filters.value.stream().filter { f -> f.checked }.count()
var checkedFilters = 0 var checkedFilters = 0
for (filter in filters.value) { for (filter in filters.value) {
@ -61,10 +66,9 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
.filter { tag -> tag.name.contains(filter.tag, ignoreCase = true) } .filter { tag -> tag.name.contains(filter.tag, ignoreCase = true) }
.count() > 0 .count() > 0
if(hasTagFilters) { if (hasTagFilters) {
checkedFilters += 1 checkedFilters += 1
} } else {
else {
checkedFilters -= 1 checkedFilters -= 1
} }
} }
@ -78,8 +82,6 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
true true
} }
val openTagFilterDialog = remember { mutableStateOf(false) }
NavHost( NavHost(
navController = navController, navController = navController,
startDestination = "list" startDestination = "list"
@ -108,24 +110,55 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
contentDescription = "Toggle tags filtering menu" contentDescription = "Toggle tags filtering menu"
) )
} }
// Load // Load / Delete
IconButton( if (selectedEntries.value.isNotEmpty()) {
modifier = Modifier.weight(0.15f), IconButton(
onClick = { ctx.sourceChooser.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)) }, modifier = Modifier.weight(0.15f),
) { onClick = {
Icon( // TODO I feel like this could be done in batch or something...but I truly don't care atm
imageVector = Icons.Default.Add, for (entry in selectedEntries.value) {
contentDescription = "Load recipes from filesystem" view.removeRecipe(entry)
) ctx.db.recipeDao().delete(entry.recipe)
ctx.db.recipeWithTagsDao().delete(entry.recipe.title)
}
},
) {
Icon(
imageVector = Icons.Default.Delete,
tint = Color(0xFFFF0000),
contentDescription = "Load recipes from filesystem"
)
}
}
else {
IconButton(
modifier = Modifier.weight(0.15f),
onClick = { ctx.sourceChooser.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)) },
) {
Icon(
imageVector = Icons.Default.Add,
contentDescription = "Load recipes from filesystem"
)
}
} }
} }
LazyColumn { LazyColumn {
items(recipes.value) { entry -> items(recipes.value) { entry ->
if (isInSearch(entry)) { if (isInSearch(entry)) {
val previewUri = entry.recipe.previewImage(LocalContext.current) val previewUri = entry.recipe.previewImage(LocalContext.current)
RecipePreview(entry, previewUri, onClick = { val isSelected = selectedEntries.value.contains(entry)
RecipePreview(entry, previewUri, isSelected, onClick = {
view.setActive(entry) view.setActive(entry)
navController.navigate("info") navController.navigate("info")
}, onSelected = { flag ->
selectedEntries.value =
selectedEntries.value.toMutableList().apply {
if (flag) {
add(entry)
} else {
remove(entry)
}
}.toList()
}) })
} }
} }

View File

@ -1,5 +1,6 @@
package xyz.pixelatedw.recipe.ui.components package xyz.pixelatedw.recipe.ui.components
import android.text.format.DateFormat
import android.view.WindowManager import android.view.WindowManager
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -36,6 +37,8 @@ import xyz.pixelatedw.recipe.MainActivity
import xyz.pixelatedw.recipe.data.RecipeWithTags import xyz.pixelatedw.recipe.data.RecipeWithTags
import xyz.pixelatedw.recipe.data.RecipesView import xyz.pixelatedw.recipe.data.RecipesView
import xyz.pixelatedw.recipe.utils.parseMarkdown import xyz.pixelatedw.recipe.utils.parseMarkdown
import java.util.Calendar
import java.util.Locale
@Composable @Composable
fun RecipeInfo( fun RecipeInfo(
@ -49,6 +52,11 @@ fun RecipeInfo(
val openDeletionDialog = remember { mutableStateOf(false) } val openDeletionDialog = remember { mutableStateOf(false) }
val picsCounts = remember { active.recipe.pics.size }; val picsCounts = remember { active.recipe.pics.size };
val timestamp = view.activeRecipe.collectAsState().value?.recipe?.lastModified ?: 0
val calendar = Calendar.getInstance(Locale.ENGLISH)
calendar.timeInMillis = timestamp
val lastModified = DateFormat.format("yyyy-MM-dd",calendar).toString()
Column( Column(
modifier = Modifier modifier = Modifier
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
@ -113,29 +121,20 @@ fun RecipeInfo(
} }
) )
} }
Row( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxHeight()
.padding(end = 16.dp), .padding(start = 16.dp)
horizontalArrangement = Arrangement.End
) { ) {
Box( Text(
modifier = Modifier text = "Last Modified",
.fillMaxHeight() style = MaterialTheme.typography.bodySmall,
.padding(start = 16.dp) )
) { Text(
Button( text = lastModified,
onClick = { modifier = Modifier.padding(start = 2.dp, top = 24.dp),
openDeletionDialog.value = true style = MaterialTheme.typography.bodyMedium,
}, )
colors = ButtonDefaults.buttonColors(containerColor = Color.Red)
) {
Text(
text = "Delete",
style = MaterialTheme.typography.bodyMedium
)
}
}
} }
} }

View File

@ -2,7 +2,9 @@ package xyz.pixelatedw.recipe.ui.components
import android.graphics.Bitmap import android.graphics.Bitmap
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -12,7 +14,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
@ -28,10 +33,14 @@ import xyz.pixelatedw.recipe.data.Recipe
import xyz.pixelatedw.recipe.data.RecipeWithTags import xyz.pixelatedw.recipe.data.RecipeWithTags
@Composable @Composable
fun RecipePreview(entry: RecipeWithTags, previewUri: Bitmap?, onClick: () -> Unit) { fun RecipePreview(entry: RecipeWithTags, previewUri: Bitmap?, isSelected: Boolean, onClick: () -> Unit, onSelected: (Boolean) -> Unit) {
Column(modifier = Modifier Column(modifier = Modifier
.background(color = if(isSelected) Color(0x11FF0000) else Color(0x00FFFFFF) )
.padding(8.dp) .padding(8.dp)
.clickable(onClick = onClick)) { .combinedClickable(
onLongClick = { onSelected(!isSelected) },
onClick = onClick
)) {
Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth().height(256.dp)) { Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth().height(256.dp)) {
if (previewUri != null) { if (previewUri != null) {
Image( Image(

29
example.md 100644
View File

@ -0,0 +1,29 @@
+++
title = "Example Recipe Name" # mandatory
tags = ["test", "recipe"] # optional
pics = ["pics/test.jpg", "pics/test2.jpg"] # optional, only reads jpgs
# Everything below here is just plain markdown,
# use whatever format or layout you feel fits your needs best
+++
- **Portions:**
- **Prep Time:**
- **Cook Time:**
## Ingredients
- Ingredient 1
- Ingredient 2
- Ingredient 3
## Steps
1. Step 1
2. Step 2
3. Step 3