diff --git a/app/src/main/java/com/shaarit/data/local/dao/LinkDao.kt b/app/src/main/java/com/shaarit/data/local/dao/LinkDao.kt index 07ad840..c4a04c7 100644 --- a/app/src/main/java/com/shaarit/data/local/dao/LinkDao.kt +++ b/app/src/main/java/com/shaarit/data/local/dao/LinkDao.kt @@ -67,6 +67,9 @@ interface LinkDao { ORDER BY is_pinned DESC, created_at DESC """) fun getLinksByMultipleTags(tag1: String, tag2: String): PagingSource + + @RawQuery(observedEntities = [LinkEntity::class]) + fun getLinksByTags(query: SupportSQLiteQuery): PagingSource // ====== Filtres temporels ====== diff --git a/app/src/main/java/com/shaarit/data/repository/LinkRepositoryImpl.kt b/app/src/main/java/com/shaarit/data/repository/LinkRepositoryImpl.kt index 57ed769..5d327ee 100644 --- a/app/src/main/java/com/shaarit/data/repository/LinkRepositoryImpl.kt +++ b/app/src/main/java/com/shaarit/data/repository/LinkRepositoryImpl.kt @@ -25,6 +25,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map import retrofit2.HttpException +import androidx.sqlite.db.SimpleSQLiteQuery import javax.inject.Inject import javax.inject.Singleton @@ -56,12 +57,22 @@ constructor( when { !searchTerm.isNullOrBlank() -> linkDao.searchLinks(searchTerm) !searchTags.isNullOrBlank() -> { - val tags = searchTags.split(" ").filter { it.isNotBlank() } - if (tags.size == 1) { - linkDao.getLinksByTag(tags.first()) + val tags = + searchTags + .trim() + .split(Regex("\\s+")) + .map { it.trim() } + .filter { it.isNotBlank() } + .distinct() + + if (tags.isEmpty()) { + linkDao.getAllLinksPaged() } else { - // Pour plusieurs tags, on prend les liens qui ont au moins un des tags - linkDao.getLinksByTag(tags.first()) + val whereClause = tags.joinToString(" AND ") { "tags LIKE ?" } + val sql = + "SELECT * FROM links WHERE $whereClause ORDER BY is_pinned DESC, created_at DESC" + val args: Array = tags.map { "%\"$it\"%" }.toTypedArray() + linkDao.getLinksByTags(SimpleSQLiteQuery(sql, args)) } } else -> linkDao.getAllLinksPaged() diff --git a/app/src/main/java/com/shaarit/presentation/feed/FeedScreen.kt b/app/src/main/java/com/shaarit/presentation/feed/FeedScreen.kt index a0ee483..fb3d146 100644 --- a/app/src/main/java/com/shaarit/presentation/feed/FeedScreen.kt +++ b/app/src/main/java/com/shaarit/presentation/feed/FeedScreen.kt @@ -8,6 +8,8 @@ import androidx.compose.foundation.layout.* 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.material.ExperimentalMaterialApi import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* @@ -15,6 +17,8 @@ import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material3.* +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -31,7 +35,7 @@ import com.shaarit.ui.components.PremiumTextField import com.shaarit.ui.components.TagChip import com.shaarit.ui.theme.* -@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class, ExperimentalLayoutApi::class) @Composable fun FeedScreen( onNavigateToAdd: () -> Unit, @@ -241,27 +245,52 @@ fun FeedScreen( modifier = Modifier .fillMaxWidth() .background(DarkNavy) - .padding(horizontal = 16.dp, vertical = 12.dp), - verticalAlignment = Alignment.CenterVertically, + .padding(horizontal = 16.dp, vertical = 8.dp), + verticalAlignment = Alignment.Top, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { Text( "Filtering by:", style = MaterialTheme.typography.bodyMedium, - color = TextSecondary + color = TextSecondary, + modifier = Modifier.padding(top = 6.dp) ) - TagChip( - tag = searchTags!!, - isSelected = true, - onClick = { viewModel.clearTagFilter() } - ) - Spacer(modifier = Modifier.weight(1f)) - IconButton(onClick = { viewModel.clearTagFilter() }) { + + val selectedTags = + remember(searchTags) { + searchTags + .orEmpty() + .trim() + .split(Regex("\\s+")) + .map { it.trim() } + .filter { it.isNotBlank() } + .distinct() + } + + FlowRow( + modifier = Modifier.weight(1f), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(6.dp), + maxItemsInEachRow = 3 + ) { + selectedTags.forEach { tag -> + TagChip( + tag = tag, + isSelected = true, + onClick = { viewModel.onTagClicked(tag) } + ) + } + } + + IconButton( + onClick = { viewModel.clearTagFilter() }, + modifier = Modifier.size(32.dp) + ) { Icon( Icons.Default.Close, contentDescription = "Clear filter", tint = TextMuted, - modifier = Modifier.size(20.dp) + modifier = Modifier.size(18.dp) ) } }