Fixed sync/import updates not happening in the recipe list, removing sync temp files after parsing and removing unused tags

master
Wynd 2026-03-13 00:27:49 +02:00
parent d60cbe4678
commit 6dbd014519
8 changed files with 93 additions and 60 deletions

1
app/.gitignore vendored
View File

@ -1 +1,2 @@
/build /build
/release

View File

@ -13,8 +13,8 @@ android {
applicationId = "xyz.pixelatedw.recipe" applicationId = "xyz.pixelatedw.recipe"
minSdk = 24 minSdk = 24
targetSdk = 34 targetSdk = 34
versionCode = 1 versionCode = 2
versionName = "1.0" versionName = "1.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
@ -28,16 +28,24 @@ android {
) )
} }
} }
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11
} }
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = "11"
} }
buildFeatures { buildFeatures {
compose = true compose = true
} }
lint {
checkReleaseBuilds = false
abortOnError = false
}
} }
dependencies { dependencies {

View File

@ -11,10 +11,10 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.room.Room import androidx.room.Room
import xyz.pixelatedw.recipe.ui.theme.RecipeTheme
import xyz.pixelatedw.recipe.data.AppDatabase import xyz.pixelatedw.recipe.data.AppDatabase
import xyz.pixelatedw.recipe.data.RecipesView import xyz.pixelatedw.recipe.data.RecipesView
import xyz.pixelatedw.recipe.ui.components.MainScreen import xyz.pixelatedw.recipe.ui.components.MainScreen
import xyz.pixelatedw.recipe.ui.theme.RecipeTheme
import xyz.pixelatedw.recipe.utils.parseRecipes import xyz.pixelatedw.recipe.utils.parseRecipes
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@ -54,8 +54,7 @@ class MainActivity : ComponentActivity() {
result.data?.data?.let { uri -> result.data?.data?.let { uri ->
parseRecipes(this, uri) parseRecipes(this, uri)
} }
} } else {
else {
importError = "Import cancelled" importError = "Import cancelled"
} }

View File

@ -37,7 +37,6 @@ data class RecipeWithTags(
@Dao @Dao
interface RecipeWithTagsDao { interface RecipeWithTagsDao {
@Transaction
@Query("SELECT * FROM recipe") @Query("SELECT * FROM recipe")
fun getAll(): List<RecipeWithTags> fun getAll(): List<RecipeWithTags>
@ -47,6 +46,7 @@ interface RecipeWithTagsDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(recipe: RecipeTag) fun insert(recipe: RecipeTag)
@Transaction
@Query("DELETE FROM recipetag WHERE recipetag.title = :recipe") @Query("DELETE FROM recipetag WHERE recipetag.title = :recipe")
fun delete(recipe: String) fun delete(recipe: String)
} }

View File

@ -5,6 +5,8 @@ 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 androidx.room.Transaction
@Entity @Entity
data class Tag( data class Tag(
@ -14,6 +16,16 @@ data class Tag(
@Dao @Dao
interface TagDao { interface TagDao {
@Query("SELECT * FROM tag")
fun getAll(): List<Tag>
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(tag: Tag) fun insert(tag: Tag)
@Query("SELECT COUNT(*) FROM tag")
fun count(): Int
@Transaction
@Query("DELETE FROM tag WHERE tag.name = :name")
fun delete(name: String)
} }

View File

@ -49,7 +49,6 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
val search by view.search.collectAsState() val search by view.search.collectAsState()
val filters by view.tagFilters.collectAsState() val filters by view.tagFilters.collectAsState()
var activeRecipes by remember { mutableStateOf(recipes) }
var activeTags = 0 var activeTags = 0
val navController = rememberNavController() val navController = rememberNavController()
@ -80,8 +79,9 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
value = search.orEmpty(), value = search.orEmpty(),
onValueChange = { search -> onValueChange = { search ->
view.setSearch(search) view.setSearch(search)
activeRecipes = val newRecipes =
recipes.filter { filterRecipe(it, search, filters) } recipes.filter { filterRecipe(it, search, filters) }
view.setRecipes(newRecipes)
}, },
) )
// Tags / Delete // Tags / Delete
@ -141,10 +141,10 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
state = gridState, state = gridState,
) { ) {
items( items(
count = activeRecipes.size, count = recipes.size,
key = { activeRecipes[it].recipe.title }, key = { recipes[it].recipe.title },
itemContent = { entryId -> itemContent = { entryId ->
val entry = activeRecipes[entryId] val entry = recipes[entryId]
val isSelected = selectedEntries.contains(entry) val isSelected = selectedEntries.contains(entry)
RecipePreview(entry, isSelected, onClick = { RecipePreview(entry, isSelected, onClick = {
view.setActive(entry) view.setActive(entry)
@ -194,13 +194,14 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
openTagFilterDialog = false openTagFilterDialog = false
view.reloadTagFilterState() view.reloadTagFilterState()
activeTags = filters.count { f -> f.checked } activeTags = filters.count { f -> f.checked }
activeRecipes = recipes.filter { val newRecipes = recipes.filter {
filterRecipe( filterRecipe(
it, it,
search, search,
filters filters
) )
} }
ctx.recipeView.setRecipes(newRecipes)
}, },
view, view,
) )

View File

@ -42,6 +42,14 @@ fun parseRecipeFiles(ctx: MainActivity) {
reader.close() reader.close()
} }
// Clearing unused tags after an import/sync
val usedTags = ctx.recipeView.tagFilters.value.map { t -> t.tag }
for (tag in ctx.db.tagDao().getAll()) {
if (!usedTags.contains(tag.name)) {
ctx.db.tagDao().delete(tag.name)
}
}
} }
fun parseDir(ctx: Context, dir: DocumentFile, path: String) { fun parseDir(ctx: Context, dir: DocumentFile, path: String) {

View File

@ -59,11 +59,11 @@ fun sync(ctx: MainActivity, nav: NavHostController, setLoading: (Boolean) -> Uni
inputStream.read(buffer) inputStream.read(buffer)
val contentBufLen = ByteBuffer.wrap(buffer).getLong().toInt() val contentBufLen = ByteBuffer.wrap(buffer).getLong().toInt()
var parentDir = ctx.filesDir var parentDir = File(ctx.filesDir, ".sync")
if (filePath.isNotEmpty()) { if (filePath.isNotEmpty()) {
parentDir = File(ctx.filesDir, filePath) parentDir = File(ctx.filesDir, filePath)
parentDir.mkdirs()
} }
parentDir.mkdirs()
val newFile = File(parentDir, fileName) val newFile = File(parentDir, fileName)
@ -94,6 +94,10 @@ fun sync(ctx: MainActivity, nav: NavHostController, setLoading: (Boolean) -> Uni
parseDir(ctx, docDir, "") parseDir(ctx, docDir, "")
parseRecipeFiles(ctx) parseRecipeFiles(ctx)
val syncFolder = File(ctx.filesDir, ".sync")
if (syncFolder.exists()) {
syncFolder.deleteRecursively()
}
} catch (e: Exception) { } catch (e: Exception) {
ctx.importError = when (e) { ctx.importError = when (e) {
is SocketTimeoutException -> "Connection timed out" is SocketTimeoutException -> "Connection timed out"