Compare commits

...

2 Commits

6 changed files with 55 additions and 29 deletions

View File

@ -10,6 +10,7 @@ import androidx.room.Entity
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import androidx.room.Query
import java.io.File import java.io.File
@Entity @Entity
@ -18,7 +19,8 @@ data class Recipe(
val title: String, val title: String,
val preview: String?, val preview: String?,
val lastModified: Long, val lastModified: Long,
val content: String val content: String,
val hash: Int
) { ) {
fun previewImage(ctx: Context): Bitmap? { fun previewImage(ctx: Context): Bitmap? {
if (this.preview != null) { if (this.preview != null) {
@ -41,4 +43,7 @@ interface RecipeDao {
@Delete @Delete
fun delete(recipe: Recipe) fun delete(recipe: Recipe)
@Query("SELECT EXISTS(SELECT hash FROM recipe WHERE recipe.hash = :hash LIMIT 1)")
fun hasHash(hash: Int): Boolean
} }

View File

@ -36,13 +36,18 @@ class RecipesView : ViewModel() {
fun setRecipes(recipes: List<RecipeWithTags>) { fun setRecipes(recipes: List<RecipeWithTags>) {
_recipes.update { recipes.sortedBy { it.recipe.title } } _recipes.update { recipes.sortedBy { it.recipe.title } }
val filters = arrayListOf<TagFilter>() val filtersMap = mutableMapOf<String, Int>()
_recipes.value.stream() _recipes.value.stream()
.filter { it.tags.isNotEmpty() } .filter { it.tags.isNotEmpty() }
.forEach { .forEach {
it.tags.forEach {tag -> filters.add(TagFilter(tag.name))} it.tags.forEach { tag ->
val count = filtersMap[tag.name] ?: 0
filtersMap[tag.name] = count + 1;
} }
}
val filters = filtersMap.map { it -> TagFilter(it.key, count = it.value) }.toList()
_tagFilters.update { filters.distinct() } _tagFilters.update { filters.distinct() }
} }

View File

@ -1,4 +1,4 @@
package xyz.pixelatedw.recipe.data package xyz.pixelatedw.recipe.data
data class TagFilter(val tag: String, var checked: Boolean = false) { data class TagFilter(val tag: String, var checked: Boolean = false, var count: Int = 0) {
} }

View File

@ -33,11 +33,11 @@ fun RecipePreview(entry: RecipeWithTags, previewUri: Bitmap?, onClick: () -> Uni
bitmap = previewUri.asImageBitmap(), bitmap = previewUri.asImageBitmap(),
contentDescription = "Recipe image", contentDescription = "Recipe image",
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
modifier = Modifier.size(256.dp).padding(top = 16.dp, bottom = 16.dp), modifier = Modifier.size(256.dp).padding(top = 16.dp, bottom = 8.dp),
) )
} }
} }
Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth()) { Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp)) {
Text( Text(
text = entry.recipe.title, text = entry.recipe.title,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),

View File

@ -1,22 +1,15 @@
package xyz.pixelatedw.recipe.ui.components package xyz.pixelatedw.recipe.ui.components
import androidx.collection.arraySetOf
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -25,17 +18,14 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.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.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.em import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import xyz.pixelatedw.recipe.data.RecipesView import xyz.pixelatedw.recipe.data.RecipesView
import xyz.pixelatedw.recipe.data.TagFilter
@Composable @Composable
fun TagFilterDialog(onAccept: () -> Unit, view: RecipesView) { fun TagFilterDialog(onAccept: () -> Unit, view: RecipesView) {
@ -54,24 +44,45 @@ fun TagFilterDialog(onAccept: ( ) -> Unit, view: RecipesView) {
modifier = Modifier modifier = Modifier
.fillMaxHeight(0.9f) .fillMaxHeight(0.9f)
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp) .padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.wrapContentSize(Alignment.TopStart)) { .wrapContentSize(Alignment.TopStart)
) {
items(filters.value) { tag -> items(filters.value) { tag ->
// TODO This doesn't really feel right lmao, but for now it works // TODO This doesn't really feel right lmao, but for now it works
val filterChecked = remember { mutableStateOf(tag.checked) } val filterChecked = remember { mutableStateOf(tag.checked) }
Row(verticalAlignment = Alignment.CenterVertically) { Row(
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(checked = filterChecked.value, onCheckedChange = { Checkbox(checked = filterChecked.value, onCheckedChange = {
filterChecked.value = !tag.checked filterChecked.value = !tag.checked
view.setTagFilterState(tag.tag, !tag.checked) view.setTagFilterState(tag.tag, !tag.checked)
}) })
Text(tag.tag) TextButton(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(
contentColor = Color.White,
containerColor = Color.Transparent
),
onClick = {
filterChecked.value = !tag.checked
view.setTagFilterState(tag.tag, !tag.checked)
}
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = tag.tag + " (" + tag.count + ")",
textAlign = TextAlign.Left
)
}
} }
} }
} }
Row(modifier = Modifier Row(
modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(end = 16.dp), horizontalArrangement = Arrangement.End) { .padding(end = 16.dp), horizontalArrangement = Arrangement.End
) {
TextButton(onClick = onAccept) { TextButton(onClick = onAccept) {
Text("Done") Text("Done")
} }

View File

@ -65,6 +65,12 @@ private fun parseRecipe(ctx: Context, db: AppDatabase, file: DocumentFile) {
val inputStream = ctx.contentResolver.openInputStream(file.uri) val inputStream = ctx.contentResolver.openInputStream(file.uri)
val reader = BufferedReader(InputStreamReader(inputStream)) val reader = BufferedReader(InputStreamReader(inputStream))
val text = reader.readText() val text = reader.readText()
val hash = text.hashCode() // Probably not the best way but its stable and works
if (db.recipeDao().hasHash(hash)) {
return
}
val lines = text.lines() val lines = text.lines()
val sb = StringBuilder() val sb = StringBuilder()
@ -133,12 +139,11 @@ private fun parseRecipe(ctx: Context, db: AppDatabase, file: DocumentFile) {
title = recipeTitle, title = recipeTitle,
preview = recipePreview, preview = recipePreview,
lastModified = lastModified, lastModified = lastModified,
content = content content = content,
hash = hash
) )
db.recipeDao().insert(recipe) db.recipeDao().insert(recipe)
println(recipe)
reader.close() reader.close()
} }