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

3
app/.gitignore vendored
View File

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

View File

@ -1,55 +1,63 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.devtools.ksp)
}
android {
namespace = "xyz.pixelatedw.recipe"
compileSdk = 35
namespace = "xyz.pixelatedw.recipe"
compileSdk = 35
defaultConfig {
applicationId = "xyz.pixelatedw.recipe"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
defaultConfig {
applicationId = "xyz.pixelatedw.recipe"
minSdk = 24
targetSdk = 34
versionCode = 2
versionName = "1.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
compose = true
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
compose = true
}
lint {
checkReleaseBuilds = false
abortOnError = false
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.androidx.documentfile)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.androidx.documentfile)
implementation(libs.androidx.navigation.fragment)
implementation(libs.androidx.navigation.navigation.ui)
implementation(libs.androidx.navigation.navigation.compose)
@ -62,11 +70,11 @@ dependencies {
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.ui.test.junit4)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
}

View File

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

View File

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

View File

@ -5,6 +5,8 @@ import androidx.room.Entity
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.PrimaryKey
import androidx.room.Query
import androidx.room.Transaction
@Entity
data class Tag(
@ -14,6 +16,16 @@ data class Tag(
@Dao
interface TagDao {
@Query("SELECT * FROM tag")
fun getAll(): List<Tag>
@Insert(onConflict = OnConflictStrategy.REPLACE)
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 filters by view.tagFilters.collectAsState()
var activeRecipes by remember { mutableStateOf(recipes) }
var activeTags = 0
val navController = rememberNavController()
@ -80,8 +79,9 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
value = search.orEmpty(),
onValueChange = { search ->
view.setSearch(search)
activeRecipes =
val newRecipes =
recipes.filter { filterRecipe(it, search, filters) }
view.setRecipes(newRecipes)
},
)
// Tags / Delete
@ -141,10 +141,10 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
state = gridState,
) {
items(
count = activeRecipes.size,
key = { activeRecipes[it].recipe.title },
count = recipes.size,
key = { recipes[it].recipe.title },
itemContent = { entryId ->
val entry = activeRecipes[entryId]
val entry = recipes[entryId]
val isSelected = selectedEntries.contains(entry)
RecipePreview(entry, isSelected, onClick = {
view.setActive(entry)
@ -194,13 +194,14 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) {
openTagFilterDialog = false
view.reloadTagFilterState()
activeTags = filters.count { f -> f.checked }
activeRecipes = recipes.filter {
val newRecipes = recipes.filter {
filterRecipe(
it,
search,
filters
)
}
ctx.recipeView.setRecipes(newRecipes)
},
view,
)

View File

@ -42,6 +42,14 @@ fun parseRecipeFiles(ctx: MainActivity) {
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) {

View File

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